[gemrb] 12/13: Imported Upstream version 0.6.4
Beren Minor
berenm-guest at alioth.debian.org
Fri Aug 16 18:30:17 UTC 2013
This is an automated email from the git hooks/post-receive script.
berenm-guest pushed a commit to branch master
in repository gemrb.
commit 3b87099018ed7046bc757af7bcc4be841167b53c
Author: Beren Minor <beren.minor+debian at gmail.com>
Date: Fri Aug 16 20:32:23 2013 +0200
Imported Upstream version 0.6.4
---
.gitignore | 31 +
.mailmap | 2 +
.pydevproject | 7 +
AUTHORS | 17 +
CMakeLists.txt | 350 +
COPYING | 340 +
ChangeLog | 112 +
Doxyfile | 1227 +++
INSTALL | 60 +
Makefile.am | 39 +
NEWS | 392 +
README | 77 +
TODO | 98 +
acinclude.m4 | 581 ++
admin/add_missing_gui_docs.sh | 67 +
admin/announcement.template | 31 +
admin/check_copyright.pl | 167 +
admin/check_gui_doc.pl | 103 +
admin/generate_formation_files.sh | 8 +
admin/guidoc_wikifier.sh | 100 +
admin/make_formation.py | 367 +
admin/restart_news.sh | 44 +
artwork/gemrb-logo.png | Bin 0 -> 1675 bytes
artwork/logo04-8cog.svg | 43 +
artwork/logo04-gem_only.svg | 16 +
artwork/logo04-outer_cog.svg | 62 +
artwork/logo04-rb_only.svg | 22 +
autogen.sh | 195 +
cmake_config.h.in | 16 +
cmake_uninstall.cmake.in | 21 +
configure.in | 295 +
gemrb.6.in | 393 +
gemrb.desktop | 10 +
gemrb.lsm | 14 +
gemrb.spec.in | 72 +
gemrb/CMakeLists.txt | 41 +
gemrb/GUIScripts/Actor.py | 226 +
gemrb/GUIScripts/AutodetectCommon.py | 51 +
gemrb/GUIScripts/BGCommon.py | 40 +
gemrb/GUIScripts/CMakeLists.txt | 11 +
gemrb/GUIScripts/CommonTables.py | 47 +
gemrb/GUIScripts/CommonWindow.py | 328 +
gemrb/GUIScripts/DualClass.py | 597 ++
gemrb/GUIScripts/GUIClasses.py | 199 +
gemrb/GUIScripts/GUICommon.py | 911 ++
gemrb/GUIScripts/GUIDefines.py | 292 +
gemrb/GUIScripts/GUISTORE.py | 1526 +++
gemrb/GUIScripts/InventoryCommon.py | 910 ++
gemrb/GUIScripts/LUCommon.py | 386 +
gemrb/GUIScripts/LUProfsSelection.py | 477 +
gemrb/GUIScripts/LUSkillsSelection.py | 408 +
gemrb/GUIScripts/LUSpellSelection.py | 532 +
gemrb/GUIScripts/LevelUp.py | 749 ++
gemrb/GUIScripts/Makefile.am | 7 +
gemrb/GUIScripts/MetaClasses.py | 79 +
gemrb/GUIScripts/TextScreen.py | 156 +
gemrb/GUIScripts/bg1/Autodetect.py | 34 +
gemrb/GUIScripts/bg1/CMakeLists.txt | 3 +
gemrb/GUIScripts/bg1/CharGen.py | 66 +
gemrb/GUIScripts/bg1/CharGenCommon.py | 283 +
gemrb/GUIScripts/bg1/CharGenGui.py | 393 +
gemrb/GUIScripts/bg1/ExportFile.py | 87 +
gemrb/GUIScripts/bg1/GUICG1.py | 86 +
gemrb/GUIScripts/bg1/GUICG10.py | 100 +
gemrb/GUIScripts/bg1/GUICG12.py | 240 +
gemrb/GUIScripts/bg1/GUICG13.py | 207 +
gemrb/GUIScripts/bg1/GUICG15.py | 113 +
gemrb/GUIScripts/bg1/GUICG19.py | 103 +
gemrb/GUIScripts/bg1/GUICG2.py | 147 +
gemrb/GUIScripts/bg1/GUICG22.py | 137 +
gemrb/GUIScripts/bg1/GUICG3.py | 103 +
gemrb/GUIScripts/bg1/GUICG4.py | 301 +
gemrb/GUIScripts/bg1/GUICG5.py | 75 +
gemrb/GUIScripts/bg1/GUICG6.py | 78 +
gemrb/GUIScripts/bg1/GUICG7.py | 53 +
gemrb/GUIScripts/bg1/GUICG8.py | 84 +
gemrb/GUIScripts/bg1/GUICG9.py | 69 +
gemrb/GUIScripts/bg1/GUICommonWindows.py | 866 ++
gemrb/GUIScripts/bg1/GUIINV.py | 316 +
gemrb/GUIScripts/bg1/GUIJRNL.py | 153 +
gemrb/GUIScripts/bg1/GUILOAD.py | 176 +
gemrb/GUIScripts/bg1/GUIMA.py | 295 +
gemrb/GUIScripts/bg1/GUIMG.py | 354 +
gemrb/GUIScripts/bg1/GUIMOVIE.py | 90 +
gemrb/GUIScripts/bg1/GUIMP.py | 132 +
gemrb/GUIScripts/bg1/GUIOPT.py | 733 ++
gemrb/GUIScripts/bg1/GUIPR.py | 359 +
gemrb/GUIScripts/bg1/GUIREC.py | 1449 +++
gemrb/GUIScripts/bg1/GUISAVE.py | 279 +
gemrb/GUIScripts/bg1/GUISONGS.py | 69 +
gemrb/GUIScripts/bg1/GUIWORLD.py | 336 +
gemrb/GUIScripts/bg1/ImportFile.py | 75 +
gemrb/GUIScripts/bg1/LoadScreen.py | 58 +
gemrb/GUIScripts/bg1/Makefile.am | 4 +
gemrb/GUIScripts/bg1/MessageWindow.py | 149 +
gemrb/GUIScripts/bg1/QuitGame.py | 29 +
gemrb/GUIScripts/bg1/Start.py | 186 +
gemrb/GUIScripts/bg2/Autodetect.py | 45 +
gemrb/GUIScripts/bg2/CMakeLists.txt | 3 +
gemrb/GUIScripts/bg2/CharGen.py | 37 +
gemrb/GUIScripts/bg2/CharGen2.py | 31 +
gemrb/GUIScripts/bg2/CharGen3.py | 38 +
gemrb/GUIScripts/bg2/CharGen4.py | 32 +
gemrb/GUIScripts/bg2/CharGen5.py | 40 +
gemrb/GUIScripts/bg2/CharGen6.py | 98 +
gemrb/GUIScripts/bg2/CharGen7.py | 24 +
gemrb/GUIScripts/bg2/CharGen8.py | 25 +
gemrb/GUIScripts/bg2/CharGen9.py | 25 +
gemrb/GUIScripts/bg2/CharGenCommon.py | 399 +
gemrb/GUIScripts/bg2/CharGenEnd.py | 215 +
gemrb/GUIScripts/bg2/ExportFile.py | 72 +
gemrb/GUIScripts/bg2/GUICG1.py | 106 +
gemrb/GUIScripts/bg2/GUICG10.py | 116 +
gemrb/GUIScripts/bg2/GUICG12.py | 242 +
gemrb/GUIScripts/bg2/GUICG13.py | 208 +
gemrb/GUIScripts/bg2/GUICG15.py | 122 +
gemrb/GUIScripts/bg2/GUICG19.py | 112 +
gemrb/GUIScripts/bg2/GUICG2.py | 170 +
gemrb/GUIScripts/bg2/GUICG22.py | 249 +
gemrb/GUIScripts/bg2/GUICG23.py | 92 +
gemrb/GUIScripts/bg2/GUICG24.py | 65 +
gemrb/GUIScripts/bg2/GUICG3.py | 122 +
gemrb/GUIScripts/bg2/GUICG4.py | 355 +
gemrb/GUIScripts/bg2/GUICG5.py | 72 +
gemrb/GUIScripts/bg2/GUICG6.py | 87 +
gemrb/GUIScripts/bg2/GUICG7.py | 65 +
gemrb/GUIScripts/bg2/GUICG8.py | 93 +
gemrb/GUIScripts/bg2/GUICG9.py | 76 +
gemrb/GUIScripts/bg2/GUICommonWindows.py | 903 ++
gemrb/GUIScripts/bg2/GUIINV.py | 318 +
gemrb/GUIScripts/bg2/GUIJRNL.py | 222 +
gemrb/GUIScripts/bg2/GUILOAD.py | 173 +
gemrb/GUIScripts/bg2/GUIMA.py | 434 +
gemrb/GUIScripts/bg2/GUIMG.py | 737 ++
gemrb/GUIScripts/bg2/GUIMOVIE.py | 89 +
gemrb/GUIScripts/bg2/GUIOPT.py | 737 ++
gemrb/GUIScripts/bg2/GUIOPT10.py | 188 +
gemrb/GUIScripts/bg2/GUIOPT12.py | 127 +
gemrb/GUIScripts/bg2/GUIOPT6.py | 149 +
gemrb/GUIScripts/bg2/GUIOPT7.py | 125 +
gemrb/GUIScripts/bg2/GUIOPT8.py | 176 +
gemrb/GUIScripts/bg2/GUIOPT9.py | 176 +
gemrb/GUIScripts/bg2/GUIPR.py | 356 +
gemrb/GUIScripts/bg2/GUIREC.py | 1567 +++
gemrb/GUIScripts/bg2/GUISAVE.py | 281 +
gemrb/GUIScripts/bg2/GUISONGS.py | 69 +
gemrb/GUIScripts/bg2/GUIWORLD.py | 335 +
gemrb/GUIScripts/bg2/ImportFile.py | 76 +
gemrb/GUIScripts/bg2/ImportGame.py | 68 +
gemrb/GUIScripts/bg2/LUHLASelection.py | 444 +
gemrb/GUIScripts/bg2/LoadScreen.py | 66 +
gemrb/GUIScripts/bg2/Makefile.am | 4 +
gemrb/GUIScripts/bg2/MessageWindow.py | 239 +
gemrb/GUIScripts/bg2/QuitGame.py | 29 +
gemrb/GUIScripts/bg2/Start.py | 95 +
gemrb/GUIScripts/bg2/Start2.py | 331 +
gemrb/GUIScripts/bg2/StartOpt.py | 70 +
gemrb/GUIScripts/ie_action.py | 53 +
gemrb/GUIScripts/ie_modal.py | 24 +
gemrb/GUIScripts/ie_restype.py | 67 +
gemrb/GUIScripts/ie_slots.py | 51 +
gemrb/GUIScripts/ie_spells.py | 17 +
gemrb/GUIScripts/ie_stats.py | 368 +
gemrb/GUIScripts/include.py | 2 +
gemrb/GUIScripts/iwd/Autodetect.py | 45 +
gemrb/GUIScripts/iwd/CMakeLists.txt | 3 +
gemrb/GUIScripts/iwd/CharGen.py | 2761 +++++
gemrb/GUIScripts/iwd/GUICommonWindows.py | 843 ++
gemrb/GUIScripts/iwd/GUIINV.py | 303 +
gemrb/GUIScripts/iwd/GUIJRNL.py | 149 +
gemrb/GUIScripts/iwd/GUILOAD.py | 177 +
gemrb/GUIScripts/iwd/GUIMA.py | 295 +
gemrb/GUIScripts/iwd/GUIMG.py | 356 +
gemrb/GUIScripts/iwd/GUIMOVIE.py | 85 +
gemrb/GUIScripts/iwd/GUIOPT.py | 724 ++
gemrb/GUIScripts/iwd/GUIPR.py | 349 +
gemrb/GUIScripts/iwd/GUIREC.py | 1468 +++
gemrb/GUIScripts/iwd/GUISAVE.py | 279 +
gemrb/GUIScripts/iwd/GUIWORLD.py | 312 +
gemrb/GUIScripts/iwd/LoadScreen.py | 73 +
gemrb/GUIScripts/iwd/Makefile.am | 4 +
gemrb/GUIScripts/iwd/MessageWindow.py | 137 +
gemrb/GUIScripts/iwd/PartyFormation.py | 202 +
gemrb/GUIScripts/iwd/Portrait.py | 95 +
gemrb/GUIScripts/iwd/QuitGame.py | 29 +
gemrb/GUIScripts/iwd/Start.py | 323 +
gemrb/GUIScripts/iwd2/Abilities.py | 246 +
gemrb/GUIScripts/iwd2/Alignment.py | 88 +
gemrb/GUIScripts/iwd2/Appearance.py | 271 +
gemrb/GUIScripts/iwd2/AutoPause.py | 206 +
gemrb/GUIScripts/iwd2/Autodetect.py | 41 +
gemrb/GUIScripts/iwd2/CMakeLists.txt | 3 +
gemrb/GUIScripts/iwd2/CSound.py | 78 +
gemrb/GUIScripts/iwd2/CharGen.py | 29 +
gemrb/GUIScripts/iwd2/CharGen2.py | 24 +
gemrb/GUIScripts/iwd2/CharGen3.py | 31 +
gemrb/GUIScripts/iwd2/CharGen4.py | 47 +
gemrb/GUIScripts/iwd2/CharGen5.py | 24 +
gemrb/GUIScripts/iwd2/CharGen6.py | 34 +
gemrb/GUIScripts/iwd2/CharGen7.py | 25 +
gemrb/GUIScripts/iwd2/CharGen8.py | 24 +
gemrb/GUIScripts/iwd2/CharGen9.py | 218 +
gemrb/GUIScripts/iwd2/CharOverview.py | 304 +
gemrb/GUIScripts/iwd2/CharSound.py | 139 +
gemrb/GUIScripts/iwd2/Class.py | 192 +
gemrb/GUIScripts/iwd2/Enemy.py | 110 +
gemrb/GUIScripts/iwd2/Feats.py | 390 +
gemrb/GUIScripts/iwd2/Feedback.py | 184 +
gemrb/GUIScripts/iwd2/GUICommonWindows.py | 769 ++
gemrb/GUIScripts/iwd2/GUIINV.py | 293 +
gemrb/GUIScripts/iwd2/GUIJRNL.py | 123 +
gemrb/GUIScripts/iwd2/GUILOAD.py | 185 +
gemrb/GUIScripts/iwd2/GUIMA.py | 385 +
gemrb/GUIScripts/iwd2/GUIOPT.py | 778 ++
gemrb/GUIScripts/iwd2/GUIREC.py | 1513 +++
gemrb/GUIScripts/iwd2/GUISAVE.py | 279 +
gemrb/GUIScripts/iwd2/GUISPL.py | 395 +
gemrb/GUIScripts/iwd2/GUIWORLD.py | 184 +
gemrb/GUIScripts/iwd2/GamePlay.py | 189 +
gemrb/GUIScripts/iwd2/Gender.py | 114 +
gemrb/GUIScripts/iwd2/Graphics.py | 184 +
gemrb/GUIScripts/iwd2/ImportFile.py | 100 +
gemrb/GUIScripts/iwd2/LoadScreen.py | 72 +
gemrb/GUIScripts/iwd2/Makefile.am | 4 +
gemrb/GUIScripts/iwd2/MessageWindow.py | 124 +
gemrb/GUIScripts/iwd2/Movies.py | 79 +
gemrb/GUIScripts/iwd2/Name.py | 73 +
gemrb/GUIScripts/iwd2/Options.py | 112 +
gemrb/GUIScripts/iwd2/Portrait.py | 225 +
gemrb/GUIScripts/iwd2/QuitGame.py | 27 +
gemrb/GUIScripts/iwd2/Race.py | 94 +
gemrb/GUIScripts/iwd2/SPParty.py | 152 +
gemrb/GUIScripts/iwd2/SPParty2.py | 36 +
gemrb/GUIScripts/iwd2/SPPartyFormation.py | 137 +
gemrb/GUIScripts/iwd2/Skills.py | 261 +
gemrb/GUIScripts/iwd2/Songs.py | 68 +
gemrb/GUIScripts/iwd2/Sound.py | 132 +
gemrb/GUIScripts/iwd2/Start.py | 217 +
gemrb/GUIScripts/iwd2/SubRaces.py | 110 +
gemrb/GUIScripts/maze_defs.py | 34 +
gemrb/GUIScripts/pst/Autodetect.py | 20 +
gemrb/GUIScripts/pst/CMakeLists.txt | 3 +
gemrb/GUIScripts/pst/FloatMenuWindow.py | 507 +
gemrb/GUIScripts/pst/GUICommonWindows.py | 653 ++
gemrb/GUIScripts/pst/GUIINV.py | 517 +
gemrb/GUIScripts/pst/GUIJRNL.py | 455 +
gemrb/GUIScripts/pst/GUILOAD.py | 180 +
gemrb/GUIScripts/pst/GUIMA.py | 192 +
gemrb/GUIScripts/pst/GUIMG.py | 272 +
gemrb/GUIScripts/pst/GUIOPT.py | 940 ++
gemrb/GUIScripts/pst/GUIPR.py | 281 +
gemrb/GUIScripts/pst/GUIREC.py | 1301 +++
gemrb/GUIScripts/pst/GUISAVE.py | 317 +
gemrb/GUIScripts/pst/GUISTORE.py | 1373 +++
gemrb/GUIScripts/pst/GUIWORLD.py | 187 +
gemrb/GUIScripts/pst/LoadScreen.py | 74 +
gemrb/GUIScripts/pst/Makefile.am | 4 +
gemrb/GUIScripts/pst/Maze.py | 455 +
gemrb/GUIScripts/pst/MessageWindow.py | 112 +
gemrb/GUIScripts/pst/NewLife.py | 506 +
gemrb/GUIScripts/pst/QuitGame.py | 72 +
gemrb/GUIScripts/pst/Start.py | 109 +
gemrb/GUIScripts/test/Start.py | 3 +
gemrb/GemRB.cfg.noinstall.sample | 256 +
gemrb/GemRB.cfg.sample.in | 261 +
gemrb/GemRB.cpp | 71 +
gemrb/Makefile.am | 13 +
gemrb/core/ActorMgr.cpp | 29 +
gemrb/core/ActorMgr.h | 41 +
gemrb/core/Ambient.cpp | 36 +
gemrb/core/Ambient.h | 77 +
gemrb/core/AmbientMgr.cpp | 62 +
gemrb/core/AmbientMgr.h | 48 +
gemrb/core/AnimStructures.h | 29 +
gemrb/core/Animation.cpp | 261 +
gemrb/core/Animation.h | 72 +
gemrb/core/AnimationFactory.cpp | 168 +
gemrb/core/AnimationFactory.h | 58 +
gemrb/core/AnimationMgr.cpp | 29 +
gemrb/core/AnimationMgr.h | 47 +
gemrb/core/ArchiveImporter.cpp | 29 +
gemrb/core/ArchiveImporter.h | 40 +
gemrb/core/Audio.cpp | 35 +
gemrb/core/Audio.h | 82 +
gemrb/core/Bitmap.cpp | 29 +
gemrb/core/Bitmap.h | 55 +
gemrb/core/CMakeLists.txt | 52 +
gemrb/core/Cache.cpp | 311 +
gemrb/core/Cache.h | 93 +
gemrb/core/Calendar.cpp | 96 +
gemrb/core/Calendar.h | 40 +
gemrb/core/Callback.cpp | 33 +
gemrb/core/Callback.h | 35 +
gemrb/core/CharAnimations.cpp | 2408 +++++
gemrb/core/CharAnimations.h | 233 +
gemrb/core/Compressor.cpp | 37 +
gemrb/core/Compressor.h | 41 +
gemrb/core/ControlAnimation.cpp | 138 +
gemrb/core/ControlAnimation.h | 51 +
gemrb/core/Core.cpp | 323 +
gemrb/core/DataFileMgr.cpp | 29 +
gemrb/core/DataFileMgr.h | 58 +
gemrb/core/Dialog.cpp | 100 +
gemrb/core/Dialog.h | 83 +
gemrb/core/DialogHandler.cpp | 489 +
gemrb/core/DialogHandler.h | 51 +
gemrb/core/DialogMgr.cpp | 29 +
gemrb/core/DialogMgr.h | 36 +
gemrb/core/DisplayMessage.cpp | 239 +
gemrb/core/DisplayMessage.h | 70 +
gemrb/core/Effect.h | 139 +
gemrb/core/EffectMgr.cpp | 29 +
gemrb/core/EffectMgr.h | 56 +
gemrb/core/EffectQueue.cpp | 1942 ++++
gemrb/core/EffectQueue.h | 313 +
gemrb/core/Factory.cpp | 65 +
gemrb/core/Factory.h | 42 +
gemrb/core/FactoryObject.cpp | 33 +
gemrb/core/FactoryObject.h | 35 +
gemrb/core/Font.cpp | 560 +
gemrb/core/Font.h | 111 +
gemrb/core/GUI/Button.cpp | 726 ++
gemrb/core/GUI/Button.h | 219 +
gemrb/core/GUI/Console.cpp | 221 +
gemrb/core/GUI/Console.h | 93 +
gemrb/core/GUI/Control.cpp | 270 +
gemrb/core/GUI/Control.h | 149 +
gemrb/core/GUI/EventMgr.cpp | 439 +
gemrb/core/GUI/EventMgr.h | 142 +
gemrb/core/GUI/GameControl.cpp | 2849 ++++++
gemrb/core/GUI/GameControl.h | 257 +
gemrb/core/GUI/Label.cpp | 152 +
gemrb/core/GUI/Label.h | 83 +
gemrb/core/GUI/MapControl.cpp | 540 +
gemrb/core/GUI/MapControl.h | 110 +
gemrb/core/GUI/Progressbar.cpp | 184 +
gemrb/core/GUI/Progressbar.h | 89 +
gemrb/core/GUI/ScrollBar.cpp | 297 +
gemrb/core/GUI/ScrollBar.h | 103 +
gemrb/core/GUI/Slider.cpp | 296 +
gemrb/core/GUI/Slider.h | 106 +
gemrb/core/GUI/TextArea.cpp | 979 ++
gemrb/core/GUI/TextArea.h | 177 +
gemrb/core/GUI/TextEdit.cpp | 233 +
gemrb/core/GUI/TextEdit.h | 101 +
gemrb/core/GUI/Window.cpp | 442 +
gemrb/core/GUI/Window.h | 190 +
gemrb/core/GUI/WorldMapControl.cpp | 417 +
gemrb/core/GUI/WorldMapControl.h | 116 +
gemrb/core/Game.cpp | 1848 ++++
gemrb/core/Game.h | 485 +
gemrb/core/GameData.cpp | 511 +
gemrb/core/GameData.h | 122 +
gemrb/core/GameScript/Actions.cpp | 7202 +++++++++++++
gemrb/core/GameScript/GSUtils.cpp | 2309 +++++
gemrb/core/GameScript/GSUtils.h | 140 +
gemrb/core/GameScript/GameScript.cpp | 2344 +++++
gemrb/core/GameScript/GameScript.h | 1535 +++
gemrb/core/GameScript/Matching.cpp | 674 ++
gemrb/core/GameScript/Matching.h | 47 +
gemrb/core/GameScript/Objects.cpp | 1158 +++
gemrb/core/GameScript/Triggers.cpp | 4443 ++++++++
gemrb/core/GlobalTimer.cpp | 339 +
gemrb/core/GlobalTimer.h | 78 +
gemrb/core/Holder.h | 95 +
gemrb/core/Image.cpp | 47 +
gemrb/core/Image.h | 61 +
gemrb/core/ImageFactory.cpp | 42 +
gemrb/core/ImageFactory.h | 40 +
gemrb/core/ImageMgr.cpp | 92 +
gemrb/core/ImageMgr.h | 68 +
gemrb/core/ImageWriter.cpp | 27 +
gemrb/core/ImageWriter.h | 35 +
gemrb/core/IniSpawn.cpp | 716 ++
gemrb/core/IniSpawn.h | 168 +
gemrb/core/Interface.cpp | 5492 ++++++++++
gemrb/core/Interface.h | 811 ++
gemrb/core/Inventory.cpp | 1856 ++++
gemrb/core/Inventory.h | 351 +
gemrb/core/Item.cpp | 248 +
gemrb/core/Item.h | 268 +
gemrb/core/ItemMgr.cpp | 29 +
gemrb/core/ItemMgr.h | 48 +
gemrb/core/LRUCache.cpp | 219 +
gemrb/core/LRUCache.h | 59 +
gemrb/core/Makefile.am | 119 +
gemrb/core/Map.cpp | 3704 +++++++
gemrb/core/Map.h | 499 +
gemrb/core/MapMgr.cpp | 29 +
gemrb/core/MapMgr.h | 52 +
gemrb/core/MoviePlayer.cpp | 31 +
gemrb/core/MoviePlayer.h | 50 +
gemrb/core/MusicMgr.cpp | 33 +
gemrb/core/MusicMgr.h | 49 +
gemrb/core/Palette.cpp | 243 +
gemrb/core/Palette.h | 102 +
gemrb/core/PalettedImageMgr.cpp | 29 +
gemrb/core/PalettedImageMgr.h | 49 +
gemrb/core/Particles.cpp | 387 +
gemrb/core/Particles.h | 137 +
gemrb/core/PathFinder.h | 52 +
gemrb/core/Plugin.cpp | 29 +
gemrb/core/Plugin.h | 65 +
gemrb/core/PluginMgr.cpp | 321 +
gemrb/core/PluginMgr.h | 153 +
gemrb/core/Polygon.cpp | 387 +
gemrb/core/Polygon.h | 81 +
gemrb/core/PolymorphCache.h | 34 +
gemrb/core/Projectile.cpp | 1763 ++++
gemrb/core/Projectile.h | 406 +
gemrb/core/ProjectileMgr.cpp | 29 +
gemrb/core/ProjectileMgr.h | 36 +
gemrb/core/ProjectileServer.cpp | 325 +
gemrb/core/ProjectileServer.h | 97 +
gemrb/core/Region.cpp | 224 +
gemrb/core/Region.h | 96 +
gemrb/core/Resource.cpp | 35 +
gemrb/core/Resource.h | 57 +
gemrb/core/ResourceDesc.cpp | 50 +
gemrb/core/ResourceDesc.h | 60 +
gemrb/core/ResourceManager.cpp | 160 +
gemrb/core/ResourceManager.h | 70 +
gemrb/core/ResourceSource.cpp | 29 +
gemrb/core/ResourceSource.h | 47 +
gemrb/core/SaveGame.h | 85 +
gemrb/core/SaveGameIterator.cpp | 634 ++
gemrb/core/SaveGameIterator.h | 51 +
gemrb/core/SaveGameMgr.cpp | 29 +
gemrb/core/SaveGameMgr.h | 38 +
gemrb/core/ScriptEngine.cpp | 29 +
gemrb/core/ScriptEngine.h | 40 +
gemrb/core/Scriptable/Actor.cpp | 7078 +++++++++++++
gemrb/core/Scriptable/Actor.h | 750 ++
gemrb/core/Scriptable/Container.cpp | 292 +
gemrb/core/Scriptable/Container.h | 70 +
gemrb/core/Scriptable/Door.cpp | 409 +
gemrb/core/Scriptable/Door.h | 98 +
gemrb/core/Scriptable/InfoPoint.cpp | 272 +
gemrb/core/Scriptable/InfoPoint.h | 68 +
gemrb/core/Scriptable/PCStatStruct.cpp | 185 +
gemrb/core/Scriptable/PCStatStruct.h | 117 +
gemrb/core/Scriptable/Scriptable.cpp | 1991 ++++
gemrb/core/Scriptable/Scriptable.h | 438 +
gemrb/core/ScriptedAnimation.cpp | 777 ++
gemrb/core/ScriptedAnimation.h | 153 +
gemrb/core/SoundMgr.cpp | 32 +
gemrb/core/SoundMgr.h | 64 +
gemrb/core/Spell.cpp | 237 +
gemrb/core/Spell.h | 174 +
gemrb/core/SpellMgr.cpp | 29 +
gemrb/core/SpellMgr.h | 47 +
gemrb/core/Spellbook.cpp | 1016 ++
gemrb/core/Spellbook.h | 248 +
gemrb/core/Sprite2D.cpp | 151 +
gemrb/core/Sprite2D.h | 82 +
gemrb/core/SpriteCover.cpp | 49 +
gemrb/core/SpriteCover.h | 39 +
gemrb/core/Store.cpp | 283 +
gemrb/core/Store.h | 178 +
gemrb/core/StoreMgr.cpp | 29 +
gemrb/core/StoreMgr.h | 51 +
gemrb/core/StringMgr.cpp | 29 +
gemrb/core/StringMgr.h | 62 +
gemrb/core/SymbolMgr.cpp | 29 +
gemrb/core/SymbolMgr.h | 56 +
gemrb/core/System/CachedFileStream.cpp | 213 +
gemrb/core/System/CachedFileStream.h | 45 +
gemrb/core/System/DataStream.cpp | 185 +
gemrb/core/System/DataStream.h | 77 +
gemrb/core/System/FileStream.cpp | 281 +
gemrb/core/System/FileStream.h | 63 +
gemrb/core/System/MemoryStream.cpp | 110 +
gemrb/core/System/MemoryStream.h | 58 +
gemrb/core/System/VFS.cpp | 538 +
gemrb/core/System/VFS.h | 164 +
gemrb/core/System/android_log_printf.cpp | 35 +
gemrb/core/System/android_log_printf.h | 24 +
gemrb/core/System/snprintf.cpp | 980 ++
gemrb/core/System/snprintf.h | 41 +
gemrb/core/System/swab.c | 33 +
gemrb/core/System/swab.h | 35 +
gemrb/core/TableMgr.cpp | 84 +
gemrb/core/TableMgr.h | 99 +
gemrb/core/Tile.cpp | 42 +
gemrb/core/Tile.h | 42 +
gemrb/core/TileMap.cpp | 649 ++
gemrb/core/TileMap.h | 100 +
gemrb/core/TileMapMgr.cpp | 29 +
gemrb/core/TileMapMgr.h | 46 +
gemrb/core/TileOverlay.cpp | 132 +
gemrb/core/TileOverlay.h | 46 +
gemrb/core/TileSetMgr.cpp | 29 +
gemrb/core/TileSetMgr.h | 37 +
gemrb/core/TypeID.h | 29 +
gemrb/core/Variables.cpp | 506 +
gemrb/core/Variables.h | 127 +
gemrb/core/Video.cpp | 230 +
gemrb/core/Video.h | 237 +
gemrb/core/VideoMode.h | 51 +
gemrb/core/WindowMgr.cpp | 28 +
gemrb/core/WindowMgr.h | 52 +
gemrb/core/WorldMap.cpp | 593 ++
gemrb/core/WorldMap.h | 205 +
gemrb/core/WorldMapMgr.cpp | 29 +
gemrb/core/WorldMapMgr.h | 50 +
gemrb/core/damages.h | 53 +
gemrb/docs/CMakeLists.txt | 1 +
gemrb/docs/Makefile.am | 1 +
gemrb/docs/en/CMakeLists.txt | 7 +
gemrb/docs/en/CheatKeys.txt | 88 +
gemrb/docs/en/CodingStyle.txt | 9 +
gemrb/docs/en/Engine/Charcolors.txt | 36 +
gemrb/docs/en/Engine/Containers.txt | 57 +
gemrb/docs/en/Engine/Doors.txt | 101 +
gemrb/docs/en/Engine/Effects.txt | 116 +
gemrb/docs/en/Engine/Makefile.am | 3 +
gemrb/docs/en/Engine/Projectile.txt | 38 +
gemrb/docs/en/Engine/Triggers.txt | 8 +
gemrb/docs/en/Engine/Usability.txt | 5 +
gemrb/docs/en/EngineChanges.txt | 29 +
gemrb/docs/en/GUIScript/ActOnPC.txt | 13 +
gemrb/docs/en/GUIScript/AddGameTypeHint.txt | 17 +
gemrb/docs/en/GUIScript/AdjustScrolling.txt | 26 +
gemrb/docs/en/GUIScript/ApplyEffect.txt | 31 +
gemrb/docs/en/GUIScript/ApplySpell.txt | 14 +
gemrb/docs/en/GUIScript/AttachScrollBar.txt | 12 +
gemrb/docs/en/GUIScript/CanUseItemType.txt | 15 +
gemrb/docs/en/GUIScript/ChangeContainerItem.txt | 18 +
gemrb/docs/en/GUIScript/ChangeItemFlag.txt | 28 +
gemrb/docs/en/GUIScript/ChangeStoreItem.txt | 23 +
gemrb/docs/en/GUIScript/CheckFeatCondition.txt | 15 +
gemrb/docs/en/GUIScript/CheckVar.txt | 16 +
gemrb/docs/en/GUIScript/ClearActions.txt | 9 +
gemrb/docs/en/GUIScript/ConvertEdit.txt | 13 +
gemrb/docs/en/GUIScript/CountEffects.txt | 22 +
gemrb/docs/en/GUIScript/CreateButton.txt | 17 +
gemrb/docs/en/GUIScript/CreateCreature.txt | 13 +
gemrb/docs/en/GUIScript/CreateItem.txt | 15 +
gemrb/docs/en/GUIScript/CreateLabel.txt | 27 +
gemrb/docs/en/GUIScript/CreateLabelOnButton.txt | 18 +
gemrb/docs/en/GUIScript/CreateMapControl.txt | 20 +
gemrb/docs/en/GUIScript/CreateMovement.txt | 14 +
gemrb/docs/en/GUIScript/CreatePlayer.txt | 25 +
gemrb/docs/en/GUIScript/CreateScrollBar.txt | 19 +
gemrb/docs/en/GUIScript/CreateTextEdit.txt | 19 +
gemrb/docs/en/GUIScript/CreateWindow.txt | 17 +
gemrb/docs/en/GUIScript/CreateWorldMapControl.txt | 25 +
gemrb/docs/en/GUIScript/DeleteControl.txt | 16 +
gemrb/docs/en/GUIScript/DeleteSaveGame.txt | 11 +
gemrb/docs/en/GUIScript/DisplayString.txt | 14 +
gemrb/docs/en/GUIScript/DragItem.txt | 14 +
gemrb/docs/en/GUIScript/DrawWindows.txt | 11 +
gemrb/docs/en/GUIScript/DropDraggedItem.txt | 15 +
gemrb/docs/en/GUIScript/EnableButtonBorder.txt | 16 +
gemrb/docs/en/GUIScript/EnableCheatKeys.txt | 11 +
gemrb/docs/en/GUIScript/EndCutSceneMode.txt | 11 +
gemrb/docs/en/GUIScript/EnterGame.txt | 11 +
gemrb/docs/en/GUIScript/EnterStore.txt | 11 +
gemrb/docs/en/GUIScript/EvaluateString.txt | 11 +
gemrb/docs/en/GUIScript/ExecuteString.txt | 20 +
gemrb/docs/en/GUIScript/ExploreArea.txt | 13 +
gemrb/docs/en/GUIScript/FillPlayerInfo.txt | 38 +
gemrb/docs/en/GUIScript/FindTableValue.txt | 19 +
.../docs/en/GUIScript/GameControlGetTargetMode.txt | 9 +
.../docs/en/GUIScript/GameControlSetLastActor.txt | 11 +
.../en/GUIScript/GameControlSetScreenFlags.txt | 18 +
.../docs/en/GUIScript/GameControlSetTargetMode.txt | 22 +
gemrb/docs/en/GUIScript/GameGetFirstSelectedPC.txt | 11 +
gemrb/docs/en/GUIScript/GameGetFormation.txt | 11 +
gemrb/docs/en/GUIScript/GameGetPartyGold.txt | 11 +
gemrb/docs/en/GUIScript/GameGetReputation.txt | 11 +
.../docs/en/GUIScript/GameGetSelectedPCSingle.txt | 13 +
gemrb/docs/en/GUIScript/GameIsBeastKnown.txt | 11 +
gemrb/docs/en/GUIScript/GameIsPCSelected.txt | 11 +
gemrb/docs/en/GUIScript/GamePause.txt | 12 +
gemrb/docs/en/GUIScript/GameSelectPC.txt | 21 +
gemrb/docs/en/GUIScript/GameSelectPCSingle.txt | 13 +
gemrb/docs/en/GUIScript/GameSetExpansion.txt | 14 +
gemrb/docs/en/GUIScript/GameSetFormation.txt | 11 +
gemrb/docs/en/GUIScript/GameSetPartyGold.txt | 11 +
gemrb/docs/en/GUIScript/GameSetPartySize.txt | 11 +
gemrb/docs/en/GUIScript/GameSetProtagonistMode.txt | 14 +
gemrb/docs/en/GUIScript/GameSetReputation.txt | 11 +
gemrb/docs/en/GUIScript/GameSetScreenFlags.txt | 14 +
gemrb/docs/en/GUIScript/GetAbilityBonus.txt | 23 +
gemrb/docs/en/GUIScript/GetCharSounds.txt | 20 +
gemrb/docs/en/GUIScript/GetCharacters.txt | 20 +
gemrb/docs/en/GUIScript/GetCombatDetails.txt | 13 +
gemrb/docs/en/GUIScript/GetContainer.txt | 15 +
gemrb/docs/en/GUIScript/GetContainerItem.txt | 19 +
gemrb/docs/en/GUIScript/GetControl.txt | 14 +
gemrb/docs/en/GUIScript/GetControlObject.txt | 13 +
gemrb/docs/en/GUIScript/GetCurrentArea.txt | 11 +
gemrb/docs/en/GUIScript/GetDestinationArea.txt | 21 +
gemrb/docs/en/GUIScript/GetEquippedAmmunition.txt | 11 +
gemrb/docs/en/GUIScript/GetEquippedQuickSlot.txt | 14 +
gemrb/docs/en/GUIScript/GetGameString.txt | 13 +
gemrb/docs/en/GUIScript/GetGameTime.txt | 11 +
gemrb/docs/en/GUIScript/GetGameVar.txt | 13 +
gemrb/docs/en/GUIScript/GetINIBeastsKey.txt | 13 +
gemrb/docs/en/GUIScript/GetINIPartyCount.txt | 11 +
gemrb/docs/en/GUIScript/GetINIPartyKey.txt | 13 +
gemrb/docs/en/GUIScript/GetINIQuestsKey.txt | 14 +
gemrb/docs/en/GUIScript/GetItem.txt | 33 +
gemrb/docs/en/GUIScript/GetJournalEntry.txt | 17 +
gemrb/docs/en/GUIScript/GetJournalSize.txt | 12 +
gemrb/docs/en/GUIScript/GetKnownSpell.txt | 17 +
gemrb/docs/en/GUIScript/GetKnownSpellsCount.txt | 14 +
.../en/GUIScript/GetMemorizableSpellsCount.txt | 15 +
gemrb/docs/en/GUIScript/GetMemorizedSpell.txt | 18 +
.../docs/en/GUIScript/GetMemorizedSpellsCount.txt | 14 +
gemrb/docs/en/GUIScript/GetMessageWindowSize.txt | 11 +
gemrb/docs/en/GUIScript/GetPCStats.txt | 20 +
gemrb/docs/en/GUIScript/GetPartySize.txt | 11 +
gemrb/docs/en/GUIScript/GetPlayerName.txt | 17 +
gemrb/docs/en/GUIScript/GetPlayerPortrait.txt | 13 +
gemrb/docs/en/GUIScript/GetPlayerScript.txt | 13 +
gemrb/docs/en/GUIScript/GetPlayerStat.txt | 13 +
gemrb/docs/en/GUIScript/GetPlayerStates.txt | 11 +
gemrb/docs/en/GUIScript/GetPlayerString.txt | 15 +
gemrb/docs/en/GUIScript/GetPortraits.txt | 21 +
gemrb/docs/en/GUIScript/GetRumour.txt | 12 +
gemrb/docs/en/GUIScript/GetSaveGameAttrib.txt | 20 +
gemrb/docs/en/GUIScript/GetSaveGameCount.txt | 11 +
gemrb/docs/en/GUIScript/GetSelectedSize.txt | 11 +
gemrb/docs/en/GUIScript/GetSlotItem.txt | 29 +
gemrb/docs/en/GUIScript/GetSlotType.txt | 15 +
gemrb/docs/en/GUIScript/GetSlots.txt | 15 +
gemrb/docs/en/GUIScript/GetSpell.txt | 21 +
gemrb/docs/en/GUIScript/GetStore.txt | 24 +
gemrb/docs/en/GUIScript/GetStoreCure.txt | 14 +
gemrb/docs/en/GUIScript/GetStoreDrink.txt | 14 +
gemrb/docs/en/GUIScript/GetStoreItem.txt | 20 +
gemrb/docs/en/GUIScript/GetString.txt | 23 +
gemrb/docs/en/GUIScript/GetSymbolValue.txt | 26 +
gemrb/docs/en/GUIScript/GetSystemVariable.txt | 16 +
gemrb/docs/en/GUIScript/GetTableColumnCount.txt | 17 +
gemrb/docs/en/GUIScript/GetTableColumnIndex.txt | 13 +
gemrb/docs/en/GUIScript/GetTableColumnName.txt | 16 +
gemrb/docs/en/GUIScript/GetTableRowCount.txt | 15 +
gemrb/docs/en/GUIScript/GetTableRowIndex.txt | 16 +
gemrb/docs/en/GUIScript/GetTableRowName.txt | 16 +
gemrb/docs/en/GUIScript/GetTableValue.txt | 23 +
gemrb/docs/en/GUIScript/GetToken.txt | 16 +
gemrb/docs/en/GUIScript/GetVar.txt | 16 +
gemrb/docs/en/GUIScript/HardEndPL.txt | 11 +
gemrb/docs/en/GUIScript/HasControl.txt | 16 +
gemrb/docs/en/GUIScript/HasResource.txt | 9 +
gemrb/docs/en/GUIScript/HasSpecialItem.txt | 14 +
gemrb/docs/en/GUIScript/HasSpecialSpell.txt | 14 +
gemrb/docs/en/GUIScript/HideGUI.txt | 28 +
gemrb/docs/en/GUIScript/IncreaseReputation.txt | 11 +
gemrb/docs/en/GUIScript/InvalidateWindow.txt | 15 +
gemrb/docs/en/GUIScript/IsDraggingItem.txt | 13 +
gemrb/docs/en/GUIScript/IsDualWielding.txt | 12 +
gemrb/docs/en/GUIScript/IsValidStoreItem.txt | 18 +
gemrb/docs/en/GUIScript/LearnSpell.txt | 19 +
gemrb/docs/en/GUIScript/LeaveContainer.txt | 11 +
gemrb/docs/en/GUIScript/LeaveParty.txt | 13 +
gemrb/docs/en/GUIScript/LeaveStore.txt | 11 +
gemrb/docs/en/GUIScript/LoadGame.txt | 20 +
gemrb/docs/en/GUIScript/LoadMusicPL.txt | 12 +
gemrb/docs/en/GUIScript/LoadSymbol.txt | 14 +
gemrb/docs/en/GUIScript/LoadTable.txt | 14 +
gemrb/docs/en/GUIScript/LoadWindow.txt | 15 +
gemrb/docs/en/GUIScript/LoadWindowFrame.txt | 32 +
gemrb/docs/en/GUIScript/LoadWindowPack.txt | 19 +
gemrb/docs/en/GUIScript/Makefile.am | 3 +
gemrb/docs/en/GUIScript/MemorizeSpell.txt | 15 +
gemrb/docs/en/GUIScript/ModifyEffect.txt | 16 +
gemrb/docs/en/GUIScript/MoveTAText.txt | 14 +
gemrb/docs/en/GUIScript/MoveToArea.txt | 11 +
gemrb/docs/en/GUIScript/PlayMovie.txt | 16 +
gemrb/docs/en/GUIScript/PlaySound.txt | 16 +
gemrb/docs/en/GUIScript/QueryText.txt | 23 +
gemrb/docs/en/GUIScript/Quit.txt | 13 +
gemrb/docs/en/GUIScript/QuitGame.txt | 13 +
gemrb/docs/en/GUIScript/RemoveEffects.txt | 12 +
gemrb/docs/en/GUIScript/RemoveItem.txt | 15 +
gemrb/docs/en/GUIScript/RemoveSpell.txt | 15 +
gemrb/docs/en/GUIScript/RestParty.txt | 13 +
gemrb/docs/en/GUIScript/RevealArea.txt | 15 +
gemrb/docs/en/GUIScript/RewindTA.txt | 13 +
gemrb/docs/en/GUIScript/Roll.txt | 21 +
gemrb/docs/en/GUIScript/SaveCharacter.txt | 19 +
gemrb/docs/en/GUIScript/SaveGame.txt | 16 +
gemrb/docs/en/GUIScript/SetActionIcon.txt | 14 +
gemrb/docs/en/GUIScript/SetAnimation.txt | 17 +
gemrb/docs/en/GUIScript/SetAnimationPalette.txt | 16 +
gemrb/docs/en/GUIScript/SetBufferLength.txt | 16 +
gemrb/docs/en/GUIScript/SetButtonBAM.txt | 17 +
gemrb/docs/en/GUIScript/SetButtonBorder.txt | 27 +
gemrb/docs/en/GUIScript/SetButtonFlags.txt | 35 +
gemrb/docs/en/GUIScript/SetButtonFont.txt | 15 +
gemrb/docs/en/GUIScript/SetButtonMOS.txt | 15 +
gemrb/docs/en/GUIScript/SetButtonOverlay.txt | 17 +
gemrb/docs/en/GUIScript/SetButtonPLT.txt | 21 +
gemrb/docs/en/GUIScript/SetButtonPicture.txt | 15 +
.../docs/en/GUIScript/SetButtonPictureClipping.txt | 15 +
gemrb/docs/en/GUIScript/SetButtonSprites.txt | 20 +
gemrb/docs/en/GUIScript/SetButtonState.txt | 23 +
gemrb/docs/en/GUIScript/SetButtonTextColor.txt | 15 +
gemrb/docs/en/GUIScript/SetControlPos.txt | 16 +
gemrb/docs/en/GUIScript/SetControlSize.txt | 16 +
gemrb/docs/en/GUIScript/SetControlStatus.txt | 31 +
gemrb/docs/en/GUIScript/SetDefaultActions.txt | 13 +
gemrb/docs/en/GUIScript/SetDefaultScrollBar.txt | 13 +
gemrb/docs/en/GUIScript/SetEquippedQuickSlot.txt | 14 +
gemrb/docs/en/GUIScript/SetEvent.txt | 43 +
gemrb/docs/en/GUIScript/SetFullScreen.txt | 15 +
gemrb/docs/en/GUIScript/SetGamePortraitPreview.txt | 16 +
gemrb/docs/en/GUIScript/SetGamePreview.txt | 15 +
gemrb/docs/en/GUIScript/SetGamma.txt | 12 +
gemrb/docs/en/GUIScript/SetGlobal.txt | 14 +
gemrb/docs/en/GUIScript/SetInfoTextColor.txt | 13 +
gemrb/docs/en/GUIScript/SetItemIcon.txt | 26 +
gemrb/docs/en/GUIScript/SetJournalEntry.txt | 13 +
gemrb/docs/en/GUIScript/SetLabelTextColor.txt | 16 +
gemrb/docs/en/GUIScript/SetLabelUseRGB.txt | 17 +
gemrb/docs/en/GUIScript/SetMapnote.txt | 16 +
gemrb/docs/en/GUIScript/SetMasterScript.txt | 15 +
.../en/GUIScript/SetMemorizableSpellsCount.txt | 15 +
gemrb/docs/en/GUIScript/SetModalState.txt | 18 +
gemrb/docs/en/GUIScript/SetMouseScrollSpeed.txt | 11 +
gemrb/docs/en/GUIScript/SetNextScript.txt | 16 +
gemrb/docs/en/GUIScript/SetPlayerName.txt | 17 +
gemrb/docs/en/GUIScript/SetPlayerScript.txt | 12 +
gemrb/docs/en/GUIScript/SetPlayerSound.txt | 11 +
gemrb/docs/en/GUIScript/SetPlayerStat.txt | 19 +
gemrb/docs/en/GUIScript/SetPlayerString.txt | 12 +
gemrb/docs/en/GUIScript/SetPurchasedAmount.txt | 12 +
gemrb/docs/en/GUIScript/SetRepeatClickFlags.txt | 13 +
gemrb/docs/en/GUIScript/SetSaveGamePortrait.txt | 15 +
gemrb/docs/en/GUIScript/SetSaveGamePreview.txt | 14 +
gemrb/docs/en/GUIScript/SetScrollBarSprites.txt | 23 +
gemrb/docs/en/GUIScript/SetSpellIcon.txt | 20 +
gemrb/docs/en/GUIScript/SetTAHistory.txt | 13 +
gemrb/docs/en/GUIScript/SetText.txt | 18 +
gemrb/docs/en/GUIScript/SetTextAreaFlags.txt | 34 +
gemrb/docs/en/GUIScript/SetTimedEvent.txt | 12 +
gemrb/docs/en/GUIScript/SetToken.txt | 27 +
gemrb/docs/en/GUIScript/SetTooltip.txt | 26 +
gemrb/docs/en/GUIScript/SetTooltipDelay.txt | 9 +
gemrb/docs/en/GUIScript/SetVar.txt | 28 +
gemrb/docs/en/GUIScript/SetVarAssoc.txt | 20 +
gemrb/docs/en/GUIScript/SetVisible.txt | 21 +
gemrb/docs/en/GUIScript/SetWindowFrame.txt | 24 +
gemrb/docs/en/GUIScript/SetWindowPicture.txt | 23 +
gemrb/docs/en/GUIScript/SetWindowPos.txt | 36 +
gemrb/docs/en/GUIScript/SetWindowSize.txt | 16 +
gemrb/docs/en/GUIScript/SetWorldMapTextColor.txt | 19 +
gemrb/docs/en/GUIScript/SetupControls.txt | 14 +
gemrb/docs/en/GUIScript/SetupEquipmentIcons.txt | 17 +
gemrb/docs/en/GUIScript/SetupQuickSlot.txt | 22 +
gemrb/docs/en/GUIScript/SetupSpellIcons.txt | 18 +
gemrb/docs/en/GUIScript/ShowModal.txt | 18 +
gemrb/docs/en/GUIScript/SoftEndPL.txt | 11 +
gemrb/docs/en/GUIScript/SpellCast.txt | 13 +
gemrb/docs/en/GUIScript/StatComment.txt | 20 +
gemrb/docs/en/GUIScript/StealFailed.txt | 12 +
gemrb/docs/en/GUIScript/SwapPCs.txt | 13 +
gemrb/docs/en/GUIScript/TextAreaAppend.txt | 23 +
gemrb/docs/en/GUIScript/TextAreaClear.txt | 14 +
gemrb/docs/en/GUIScript/TextAreaScroll.txt | 16 +
gemrb/docs/en/GUIScript/UnhideGUI.txt | 13 +
gemrb/docs/en/GUIScript/UnloadSymbol.txt | 15 +
gemrb/docs/en/GUIScript/UnloadTable.txt | 15 +
gemrb/docs/en/GUIScript/UnloadWindow.txt | 15 +
gemrb/docs/en/GUIScript/UnmemorizeSpell.txt | 15 +
gemrb/docs/en/GUIScript/UpdateAmbientsVolume.txt | 11 +
gemrb/docs/en/GUIScript/UpdateMusicVolume.txt | 11 +
gemrb/docs/en/GUIScript/UseItem.txt | 16 +
gemrb/docs/en/GUIScript/accessing_gui_controls.txt | 11 +
gemrb/docs/en/GUIScript/bit_operation.txt | 7 +
gemrb/docs/en/GUIScript/console.txt | 9 +
gemrb/docs/en/GUIScript/controls.txt | 52 +
gemrb/docs/en/GUIScript/data_exchange.txt | 48 +
gemrb/docs/en/GUIScript/doc_template.txt | 13 +
gemrb/docs/en/GUIScript/reserved_functions.txt | 44 +
gemrb/docs/en/GUIScript_introduction.txt | 275 +
gemrb/docs/en/Makefile.am | 6 +
gemrb/docs/en/Release.txt | 79 +
gemrb/docs/en/Tables/HPBARB.txt | 8 +
gemrb/docs/en/Tables/Makefile.am | 3 +
gemrb/docs/en/Tables/ability.txt | 10 +
gemrb/docs/en/Tables/aligns.txt | 12 +
gemrb/docs/en/Tables/areapro.txt | 25 +
gemrb/docs/en/Tables/avatars.txt | 66 +
gemrb/docs/en/Tables/avprefix.txt | 34 +
gemrb/docs/en/Tables/cgtable.txt | 6 +
gemrb/docs/en/Tables/classes.txt | 15 +
gemrb/docs/en/Tables/clowncol.txt | 10 +
gemrb/docs/en/Tables/containr.txt | 11 +
gemrb/docs/en/Tables/damage.txt | 10 +
gemrb/docs/en/Tables/defsound.txt | 1 +
gemrb/docs/en/Tables/dmgtype.txt | 11 +
gemrb/docs/en/Tables/fistweap.txt | 7 +
gemrb/docs/en/Tables/fonts.txt | 13 +
gemrb/docs/en/Tables/formatio.txt | 10 +
gemrb/docs/en/Tables/guibtact.txt | 9 +
gemrb/docs/en/Tables/item_use.txt | 7 +
gemrb/docs/en/Tables/itemsnd.txt | 9 +
gemrb/docs/en/Tables/itemspec.txt | 6 +
gemrb/docs/en/Tables/itemtype.txt | 17 +
gemrb/docs/en/Tables/itemuse.txt | 10 +
gemrb/docs/en/Tables/magesch.txt | 9 +
gemrb/docs/en/Tables/modal.txt | 15 +
gemrb/docs/en/Tables/overlay.txt | 5 +
gemrb/docs/en/Tables/pathfind.txt | 16 +
gemrb/docs/en/Tables/pdolls.txt | 8 +
gemrb/docs/en/Tables/pictures.txt | 11 +
gemrb/docs/en/Tables/polystat.txt | 4 +
gemrb/docs/en/Tables/qslots.txt | 11 +
gemrb/docs/en/Tables/races.txt | 11 +
gemrb/docs/en/Tables/randitem.txt | 25 +
gemrb/docs/en/Tables/restmov.txt | 12 +
gemrb/docs/en/Tables/savegame.txt | 1 +
gemrb/docs/en/Tables/script.txt | 14 +
gemrb/docs/en/Tables/shtable.txt | 6 +
gemrb/docs/en/Tables/skills.txt | 9 +
gemrb/docs/en/Tables/skillsta.txt | 1 +
gemrb/docs/en/Tables/slottype.txt | 20 +
gemrb/docs/en/Tables/spells.txt | 6 +
gemrb/docs/en/Tables/splprot.txt | 41 +
gemrb/docs/en/Tables/start.txt | 12 +
gemrb/docs/en/Tables/states.txt | 16 +
gemrb/docs/en/Tables/strings.txt | 5 +
gemrb/docs/en/Tables/table_template.txt | 7 +
gemrb/docs/en/Tables/wildmag.txt | 27 +
gemrb/docs/en/Tables/wsshield.txt | 6 +
gemrb/docs/en/Tables/wssingle.txt | 7 +
gemrb/docs/en/Tables/wstwohnd.txt | 8 +
gemrb/docs/en/Tables/wstwowpn.txt | 7 +
gemrb/docs/en/gemrb_ini.txt | 322 +
gemrb/includes/Makefile.am | 2 +
gemrb/includes/RGBAColor.h | 31 +
gemrb/includes/SClassID.h | 89 +
gemrb/includes/defsounds.h | 49 +
gemrb/includes/errors.h | 27 +
gemrb/includes/exports.h | 48 +
gemrb/includes/globals.h | 241 +
gemrb/includes/ie_feats.h | 94 +
gemrb/includes/ie_stats.h | 536 +
gemrb/includes/ie_types.h | 70 +
gemrb/includes/iless.h | 31 +
gemrb/includes/logging.h | 119 +
gemrb/includes/opcode_params.h | 204 +
gemrb/includes/operatorbool.h | 12 +
gemrb/includes/overlays.h | 60 +
gemrb/includes/plugindef.h | 204 +
gemrb/includes/strrefs.h | 201 +
gemrb/includes/win32def.h | 88 +
gemrb/override/CMakeLists.txt | 7 +
gemrb/override/Makefile.am | 2 +
gemrb/override/bg1/CMakeLists.txt | 1 +
gemrb/override/bg1/Makefile.am | 3 +
gemrb/override/bg1/ability.2da | 9 +
gemrb/override/bg1/acidblgr.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/acidblmu.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/acidblob.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/acidbloc.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/aligns.2da | 12 +
gemrb/override/bg1/areapro.2da | 9 +
gemrb/override/bg1/arrow.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/arrowex.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/arrowflb.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/arrowflg.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/arrowfli.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/arrowflm.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/arrowhvy.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/avatars.2da | 322 +
gemrb/override/bg1/avprefc.2da | 25 +
gemrb/override/bg1/avprefr.2da | 11 +
gemrb/override/bg1/axe.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/axeex.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/bloodclr.2da | 29 +
gemrb/override/bg1/bolt.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/boltex.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/bullet.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/bulletex.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/cgtable.2da | 20 +
gemrb/override/bg1/chromorb.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/classes.2da | 21 +
gemrb/override/bg1/cloud.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/cloudkil.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/clowncol.2da | 7 +
gemrb/override/bg1/clskills.2da | 24 +
gemrb/override/bg1/dagger.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/daggerex.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/damage.2da | 16 +
gemrb/override/bg1/dart.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/dartex.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/defsound.2da | 29 +
gemrb/override/bg1/dualclas.2da | 29 +
gemrb/override/bg1/effects.ids | 319 +
gemrb/override/bg1/fireball.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/fireblic.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/firebolt.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/firebtbl.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/firestor.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/fistweap.2da | 3 +
gemrb/override/bg1/fonts.2da | 14 +
gemrb/override/bg1/formatio.2da | 16 +
gemrb/override/bg1/gametime.2da | 5 +
gemrb/override/bg1/gaze.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/gemprjtl.ids | 207 +
gemrb/override/bg1/gemrb.ini | 80 +
gemrb/override/bg1/grease.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/guibtact.2da | 37 +
gemrb/override/bg1/guils.chu | Bin 0 -> 230 bytes
gemrb/override/bg1/haterace.2da | 13 +
gemrb/override/bg1/icestorm.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/inarea.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/inareasm.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/item_use.2da | 9 +
gemrb/override/bg1/itemsnd.2da | 46 +
gemrb/override/bg1/itemtype.2da | 42 +
gemrb/override/bg1/itemuse.2da | 6 +
gemrb/override/bg1/k_m_e.2da | 5 +
gemrb/override/bg1/k_m_g.2da | 4 +
gemrb/override/bg1/k_m_h.2da | 11 +
gemrb/override/bg1/k_m_he.2da | 7 +
gemrb/override/bg1/kitlist.2da | 12 +
gemrb/override/bg1/kittable.2da | 4 +
gemrb/override/bg1/lightsto.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/magesch.2da | 13 +
gemrb/override/bg1/magicmis.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/mpal256.bmp | Bin 0 -> 92214 bytes
gemrb/override/bg1/music.2da | 42 +
gemrb/override/bg1/overlay.2da | 35 +
gemrb/override/bg1/pathfind.2da | 6 +
gemrb/override/bg1/pdolls.2da | 86 +
gemrb/override/bg1/pictures.2da | 35 +
gemrb/override/bg1/qslots.2da | 24 +
gemrb/override/bg1/races.2da | 9 +
gemrb/override/bg1/randitem.2da | 9 +
gemrb/override/bg1/savegame.2da | 6 +
gemrb/override/bg1/script.2da | 8 +
gemrb/override/bg1/shtable.2da | 42 +
gemrb/override/bg1/skills.2da | 9 +
gemrb/override/bg1/sleep.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/slottype.2da | 42 +
gemrb/override/bg1/sparbknp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparbkpa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparblnp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparblpa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparchnp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparchpa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spargonp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spargopa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spargrnp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spargrpa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparicnp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparicpa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparklbk.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklbl.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklch.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklgo.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklgr.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklic.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklma.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklor.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklpu.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklre.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparklst.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/sparmanp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparmapa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparornp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparorpa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparpunp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparpupa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparrenp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparrepa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparstnp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/sparstpa.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spear.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/spearex.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spfirebl.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/spklarbk.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklarbl.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklarch.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklargo.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklargr.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklaric.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklarma.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklaror.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklarpu.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklarre.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/spklarst.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/splspec.2da | 5 +
gemrb/override/bg1/spscorch.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/spscoric.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/start.2da | 4 +
gemrb/override/bg1/stone.pro | Bin 0 -> 512 bytes
gemrb/override/bg1/strings.2da | 170 +
gemrb/override/bg1/trapglyp.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/trapskul.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/walksnd.2da | 66 +
gemrb/override/bg1/weapprof.2da | 11 +
gemrb/override/bg1/web.pro | Bin 0 -> 768 bytes
gemrb/override/bg1/wssingle.2da | 7 +
gemrb/override/bg2/CMakeLists.txt | 1 +
gemrb/override/bg2/HPBARB.2da | 43 +
gemrb/override/bg2/Makefile.am | 3 +
gemrb/override/bg2/ability.2da | 9 +
gemrb/override/bg2/aligns.2da | 12 +
gemrb/override/bg2/areapro.2da | 22 +
gemrb/override/bg2/at2xlvl.spl | Bin 0 -> 250 bytes
gemrb/override/bg2/avatars.2da | 332 +
gemrb/override/bg2/avprefc.2da | 25 +
gemrb/override/bg2/avprefr.2da | 11 +
gemrb/override/bg2/bloodclr.2da | 29 +
gemrb/override/bg2/cgtable.2da | 38 +
gemrb/override/bg2/classes.2da | 24 +
gemrb/override/bg2/clowncol.2da | 7 +
gemrb/override/bg2/clskills.2da | 24 +
gemrb/override/bg2/colrspry.pro | Bin 0 -> 768 bytes
gemrb/override/bg2/comet.pro | Bin 0 -> 768 bytes
gemrb/override/bg2/damage.2da | 16 +
gemrb/override/bg2/defsound.2da | 29 +
gemrb/override/bg2/denyspl.2da | 4 +
gemrb/override/bg2/effects.ids | 320 +
gemrb/override/bg2/efftext.2da | 10 +
gemrb/override/bg2/findtrap.spl | Bin 0 -> 298 bytes
gemrb/override/bg2/fistweap.2da | 4 +
gemrb/override/bg2/fonts.2da | 19 +
gemrb/override/bg2/formatio.2da | 16 +
gemrb/override/bg2/gametime.2da | 5 +
gemrb/override/bg2/gemprjtl.ids | 78 +
gemrb/override/bg2/gemrb.ini | 92 +
gemrb/override/bg2/gender.2da | 23 +
gemrb/override/bg2/guibtact.2da | 37 +
gemrb/override/bg2/guiid.chu | Bin 0 -> 92 bytes
gemrb/override/bg2/guils.chu | Bin 0 -> 234 bytes
gemrb/override/bg2/halvdur.spl | Bin 0 -> 250 bytes
gemrb/override/bg2/hlymite.pro | Bin 0 -> 512 bytes
gemrb/override/bg2/item_use.2da | 9 +
gemrb/override/bg2/itemsnd.2da | 46 +
gemrb/override/bg2/itemspec.2da | 5 +
gemrb/override/bg2/itemtype.2da | 42 +
gemrb/override/bg2/itemuse.2da | 7 +
gemrb/override/bg2/k_bn_d.2da | 4 +
gemrb/override/bg2/k_bn_e.2da | 4 +
gemrb/override/bg2/k_bn_g.2da | 4 +
gemrb/override/bg2/k_bn_h.2da | 4 +
gemrb/override/bg2/k_bn_he.2da | 4 +
gemrb/override/bg2/k_bn_hl.2da | 4 +
gemrb/override/bg2/k_bn_ho.2da | 4 +
gemrb/override/bg2/k_m_e.2da | 6 +
gemrb/override/bg2/k_m_g.2da | 4 +
gemrb/override/bg2/k_m_h.2da | 13 +
gemrb/override/bg2/k_m_he.2da | 9 +
gemrb/override/bg2/lightb.pro | Bin 0 -> 512 bytes
gemrb/override/bg2/magesch.2da | 13 +
gemrb/override/bg2/minorglb.vvc | Bin 0 -> 492 bytes
gemrb/override/bg2/modal.2da | 8 +
gemrb/override/bg2/monkbon.2da | 6 +
gemrb/override/bg2/overlay.2da | 35 +
gemrb/override/bg2/pathfind.2da | 6 +
gemrb/override/bg2/pdolls.2da | 106 +
gemrb/override/bg2/pictures.2da | 51 +
gemrb/override/bg2/qslot2.2da | 4 +
gemrb/override/bg2/qslots.2da | 24 +
gemrb/override/bg2/races.2da | 11 +
gemrb/override/bg2/randitem.2da | 10 +
gemrb/override/bg2/rest.spl | Bin 0 -> 202 bytes
gemrb/override/bg2/restmov.2da | 11 +
gemrb/override/bg2/sanctry.vvc | Bin 0 -> 492 bytes
gemrb/override/bg2/savegame.2da | 9 +
gemrb/override/bg2/script.2da | 9 +
gemrb/override/bg2/shtable.2da | 43 +
gemrb/override/bg2/skills.2da | 12 +
gemrb/override/bg2/slottype.2da | 42 +
gemrb/override/bg2/snow.spl | Bin 0 -> 202 bytes
gemrb/override/bg2/spbrnhnd.pro | Bin 0 -> 768 bytes
gemrb/override/bg2/spconeco.pro | Bin 0 -> 768 bytes
gemrb/override/bg2/spells.2da | 5 +
gemrb/override/bg2/spentaci.vvc | Bin 0 -> 492 bytes
gemrb/override/bg2/spfdeath.pro | Bin 0 -> 512 bytes
gemrb/override/bg2/spfirewl.pro | Bin 0 -> 768 bytes
gemrb/override/bg2/spgenhla.pro | Bin 0 -> 512 bytes
gemrb/override/bg2/spholymt.pro | Bin 0 -> 512 bytes
gemrb/override/bg2/splspec.2da | 8 +
gemrb/override/bg2/spscorch.pro | Bin 0 -> 768 bytes
gemrb/override/bg2/spscoric.pro | Bin 0 -> 512 bytes
gemrb/override/bg2/spshield.vvc | Bin 0 -> 492 bytes
gemrb/override/bg2/start.2da | 6 +
gemrb/override/bg2/strings.2da | 169 +
gemrb/override/bg2/walksnd.2da | 66 +
gemrb/override/bg2/wildmag.2da | 103 +
gemrb/override/bg2/wishcode.2da | 28 +
gemrb/override/bg2/wsshield.2da | 7 +
gemrb/override/bg2/wssingle.2da | 7 +
gemrb/override/bg2/wstwohnd.2da | 7 +
gemrb/override/bg2/wstwowpn.2da | 7 +
gemrb/override/how/CMakeLists.txt | 1 +
gemrb/override/how/Makefile.am | 3 +
gemrb/override/how/ability.2da | 9 +
gemrb/override/how/abjurap.pro | Bin 0 -> 768 bytes
gemrb/override/how/abjurh.pro | Bin 0 -> 512 bytes
gemrb/override/how/abjurt.pro | Bin 0 -> 512 bytes
gemrb/override/how/acidblgr.pro | Bin 0 -> 512 bytes
gemrb/override/how/acidblmu.pro | Bin 0 -> 512 bytes
gemrb/override/how/acidblob.pro | Bin 0 -> 512 bytes
gemrb/override/how/acidbloc.pro | Bin 0 -> 512 bytes
gemrb/override/how/acidh.pro | Bin 0 -> 512 bytes
gemrb/override/how/adhwil.pro | Bin 0 -> 768 bytes
gemrb/override/how/adhwilh.pro | Bin 0 -> 512 bytes
gemrb/override/how/alance.pro | Bin 0 -> 512 bytes
gemrb/override/how/aligns.2da | 12 +
gemrb/override/how/altera.pro | Bin 0 -> 768 bytes
gemrb/override/how/alteranp.pro | Bin 0 -> 768 bytes
gemrb/override/how/alterap.pro | Bin 0 -> 768 bytes
gemrb/override/how/alteras.pro | Bin 0 -> 768 bytes
gemrb/override/how/alterh.pro | Bin 0 -> 512 bytes
gemrb/override/how/altert.pro | Bin 0 -> 512 bytes
gemrb/override/how/area1np.pro | Bin 0 -> 768 bytes
gemrb/override/how/area1p.pro | Bin 0 -> 768 bytes
gemrb/override/how/area2.pro | Bin 0 -> 768 bytes
gemrb/override/how/area2np.pro | Bin 0 -> 768 bytes
gemrb/override/how/area3p.pro | Bin 0 -> 768 bytes
gemrb/override/how/area4np.pro | Bin 0 -> 768 bytes
gemrb/override/how/areapro.2da | 22 +
gemrb/override/how/armorh.pro | Bin 0 -> 512 bytes
gemrb/override/how/arrow.pro | Bin 0 -> 512 bytes
gemrb/override/how/arrowex.pro | Bin 0 -> 768 bytes
gemrb/override/how/arrowflb.pro | Bin 0 -> 512 bytes
gemrb/override/how/arrowflg.pro | Bin 0 -> 512 bytes
gemrb/override/how/arrowfli.pro | Bin 0 -> 512 bytes
gemrb/override/how/arrowflm.pro | Bin 0 -> 512 bytes
gemrb/override/how/arrowhvy.pro | Bin 0 -> 512 bytes
gemrb/override/how/ascorch.pro | Bin 0 -> 512 bytes
gemrb/override/how/astorm.pro | Bin 0 -> 768 bytes
gemrb/override/how/asumm1.pro | Bin 0 -> 512 bytes
gemrb/override/how/asumm1h.pro | Bin 0 -> 512 bytes
gemrb/override/how/asumm1x.pro | Bin 0 -> 512 bytes
gemrb/override/how/asumm2h.pro | Bin 0 -> 512 bytes
gemrb/override/how/asumm3h.pro | Bin 0 -> 512 bytes
gemrb/override/how/avatars.2da | 417 +
gemrb/override/how/avprefc.2da | 25 +
gemrb/override/how/avprefr.2da | 10 +
gemrb/override/how/axe.pro | Bin 0 -> 512 bytes
gemrb/override/how/axeex.pro | Bin 0 -> 768 bytes
gemrb/override/how/baldur.bcs | 74 +
gemrb/override/how/bbarrh1.pro | Bin 0 -> 512 bytes
gemrb/override/how/bbarrh2.pro | Bin 0 -> 512 bytes
gemrb/override/how/bbarrier.pro | Bin 0 -> 768 bytes
gemrb/override/how/bdeath.pro | Bin 0 -> 512 bytes
gemrb/override/how/blessh.pro | Bin 0 -> 512 bytes
gemrb/override/how/bloodclr.2da | 29 +
gemrb/override/how/bolt.pro | Bin 0 -> 512 bytes
gemrb/override/how/boltex.pro | Bin 0 -> 768 bytes
gemrb/override/how/bscloud.pro | Bin 0 -> 768 bytes
gemrb/override/how/bullet.pro | Bin 0 -> 512 bytes
gemrb/override/how/bulletex.pro | Bin 0 -> 768 bytes
gemrb/override/how/calllih.pro | Bin 0 -> 512 bytes
gemrb/override/how/ccdamah.pro | Bin 0 -> 512 bytes
gemrb/override/how/ccommah.pro | Bin 0 -> 512 bytes
gemrb/override/how/ccwounh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cdiseah.pro | Bin 0 -> 512 bytes
gemrb/override/how/ceelem1.pro | Bin 0 -> 512 bytes
gemrb/override/how/ceelemh.pro | Bin 0 -> 512 bytes
gemrb/override/how/ceelemx.pro | Bin 0 -> 512 bytes
gemrb/override/how/cfelem1.pro | Bin 0 -> 512 bytes
gemrb/override/how/cfelemh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cfelemx.pro | Bin 0 -> 512 bytes
gemrb/override/how/cfog.pro | Bin 0 -> 768 bytes
gemrb/override/how/cgraceh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cgtable.2da | 20 +
gemrb/override/how/chant.pro | Bin 0 -> 768 bytes
gemrb/override/how/chromorb.pro | Bin 0 -> 512 bytes
gemrb/override/how/classes.2da | 21 +
gemrb/override/how/cldamah.pro | Bin 0 -> 512 bytes
gemrb/override/how/cloud.pro | Bin 0 -> 768 bytes
gemrb/override/how/cloudb.pro | Bin 0 -> 768 bytes
gemrb/override/how/cloudbh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cloudkil.pro | Bin 0 -> 768 bytes
gemrb/override/how/cloudks.pro | Bin 0 -> 768 bytes
gemrb/override/how/clowncol.2da | 7 +
gemrb/override/how/clskills.2da | 22 +
gemrb/override/how/clssplab.2da | 22 +
gemrb/override/how/clwounh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cmdamah.pro | Bin 0 -> 512 bytes
gemrb/override/how/cmwounh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cobones.pro | Bin 0 -> 768 bytes
gemrb/override/how/cobonh1.pro | Bin 0 -> 512 bytes
gemrb/override/how/cobonh2.pro | Bin 0 -> 512 bytes
gemrb/override/how/cocold.pro | Bin 0 -> 768 bytes
gemrb/override/how/cocoldh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cofire.pro | Bin 0 -> 768 bytes
gemrb/override/how/coldh.pro | Bin 0 -> 512 bytes
gemrb/override/how/colrspry.pro | Bin 0 -> 768 bytes
gemrb/override/how/confush.pro | Bin 0 -> 512 bytes
gemrb/override/how/confusp.pro | Bin 0 -> 768 bytes
gemrb/override/how/confusw.pro | Bin 0 -> 768 bytes
gemrb/override/how/conjuh.pro | Bin 0 -> 512 bytes
gemrb/override/how/conjut.pro | Bin 0 -> 512 bytes
gemrb/override/how/copest.pro | Bin 0 -> 768 bytes
gemrb/override/how/cry150.pro | Bin 0 -> 768 bytes
gemrb/override/how/cry200.pro | Bin 0 -> 768 bytes
gemrb/override/how/cry225.pro | Bin 0 -> 768 bytes
gemrb/override/how/cry250.pro | Bin 0 -> 768 bytes
gemrb/override/how/cry500np.pro | Bin 0 -> 768 bytes
gemrb/override/how/csdamah.pro | Bin 0 -> 512 bytes
gemrb/override/how/cstrenh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cswounh.pro | Bin 0 -> 512 bytes
gemrb/override/how/curseh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cwelem1.pro | Bin 0 -> 512 bytes
gemrb/override/how/cwelemh.pro | Bin 0 -> 512 bytes
gemrb/override/how/cwelemx.pro | Bin 0 -> 512 bytes
gemrb/override/how/cynicism.2da | 14 +
gemrb/override/how/dagger.pro | Bin 0 -> 512 bytes
gemrb/override/how/daggerex.pro | Bin 0 -> 768 bytes
gemrb/override/how/damage.2da | 16 +
gemrb/override/how/dart.pro | Bin 0 -> 512 bytes
gemrb/override/how/dartex.pro | Bin 0 -> 768 bytes
gemrb/override/how/dbreath.pro | Bin 0 -> 768 bytes
gemrb/override/how/ddeath.pro | Bin 0 -> 512 bytes
gemrb/override/how/ddeath2.pro | Bin 0 -> 512 bytes
gemrb/override/how/ddoorh.pro | Bin 0 -> 512 bytes
gemrb/override/how/defsound.2da | 29 +
gemrb/override/how/destruh.pro | Bin 0 -> 512 bytes
gemrb/override/how/dfog.pro | Bin 0 -> 768 bytes
gemrb/override/how/disint.pro | Bin 0 -> 512 bytes
gemrb/override/how/disinth.pro | Bin 0 -> 512 bytes
gemrb/override/how/dispel.pro | Bin 0 -> 768 bytes
gemrb/override/how/divinh.pro | Bin 0 -> 512 bytes
gemrb/override/how/divint.pro | Bin 0 -> 512 bytes
gemrb/override/how/dspell.pro | Bin 0 -> 768 bytes
gemrb/override/how/dspellh.pro | Bin 0 -> 512 bytes
gemrb/override/how/dualclas.2da | 29 +
gemrb/override/how/effects.ids | 301 +
gemrb/override/how/efftext.2da | 10 +
gemrb/override/how/electrh.pro | Bin 0 -> 512 bytes
gemrb/override/how/emotion.pro | Bin 0 -> 768 bytes
gemrb/override/how/enchah.pro | Bin 0 -> 512 bytes
gemrb/override/how/enchannp.pro | Bin 0 -> 768 bytes
gemrb/override/how/enchat.pro | Bin 0 -> 512 bytes
gemrb/override/how/entangle.pro | Bin 0 -> 768 bytes
gemrb/override/how/equake.pro | Bin 0 -> 768 bytes
gemrb/override/how/exaltah.pro | Bin 0 -> 512 bytes
gemrb/override/how/factioh.pro | Bin 0 -> 512 bytes
gemrb/override/how/findtrap.pro | Bin 0 -> 768 bytes
gemrb/override/how/fireball.pro | Bin 0 -> 768 bytes
gemrb/override/how/fireblic.pro | Bin 0 -> 768 bytes
gemrb/override/how/firebolt.pro | Bin 0 -> 512 bytes
gemrb/override/how/firebtbl.pro | Bin 0 -> 512 bytes
gemrb/override/how/fireh.pro | Bin 0 -> 512 bytes
gemrb/override/how/firestor.pro | Bin 0 -> 768 bytes
gemrb/override/how/fistweap.2da | 3 +
gemrb/override/how/fodeath.pro | Bin 0 -> 512 bytes
gemrb/override/how/fonts.2da | 19 +
gemrb/override/how/formatio.2da | 16 +
gemrb/override/how/fseed.pro | Bin 0 -> 768 bytes
gemrb/override/how/fstrikh.pro | Bin 0 -> 512 bytes
gemrb/override/how/gametime.2da | 5 +
gemrb/override/how/garmorh.pro | Bin 0 -> 512 bytes
gemrb/override/how/gaze.pro | Bin 0 -> 512 bytes
gemrb/override/how/gemprjtl.ids | 465 +
gemrb/override/how/gemrb.ini | 123 +
gemrb/override/how/gender.2da | 22 +
gemrb/override/how/golcloud.pro | Bin 0 -> 768 bytes
gemrb/override/how/grease.pro | Bin 0 -> 768 bytes
gemrb/override/how/gshout.pro | Bin 0 -> 768 bytes
gemrb/override/how/guibtact.2da | 37 +
gemrb/override/how/guils.chu | Bin 0 -> 230 bytes
gemrb/override/how/harmh.pro | Bin 0 -> 512 bytes
gemrb/override/how/harmony.pro | Bin 0 -> 768 bytes
gemrb/override/how/haterace.2da | 15 +
gemrb/override/how/healh.pro | Bin 0 -> 512 bytes
gemrb/override/how/hmighth.pro | Bin 0 -> 512 bytes
gemrb/override/how/hold.pro | Bin 0 -> 768 bytes
gemrb/override/how/hsmite.pro | Bin 0 -> 768 bytes
gemrb/override/how/hword.pro | Bin 0 -> 768 bytes
gemrb/override/how/icelance.pro | Bin 0 -> 512 bytes
gemrb/override/how/icestorm.pro | Bin 0 -> 768 bytes
gemrb/override/how/icloud.pro | Bin 0 -> 768 bytes
gemrb/override/how/iclouda.pro | Bin 0 -> 768 bytes
gemrb/override/how/icloudb.pro | Bin 0 -> 768 bytes
gemrb/override/how/illush.pro | Bin 0 -> 512 bytes
gemrb/override/how/illust.pro | Bin 0 -> 512 bytes
gemrb/override/how/inarea.pro | Bin 0 -> 768 bytes
gemrb/override/how/inareanp.pro | Bin 0 -> 768 bytes
gemrb/override/how/inareasm.pro | Bin 0 -> 768 bytes
gemrb/override/how/invoch.pro | Bin 0 -> 512 bytes
gemrb/override/how/invoct.pro | Bin 0 -> 512 bytes
gemrb/override/how/iplague.pro | Bin 0 -> 768 bytes
gemrb/override/how/island00.2da | 11 +
gemrb/override/how/itemsnd.2da | 80 +
gemrb/override/how/itemtype.2da | 77 +
gemrb/override/how/itemuse.2da | 6 +
gemrb/override/how/kitlist.2da | 12 +
gemrb/override/how/lightb.pro | Bin 0 -> 512 bytes
gemrb/override/how/lightsto.pro | Bin 0 -> 768 bytes
gemrb/override/how/lodisr.pro | Bin 0 -> 768 bytes
gemrb/override/how/magesch.2da | 12 +
gemrb/override/how/magicmis.pro | Bin 0 -> 512 bytes
gemrb/override/how/malison.pro | Bin 0 -> 768 bytes
gemrb/override/how/mfmiss.pro | Bin 0 -> 512 bytes
gemrb/override/how/mfmiss2.pro | Bin 0 -> 512 bytes
gemrb/override/how/mfmissh.pro | Bin 0 -> 512 bytes
gemrb/override/how/mmagich.pro | Bin 0 -> 512 bytes
gemrb/override/how/mmissile.pro | Bin 0 -> 512 bytes
gemrb/override/how/moelda.pro | Bin 0 -> 768 bytes
gemrb/override/how/mrage.pro | Bin 0 -> 768 bytes
gemrb/override/how/mrageh.pro | Bin 0 -> 512 bytes
gemrb/override/how/mspore.pro | Bin 0 -> 512 bytes
gemrb/override/how/msumm1.pro | Bin 0 -> 512 bytes
gemrb/override/how/msumm1h.pro | Bin 0 -> 512 bytes
gemrb/override/how/msumm1x.pro | Bin 0 -> 512 bytes
gemrb/override/how/msumm2h.pro | Bin 0 -> 512 bytes
gemrb/override/how/msumm3h.pro | Bin 0 -> 512 bytes
gemrb/override/how/msumm4h.pro | Bin 0 -> 512 bytes
gemrb/override/how/msumm5h.pro | Bin 0 -> 512 bytes
gemrb/override/how/msumm6h.pro | Bin 0 -> 512 bytes
gemrb/override/how/msumm7h.pro | Bin 0 -> 512 bytes
gemrb/override/how/mswordh.pro | Bin 0 -> 512 bytes
gemrb/override/how/mtouchh.pro | Bin 0 -> 512 bytes
gemrb/override/how/necroh.pro | Bin 0 -> 512 bytes
gemrb/override/how/necrot.pro | Bin 0 -> 512 bytes
gemrb/override/how/npoisoh.pro | Bin 0 -> 512 bytes
gemrb/override/how/ofsphe.pro | Bin 0 -> 512 bytes
gemrb/override/how/overlay.2da | 35 +
gemrb/override/how/paralh.pro | Bin 0 -> 512 bytes
gemrb/override/how/pathfind.2da | 6 +
gemrb/override/how/pdolls.2da | 75 +
gemrb/override/how/pfire.pro | Bin 0 -> 768 bytes
gemrb/override/how/pictures.2da | 36 +
gemrb/override/how/poisonh.pro | Bin 0 -> 512 bytes
gemrb/override/how/pomab.2da | 10 +
gemrb/override/how/portal.pro | Bin 0 -> 512 bytes
gemrb/override/how/prayer.pro | Bin 0 -> 768 bytes
gemrb/override/how/prayerh.pro | Bin 0 -> 512 bytes
gemrb/override/how/protevil.pro | Bin 0 -> 768 bytes
gemrb/override/how/prtl_cl.pro | Bin 0 -> 768 bytes
gemrb/override/how/prtl_cl.spl | Bin 0 -> 202 bytes
gemrb/override/how/prtl_op.pro | Bin 0 -> 768 bytes
gemrb/override/how/prtl_op.spl | Bin 0 -> 202 bytes
gemrb/override/how/pspray.pro | Bin 0 -> 768 bytes
gemrb/override/how/pwkill.pro | Bin 0 -> 768 bytes
gemrb/override/how/pwsileh.pro | Bin 0 -> 512 bytes
gemrb/override/how/pwstun.pro | Bin 0 -> 768 bytes
gemrb/override/how/pwstunh.pro | Bin 0 -> 512 bytes
gemrb/override/how/qslots.2da | 24 +
gemrb/override/how/races.2da | 9 +
gemrb/override/how/rad100.pro | Bin 0 -> 768 bytes
gemrb/override/how/rad250.pro | Bin 0 -> 768 bytes
gemrb/override/how/randitem.2da | 5 +
gemrb/override/how/rdeadh.pro | Bin 0 -> 512 bytes
gemrb/override/how/recitah.pro | Bin 0 -> 512 bytes
gemrb/override/how/resurrh.pro | Bin 0 -> 512 bytes
gemrb/override/how/rng450.pro | Bin 0 -> 768 bytes
gemrb/override/how/rparalh.pro | Bin 0 -> 512 bytes
gemrb/override/how/rwotfag.pro | Bin 0 -> 512 bytes
gemrb/override/how/rwotfah.pro | Bin 0 -> 512 bytes
gemrb/override/how/sarmorh.pro | Bin 0 -> 512 bytes
gemrb/override/how/savegame.2da | 6 +
gemrb/override/how/scharge.pro | Bin 0 -> 768 bytes
gemrb/override/how/scharge.spl | Bin 0 -> 250 bytes
gemrb/override/how/schargh.pro | Bin 0 -> 512 bytes
gemrb/override/how/script.2da | 8 +
gemrb/override/how/seater.pro | Bin 0 -> 768 bytes
gemrb/override/how/seaterh.pro | Bin 0 -> 512 bytes
gemrb/override/how/sgrowth.pro | Bin 0 -> 768 bytes
gemrb/override/how/shout.pro | Bin 0 -> 768 bytes
gemrb/override/how/shroud.pro | Bin 0 -> 768 bytes
gemrb/override/how/shtable.2da | 43 +
gemrb/override/how/skills.2da | 9 +
gemrb/override/how/sleep.pro | Bin 0 -> 768 bytes
gemrb/override/how/slivinh.pro | Bin 0 -> 512 bytes
gemrb/override/how/slottype.2da | 42 +
gemrb/override/how/soflamh.pro | Bin 0 -> 512 bytes
gemrb/override/how/sohope.pro | Bin 0 -> 768 bytes
gemrb/override/how/sooneh.pro | Bin 0 -> 512 bytes
gemrb/override/how/sopain.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparbknp.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparbkpa.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparblnp.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparblpa.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparchnp.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparchpa.pro | Bin 0 -> 768 bytes
gemrb/override/how/spargonp.pro | Bin 0 -> 768 bytes
gemrb/override/how/spargopa.pro | Bin 0 -> 768 bytes
gemrb/override/how/spargrnp.pro | Bin 0 -> 768 bytes
gemrb/override/how/spargrpa.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparicnp.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparicpa.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparklbk.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklbl.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklch.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklgo.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklgr.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklic.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklma.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklor.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklpu.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklre.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparklst.pro | Bin 0 -> 512 bytes
gemrb/override/how/sparmanp.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparmapa.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparornp.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparorpa.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparpunp.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparpupa.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparrenp.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparrepa.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparstnp.pro | Bin 0 -> 768 bytes
gemrb/override/how/sparstpa.pro | Bin 0 -> 768 bytes
gemrb/override/how/spear.pro | Bin 0 -> 512 bytes
gemrb/override/how/spearex.pro | Bin 0 -> 768 bytes
gemrb/override/how/spfirebl.pro | Bin 0 -> 512 bytes
gemrb/override/how/spklarbk.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklarbl.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklarch.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklargo.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklargr.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklaric.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklarma.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklaror.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklarpu.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklarre.pro | Bin 0 -> 768 bytes
gemrb/override/how/spklarst.pro | Bin 0 -> 768 bytes
gemrb/override/how/splprot.2da | 105 +
gemrb/override/how/splspec.2da | 4 +
gemrb/override/how/spoisoh.pro | Bin 0 -> 512 bytes
gemrb/override/how/spscorch.pro | Bin 0 -> 512 bytes
gemrb/override/how/spscoric.pro | Bin 0 -> 512 bytes
gemrb/override/how/spwrath.pro | Bin 0 -> 768 bytes
gemrb/override/how/ssorbh.pro | Bin 0 -> 512 bytes
gemrb/override/how/ssswarm.pro | Bin 0 -> 768 bytes
gemrb/override/how/sstone.pro | Bin 0 -> 768 bytes
gemrb/override/how/sstoneh.pro | Bin 0 -> 512 bytes
gemrb/override/how/start.2da | 5 +
gemrb/override/how/stone.pro | Bin 0 -> 512 bytes
gemrb/override/how/strengh.pro | Bin 0 -> 512 bytes
gemrb/override/how/strings.2da | 170 +
gemrb/override/how/suffoc.pro | Bin 0 -> 768 bytes
gemrb/override/how/suffoch.pro | Bin 0 -> 512 bytes
gemrb/override/how/sunfire.pro | Bin 0 -> 768 bytes
gemrb/override/how/sunray.pro | Bin 0 -> 768 bytes
gemrb/override/how/sunscoh.pro | Bin 0 -> 512 bytes
gemrb/override/how/swave.pro | Bin 0 -> 768 bytes
gemrb/override/how/swaveh.pro | Bin 0 -> 512 bytes
gemrb/override/how/trapglyp.pro | Bin 0 -> 768 bytes
gemrb/override/how/trapskul.pro | Bin 0 -> 768 bytes
gemrb/override/how/tspray.pro | Bin 0 -> 768 bytes
gemrb/override/how/turn.spl | Bin 0 -> 202 bytes
gemrb/override/how/ublight.pro | Bin 0 -> 768 bytes
gemrb/override/how/uward.pro | Bin 0 -> 768 bytes
gemrb/override/how/uwardh.pro | Bin 0 -> 512 bytes
gemrb/override/how/vcremap.2da | 4 +
gemrb/override/how/vspherh.pro | Bin 0 -> 512 bytes
gemrb/override/how/wdeath1.pro | Bin 0 -> 512 bytes
gemrb/override/how/wdeath2.pro | Bin 0 -> 512 bytes
gemrb/override/how/weapprof.2da | 18 +
gemrb/override/how/web.pro | Bin 0 -> 768 bytes
gemrb/override/how/whirlw.pro | Bin 0 -> 768 bytes
gemrb/override/how/womoon.pro | Bin 0 -> 768 bytes
gemrb/override/how/wowisp.pro | Bin 0 -> 768 bytes
gemrb/override/how/wvdeath.pro | Bin 0 -> 512 bytes
gemrb/override/how/wvhith.pro | Bin 0 -> 512 bytes
gemrb/override/how/wwolf.pro | Bin 0 -> 768 bytes
gemrb/override/how/zlaura.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/CMakeLists.txt | 1 +
gemrb/override/iwd/Makefile.am | 3 +
gemrb/override/iwd/ability.2da | 9 +
gemrb/override/iwd/abjurap.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/abjurh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/abjurt.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/acidblgr.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/acidblmu.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/acidblob.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/acidbloc.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/acidh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/adhwil.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/adhwilh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/alance.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/aligns.2da | 12 +
gemrb/override/iwd/altera.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/alteranp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/alterap.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/alteras.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/alterh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/altert.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/area1np.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/area1p.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/area2.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/area2np.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/area3p.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/area4np.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/armorh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/arrow.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/arrowex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/arrowflb.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/arrowflg.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/arrowfli.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/arrowflm.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/arrowhvy.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ascorch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/astorm.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/asumm1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/asumm1h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/asumm1x.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/asumm2h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/asumm3h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/avatars.2da | 414 +
gemrb/override/iwd/avprefc.2da | 25 +
gemrb/override/iwd/avprefr.2da | 10 +
gemrb/override/iwd/axe.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/axeex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/baldur.bcs | 74 +
gemrb/override/iwd/bbarrh1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/bbarrh2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/bbarrier.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/bdeath.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/blessh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/bloodclr.2da | 29 +
gemrb/override/iwd/bolt.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/boltex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/bscloud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/bullet.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/bulletex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/calllih.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ccdamah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ccommah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ccwounh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cdiseah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ceelem1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ceelemh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ceelemx.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cfelem1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cfelemh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cfelemx.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cfog.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cgraceh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cgtable.2da | 20 +
gemrb/override/iwd/chant.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/chromorb.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/classes.2da | 21 +
gemrb/override/iwd/cldamah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cloud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cloudb.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cloudbh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cloudkil.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cloudks.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/clowncol.2da | 7 +
gemrb/override/iwd/clskills.2da | 22 +
gemrb/override/iwd/clssplab.2da | 22 +
gemrb/override/iwd/clwounh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cmdamah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cmwounh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cobones.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cobonh1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cobonh2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cocold.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cocoldh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cofire.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/coldh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/colrspry.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/confush.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/confusp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/confusw.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/conjuh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/conjut.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/copest.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cry150.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cry200.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cry225.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cry250.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/cry500np.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/csdamah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cstrenh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cswounh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/curseh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cwelem1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cwelemh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cwelemx.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/cynicism.2da | 14 +
gemrb/override/iwd/dagger.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/daggerex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/damage.2da | 16 +
gemrb/override/iwd/dart.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/dartex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/dbreath.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/ddeath.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ddeath2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ddoorh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/defsound.2da | 29 +
gemrb/override/iwd/destruh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/dfog.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/disint.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/disinth.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/dispel.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/divinh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/divint.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/dspell.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/dspellh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/dualclas.2da | 29 +
gemrb/override/iwd/effects.ids | 300 +
gemrb/override/iwd/efftext.2da | 10 +
gemrb/override/iwd/electrh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/emotion.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/enchah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/enchannp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/enchat.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/entangle.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/equake.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/exaltah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/factioh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/findtrap.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/fireball.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/fireblic.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/firebolt.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/firebtbl.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/fireh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/firestor.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/fistweap.2da | 3 +
gemrb/override/iwd/fodeath.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/fonts.2da | 19 +
gemrb/override/iwd/formatio.2da | 16 +
gemrb/override/iwd/fseed.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/fstrikh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/gametime.2da | 5 +
gemrb/override/iwd/garmorh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/gaze.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/gemprjtl.ids | 465 +
gemrb/override/iwd/gemrb.ini | 121 +
gemrb/override/iwd/gender.2da | 22 +
gemrb/override/iwd/golcloud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/grease.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/gshout.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/guibtact.2da | 37 +
gemrb/override/iwd/guils.chu | Bin 0 -> 230 bytes
gemrb/override/iwd/harmh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/harmony.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/haterace.2da | 15 +
gemrb/override/iwd/healh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/hmighth.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/hold.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/hsmite.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/hword.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/icelance.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/icestorm.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/icloud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/iclouda.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/icloudb.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/illush.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/illust.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/inarea.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/inareanp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/inareasm.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/invoch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/invoct.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/iplague.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/itemsnd.2da | 80 +
gemrb/override/iwd/itemtype.2da | 77 +
gemrb/override/iwd/itemuse.2da | 6 +
gemrb/override/iwd/kitlist.2da | 12 +
gemrb/override/iwd/lightb.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/lightsto.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/lodisr.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/magesch.2da | 12 +
gemrb/override/iwd/magicmis.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/malison.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/mfmiss.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/mfmiss2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/mfmissh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/mmagich.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/mmissile.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/moelda.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/mpal256.bmp | Bin 0 -> 92214 bytes
gemrb/override/iwd/mrage.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/mrageh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/mspore.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/msumm1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/msumm1h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/msumm1x.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/msumm2h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/msumm3h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/msumm4h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/msumm5h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/msumm6h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/msumm7h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/mswordh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/mtouchh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/necroh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/necrot.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/npoisoh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ofsphe.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/overlay.2da | 35 +
gemrb/override/iwd/paralh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/pathfind.2da | 5 +
gemrb/override/iwd/pdolls.2da | 75 +
gemrb/override/iwd/pfire.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/pictures.2da | 36 +
gemrb/override/iwd/poisonh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/pomab.2da | 10 +
gemrb/override/iwd/portal.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/prayer.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/prayerh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/protevil.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/prtl_cl.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/prtl_cl.spl | Bin 0 -> 202 bytes
gemrb/override/iwd/prtl_op.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/prtl_op.spl | Bin 0 -> 202 bytes
gemrb/override/iwd/pspray.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/pwkill.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/pwsileh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/pwstun.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/pwstunh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/qslots.2da | 24 +
gemrb/override/iwd/races.2da | 9 +
gemrb/override/iwd/rad100.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/rad250.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/randitem.2da | 5 +
gemrb/override/iwd/rdeadh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/recitah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/resurrh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/rng450.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/rparalh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/rwotfag.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/rwotfah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sarmorh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/savegame.2da | 6 +
gemrb/override/iwd/scharge.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/scharge.spl | Bin 0 -> 250 bytes
gemrb/override/iwd/schargh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/script.2da | 8 +
gemrb/override/iwd/seater.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/seaterh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sgrowth.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/shout.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/shroud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/shtable.2da | 43 +
gemrb/override/iwd/skills.2da | 9 +
gemrb/override/iwd/sleep.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/slivinh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/slottype.2da | 42 +
gemrb/override/iwd/soflamh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sohope.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sooneh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sopain.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparbknp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparbkpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparblnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparblpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparchnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparchpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spargonp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spargopa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spargrnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spargrpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparicnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparicpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparklbk.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklbl.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklgo.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklgr.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklic.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklma.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklor.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklpu.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklre.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparklst.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sparmanp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparmapa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparornp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparorpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparpunp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparpupa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparrenp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparrepa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparstnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sparstpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spear.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/spearex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spfirebl.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/spklarbk.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklarbl.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklarch.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklargo.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklargr.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklaric.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklarma.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklaror.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklarpu.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklarre.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/spklarst.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/splprot.2da | 105 +
gemrb/override/iwd/splspec.2da | 4 +
gemrb/override/iwd/spoisoh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/spscorch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/spscoric.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/spwrath.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/ssorbh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/ssswarm.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sstone.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sstoneh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/start.2da | 5 +
gemrb/override/iwd/stone.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/strengh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/strings.2da | 170 +
gemrb/override/iwd/suffoc.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/suffoch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/sunfire.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sunray.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/sunscoh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/swave.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/swaveh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/trapglyp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/trapskul.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/tspray.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/ublight.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/uward.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/uwardh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/vcremap.2da | 4 +
gemrb/override/iwd/vspherh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/wdeath1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/wdeath2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/weapprof.2da | 18 +
gemrb/override/iwd/web.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/whirlw.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/womoon.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/wowisp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/wvdeath.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/wvhith.pro | Bin 0 -> 512 bytes
gemrb/override/iwd/wwolf.pro | Bin 0 -> 768 bytes
gemrb/override/iwd/zlaura.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/CMakeLists.txt | 1 +
gemrb/override/iwd2/Makefile.am | 3 +
gemrb/override/iwd2/ability.2da | 9 +
gemrb/override/iwd2/abjura.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/abjurap.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/abjurh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/abjurt.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/abreath.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/acidblgr.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/acidblmu.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/acidblob.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/acidbloc.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/acidh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/adhwil.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/adhwilh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/alance.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/aligns.2da | 12 +
gemrb/override/iwd2/altera.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/alteranp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/alterap.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/alteraps.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/alteras.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/alterh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/altert.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/area1np.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/area1npl.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/area1p.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/area1pl.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/area1ps.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/area2.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/area2np.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/area3p.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/area4np.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/armorh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/arrow.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/arrowex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/arrowflb.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/arrowflg.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/arrowfli.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/arrowflm.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/arrowhvy.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ascorch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/astorm.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/asumm1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/asumm1h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/asumm1x.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/asumm2h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/asumm3h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/avatars.2da | 432 +
gemrb/override/iwd2/avprefc.2da | 15 +
gemrb/override/iwd2/avprefr.2da | 11 +
gemrb/override/iwd2/axe.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/axeex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/bbarrh1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/bbarrh2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/bbarrier.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/bdeath.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/bigboldr.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/bios.2da | 14 +
gemrb/override/iwd2/blessh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/bloodclr.2da | 29 +
gemrb/override/iwd2/bolt.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/boltex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/boulder.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/bscloud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/bullet.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/bulletex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/calllih.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ccdamah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ccommah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ccwounh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cdiseah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ceelem1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ceelemh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ceelemx.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cfelem1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cfelemh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cfelemx.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cfog.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cgraceh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cgtable.2da | 20 +
gemrb/override/iwd2/chant.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/chromorb.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/classes.2da | 38 +
gemrb/override/iwd2/cldamah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cloud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cloudb.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cloudbh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cloudkil.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cloudks.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/clowncol.2da | 7 +
gemrb/override/iwd2/clskills.2da | 15 +
gemrb/override/iwd2/clwounh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cmdamah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cmwounh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cobones.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cobonh1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cobonh2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cocold.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cocoldh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cofire.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/coldh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/colrspry.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/confush.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/confusp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/confusw.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/conjuh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/conjut.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/copest.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cry150.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cry200.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cry225.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cry250.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/cry500np.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/csdamah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cstrenh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cswounh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/curseh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cwelem1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cwelemh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/cwelemx.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/dagger.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/daggerex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/damage.2da | 16 +
gemrb/override/iwd2/dart.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/dartex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/dattach.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/dbreath.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/ddeath.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ddeath2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ddoorh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/defsound.2da | 29 +
gemrb/override/iwd2/destruh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/dfog.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/disint.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/disinth.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/dispel.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/divinh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/divint.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/dspell.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/dspellh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/effects.ids | 356 +
gemrb/override/iwd2/efftext.2da | 25 +
gemrb/override/iwd2/electrh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/emotion.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/enchah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/enchan.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/enchannp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/enchat.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/entangle.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/entropy.2da | 62 +
gemrb/override/iwd2/equake.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/exaltah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/factioh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/fbreath.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/featreq.2da | 79 +
gemrb/override/iwd2/ffinger.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/findtrap.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/fireball.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/fireblic.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/firebolt.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/firebtbl.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/fireh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/firestor.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/fistweap.2da | 4 +
gemrb/override/iwd2/fodeath.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/fonts.2da | 19 +
gemrb/override/iwd2/formatio.2da | 16 +
gemrb/override/iwd2/fseed.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/fstrikh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/gabreath.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/gametime.2da | 5 +
gemrb/override/iwd2/garmorh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/gate.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/gaze.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/geloopx.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/gemprjtl.ids | 498 +
gemrb/override/iwd2/gemrb.ini | 98 +
gemrb/override/iwd2/gender.2da | 22 +
gemrb/override/iwd2/golcloud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/grease.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/gshout.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/guibtact.2da | 30 +
gemrb/override/iwd2/guils.chu | Bin 0 -> 284 bytes
gemrb/override/iwd2/harmh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/harmony.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/haterace.2da | 19 +
gemrb/override/iwd2/healh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/hmighth.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/hold.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/hsmite.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/hword.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/icelance.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/icestorm.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/icloud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/iclouda.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/icloudb.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/illush.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/illust.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/inarea.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/inareanp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/inareasm.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/invoch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/invoct.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/iplague.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/island00.2da | 11 +
gemrb/override/iwd2/itemsnd.2da | 80 +
gemrb/override/iwd2/itemtype.2da | 77 +
gemrb/override/iwd2/itemuse.2da | 7 +
gemrb/override/iwd2/iwdshtab.2da | 117 +
gemrb/override/iwd2/lightb.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/lightsto.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/lodisr.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/magicmis.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/malison.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/mfmiss.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/mfmiss2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/mfmissh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/mfog.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/mmagich.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/mmissile.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/modal.2da | 8 +
gemrb/override/iwd2/moelda.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/mrage.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/mrageh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/mspore.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/msumm1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/msumm1h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/msumm1x.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/msumm2h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/msumm3h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/msumm4h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/msumm5h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/msumm6h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/msumm7h.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/mswordh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/mtouchh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/necroh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/necrot.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/npoisoh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ofsphe.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/overlay.2da | 35 +
gemrb/override/iwd2/paralh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/pathfind.2da | 5 +
gemrb/override/iwd2/pfire.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/pictures.2da | 86 +
gemrb/override/iwd2/poisonh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/polystat.2da | 25 +
gemrb/override/iwd2/portal.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/prayer.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/prayerh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/protevil.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/prtl_cl.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/prtl_op.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/pspray.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/pwkill.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/pwsileh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/pwstun.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/pwstunh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/races.2da | 19 +
gemrb/override/iwd2/rad100.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/rad250.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/rad300.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/randitem.2da | 5 +
gemrb/override/iwd2/rdeadh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/recitah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/resurrh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/rng450.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/rparalh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/rwotfag.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/rwotfah.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sarmorh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/savegame.2da | 6 +
gemrb/override/iwd2/scharge.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/schargh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/script.2da | 8 +
gemrb/override/iwd2/seater.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/seaterh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sgrowth.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/shout.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/shroud.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/shtable.2da | 43 +
gemrb/override/iwd2/skillsta.2da | 19 +
gemrb/override/iwd2/sleep.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/slivinh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/slottype.2da | 54 +
gemrb/override/iwd2/soflamh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sohope.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sooneh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sopain.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparbknp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparbkpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparblnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparblpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparchnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparchpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spargonp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spargopa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spargrnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spargrpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparicnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparicpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparklbk.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklbl.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklgo.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklgr.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklic.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklma.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklor.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklpu.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklre.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparklst.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sparmanp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparmapa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparornp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparorpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparpunp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparpupa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparrenp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparrepa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparstnp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sparstpa.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spbrnhnd.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spear.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/spearex.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spfdeath.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/spfirebl.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/spklarbk.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklarbl.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklarch.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklargo.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklargr.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklaric.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklarma.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklaror.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklarpu.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklarre.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/spklarst.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/splprot.2da | 105 +
gemrb/override/iwd2/splspec.2da | 4 +
gemrb/override/iwd2/spoisoh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/spscorch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/spscoric.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/spwrath.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/ssorbh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/ssswarm.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sstone.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sstoneh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/start.2da | 5 +
gemrb/override/iwd2/stone.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/strengh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/strings.2da | 170 +
gemrb/override/iwd2/suffoc.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/suffoch.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/sunfire.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sunray.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/sunscoh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/swave.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/swaveh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/topics.2da | 9 +
gemrb/override/iwd2/trap.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/trapglyp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/trapskul.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/tspray.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/turn.spl | Bin 0 -> 202 bytes
gemrb/override/iwd2/ublight.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/uhold.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/uward.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/uwardh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/vspherh.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/wdeath1.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/wdeath2.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/web.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/whirlw.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/wofire.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/womoon.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/womoonx.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/wowisp.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/wvdeath.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/wvhith.pro | Bin 0 -> 512 bytes
gemrb/override/iwd2/wwolf.pro | Bin 0 -> 768 bytes
gemrb/override/iwd2/zlaura.pro | Bin 0 -> 768 bytes
gemrb/override/pst/CMakeLists.txt | 1 +
gemrb/override/pst/MPALETTE.bmp | Bin 0 -> 7638 bytes
gemrb/override/pst/Makefile.am | 3 +
gemrb/override/pst/abcomm.2da | 29 +
gemrb/override/pst/acidblgr.pro | Bin 0 -> 512 bytes
gemrb/override/pst/acidblmu.pro | Bin 0 -> 512 bytes
gemrb/override/pst/acidblob.pro | Bin 0 -> 512 bytes
gemrb/override/pst/acidbloc.pro | Bin 0 -> 512 bytes
gemrb/override/pst/aligns.2da | 12 +
gemrb/override/pst/amiss.pro | Bin 0 -> 512 bytes
gemrb/override/pst/amiss2.pro | Bin 0 -> 512 bytes
gemrb/override/pst/anims.2da | 10 +
gemrb/override/pst/aotorm.pro | Bin 0 -> 512 bytes
gemrb/override/pst/areapro.2da | 9 +
gemrb/override/pst/arrow.pro | Bin 0 -> 512 bytes
gemrb/override/pst/arrowex.pro | Bin 0 -> 768 bytes
gemrb/override/pst/arrowflb.pro | Bin 0 -> 512 bytes
gemrb/override/pst/arrowflg.pro | Bin 0 -> 512 bytes
gemrb/override/pst/arrowfli.pro | Bin 0 -> 512 bytes
gemrb/override/pst/arrowflm.pro | Bin 0 -> 512 bytes
gemrb/override/pst/arrowhvy.pro | Bin 0 -> 512 bytes
gemrb/override/pst/astorm.pro | Bin 0 -> 768 bytes
gemrb/override/pst/astorm.spl | Bin 0 -> 298 bytes
gemrb/override/pst/avatars.2da | 105 +
gemrb/override/pst/avprefix.2da | 4 +
gemrb/override/pst/avslots.2da | 13 +
gemrb/override/pst/axe.pro | Bin 0 -> 512 bytes
gemrb/override/pst/axeex.pro | Bin 0 -> 768 bytes
gemrb/override/pst/axoft.spl | Bin 0 -> 770 bytes
gemrb/override/pst/baldur.bcs | 402 +
gemrb/override/pst/bbridge.pro | Bin 0 -> 512 bytes
gemrb/override/pst/bbridge.spl | Bin 0 -> 202 bytes
gemrb/override/pst/bios.2da | 13 +
gemrb/override/pst/blessed.pro | Bin 0 -> 768 bytes
gemrb/override/pst/bluetint.spl | Bin 0 -> 202 bytes
gemrb/override/pst/bolt.pro | Bin 0 -> 512 bytes
gemrb/override/pst/boltex.pro | Bin 0 -> 768 bytes
gemrb/override/pst/bstorm.pro | Bin 0 -> 768 bytes
gemrb/override/pst/bstorm.spl | Bin 0 -> 1066 bytes
gemrb/override/pst/bstorm2.pro | Bin 0 -> 512 bytes
gemrb/override/pst/bstorm2.spl | Bin 0 -> 586 bytes
gemrb/override/pst/bullet.pro | Bin 0 -> 512 bytes
gemrb/override/pst/bulletex.pro | Bin 0 -> 768 bytes
gemrb/override/pst/callih.pro | Bin 0 -> 768 bytes
gemrb/override/pst/chost.2da | 22 +
gemrb/override/pst/chost.spl | Bin 0 -> 298 bytes
gemrb/override/pst/chromorb.pro | Bin 0 -> 768 bytes
gemrb/override/pst/classes.2da | 12 +
gemrb/override/pst/cloud.pro | Bin 0 -> 768 bytes
gemrb/override/pst/cloudkil.pro | Bin 0 -> 768 bytes
gemrb/override/pst/clskills.2da | 13 +
gemrb/override/pst/clssplab.2da | 22 +
gemrb/override/pst/cocold.pro | Bin 0 -> 768 bytes
gemrb/override/pst/colrspry.pro | Bin 0 -> 768 bytes
gemrb/override/pst/conflag.pro | Bin 0 -> 512 bytes
gemrb/override/pst/conflag.spl | Bin 0 -> 250 bytes
gemrb/override/pst/confus.pro | Bin 0 -> 768 bytes
gemrb/override/pst/cursed.pro | Bin 0 -> 768 bytes
gemrb/override/pst/dagger.pro | Bin 0 -> 512 bytes
gemrb/override/pst/daggerex.pro | Bin 0 -> 768 bytes
gemrb/override/pst/damage.2da | 16 +
gemrb/override/pst/dart.pro | Bin 0 -> 512 bytes
gemrb/override/pst/dartex.pro | Bin 0 -> 768 bytes
gemrb/override/pst/dbolt.pro | Bin 0 -> 768 bytes
gemrb/override/pst/dbolt.spl | Bin 0 -> 202 bytes
gemrb/override/pst/defsound.2da | 29 +
gemrb/override/pst/dhell.pro | Bin 0 -> 768 bytes
gemrb/override/pst/dhell.spl | Bin 0 -> 490 bytes
gemrb/override/pst/easymaze.2da | 12 +
gemrb/override/pst/ebomb.pro | Bin 0 -> 768 bytes
gemrb/override/pst/effects.ids | 213 +
gemrb/override/pst/efftext.2da | 2 +
gemrb/override/pst/elyfire.pro | Bin 0 -> 768 bytes
gemrb/override/pst/elytear.pro | Bin 0 -> 768 bytes
gemrb/override/pst/embalm.pro | Bin 0 -> 512 bytes
gemrb/override/pst/estrike.pro | Bin 0 -> 512 bytes
gemrb/override/pst/factions.2da | 20 +
gemrb/override/pst/faerie.spl | Bin 0 -> 250 bytes
gemrb/override/pst/fbomb.pro | Bin 0 -> 768 bytes
gemrb/override/pst/fbyello.pro | Bin 0 -> 512 bytes
gemrb/override/pst/fearaura.pro | Bin 0 -> 768 bytes
gemrb/override/pst/ffire.pro | Bin 0 -> 768 bytes
gemrb/override/pst/fireball.pro | Bin 0 -> 768 bytes
gemrb/override/pst/firebolt.pro | Bin 0 -> 512 bytes
gemrb/override/pst/firebtbl.pro | Bin 0 -> 512 bytes
gemrb/override/pst/fireice.pro | Bin 0 -> 768 bytes
gemrb/override/pst/fireice.spl | Bin 0 -> 250 bytes
gemrb/override/pst/firestor.pro | Bin 0 -> 768 bytes
gemrb/override/pst/fistweap.2da | 13 +
gemrb/override/pst/fizzle.spl | Bin 0 -> 250 bytes
gemrb/override/pst/fmiss.pro | Bin 0 -> 512 bytes
gemrb/override/pst/fonts.2da | 12 +
gemrb/override/pst/formatio.2da | 17 +
gemrb/override/pst/frain.pro | Bin 0 -> 768 bytes
gemrb/override/pst/gametime.2da | 5 +
gemrb/override/pst/gaze.pro | Bin 0 -> 512 bytes
gemrb/override/pst/gemprjtl.ids | 292 +
gemrb/override/pst/gemrb.ini | 121 +
gemrb/override/pst/genders.2da | 8 +
gemrb/override/pst/goinvul.pro | Bin 0 -> 768 bytes
gemrb/override/pst/goinvul.spl | Bin 0 -> 346 bytes
gemrb/override/pst/grease.pro | Bin 0 -> 768 bytes
gemrb/override/pst/guibtact.2da | 35 +
gemrb/override/pst/guiid.chu | Bin 0 -> 92 bytes
gemrb/override/pst/guils.chu | Bin 0 -> 146 bytes
gemrb/override/pst/guiw08.chu | Bin 0 -> 5148 bytes
gemrb/override/pst/hlymite.pro | Bin 0 -> 512 bytes
gemrb/override/pst/hold.pro | Bin 0 -> 768 bytes
gemrb/override/pst/holdund.pro | Bin 0 -> 768 bytes
gemrb/override/pst/holdund.spl | Bin 0 -> 818 bytes
gemrb/override/pst/horror.pro | Bin 0 -> 768 bytes
gemrb/override/pst/ice.pro | Bin 0 -> 768 bytes
gemrb/override/pst/ice.spl | Bin 0 -> 250 bytes
gemrb/override/pst/icestorm.pro | Bin 0 -> 768 bytes
gemrb/override/pst/ifury.pro | Bin 0 -> 768 bytes
gemrb/override/pst/ifury.spl | Bin 0 -> 994 bytes
gemrb/override/pst/ignusorb.pro | Bin 0 -> 768 bytes
gemrb/override/pst/ignusorb.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/iknife.pro | Bin 0 -> 768 bytes
gemrb/override/pst/imiss.pro | Bin 0 -> 512 bytes
gemrb/override/pst/imiss2.pro | Bin 0 -> 512 bytes
gemrb/override/pst/impstr.spl | Bin 0 -> 346 bytes
gemrb/override/pst/inarea.pro | Bin 0 -> 768 bytes
gemrb/override/pst/istorm.pro | Bin 0 -> 768 bytes
gemrb/override/pst/istorm.spl | Bin 0 -> 250 bytes
gemrb/override/pst/istr.pro | Bin 0 -> 512 bytes
gemrb/override/pst/iswarm.pro | Bin 0 -> 768 bytes
gemrb/override/pst/iswarm.spl | Bin 0 -> 466 bytes
gemrb/override/pst/item_use.2da | 27 +
gemrb/override/pst/itemsnd.2da | 49 +
gemrb/override/pst/itemtype.2da | 45 +
gemrb/override/pst/itemuse.2da | 7 +
gemrb/override/pst/iterror.pro | Bin 0 -> 768 bytes
gemrb/override/pst/kiss.pro | Bin 0 -> 512 bytes
gemrb/override/pst/kitlist.2da | 4 +
gemrb/override/pst/knock.pro | Bin 0 -> 768 bytes
gemrb/override/pst/lightb.pro | Bin 0 -> 512 bytes
gemrb/override/pst/lightnin.spl | Bin 0 -> 202 bytes
gemrb/override/pst/lightsto.pro | Bin 0 -> 768 bytes
gemrb/override/pst/litany.pro | Bin 0 -> 512 bytes
gemrb/override/pst/litany.spl | Bin 0 -> 202 bytes
gemrb/override/pst/lmiss.pro | Bin 0 -> 512 bytes
gemrb/override/pst/lstorm.2da | 15 +
gemrb/override/pst/lstorm.pro | Bin 0 -> 768 bytes
gemrb/override/pst/lstorm2.pro | Bin 0 -> 512 bytes
gemrb/override/pst/lstorm2.spl | Bin 0 -> 250 bytes
gemrb/override/pst/magicmis.pro | Bin 0 -> 512 bytes
gemrb/override/pst/mcannon.pro | Bin 0 -> 512 bytes
gemrb/override/pst/mcannon.spl | Bin 0 -> 394 bytes
gemrb/override/pst/mmiss.pro | Bin 0 -> 512 bytes
gemrb/override/pst/mmiss2.pro | Bin 0 -> 512 bytes
gemrb/override/pst/modal.2da | 8 +
gemrb/override/pst/mpal256.bmp | Bin 0 -> 92214 bytes
gemrb/override/pst/mswarm.pro | Bin 0 -> 768 bytes
gemrb/override/pst/mswarm.spl | Bin 0 -> 250 bytes
gemrb/override/pst/music.2da | 39 +
gemrb/override/pst/nbolt.pro | Bin 0 -> 512 bytes
gemrb/override/pst/orb.spl | Bin 0 -> 546 bytes
gemrb/override/pst/orngtint.spl | Bin 0 -> 202 bytes
gemrb/override/pst/overlay.2da | 35 +
gemrb/override/pst/pacify.pro | Bin 0 -> 768 bytes
gemrb/override/pst/pacify.spl | Bin 0 -> 298 bytes
gemrb/override/pst/pathfind.2da | 5 +
gemrb/override/pst/pdolls.2da | 18 +
gemrb/override/pst/poo.spl | Bin 0 -> 250 bytes
gemrb/override/pst/poone.pro | Bin 0 -> 512 bytes
gemrb/override/pst/pwb.spl | Bin 0 -> 298 bytes
gemrb/override/pst/pwblind.pro | Bin 0 -> 768 bytes
gemrb/override/pst/pwk.spl | Bin 0 -> 298 bytes
gemrb/override/pst/pwkill.pro | Bin 0 -> 768 bytes
gemrb/override/pst/races.2da | 78 +
gemrb/override/pst/randitem.2da | 6 +
gemrb/override/pst/rdead.pro | Bin 0 -> 512 bytes
gemrb/override/pst/rdead.spl | Bin 0 -> 250 bytes
gemrb/override/pst/rndtreas.2da | 10 +
gemrb/override/pst/rock.pro | Bin 0 -> 512 bytes
gemrb/override/pst/rotorm.pro | Bin 0 -> 768 bytes
gemrb/override/pst/rune.2da | 9 +
gemrb/override/pst/rune.pro | Bin 0 -> 512 bytes
gemrb/override/pst/rune.spl | Bin 0 -> 346 bytes
gemrb/override/pst/s015hwav.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s025melt.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s046ist2.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s046istm.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s052cone.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s056itrl.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s061beam.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s064bglt.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s064hglt.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s064lort.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s064uplt.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s070boom.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s075wdsh.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s914bgbl.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s914hgbl.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/s914rune.vvc | Bin 0 -> 492 bytes
gemrb/override/pst/savegame.2da | 5 +
gemrb/override/pst/script.2da | 8 +
gemrb/override/pst/shammer.pro | Bin 0 -> 512 bytes
gemrb/override/pst/shammer.spl | Bin 0 -> 810 bytes
gemrb/override/pst/skmob.pro | Bin 0 -> 768 bytes
gemrb/override/pst/skmob.spl | Bin 0 -> 250 bytes
gemrb/override/pst/skmob2.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sleep.pro | Bin 0 -> 768 bytes
gemrb/override/pst/slots.ids | 50 +
gemrb/override/pst/slottype.2da | 58 +
gemrb/override/pst/sparbknp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparbkpa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparblnp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparblpa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparchnp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparchpa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spargonp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spargopa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spargrnp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spargrpa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparicnp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparicpa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparklbk.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklbl.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklch.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklgo.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklgr.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklic.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklma.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklor.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklpu.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklre.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparklst.pro | Bin 0 -> 512 bytes
gemrb/override/pst/sparmanp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparmapa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparornp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparorpa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparpunp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparpupa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparrenp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparrepa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparstnp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/sparstpa.pro | Bin 0 -> 768 bytes
gemrb/override/pst/speak.eff | Bin 0 -> 272 bytes
gemrb/override/pst/spear.pro | Bin 0 -> 512 bytes
gemrb/override/pst/spearex.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spfirebl.pro | Bin 0 -> 512 bytes
gemrb/override/pst/spklarbk.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklarbl.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklarch.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklargo.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklargr.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklaric.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklarma.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklaror.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklarpu.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklarre.pro | Bin 0 -> 768 bytes
gemrb/override/pst/spklarst.pro | Bin 0 -> 768 bytes
gemrb/override/pst/splspec.2da | 7 +
gemrb/override/pst/spscorch.pro | Bin 0 -> 512 bytes
gemrb/override/pst/spscoric.pro | Bin 0 -> 512 bytes
gemrb/override/pst/start.2da | 4 +
gemrb/override/pst/states.2da | 36 +
gemrb/override/pst/stone.pro | Bin 0 -> 512 bytes
gemrb/override/pst/stories.pro | Bin 0 -> 512 bytes
gemrb/override/pst/stories.spl | Bin 0 -> 250 bytes
gemrb/override/pst/str.spl | Bin 0 -> 250 bytes
gemrb/override/pst/strength.pro | Bin 0 -> 512 bytes
gemrb/override/pst/strings.2da | 170 +
gemrb/override/pst/tlaugh.pro | Bin 0 -> 768 bytes
gemrb/override/pst/trapglyp.pro | Bin 0 -> 768 bytes
gemrb/override/pst/trapskul.pro | Bin 0 -> 768 bytes
gemrb/override/pst/vtouch.pro | Bin 0 -> 512 bytes
gemrb/override/pst/weapprof.2da | 9 +
gemrb/override/pst/web.pro | Bin 0 -> 768 bytes
gemrb/override/pst/wmmos2b.mos | Bin 0 -> 36218 bytes
gemrb/override/shared/CMakeLists.txt | 1 +
gemrb/override/shared/Makefile.am | 3 +
gemrb/override/shared/avprefg.2da | 6 +
gemrb/override/shared/avprefix.2da | 7 +
gemrb/override/shared/axeflm.pro | Bin 0 -> 512 bytes
gemrb/override/shared/bardsong.spl | Bin 0 -> 346 bytes
gemrb/override/shared/containr.2da | 16 +
gemrb/override/shared/cow.pro | Bin 0 -> 768 bytes
gemrb/override/shared/detect.spl | Bin 0 -> 202 bytes
gemrb/override/shared/dmgtypes.2da | 22 +
gemrb/override/shared/dummy.spl | Bin 0 -> 202 bytes
gemrb/override/shared/findtrap.spl | Bin 0 -> 202 bytes
gemrb/override/shared/flmstrk.pro | Bin 0 -> 512 bytes
gemrb/override/shared/gemact.ids | 10 +
gemrb/override/shared/invtrav.pro | Bin 0 -> 512 bytes
gemrb/override/shared/itemspec.2da | 4 +
gemrb/override/shared/lightbnb.pro | Bin 0 -> 512 bytes
gemrb/override/shared/modal.2da | 8 +
gemrb/override/shared/panic.spl | Bin 0 -> 346 bytes
gemrb/override/shared/polystat.2da | 27 +
gemrb/override/shared/redholy.pro | Bin 0 -> 512 bytes
gemrb/override/shared/sanctuc.vvc | Bin 0 -> 492 bytes
gemrb/override/shared/shair.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shair1.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shair2.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shair3.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shair4.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shair5.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shair6.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shair7.pro | Bin 0 -> 512 bytes
gemrb/override/shared/sharea.pro | Bin 0 -> 512 bytes
gemrb/override/shared/sharea1.pro | Bin 0 -> 512 bytes
gemrb/override/shared/sharea2.pro | Bin 0 -> 512 bytes
gemrb/override/shared/sharea3.pro | Bin 0 -> 512 bytes
gemrb/override/shared/sharea4.pro | Bin 0 -> 512 bytes
gemrb/override/shared/sharea5.pro | Bin 0 -> 512 bytes
gemrb/override/shared/sharea6.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shearth.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shearth1.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shearth2.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shearth3.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shearth4.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shearth5.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shearth6.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shearth7.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shwater.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shwater1.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shwater2.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shwater3.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shwater4.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shwater5.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shwater6.pro | Bin 0 -> 512 bytes
gemrb/override/shared/shwater7.pro | Bin 0 -> 512 bytes
gemrb/override/shared/skybolt.pro | Bin 0 -> 512 bytes
gemrb/override/shared/sneak.spl | Bin 0 -> 202 bytes
gemrb/override/shared/souleatr.2da | 4 +
gemrb/override/shared/spboom1.pro | Bin 0 -> 512 bytes
gemrb/override/shared/spboom2.pro | Bin 0 -> 512 bytes
gemrb/override/shared/spboom3.pro | Bin 0 -> 512 bytes
gemrb/override/shared/spdimdr.pro | Bin 0 -> 512 bytes
gemrb/override/shared/spmagmis.pro | Bin 0 -> 512 bytes
gemrb/override/shared/spsmkjet.pro | Bin 0 -> 512 bytes
gemrb/override/shared/spsmold.pro | Bin 0 -> 512 bytes
gemrb/override/shared/spsmpuff.pro | Bin 0 -> 512 bytes
gemrb/override/shared/stats.ids | 312 +
gemrb/override/shared/turn.spl | Bin 0 -> 202 bytes
gemrb/plugins-prepare.sh | 38 +
gemrb/plugins/2DAImporter/2DAImporter.cpp | 120 +
gemrb/plugins/2DAImporter/2DAImporter.h | 169 +
gemrb/plugins/2DAImporter/CMakeLists.txt | 1 +
gemrb/plugins/2DAImporter/Makefile.am | 3 +
gemrb/plugins/ACMReader/ACMReader.cpp | 117 +
gemrb/plugins/ACMReader/ACMReader.h | 73 +
gemrb/plugins/ACMReader/CMakeLists.txt | 3 +
gemrb/plugins/ACMReader/Makefile.am | 3 +
gemrb/plugins/ACMReader/decoder.cpp | 177 +
gemrb/plugins/ACMReader/decoder.h | 48 +
gemrb/plugins/ACMReader/general.h | 36 +
gemrb/plugins/ACMReader/unpacker.cpp | 454 +
gemrb/plugins/ACMReader/unpacker.h | 93 +
gemrb/plugins/AREImporter/AREImporter.cpp | 2334 +++++
gemrb/plugins/AREImporter/AREImporter.h | 92 +
gemrb/plugins/AREImporter/CMakeLists.txt | 1 +
gemrb/plugins/AREImporter/Makefile.am | 3 +
gemrb/plugins/BAMImporter/BAMImporter.cpp | 395 +
gemrb/plugins/BAMImporter/BAMImporter.h | 90 +
gemrb/plugins/BAMImporter/CMakeLists.txt | 1 +
gemrb/plugins/BAMImporter/Makefile.am | 3 +
gemrb/plugins/BIFImporter/BIFImporter.cpp | 348 +
gemrb/plugins/BIFImporter/BIFImporter.h | 66 +
gemrb/plugins/BIFImporter/CMakeLists.txt | 1 +
gemrb/plugins/BIFImporter/Makefile.am | 3 +
gemrb/plugins/BIKPlayer/BIKPlayer.cpp | 1617 +++
gemrb/plugins/BIKPlayer/BIKPlayer.h | 259 +
gemrb/plugins/BIKPlayer/CMakeLists.txt | 1 +
gemrb/plugins/BIKPlayer/GetBitContext.cpp | 317 +
gemrb/plugins/BIKPlayer/GetBitContext.h | 107 +
gemrb/plugins/BIKPlayer/Makefile.am | 16 +
gemrb/plugins/BIKPlayer/binkdata.h | 613 ++
gemrb/plugins/BIKPlayer/common.h | 78 +
gemrb/plugins/BIKPlayer/dct.cpp | 97 +
gemrb/plugins/BIKPlayer/dsputil.h | 230 +
gemrb/plugins/BIKPlayer/fft.cpp | 358 +
gemrb/plugins/BIKPlayer/mem.cpp | 68 +
gemrb/plugins/BIKPlayer/rational.cpp | 83 +
gemrb/plugins/BIKPlayer/rational.h | 126 +
gemrb/plugins/BIKPlayer/rdft.cpp | 130 +
gemrb/plugins/BMPImporter/BMPImporter.cpp | 295 +
gemrb/plugins/BMPImporter/BMPImporter.h | 56 +
gemrb/plugins/BMPImporter/CMakeLists.txt | 1 +
gemrb/plugins/BMPImporter/Makefile.am | 3 +
gemrb/plugins/BMPWriter/BMPWriter.cpp | 77 +
gemrb/plugins/BMPWriter/BMPWriter.h | 9 +
gemrb/plugins/BMPWriter/CMakeLists.txt | 1 +
gemrb/plugins/BMPWriter/Makefile.am | 3 +
gemrb/plugins/CHUImporter/CHUImporter.cpp | 531 +
gemrb/plugins/CHUImporter/CHUImporter.h | 48 +
gemrb/plugins/CHUImporter/CMakeLists.txt | 1 +
gemrb/plugins/CHUImporter/Makefile.am | 3 +
gemrb/plugins/CMakeLists.txt | 45 +
gemrb/plugins/CREImporter/CMakeLists.txt | 1 +
gemrb/plugins/CREImporter/CREImporter.cpp | 2961 ++++++
gemrb/plugins/CREImporter/CREImporter.h | 112 +
gemrb/plugins/CREImporter/Makefile.am | 3 +
gemrb/plugins/DLGImporter/CMakeLists.txt | 1 +
gemrb/plugins/DLGImporter/DLGImporter.cpp | 392 +
gemrb/plugins/DLGImporter/DLGImporter.h | 85 +
gemrb/plugins/DLGImporter/Makefile.am | 3 +
gemrb/plugins/DirectoryImporter/CMakeLists.txt | 1 +
.../DirectoryImporter/DirectoryImporter.cpp | 99 +
.../plugins/DirectoryImporter/DirectoryImporter.h | 46 +
gemrb/plugins/DirectoryImporter/Makefile.am | 3 +
gemrb/plugins/EFFImporter/CMakeLists.txt | 1 +
gemrb/plugins/EFFImporter/EFFImporter.cpp | 243 +
gemrb/plugins/EFFImporter/EFFImporter.h | 47 +
gemrb/plugins/EFFImporter/Makefile.am | 3 +
gemrb/plugins/FXOpcodes/CMakeLists.txt | 1 +
gemrb/plugins/FXOpcodes/FXOpcodes.cpp | 6683 ++++++++++++
gemrb/plugins/FXOpcodes/Makefile.am | 3 +
gemrb/plugins/GAMImporter/CMakeLists.txt | 1 +
gemrb/plugins/GAMImporter/GAMImporter.cpp | 1294 +++
gemrb/plugins/GAMImporter/GAMImporter.h | 83 +
gemrb/plugins/GAMImporter/Makefile.am | 3 +
gemrb/plugins/GUIScript/CMakeLists.txt | 5 +
gemrb/plugins/GUIScript/GUIScript.cpp |10680 ++++++++++++++++++++
gemrb/plugins/GUIScript/GUIScript.h | 68 +
gemrb/plugins/GUIScript/Makefile.am | 5 +
gemrb/plugins/GUIScript/PythonHelpers.cpp | 80 +
gemrb/plugins/GUIScript/PythonHelpers.h | 112 +
gemrb/plugins/IDSImporter/CMakeLists.txt | 1 +
gemrb/plugins/IDSImporter/IDSImporter.cpp | 158 +
gemrb/plugins/IDSImporter/IDSImporter.h | 53 +
gemrb/plugins/IDSImporter/IDSImporterDefs.h | 37 +
gemrb/plugins/IDSImporter/Makefile.am | 3 +
gemrb/plugins/INIImporter/CMakeLists.txt | 1 +
gemrb/plugins/INIImporter/INIImporter.cpp | 165 +
gemrb/plugins/INIImporter/INIImporter.h | 212 +
gemrb/plugins/INIImporter/Makefile.am | 3 +
gemrb/plugins/ITMImporter/CMakeLists.txt | 1 +
gemrb/plugins/ITMImporter/ITMImporter.cpp | 255 +
gemrb/plugins/ITMImporter/ITMImporter.h | 51 +
gemrb/plugins/ITMImporter/Makefile.am | 3 +
gemrb/plugins/IWDOpcodes/CMakeLists.txt | 1 +
gemrb/plugins/IWDOpcodes/IWDOpcodes.cpp | 3392 +++++++
gemrb/plugins/IWDOpcodes/Makefile.am | 3 +
gemrb/plugins/KEYImporter/CMakeLists.txt | 1 +
gemrb/plugins/KEYImporter/Dictionary.cpp | 226 +
gemrb/plugins/KEYImporter/Dictionary.h | 84 +
gemrb/plugins/KEYImporter/KEYImporter.cpp | 289 +
gemrb/plugins/KEYImporter/KEYImporter.h | 68 +
gemrb/plugins/KEYImporter/Makefile.am | 3 +
gemrb/plugins/MOSImporter/CMakeLists.txt | 1 +
gemrb/plugins/MOSImporter/MOSImporter.cpp | 155 +
gemrb/plugins/MOSImporter/MOSImporter.h | 39 +
gemrb/plugins/MOSImporter/Makefile.am | 3 +
gemrb/plugins/MUSImporter/CMakeLists.txt | 1 +
gemrb/plugins/MUSImporter/MUSImporter.cpp | 335 +
gemrb/plugins/MUSImporter/MUSImporter.h | 79 +
gemrb/plugins/MUSImporter/Makefile.am | 3 +
gemrb/plugins/MVEPlayer/CMakeLists.txt | 1 +
gemrb/plugins/MVEPlayer/MVEPlayer.cpp | 187 +
gemrb/plugins/MVEPlayer/MVEPlayer.h | 57 +
gemrb/plugins/MVEPlayer/Makefile.am | 12 +
gemrb/plugins/MVEPlayer/gstmvedemux.h | 141 +
gemrb/plugins/MVEPlayer/mve.h | 64 +
gemrb/plugins/MVEPlayer/mve_player.cpp | 473 +
gemrb/plugins/MVEPlayer/mve_player.h | 93 +
gemrb/plugins/MVEPlayer/mveaudiodec.cpp | 82 +
gemrb/plugins/MVEPlayer/mvevideodec16.cpp | 844 ++
gemrb/plugins/MVEPlayer/mvevideodec8.cpp | 797 ++
gemrb/plugins/Makefile.am | 44 +
gemrb/plugins/NullSound/CMakeLists.txt | 1 +
gemrb/plugins/NullSound/Makefile.am | 3 +
gemrb/plugins/NullSound/NullSound.cpp | 133 +
gemrb/plugins/NullSound/NullSound.h | 55 +
gemrb/plugins/OGGReader/CMakeLists.txt | 11 +
gemrb/plugins/OGGReader/Makefile.am | 6 +
gemrb/plugins/OGGReader/OGGReader.cpp | 128 +
gemrb/plugins/OGGReader/OGGReader.h | 55 +
gemrb/plugins/OpenALAudio/AmbientMgrAL.cpp | 312 +
gemrb/plugins/OpenALAudio/AmbientMgrAL.h | 89 +
gemrb/plugins/OpenALAudio/CMakeLists.txt | 7 +
gemrb/plugins/OpenALAudio/Makefile.am | 4 +
gemrb/plugins/OpenALAudio/OpenALAudio.cpp | 924 ++
gemrb/plugins/OpenALAudio/OpenALAudio.h | 148 +
gemrb/plugins/OpenALAudio/StackLock.cpp | 55 +
gemrb/plugins/OpenALAudio/StackLock.h | 38 +
gemrb/plugins/PLTImporter/CMakeLists.txt | 1 +
gemrb/plugins/PLTImporter/Makefile.am | 3 +
gemrb/plugins/PLTImporter/PLTImporter.cpp | 110 +
gemrb/plugins/PLTImporter/PLTImporter.h | 37 +
gemrb/plugins/PNGImporter/CMakeLists.txt | 5 +
gemrb/plugins/PNGImporter/Makefile.am | 4 +
gemrb/plugins/PNGImporter/PNGImporter.cpp | 217 +
gemrb/plugins/PNGImporter/PNGImporter.h | 45 +
gemrb/plugins/PROImporter/CMakeLists.txt | 1 +
gemrb/plugins/PROImporter/Makefile.am | 3 +
gemrb/plugins/PROImporter/PROImporter.cpp | 179 +
gemrb/plugins/PROImporter/PROImporter.h | 47 +
gemrb/plugins/PSTOpcodes/CMakeLists.txt | 1 +
gemrb/plugins/PSTOpcodes/Makefile.am | 3 +
gemrb/plugins/PSTOpcodes/PSTOpcodes.cpp | 725 ++
gemrb/plugins/SDLAudio/CMakeLists.txt | 6 +
gemrb/plugins/SDLAudio/Makefile.am | 5 +
gemrb/plugins/SDLAudio/SDLAudio.cpp | 388 +
gemrb/plugins/SDLAudio/SDLAudio.h | 81 +
gemrb/plugins/SDLVideo/CMakeLists.txt | 4 +
gemrb/plugins/SDLVideo/Makefile.am | 5 +
gemrb/plugins/SDLVideo/SDLVideo.cpp | 2549 +++++
gemrb/plugins/SDLVideo/SDLVideo.h | 173 +
gemrb/plugins/SDLVideo/SDLVideoDriver.inl | 539 +
gemrb/plugins/SDLVideo/TileRenderer.inl | 122 +
gemrb/plugins/SPLImporter/CMakeLists.txt | 1 +
gemrb/plugins/SPLImporter/Makefile.am | 3 +
gemrb/plugins/SPLImporter/SPLImporter.cpp | 259 +
gemrb/plugins/SPLImporter/SPLImporter.h | 48 +
gemrb/plugins/STOImporter/CMakeLists.txt | 1 +
gemrb/plugins/STOImporter/Makefile.am | 3 +
gemrb/plugins/STOImporter/STOImporter.cpp | 410 +
gemrb/plugins/STOImporter/STOImporter.h | 62 +
gemrb/plugins/TISImporter/CMakeLists.txt | 1 +
gemrb/plugins/TISImporter/Makefile.am | 3 +
gemrb/plugins/TISImporter/TISImporter.cpp | 145 +
gemrb/plugins/TISImporter/TISImporter.h | 42 +
gemrb/plugins/TLKImporter/CMakeLists.txt | 1 +
gemrb/plugins/TLKImporter/Makefile.am | 3 +
gemrb/plugins/TLKImporter/TLKImporter.cpp | 496 +
gemrb/plugins/TLKImporter/TLKImporter.h | 68 +
gemrb/plugins/TLKImporter/TlkOverride.cpp | 342 +
gemrb/plugins/TLKImporter/TlkOverride.h | 83 +
gemrb/plugins/WAVReader/CMakeLists.txt | 3 +
gemrb/plugins/WAVReader/Makefile.am | 3 +
gemrb/plugins/WAVReader/WAVReader.cpp | 193 +
gemrb/plugins/WAVReader/WAVReader.h | 59 +
gemrb/plugins/WEDImporter/CMakeLists.txt | 1 +
gemrb/plugins/WEDImporter/Makefile.am | 3 +
gemrb/plugins/WEDImporter/WEDImporter.cpp | 360 +
gemrb/plugins/WEDImporter/WEDImporter.h | 66 +
gemrb/plugins/WMPImporter/CMakeLists.txt | 1 +
gemrb/plugins/WMPImporter/Makefile.am | 3 +
gemrb/plugins/WMPImporter/WMPImporter.cpp | 441 +
gemrb/plugins/WMPImporter/WMPImporter.h | 61 +
gemrb/plugins/ZLibManager/CMakeLists.txt | 3 +
gemrb/plugins/ZLibManager/Makefile.am | 3 +
gemrb/plugins/ZLibManager/ZLibManager.cpp | 159 +
gemrb/plugins/ZLibManager/ZLibManager.h | 36 +
gemrb/templates/cpp_template | 16 +
gemrb/templates/header_template | 16 +
gemrb/tests/CMakeLists.txt | 1 +
gemrb/tests/minimal/README | 17 +
gemrb/tests/minimal/chitin.key | Bin 0 -> 25 bytes
gemrb/tests/minimal/data/carot.bam | Bin 0 -> 306 bytes
gemrb/tests/minimal/data/chrmodst.2da | 4 +
gemrb/tests/minimal/data/cursors.bam | Bin 0 -> 305 bytes
gemrb/tests/minimal/data/defsound.2da | 4 +
gemrb/tests/minimal/data/dexmod.2da | 4 +
gemrb/tests/minimal/data/fogowar.bam | Bin 0 -> 306 bytes
gemrb/tests/minimal/data/fonts.2da | 4 +
gemrb/tests/minimal/data/gametime.2da | 5 +
gemrb/tests/minimal/data/gemrb.ini | 84 +
gemrb/tests/minimal/data/hpconbon.2da | 4 +
gemrb/tests/minimal/data/intmod.2da | 4 +
gemrb/tests/minimal/data/itemtype.2da | 4 +
gemrb/tests/minimal/data/lorebon.2da | 4 +
gemrb/tests/minimal/data/palette.png | Bin 0 -> 267 bytes
gemrb/tests/minimal/data/script.2da | 4 +
gemrb/tests/minimal/data/slottype.2da | 4 +
gemrb/tests/minimal/data/strings.2da | 4 +
gemrb/tests/minimal/data/strmod.2da | 4 +
gemrb/tests/minimal/data/strmodex.2da | 4 +
gemrb/tests/minimal/data/toolscrl.bam | Bin 0 -> 305 bytes
gemrb/tests/minimal/dialog.tlk | Bin 0 -> 19 bytes
gemrb/tests/minimal/fhs.cfg | 9 +
gemrb/tests/minimal/test.cfg | 9 +
2778 files changed, 224875 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c158f0b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+*.pyc
+*.[oa]
+*.l[oa]
+*.so
+cmake_install.cmake
+CMakeFiles
+Makefile
+Makefile.in
+.deps
+.libs
+.dirstamp
+
+/aclocal.m4
+/admin/config.guess
+/admin/config.sub
+/admin/depcomp
+/admin/install-sh
+/admin/ltmain.sh
+/admin/missing
+/autom4te.cache/
+/config.h
+/config.h.in
+/config.log
+/config.status
+/configure
+/gemrb.spec
+/gemrb/gemrb
+/libtool
+/stamp-h1
+/cmake_uninstall.cmake
+/CMakeCache.txt
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..3736258
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,2 @@
+Jaka Kranjc <lynxlupodian at users.sourceforge.net> <lynxlynxlynx at sourcemage.org>
+Edheldil <edheldil at users.sourceforge.net> Jarda Benkovsky <benkovsk at totoro.(none)>
diff --git a/.pydevproject b/.pydevproject
new file mode 100644
index 0000000..f47ecb1
--- /dev/null
+++ b/.pydevproject
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?>
+
+<pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+</pydev_project>
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..28a7ad9
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,17 @@
+GemRB authors listed in Alphabetical Order
+
+Alyssa Milburn <fuzzie at users.sourceforge.net>
+Avenger <avenger_teambg at users.sourceforge.net>
+Balrog994 <Balrog994 at yahoo.com>
+Brian Tanedo <behteh at users.sourceforge.net>
+Dark-Star <doc_wagon at users.sourceforge.net>
+Divide <divide at users.sourceforge.net>
+Edheldil <edheldil at users.sourceforge.net>
+GuidoJ <guidoj at users.sourceforge.net>
+Jaka Kranjc <lynxlupodian at users.sourceforge.net>
+Lotana <lotana at users.sourceforge.net>
+Marshall Mattingly III <mattinm at users.sourceforge.net>
+Thuy Nguyen <ndthuy at users.sourceforge.net>
+Tom Prince <tom.prince at ualberta.net>
+Willem Jan Palenstijn <wjpalenstijn at users.sourceforge.net>
+Zefklop <zefklop at users.sourceforge.net>
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..a52c538
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,350 @@
+cmake_minimum_required(VERSION 2.4.4)
+if(COMMAND cmake_policy)
+ cmake_policy(SET CMP0003 NEW)
+ cmake_policy(SET CMP0005 NEW)
+endif(COMMAND cmake_policy)
+# allow empty else and endif constructs (available by default since 2.6.0)
+set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true)
+
+# prevent in-source builds
+IF(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
+ MESSAGE(FATAL_ERROR "
+ CMake generation for this project is not allowed within the source directory!
+ Remove the CMakeCache.txt file and try again from another folder, e.g.:
+ rm CMakeCache.txt
+ mkdir build
+ cd build
+ cmake .."
+ )
+ENDIF()
+
+# If the user specifies -DCMAKE_BUILD_TYPE on the command line, take their definition
+# and dump it in the cache along with proper documentation, otherwise set CMAKE_BUILD_TYPE
+# to Release prior to calling PROJECT()
+IF(DEFINED CMAKE_BUILD_TYPE)
+ SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
+ELSE(DEFINED CMAKE_BUILD_TYPE)
+ SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
+ENDIF(DEFINED CMAKE_BUILD_TYPE)
+
+PROJECT(gemrb)
+# try to extract the version from the source
+execute_process(
+ COMMAND sed -n "s/\#define VERSION_GEMRB .\\([^\"]*\\).$/\\1/p" ${CMAKE_CURRENT_SOURCE_DIR}/gemrb/includes/globals.h
+ OUTPUT_VARIABLE GEMRB_VERSION
+ RESULT_VARIABLE RC
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+if(${RC} GREATER 0) # lookup failed
+ set(GEMRB_VERSION "unknown")
+endif(${RC} GREATER 0)
+message("Detected version: ${GEMRB_VERSION}")
+
+IF(PREFIX)
+ SET(PREFIX CACHE PATH "Abbreviation for CMAKE_INSTALL_PREFIX.")
+ SET(CMAKE_INSTALL_PREFIX ${PREFIX})
+ENDIF(PREFIX)
+
+if (NOT LAYOUT)
+ if (WIN32)
+ set(LAYOUT "home")
+ else (WIN32)
+ set(LAYOUT "fhs")
+ endif (WIN32)
+endif (NOT LAYOUT)
+
+SET(LAYOUT "${LAYOUT}" CACHE STRING "Directory layout.")
+
+# macro that sets a default (path) if one wasn't specified
+MACRO(SET_PATH variable default)
+ IF(NOT ${variable})
+ SET(${variable} ${default})
+ ENDIF(NOT ${variable})
+ENDMACRO(SET_PATH)
+
+if (${LAYOUT} MATCHES "home")
+ SET_PATH( PLUGIN_DIR ${CMAKE_INSTALL_PREFIX}/plugins )
+ SET_PATH( DATA_DIR ${CMAKE_INSTALL_PREFIX} )
+ SET_PATH( MAN_DIR ${CMAKE_INSTALL_PREFIX}/man/man6 )
+ SET_PATH( BIN_DIR ${CMAKE_INSTALL_PREFIX} )
+ SET_PATH( SYSCONF_DIR ${CMAKE_INSTALL_PREFIX} )
+ SET_PATH( LIB_DIR ${CMAKE_INSTALL_PREFIX} )
+ SET_PATH( DOC_DIR ${CMAKE_INSTALL_PREFIX}/doc )
+ SET_PATH( ICON_DIR ${CMAKE_INSTALL_PREFIX} )
+ SET_PATH( SVG_DIR ${CMAKE_INSTALL_PREFIX} )
+ SET_PATH( MENU_DIR ${CMAKE_INSTALL_PREFIX} )
+elseif (${LAYOUT} MATCHES "fhs")
+ SET_PATH( LIB_DIR ${CMAKE_INSTALL_PREFIX}/lib/gemrb )
+ SET_PATH( PLUGIN_DIR ${LIB_DIR}/plugins )
+ SET_PATH( DATA_DIR ${CMAKE_INSTALL_PREFIX}/share/gemrb )
+ SET_PATH( MAN_DIR ${CMAKE_INSTALL_PREFIX}/share/man/man6 )
+ SET_PATH( BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin )
+ IF( NOT SYSCONF_DIR )
+ if ( ${CMAKE_INSTALL_PREFIX} STREQUAL "/usr" )
+ SET( SYSCONF_DIR /etc/gemrb )
+ else ()
+ SET( SYSCONF_DIR ${CMAKE_INSTALL_PREFIX}/etc/gemrb )
+ endif ()
+ ENDIF( NOT SYSCONF_DIR )
+ SET_PATH( DOC_DIR ${CMAKE_INSTALL_PREFIX}/share/doc/gemrb )
+ SET_PATH( ICON_DIR ${CMAKE_INSTALL_PREFIX}/share/pixmaps )
+ SET_PATH( SVG_DIR ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps )
+ SET_PATH( MENU_DIR ${CMAKE_INSTALL_PREFIX}/share/applications )
+else (${LAYOUT} MATCHES "opt")
+ SET_PATH( LIB_DIR ${CMAKE_INSTALL_PREFIX}/lib )
+ SET_PATH( PLUGIN_DIR ${LIB_DIR}/plugins )
+ SET_PATH( DATA_DIR ${CMAKE_INSTALL_PREFIX}/share/ )
+ SET_PATH( MAN_DIR ${CMAKE_INSTALL_PREFIX}/man/man6 )
+ SET_PATH( BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin )
+ SET_PATH( SYSCONF_DIR ${CMAKE_INSTALL_PREFIX}/etc )
+ SET_PATH( DOC_DIR ${CMAKE_INSTALL_PREFIX}/share/doc/gemrb )
+ SET_PATH( ICON_DIR ${CMAKE_INSTALL_PREFIX}/share/pixmaps )
+ SET_PATH( SVG_DIR ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps )
+ SET_PATH( MENU_DIR ${CMAKE_INSTALL_PREFIX}/share/applications )
+endif (${LAYOUT} MATCHES "home")
+
+# check if this is a release version
+execute_process(
+ COMMAND echo ${GEMRB_VERSION}
+ COMMAND grep -q git # cmake creates a pipe by itself
+ RESULT_VARIABLE RC
+ OUTPUT_QUIET
+)
+if(${RC} GREATER 0) # no match or error
+ set (GIT_VERSION 0)
+else()
+ set (GIT_VERSION 1)
+endif()
+
+IF(CMAKE_COMPILER_IS_GNUCXX)
+ if ((NOT DISABLE_WERROR) AND GIT_VERSION)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
+ endif ()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -Wpointer-arith -Wcast-align")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wno-format-y2k -Wno-long-long -fno-strict-aliasing")
+ # only export symbols explicitly marked to be exported.
+ INCLUDE(CheckCXXCompilerFlag)
+ CHECK_CXX_COMPILER_FLAG("-fvisibility=hidden" VISIBILITY_HIDDEN)
+ IF (VISIBILITY_HIDDEN)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
+ ENDIF (VISIBILITY_HIDDEN)
+ if (WIN32)
+ # GCC 4.5.0+ has shared libstdc++ without dllimport
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--enable-auto-import")
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--enable-auto-import")
+ endif (WIN32)
+ # Ensure all plugin symbols exist.
+ if (NOT APPLE AND NOT UNSAFE_PLUGIN)
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined")
+ endif (NOT APPLE AND NOT UNSAFE_PLUGIN)
+ENDIF(CMAKE_COMPILER_IS_GNUCXX)
+
+# Check for all the required and optional dependencies
+INCLUDE(FindPythonLibs)
+IF(PYTHONLIBS_FOUND)
+ MESSAGE(STATUS "Looking for Python libraries and headers: found")
+ELSE()
+ MESSAGE(SEND_ERROR "Looking for Python libraries and headers: not found!")
+ MESSAGE(FATAL_ERROR "Please get them (www.python.org)")
+ENDIF()
+
+INCLUDE(FindSDL)
+IF(SDL_FOUND)
+ MESSAGE(STATUS "Looking for SDL: found")
+ELSE()
+ MESSAGE(SEND_ERROR "Looking for SDL: not found!")
+ MESSAGE(FATAL_ERROR "Please get it from www.libsdl.org")
+ENDIF()
+
+INCLUDE(FindZLIB)
+IF(ZLIB_FOUND)
+ MESSAGE(STATUS "Looking for Zlib: found")
+ELSE()
+ MESSAGE(SEND_ERROR "Looking for Zlib: not found!")
+ MESSAGE(FATAL_ERROR "Please install the Zlib library and headers first!")
+ENDIF()
+
+IF(UNIX)
+ SET(CMAKE_THREAD_PREFER_PTHREAD true)
+ FIND_PACKAGE(Threads REQUIRED)
+ENDIF(UNIX)
+
+INCLUDE(FindOpenAL)
+IF(OPENAL_FOUND)
+ MESSAGE(STATUS "Looking for OpenAL: found")
+ELSE()
+ MESSAGE(WARNING "Looking for OpenAL: not found!")
+ MESSAGE(WARNING "If you want to build the OpenAL plugin, get OpenAL from www.openal.org.")
+ MESSAGE(WARNING "If it just wasn't found, try setting the OPENALDIR environment variable.")
+ENDIF()
+
+INCLUDE(FindSDL_mixer)
+IF(SDLMIXER_FOUND)
+ MESSAGE(STATUS "Looking for SDL_mixer: found")
+ELSE()
+ MESSAGE(WARNING "Looking for SDL_mixer: not found!")
+ MESSAGE(WARNING "If you want to build the SDL_mixer plugin, install SDL_mixer first.")
+ENDIF()
+
+INCLUDE(FindPNG)
+IF(PNG_FOUND)
+ MESSAGE(STATUS "Looking for libPNG: found")
+ELSE()
+ MESSAGE(WARNING "Looking for libPNG: not found!")
+ MESSAGE(WARNING "GemRB will be built without any PNG support. Get it from www.libpng.org" )
+ MESSAGE(WARNING "While no original game data is in PNG format, some mod data is and will need conversion.")
+ENDIF()
+
+FIND_LIBRARY(VORBIS_LIBRARY vorbisfile)
+IF(VORBIS_LIBRARY)
+ find_path(VORBIS_FILE vorbisfile.h PATH_SUFFIXES vorbis)
+ IF(VORBIS_FILE)
+ MESSAGE(STATUS "Looking for Ogg Vorbis support: found")
+ ELSE()
+ unset(VORBIS_LIBRARY) # disable the build for this plugin
+ ENDIF()
+ENDIF()
+IF(NOT VORBIS_LIBRARY)
+ MESSAGE(WARNING "Looking for Ogg Vorbis support: not found!")
+ MESSAGE(WARNING "While no original game data is in OGG format, some mod data is and will need conversion.")
+ENDIF()
+
+ADD_DEFINITIONS("-DHAVE_CONFIG_H")
+
+# On Release builds cmake automatically defines NDEBUG, so we
+# explicitly undefine it:
+if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT MSVC)
+ ADD_DEFINITIONS("-UNDEBUG")
+endif()
+
+if (STATIC_LINK)
+ if (NOT WIN32)
+ ADD_DEFINITIONS("-DSTATIC_LINK")
+ else (NOT WIN32 AND NOT APPLE)
+ unset(STATIC_LINK CACHE)
+ MESSAGE(STATUS "Static linking not (yet) supported on this platform.")
+ endif (NOT WIN32 AND NOT APPLE)
+endif (STATIC_LINK)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} gemrb/includes gemrb/core)
+
+#Platform checks
+INCLUDE (CheckTypeSize)
+CHECK_TYPE_SIZE("int" SIZEOF_INT)
+CHECK_TYPE_SIZE("long int" SIZEOF_LONG_INT)
+
+INCLUDE (CheckFunctionExists)
+CHECK_FUNCTION_EXISTS("snprintf" HAVE_SNPRINTF)
+CHECK_FUNCTION_EXISTS("strndup" HAVE_STRNDUP)
+
+#Unneeded on windows
+IF(NOT WIN32)
+ INCLUDE (CheckCXXSourceCompiles)
+ CHECK_CXX_SOURCE_COMPILES("typedef void *(* voidvoid)(void);
+
+ void *object = 0;
+ voidvoid function;
+ function = (voidvoid) object;
+ " PERMITS_OBJECT_TO_FUNCTION_CAST)
+
+ IF( NOT PERMITS_OBJECT_TO_FUNCTION_CAST )
+ SET(HAVE_FORBIDDEN_OBJECT_TO_FUNCTION_CAST 1)
+ ENDIF( NOT PERMITS_OBJECT_TO_FUNCTION_CAST )
+ENDIF(NOT WIN32)
+
+IF(APPLE)
+ FIND_LIBRARY(SDL_MAIN_LIBRARY_PATH SDLmain)
+ FIND_LIBRARY(COCOA_LIBRARY_PATH Cocoa)
+ENDIF(APPLE)
+
+CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/cmake_config.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/config.h ESCAPE_QUOTES)
+
+#Plugin addition macro
+MACRO(ADD_GEMRB_PLUGIN plugin_name)
+ if (STATIC_LINK)
+ ADD_LIBRARY(${plugin_name} STATIC ${ARGN})
+ set(plugins "${plugins};${plugin_name}" PARENT_SCOPE)
+ else (STATIC_LINK)
+ ADD_LIBRARY(${plugin_name} MODULE ${ARGN})
+ if (NOT UNSAFE_PLUGIN)
+ TARGET_LINK_LIBRARIES(${plugin_name} gemrb_core ${CMAKE_THREAD_LIBS_INIT})
+ endif (NOT UNSAFE_PLUGIN)
+ INSTALL(TARGETS ${plugin_name} DESTINATION ${PLUGIN_DIR})
+ endif (STATIC_LINK)
+ SET_TARGET_PROPERTIES(${plugin_name} PROPERTIES PREFIX ""
+ INSTALL_RPATH ${LIB_DIR}
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/gemrb/plugins)
+ENDMACRO(ADD_GEMRB_PLUGIN)
+
+#gemrb overrides macro
+MACRO(ADD_GEMRB_OVERRIDE game_name)
+ FILE( GLOB FILES_TO_INSTALL *.2da *.bmp *.ini *.chu *.ids *.bcs *.vvc *.mos *.spl *.wav *.pro)
+ INSTALL( FILES ${FILES_TO_INSTALL} DESTINATION ${DATA_DIR}/override/${game_name} )
+ENDMACRO(ADD_GEMRB_OVERRIDE )
+
+# also put the chosen paths in the man page (Ubuntu)
+CONFIGURE_FILE(
+ "${CMAKE_CURRENT_SOURCE_DIR}/gemrb.6.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/gemrb.6"
+ IMMEDIATE @ONLY
+)
+
+ADD_SUBDIRECTORY( gemrb )
+INSTALL( FILES "${CMAKE_CURRENT_BINARY_DIR}/gemrb.6" DESTINATION ${MAN_DIR} )
+INSTALL( FILES artwork/gemrb-logo.png DESTINATION ${ICON_DIR} RENAME gemrb.png )
+INSTALL( FILES artwork/logo04-rb_only.svg DESTINATION ${SVG_DIR} RENAME gemrb.svg )
+INSTALL( FILES gemrb.desktop DESTINATION ${MENU_DIR} )
+INSTALL( FILES README INSTALL COPYING NEWS AUTHORS DESTINATION ${DOC_DIR} )
+
+CONFIGURE_FILE(
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
+ IMMEDIATE @ONLY
+)
+
+# copy the variable, since the file uses @VERSION@
+set(VERSION ${GEMRB_VERSION})
+CONFIGURE_FILE(
+ "${CMAKE_CURRENT_SOURCE_DIR}/gemrb.spec.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/gemrb.spec"
+ IMMEDIATE @ONLY
+)
+
+ADD_CUSTOM_TARGET( uninstall
+ "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" )
+
+# make dist for a gzipped tarball of current HEAD
+set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${GEMRB_VERSION})
+add_custom_target( dist
+ COMMAND git archive --prefix=${ARCHIVE_NAME}/ HEAD
+ | gzip --best > ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.gz
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+)
+
+message(STATUS "")
+message(STATUS "These are the configured paths:")
+message(STATUS " PREFIX: ${CMAKE_INSTALL_PREFIX}")
+message(STATUS " LIB_DIR: ${LIB_DIR}")
+message(STATUS " PLUGIN_DIR: ${PLUGIN_DIR}")
+message(STATUS " BIN_DIR: ${BIN_DIR}")
+message(STATUS " DATA_DIR: ${DATA_DIR}")
+message(STATUS " MAN_DIR: ${MAN_DIR}")
+message(STATUS " SYSCONF_DIR: ${SYSCONF_DIR}")
+message(STATUS " DOC_DIR: ${DOC_DIR}")
+message(STATUS " ICON_DIR: ${ICON_DIR}")
+message(STATUS " SVG_DIR: ${SVG_DIR}")
+message(STATUS " MENU_DIR: ${MENU_DIR}")
+message(STATUS "")
+message(STATUS "Options:")
+message(STATUS " LAYOUT: ${LAYOUT}")
+message(STATUS " NOCOLOR: ${NOCOLOR}")
+message(STATUS " TOUCHSCREEN: ${TOUCHSCREEN}")
+if (STATIC_LINK)
+ message(STATUS " STATIC_LINK: ${STATIC_LINK}")
+else()
+ message(STATUS " STATIC_LINK: disabled")
+endif()
+message(STATUS "")
+message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
+message(STATUS "")
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..adbec14
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,112 @@
+********************************************************************
+NOTE: since v0.2.3 (2005-02-13) the release notes are stored in NEWS
+********************************************************************
+
+2005-12-06 Edheldil <edheldil at users.sourceforge.net>
+ * release 0.2.6
+
+2005-08-23 Edheldil <edheldil at users.sourceforge.net>
+ * release 0.2.5
+
+2005-02-15 Edheldil <edheldil at users.sourceforge.net>
+ * release 0.2.3
+
+2004-07-20 Edheldil <edheldil at users.sourceforge.net>
+
+ * gemrb/plugins/Core/Control.cpp: applied patch from wjp, fixes #994601
+
+
+GemRB v0.2.2.9 Added Features:
+- Bug Fixes:
+ - Fixed Object Parsing in Dialog code
+- Added Features:
+ - Added many scripting actions, triggers, object filters
+ - Added hotkeys like '1-9', '=' to select players
+
+GemRB v0.2.2.8 Added Features:
+- Bug Fixes:
+ - Fixed a Big Bug with the Map Displayer:
+ It was crashing while trying to display
+ the left/top border of an area when the GUI was displayed.
+ - Fixed a Bug in the MUS Importer:
+ Error while decoding IWD2 Music PlayLists
+- Added Features:
+ - Full ACM Audio Streaming (no more WAV file in the Cache :)
+ - Added Travel Triggers support
+ - Added Area Travelling (now you can enter doors, etc... :)
+- Enhancements:
+ - Support for 'any-size-music-playlist' (the original engine supports up to 100 entries in a music playlist)
+ - Music JukeBox ;) (actually implemented on BG2 and IWD2. Enter the Movies men� and click on the Credits button :)
+- Cheat/Debug Codes Added:
+ - CTRL+T: Teletransport the current Actor to the mouse position
+- Added In-Game Script OpCodes:
+ - StartDialogue: it is a simple hack using the Dialogue OpCode
+
+GemRB v0.2.2.7 Added Features:
+- Bug Fixes:
+ - Fixed a bug when loading GAMEV2.2 Files (IWD2 SaveGames)
+- Added Features:
+ - Added CursorBAM=CARET option for Planescape: Torment
+ - Basic In-Game GUI for IWD, IWD2, PST, and BG2 (BG1 should work with the BG2 GUI Scripts)
+ - Added Dialog Support
+ - Added Switch Triggers Support
+- Added In-Game Script OpCodes:
+ - Actions:
+ - UnhideGUI: Shows the In-Game GUI and Ends the CutScene Mode
+ - HideGUI: Hides the In-Game GUI
+ - Dialogue: Starts a Dialogue
+ - AmbientActivate: Enables/Disables an Ambient Animation
+ - Triggers:
+ - Range: Returns true only if the specified object is within distance given (in feet) of the active CRE.
+ - Clicked: Only for trigger regions. Returns true if the specified object clicked on the trigger region running this script.
+- Developer Changes:
+ - Removed the InfoPoint structure
+ - Added a new InfoPoint class
+ - Added Map::GetAnimation(const char * Name)
+ - Added support to Activate/Deactivate Animations
+ - Added a new ScriptableType Value (ST_PROXIMITY = 1)
+
+GemRB v0.2.2.6 Added Features:
+- New Plugins:
+ - GAM Importer
+- New Features:
+ - In-Game Script Interpreter
+ - Added Console functions to run/evaluate In-Game Script Functions
+- Optimizations:
+ - Now the Decompression Phase is fast also on Systems with Low Memory (Thanks to |Cable|)
+- Console Output Cleanup
+- Applied Patches:
+ - 862003: ZLib Decompression Fix for Low Memory Systems (Submitted bu |Cable|)
+ - 862660: GameOnCD Option (Saves a big amount of Cache Space while having a full game installation) (Submitted by |Cable|)
+
+GemRB v0.2.2.5 Added Features:
+- Added some In-Game Script OpCodes:
+ - PlaySound
+ - CreateVisualEffect
+ - CreateVisualEffectObject
+ - DestroySelf
+- Fixed some In-Game Script OpCodes:
+ - Face: is now a Blocking OpCode
+ - Global: Added support for GLOBALS & LOCALS Contexts
+ - SetGlobal: Added support for GLOBALS & LOCALS Contexts
+- Optimizations:
+ - Little Speedup in the BlitSpriteMode function
+
+GemRB v0.2.2.4 Added Features:
+- Planescape: Torment support Added
+- Rewritten the Audio Driver to use OpenAL (no more FMOD)
+- Added Basic In-Game Scripts support
+- Added a PathFinder
+- Added Basic Visual Effects (VVC)
+
+GemRB v0.2.2.0 Added Features:
+- Major Bug Fixed: Never Freeing Plugin Instances
+- A fully working BG2 Character Generation GUI
+- Basic IWD/IWD2/BG1 GUI Implementation
+- Now you can Enter the Main Area after the Character Generation
+- Colored Characters
+- Basic Character Selection
+- Basic Door Manipulation (Open/Close state)
+- Minor Bug Fixes
+- Interactive Console
+and Much More.
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..fbd4118
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,1227 @@
+# Doxyfile 1.4.3-20050530
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = GemRB
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = gemrb/docs/en/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish,
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese,
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish,
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES = iespecific="\emph{(Infinity Engine specific)}"
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST = YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation.
+
+SHOW_DIRECTORIES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the progam writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS = *.cpp *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS = .git */.libs */.deps
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that a graph may be further truncated if the graph's
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..2743ebe
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,60 @@
+BUILDING GEMRB WITH CMAKE
+-------------------------
+
+Tools you will need to build GemRB:
+ - cmake
+ - make
+ - g++
+
+Libraries:
+ - ZLib
+ - Python 2.3 or better, compiled with shared libraries
+ - SDL 1.2
+ - OpenAL (optional, for sound)
+ - SDL_mixer (optional, for fast lower-quality sound)
+ - libpng (optional, for the png importer plugin)
+
+Building GemRB on unix-like systems
+-----------------------------------
+
+The following commands will try to configure, make and install GemRB.
+ mkdir build
+ cd build
+ cmake ..
+ make
+ make install
+
+By default, GemRB is installed into /usr/local ("fhs"). You can pass -DLAYOUT
+with "home" or "opt" to change the general layout and -DPREFIX to change the
+install path prefix. Check the toplevel CMakeLists.txt to get see all the
+individual path components you can additionally alter.
+
+Pass -DCMAKE_BUILD_TYPE=Debug to cmake if you want to create a debug build.
+Pass -DNOCOLOR=1 if you want to disable colored console output, which is a
+useful option for transparent terminal emulators or non white-on-black color
+schemes.
+
+If you're on an exotic platform and get a lot of errors trying to build,
+also pass -DDISABLE_WERROR=1, so warnings won't impede you. This option is
+also suggested if you're making a source package.
+
+You can also pass -DTOUCHSCREEN=1 on devices that have one. GemRB will disable
+mouse scrolling and use wider visible trigger-area scrolling on borders.
+
+Please let us know if you encounter any problems while building.
+
+Building GemRB with mingw
+-------------------------
+
+The following commands will try to configure, make and install GemRB.
+ mkdir build
+ cd build
+ cmake .. -G "MinGW MakeFiles"
+ mingw32-make
+ mingw32-make install
+
+By default, GemRB is installed into c:\Program Files\GemRB . Supporting
+files are mostly installed into the same directory.
+
+See the following for an extensive build walkthrough:
+http://forums.gibberlings3.net/index.php?showtopic=13087
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..995cfd0
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,39 @@
+SUBDIRS = gemrb
+
+SPEC_FILES = \
+ gemrb.spec \
+ gemrb.spec.in
+
+EXTRA_DIST = CMakeLists.txt \
+ cmake_config.h.in \
+ cmake_uninstall.cmake.in \
+ $(SPEC_FILES)
+
+MAINTAINERCLEANFILES = \
+ aclocal.m4 \
+ config.h.in \
+ config.log \
+ config.status \
+ configure \
+ configure.files \
+ stamp-h.in \
+ subdirs \
+ admin/config.guess \
+ admin/config.sub \
+ admin/depcomp \
+ admin/install-sh \
+ admin/ltmain.sh \
+ admin/missing \
+ admin/mkinstalldirs \
+ admin/py-compile
+
+dist_man6_MANS = gemrb.6.in
+
+install-data-hook:
+ rm -f $(DESTDIR)$(bindir)/plugins/libgemrb_core.so
+
+docs:
+ doxygen
+
+dist-hook:
+ sed -i 's,-Werror,,' $(distdir)/configure.in $(distdir)/configure
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..6b6f31c
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,392 @@
+GemRB V0.6.4 (2011-03-27):
+ New features:
+ - PST maze and (un)hardcoded projectiles
+ - full wild, dead, miscast and vocal magic support
+ - subspell selection (spell immunity, nahal's reckless dweomer...)
+ - Autodetect GameType if it is set to 'auto' or commented out
+ - compatibility with the bg2 demo
+ - VVC lightspot support
+
+ Improved features:
+ - travel regions, projectiles, dialog startup
+ - bink player has no/less artifacts, good sound quality
+ - combat, effects, actions, triggers, banters
+ - bugfixes
+
+ Applied patches:
+ a set of patches for Android support from Beholder
+ arm compilation fixes by ShadowJack
+ patch from Thomas Klausner fixing building with libpng 1.5
+ patch by Hana Dusíková to support static linking on mac
+
+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/README b/README
new file mode 100644
index 0000000..8d3b719
--- /dev/null
+++ b/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/TODO b/TODO
new file mode 100644
index 0000000..f18e8cd
--- /dev/null
+++ b/TODO
@@ -0,0 +1,98 @@
+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: "<GENDER?stref1:strref2>" (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. (DONE-almost) 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!!!
+
+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
+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/acinclude.m4 b/acinclude.m4
new file mode 100644
index 0000000..f583572
--- /dev/null
+++ b/acinclude.m4
@@ -0,0 +1,581 @@
+# -*-autoconf-*-
+# 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.
+#
+
+###################################################
+dnl Configure paths for SDL
+dnl Edheldil & Subvertir - 12/09/03
+dnl Sam Lantinga 9/21/99
+dnl stolen from Manish Singh
+dnl stolen back from Frank Belew
+dnl stolen from Manish Singh
+dnl Shamelessly stolen from Owen Taylor
+
+dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS
+dnl
+
+AC_DEFUN([AM_PATH_SDL],
+[dnl
+dnl Get the cflags and libraries from the sdl-config script
+dnl
+AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)],
+ sdl_prefix="$withval", sdl_prefix="")
+AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)],
+ sdl_exec_prefix="$withval", sdl_exec_prefix="")
+AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program],
+ , enable_sdltest=yes)
+
+ if test x$sdl_exec_prefix != x ; then
+ sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix"
+ if test x${SDL_CONFIG+set} != xset ; then
+ SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config
+ fi
+ fi
+ if test x$sdl_prefix != x ; then
+ sdl_args="$sdl_args --prefix=$sdl_prefix"
+ if test x${SDL_CONFIG+set} != xset ; then
+ SDL_CONFIG=$sdl_prefix/bin/sdl-config
+ fi
+ fi
+
+ AC_REQUIRE([AC_CANONICAL_TARGET])
+ PATH="$prefix/bin:$prefix/usr/bin:$PATH"
+ AC_PATH_PROG(SDL_CONFIG, sdl-config, no, [$PATH])
+ min_sdl_version=ifelse([$1], ,0.11.0,$1)
+ AC_MSG_CHECKING(for SDL - version >= $min_sdl_version)
+ no_sdl=""
+ if test "$SDL_CONFIG" = "no" ; then
+ no_sdl=yes
+ else
+ SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags`
+ SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs`
+
+ sdl_major_version=`$SDL_CONFIG $sdl_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+ if test "x$enable_sdltest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $SDL_CFLAGS"
+ CXXFLAGS="$CXXFLAGS $SDL_CFLAGS"
+ LIBS="$LIBS $SDL_LIBS"
+dnl
+dnl Now check if the installed SDL is sufficiently new. (Also sanity
+dnl checks the results of sdl-config to some extent
+dnl
+ rm -f conf.sdltest
+ AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "SDL.h"
+
+char*
+my_strdup (char *str)
+{
+ char *new_str;
+
+ if (str)
+ {
+ new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char));
+ strcpy (new_str, str);
+ }
+ else
+ new_str = NULL;
+
+ return new_str;
+}
+
+int main (int argc, char *argv[])
+{
+ int major, minor, micro;
+ char *tmp_version;
+
+ /* This hangs on some systems (?)
+ system ("touch conf.sdltest");
+ */
+ { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); }
+
+ /* HP/UX 9 (%@#!) writes to sscanf strings */
+ tmp_version = my_strdup("$min_sdl_version");
+ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) {
+ printf("%s, bad version string\n", "$min_sdl_version");
+ exit(1);
+ }
+
+ if (($sdl_major_version > major) ||
+ (($sdl_major_version == major) && ($sdl_minor_version > minor)) ||
+ (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro)))
+ {
+ return 0;
+ }
+ else
+ {
+ printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version);
+ printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro);
+ printf("*** best to upgrade to the required version.\n");
+ printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n");
+ printf("*** to point to the correct copy of sdl-config, and remove the file\n");
+ printf("*** config.cache before re-running configure\n");
+ return 1;
+ }
+}
+
+],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ if test "x$no_sdl" = x ; then
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT(no)
+ if test "$SDL_CONFIG" = "no" ; then
+ echo "*** The sdl-config script installed by SDL could not be found"
+ echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in"
+ echo "*** your path, or set the SDL_CONFIG environment variable to the"
+ echo "*** full path to sdl-config."
+ else
+ if test -f conf.sdltest ; then
+ :
+ else
+ echo "*** Could not run SDL test program, checking why..."
+ CFLAGS="$CFLAGS $SDL_CFLAGS"
+ LIBS="$LIBS $SDL_LIBS"
+ AC_TRY_LINK([
+#include <stdio.h>
+#include "SDL.h"
+
+int main(int argc, char *argv[])
+{ return 0; }
+#undef main
+#define main K_and_R_C_main
+], [ return 0; ],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding SDL or finding the wrong"
+ echo "*** version of SDL. If it is not finding SDL, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means SDL was incorrectly installed"
+ echo "*** or that you have moved SDL since it was installed. In the latter case, you"
+ echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ SDL_CFLAGS=""
+ SDL_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(SDL_CFLAGS)
+ AC_SUBST(SDL_LIBS)
+ rm -f conf.sdltest
+])
+
+###################################################
+dnl Configure paths for OPENAL
+dnl AM_PATH_OPENAL([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Test for OPENAL, and define OPENAL_CFLAGS and OPENAL_LIBS
+dnl
+
+AC_DEFUN([AM_PATH_OPENAL],
+[dnl
+dnl Get the cflags and libraries from the openal-config script
+dnl
+AC_ARG_WITH(openal-prefix,[ --with-openal-prefix=PFX Prefix where OPENAL is installed (optional)],
+ openal_prefix="$withval", openal_prefix="")
+AC_ARG_WITH(openal-exec-prefix,[ --with-openal-exec-prefix=PFX Exec prefix where OPENAL is installed (optional)],
+ openal_exec_prefix="$withval", openal_exec_prefix="")
+AC_ARG_ENABLE(openaltest, [ --disable-openaltest Do not try to compile and run a test OPENAL program],
+ , enable_openaltest=yes)
+
+ if test x$openal_exec_prefix != x ; then
+ openal_args="$openal_args --exec-prefix=$openal_exec_prefix"
+ if test x${OPENAL_CONFIG+set} != xset ; then
+ OPENAL_CONFIG=$openal_exec_prefix/bin/openal-config
+ fi
+ fi
+ if test x$openal_prefix != x ; then
+ openal_args="$openal_args --prefix=$openal_prefix"
+ if test x${OPENAL_CONFIG+set} != xset ; then
+ OPENAL_CONFIG=$openal_prefix/bin/openal-config
+ fi
+ fi
+
+ AC_REQUIRE([AC_CANONICAL_TARGET])
+ PATH="$prefix/bin:$prefix/usr/bin:$PATH"
+ AC_PATH_PROG(OPENAL_CONFIG, openal-config, no, [$PATH])
+ AC_PATH_PROG(PKGCONFIG, pkg-config, no, [$PATH])
+ AC_MSG_CHECKING(for OPENAL library)
+ no_openal=""
+ if test "$OPENAL_CONFIG" = "no" && test "$PKGCONFIG" = "no" ; then
+ no_openal=yes
+ else
+ if test "$OPENAL_CONFIG" = "no" ; then
+ OPENAL_CFLAGS=`$PKGCONFIG openal --cflags`
+ OPENAL_LIBS="`$PKGCONFIG openal --libs` $LIBPTHREAD"
+ else
+ OPENAL_CFLAGS=`$OPENAL_CONFIG $openalconf_args --cflags`
+ OPENAL_LIBS="`$OPENAL_CONFIG $openalconf_args --libs` $LIBPTHREAD"
+ fi
+
+ if test "x$enable_openaltest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $OPENAL_CFLAGS"
+ CXXFLAGS="$CXXFLAGS $OPENAL_CFLAGS"
+ LIBS="$LIBS $OPENAL_LIBS"
+dnl
+dnl Now check if the installed OPENAL is sufficiently new.
+dnl
+ rm -f conf.openaltest
+ AC_TRY_LINK([
+#include "AL/al.h"
+],[ return alGetError(); ],no_openal="",no_openal=yes)
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ if test "x$no_openal" = x ; then
+ AC_MSG_RESULT(yes)
+ ifelse([$1], , :, [$1])
+ else
+ AC_MSG_RESULT(no)
+ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means OPENAL is not installed,"
+ echo "*** that it's installed incorrectly or that it has been moved since"
+ echo "*** installation. In the latter case, you may want to edit the "
+ echo "*** openal-config script: $OPENAL_CONFIG"
+ OPENAL_CFLAGS=""
+ OPENAL_LIBS=""
+ ifelse([$2], , :, [$2])
+ fi
+ AC_SUBST(OPENAL_CFLAGS)
+ AC_SUBST(OPENAL_LIBS)
+ rm -f conf.openaltest
+])
+
+
+
+###################################################
+dnl Configure paths for python
+dnl Shamelessly ripped from dia 0.92
+dnl From Andrew Dalke
+dnl Updated by James Henstridge
+
+AC_DEFUN([AM_PATH_PYTHON],
+ [
+ dnl Find a version of Python. I could check for python versions 1.4
+ dnl or earlier, but the default installation locations changed from
+ dnl $prefix/lib/site-python in 1.4 to $prefix/lib/python1.5/site-packages
+ dnl in 1.5, and I don't want to maintain that logic.
+
+ if test -z "$PYTHON"; then
+ AC_PATH_PROGS(PYTHON, python python2.3 python2.2 python2.1 python2.0 python1.6 python1.5)
+ fi
+
+ dnl should we do the version check?
+ ifelse([$1],[],,[
+ AC_MSG_CHECKING(if Python version >= $1)
+ changequote(<<, >>)dnl
+ prog="
+import sys, string
+minver = '$1'
+pyver = string.split(sys.version)[0] # first word is version string
+# split strings by '.' and convert to numeric
+minver = map(string.atoi, string.split(minver, '.'))
+if hasattr(sys, 'version_info'):
+ pyver = sys.version_info[:3]
+else:
+ pyver = map(string.atoi, string.split(pyver, '.'))
+# we can now do comparisons on the two lists:
+if tuple(pyver) >= tuple(minver):
+ sys.exit(0)
+else:
+ sys.exit(1)"
+ changequote([, ])dnl
+ if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC
+ then
+ AC_MSG_RESULT(okay)
+ $2
+ else
+ AC_MSG_RESULT(too old)
+ $3
+ fi
+ ])
+
+ AC_MSG_CHECKING([local Python configuration])
+ echo
+
+ dnl Query Python for its version number. Getting [:3] seems to be
+ dnl the best way to do this; it's what "site.py" does in the standard
+ dnl library. Need to change quote character because of [:3]
+
+ AC_SUBST(PYTHON_VERSION)
+ changequote(<<, >>)dnl
+ PYTHON_VERSION=`$PYTHON -c "import sys; print sys.version[:3]"`
+ changequote([, ])dnl
+
+
+ dnl Use the values of $prefix and $exec_prefix for the corresponding
+ dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made
+ dnl distinct variables so they can be overridden if need be. However,
+ dnl general consensus is that you shouldn't need this ability.
+
+ AC_SUBST(PYTHON_PREFIX)
+ PYTHON_PREFIX='${prefix}'
+
+ AC_SUBST(PYTHON_EXEC_PREFIX)
+ PYTHON_EXEC_PREFIX='${exec_prefix}'
+
+
+])
+
+###################################################
+dnl Macro to check for availability of python headers
+dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
+dnl function also defines PYTHON_INCLUDES
+
+AC_DEFUN([AM_CHECK_PYTHON_HEADERS],
+[AC_REQUIRE([AM_PATH_PYTHON])
+AC_MSG_CHECKING(for python headers)
+dnl deduce PYTHON_INCLUDES
+py_prefix=`$PYTHON -c "import sys; print sys.prefix"`
+py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"`
+PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
+if test "$py_prefix" != "$py_exec_prefix"; then
+ PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
+fi
+AC_SUBST(PYTHON_INCLUDES)
+dnl check if the headers exist:
+save_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
+AC_TRY_CPP([#include <Python.h>],dnl
+[AC_MSG_RESULT(found)
+$1],dnl
+[AC_MSG_RESULT(not found)
+$2])
+CPPFLAGS="$save_CPPFLAGS"
+])
+
+###################################################
+dnl Macro to check for availability of python libraries
+dnl AM_CHECK_PYTHON_LIBS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
+dnl function also defines PYTHON_LIBS
+
+AC_DEFUN([AM_CHECK_PYTHON_LIBS],
+[AC_REQUIRE([AM_PATH_PYTHON])
+AC_MSG_CHECKING(for python libraries)
+dnl deduce PYTHON_LIBS
+py_prefix=`$PYTHON -c "import sys; print sys.prefix"`
+py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"`
+PYTHON_LIBS="-L${py_prefix}/lib -lpython${PYTHON_VERSION}"
+if test "$py_prefix" != "$py_exec_prefix"; then
+ PYTHON_LIBS="$PYTHON_LIBS -L${py_exec_prefix}/lib -lpython${PYTHON_VERSION}"
+fi
+AC_SUBST(PYTHON_LIBS)
+dnl check if the lib links:
+save_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
+save_LIBS="$LIBS"
+LIBS="$LIBS $PYTHON_LIBS $LIBPTHREAD"
+AC_TRY_LINK([#include <Python.h>],[
+Py_Initialize();
+],dnl
+[AC_MSG_RESULT(found)
+$1],dnl
+[AC_MSG_RESULT(not found)
+$2])
+LIBS="$save_LIBS"
+CPPFLAGS="$save_CPPFLAGS"
+])
+
+
+###################################################
+dnl Check for ZLib (gzip compression) library
+dnl Available from the GNU Autoconf Macro Archive at:
+dnl http://www.gnu.org/software/ac-archive/htmldoc/check_zlib.html
+dnl
+
+AC_DEFUN([CHECK_ZLIB],
+#
+# Handle user hints
+#
+[AC_MSG_CHECKING(if zlib is wanted)
+AC_ARG_WITH(zlib,
+[ --with-zlib=DIR root directory path of zlib installation [defaults to
+ /usr/local or /usr if not found in /usr/local]
+ --without-zlib to disable zlib usage completely],
+[if test "$withval" != no ; then
+ AC_MSG_RESULT(yes)
+ ZLIB_HOME="$withval"
+else
+ AC_MSG_RESULT(no)
+fi], [
+AC_MSG_RESULT(yes)
+ZLIB_HOME=/usr/local
+if test ! -f "${ZLIB_HOME}/include/zlib.h"
+then
+ ZLIB_HOME=/usr
+fi
+])
+
+#
+# Locate zlib, if wanted
+#
+if test -n "${ZLIB_HOME}"
+then
+ ZLIB_OLD_LDFLAGS=$LDFLAGS
+ ZLIB_OLD_CPPFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -L${ZLIB_HOME}/lib"
+ CPPFLAGS="$CPPFLAGS -I${ZLIB_HOME}/include"
+ AC_LANG_SAVE
+ AC_LANG_C
+ AC_CHECK_LIB(z, inflateEnd, [zlib_cv_libz=yes], [zlib_cv_libz=no])
+ AC_CHECK_HEADER(zlib.h, [zlib_cv_zlib_h=yes], [zlib_cvs_zlib_h=no])
+ AC_LANG_RESTORE
+ if test "$zlib_cv_libz" = "yes" -a "$zlib_cv_zlib_h" = "yes"
+ then
+ #
+ # If both library and header were found, use them
+ #
+ AC_CHECK_LIB(z, inflateEnd)
+ AC_MSG_CHECKING(zlib in ${ZLIB_HOME})
+ AC_MSG_RESULT(ok)
+ else
+ #
+ # If either header or library was not found, revert and bomb
+ #
+ AC_MSG_CHECKING(zlib in ${ZLIB_HOME})
+ LDFLAGS="$ZLIB_OLD_LDFLAGS"
+ CPPFLAGS="$ZLIB_OLD_CPPFLAGS"
+ AC_MSG_RESULT(failed)
+ AC_MSG_ERROR(either specify a valid zlib installation with --with-zlib=DIR or disable zlib usage with --without-zlib)
+ fi
+fi
+
+])
+
+
+###################################################
+dnl Test whether STL library defines method container::at().
+dnl Older versions (e.g. 2.95.x on Debian) don't and newer (3.x) do
+dnl Syntax: AC_CHECK_STL_CONTAINER_AT([ACTION-IF-YES], [ACTION-IF-NO])
+
+AC_DEFUN([AC_CHECK_STL_CONTAINER_AT],
+[
+AC_MSG_CHECKING(for container::at)
+AC_TRY_COMPILE(
+[
+#include <vector>
+#include <deque>
+#include <string>
+
+using namespace std;
+],
+[
+deque<int> test_deque(3);
+test_deque.at(2);
+vector<int> test_vector(2);
+test_vector.at(1);
+string test_string("test_string");
+test_string.at(3);
+],
+[AC_MSG_RESULT(yes)
+dnl AC_DEFINE(HAVE_CONTAINER_AT)
+$1],
+[AC_MSG_RESULT(no)
+$2])
+])
+
+###################################################
+dnl Test whether the compiler permits casting from pointer-to-object
+dnl to pointer-to-function (forbidden in GCC v4 and ISO C++).
+dnl If the cast is forbidden, define HAVE_FORBIDDEN_OBJECT_TO_FUNCTION_CAST.
+dnl Syntax: AC_CHECK_OBJECT_TO_FUNCTION_CAST()
+
+AC_DEFUN([AC_CHECK_OBJECT_TO_FUNCTION_CAST],
+[
+SAVE_CXXFLAGS="$CXXFLAGS"
+CXXFLAGS="$CXXFLAGS -Werror"
+AC_MSG_CHECKING(whether compiler permits casting between ptr-to-object and ptr-to-function)
+AC_TRY_COMPILE(
+[
+typedef void *(* voidvoid)(void);
+],
+[
+void *object = 0;
+voidvoid function;
+function = (voidvoid) object;
+],
+[AC_MSG_RESULT(yes)
+],
+[AC_MSG_RESULT(no)
+AC_DEFINE(HAVE_FORBIDDEN_OBJECT_TO_FUNCTION_CAST, 1, [Define to 1 if compiler forbids casting between pointer-to-function and pointer-to-object])
+])
+CXXFLAGS="$SAVE_CXXFLAGS"
+])
+
+
+###################################################
+dnl Check for the name of Posix threads library.
+dnl Ripped from XMMS by Peter Alm & co and modified to integrate w/ GemRB
+
+AC_DEFUN([AC_CHECK_PTHREADS],
+[
+LIBPTHREAD=error
+AC_MSG_CHECKING(for old style FreeBSD -pthread flag)
+AC_EGREP_CPP(yes,
+ [#if (defined(__FreeBSD_cc_version) && __FreeBSD_cc_version <= 500001) || defined(__OpenBSD__)
+ yes
+ #endif
+ ], AC_MSG_RESULT(yes)
+ CXXFLAGS="$CXXFLAGS -D_THREAD_SAFE"
+ LIBPTHREAD="-pthread",
+ AC_MSG_RESULT(no))
+if test "x$LIBPTHREAD" = xerror; then
+ AC_CHECK_LIB(pthread, pthread_attr_init,
+ LIBPTHREAD="-lpthread")
+fi
+if test "x$LIBPTHREAD" = xerror; then
+ AC_CHECK_LIB(pthreads, pthread_attr_init,
+ LIBPTHREAD="-lpthreads")
+fi
+if test "x$LIBPTHREAD" = xerror; then
+ AC_CHECK_LIB(c_r, pthread_attr_init,
+ LIBPTHREAD="-lc_r")
+fi
+if test "x$LIBPTHREAD" = xerror; then
+ AC_CHECK_FUNC(pthread_attr_init, LIBPTHREAD="")
+fi
+if test "x$LIBPTHREAD" = xerror; then
+ AC_MSG_ERROR(*** Unable to locate working posix thread library)
+fi
+AC_SUBST(LIBPTHREAD)
+])
+
diff --git a/admin/add_missing_gui_docs.sh b/admin/add_missing_gui_docs.sh
new file mode 100644
index 0000000..623f282
--- /dev/null
+++ b/admin/add_missing_gui_docs.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+# 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.
+#
+#
+
+if [[ ! -d admin ]]; then
+ echo 'Run me from the top gemrb dir that contains the admin subdir!'
+ exit 1
+fi
+docdir=gemrb/docs/en/GUIScript
+
+cd admin
+methods=$(./check_gui_doc.pl | grep ^+)
+cd -
+
+#+ SwapPCs : $md5_hash : SwapPCs(idx1, idx2)\n\n : Swaps the party order
+#$1 $2 $3 $4 $5 $6 $7
+while read line; do
+ set -- $line
+ method=$2
+ shift 5
+ tmp="$@"
+
+ prototype=$(cut -d: -f1 <<< "$tmp" | sed 's,nn\s*$,,')
+ description=$(cut -d: -f2- <<< "$tmp")
+
+ echo Adding $method stub with: $description
+ cp $docdir/doc_template.txt $docdir/$method.txt
+
+ return_value=$(awk '{ if(split($0,a,"=>")) print a[2]; }' <<< "$prototype")
+ if [[ -n $return_value ]]; then
+ # we also have the return "value", so remove it from the prototype
+ prototype=$(awk '{ if(split($0,a,"=>")) print a[1]; }' <<< "$prototype")
+ sed -i "s@^Return value:@& ${return_value# }@" $docdir/$method.txt
+ fi
+ sed -i "s@^Prototype:@& GemRB.${prototype% }@" $docdir/$method.txt
+ sed -i "s@^Description:@& $description@" $docdir/$method.txt
+
+ # get the parameters
+ # eg: WindowIndex ControlID x y w h direction[ font]
+ unset parameters
+ for parameter in $(sed -n 's@^[^(]*(\([^)]*\)).*$@\1 at p' <<< "$prototype"); do
+ parameter=$(tr -d ',' <<< "$parameter")
+ if [[ ${parameter:${#parameter}-1:1} == "]" ]]; then
+ # optional parameter
+ parameters="$parameters\n${parameter//]/} - (optional) "
+ else
+ parameters="$parameters\n${parameter//[/} - "
+ fi
+ done
+ sed -i "s@^Parameters:@& $parameters@" $docdir/$method.txt
+done <<< "$methods"
diff --git a/admin/announcement.template b/admin/announcement.template
new file mode 100644
index 0000000..4b099a8
--- /dev/null
+++ b/admin/announcement.template
@@ -0,0 +1,31 @@
+******************* FORUM ***************************
+GemRB 0.9.0 released!
+
+The [url=http://gemrb.sf.net]GemRB[/url] team is proud to announce a new (minor) release.
+
+(short description of major changes)
+
+Currently only the sources are available. You can get them [url=http://sourceforge.net/project/showfiles.php?group_id=10122]here[/url].
+http://sourceforge.net/project/showfiles.php?group_id=10122]here[/url].
+
+Full changelog digest:
+[code]
+NEWS entry
+[/code]
+
+******************* MODDING RING ********************
+For the modding news blurb, just skip the changelog digest and put a link
+to the main forum post. Also add a general intro, since this is syndicated:
+[url=http://gemrb.sf.net]GemRB[/url] is a portable open-source implementation of Bioware's Infinity Engine. It was written to support pseudo-3D role playing games based on the Dungeons & Dragons ruleset (Baldur's Gate and Icewind Dale series, Planescape: Torment).
+
+******************* SOURCEFORGE *********************
+For SF, just remove all of the BBCode.
+
+https://sourceforge.net/news/submit.php?group_id=10122
+
+******************** HAPPY PENGUIN ******************
+Visit http://www.happypenguin.org/update?GemRB%3A%20The%20Infinity%20Engine%20Clone
+and insert a shortened NEWS summary (3-10 items)
+
+******************** LGDB ******************
+Visit the project page and submit an update (version only).
diff --git a/admin/check_copyright.pl b/admin/check_copyright.pl
new file mode 100755
index 0000000..cc31e56
--- /dev/null
+++ b/admin/check_copyright.pl
@@ -0,0 +1,167 @@
+#!/usr/bin/perl -w
+# 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.
+#
+#
+
+use strict;
+
+my $TGT_DIR = "gemrb";
+
+# TODO: use list of exceptions, i.e. pairs file, boilerplate to use
+#
+
+
+my $copyright_cpp = <<EOT;
+/\\* 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\\.
+ \\*
+ \\* \\\$[I]d:.* \\\$
+ \\*
+ \\*/
+
+/\\*\\*
+ \\* \@file .*
+ \\* .*
+ \\* \@author The GemRB Project
+ \\*/
+EOT
+
+my $copyright_py = <<EOT;
+# \\-\\*\\-python\\-\\*\\-
+# 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\\.
+#
+# \\\$[I]d: .* \\\$
+EOT
+
+
+
+my @copyright_cpp = split (/\n/, $copyright_cpp);
+my @copyright_py = split (/\n/, $copyright_py);
+
+my @filetypes = (
+ [ '/\\.#', 'ignore'],
+ [ '~$', 'ignore'],
+ [ '\\.(pyc|lo)$', 'ignore'],
+ [ '\\.(h|cpp)$', \@copyright_cpp ],
+ [ '\\.py$', \@copyright_py ],
+ [ '.', 'ignore' ],
+ );
+
+
+sub get_filetype {
+ my ($filename) = @_;
+ foreach my $rec (@filetypes) {
+ my $re = $$rec[0];
+ if ($filename =~ /$re/) {
+ return $$rec[1];
+ }
+ }
+ return undef;
+}
+
+sub check_file {
+ my ($filename) = @_;
+
+ my $copyright = &get_filetype ($filename);
+ if (! defined ($copyright)) {
+ print "? $filename\n";
+ return;
+ }
+
+ if ($copyright eq 'ignore') {
+ return;
+ }
+
+ my $num_lines = scalar (@{$copyright});
+
+
+ open (SRC, "< $filename") || die "Can't open file $filename: $!\n";
+
+ my $index = 0;
+ while (defined (my $line = <SRC>) && ($index < $num_lines)) {
+ chomp($line);
+ my $cline = $$copyright[$index];
+ if ($line !~ /^$cline$/) {
+ $cline =~ tr/\\//d;
+ print "! $filename\n";
+ print " -: $cline\n";
+ print " +: $line\n";
+ print "\n";
+ last;
+ }
+ $index++;
+ }
+
+ if ($index >= $num_lines) {
+ print "= $filename\n";
+ }
+
+ close (SRC);
+}
+
+sub check_dir {
+ my ($dir) = @_;
+ local (*DIR);
+
+ opendir (DIR, $dir) || die "Can't open dir $dir: $!\n";
+ while (defined (my $file = readdir (DIR))) {
+ my $dirfile = "$dir/$file";
+ if (-f $dirfile) {
+ &check_file ($dirfile);
+ } elsif (-d $dirfile && substr($file, 0, 1) ne '.') {
+ &check_dir ($dirfile);
+ }
+ }
+ closedir (DIR);
+}
+
+
+if (scalar (@ARGV) != 0) {
+ $TGT_DIR = $ARGV[0];
+}
+
+&check_dir ($TGT_DIR);
diff --git a/admin/check_gui_doc.pl b/admin/check_gui_doc.pl
new file mode 100755
index 0000000..97aab19
--- /dev/null
+++ b/admin/check_gui_doc.pl
@@ -0,0 +1,103 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Digest::MD5 qw(md5_hex);
+
+my $SRCFILE = "../gemrb/plugins/GUIScript/GUIScript.cpp";
+my $TGTDIR = "../gemrb/docs/en/GUIScript";
+
+my %fn_hash = ();
+my %file_hash = ();
+my %desc_hash = ();
+
+my %file_ignore = (
+ 'CVS' => 1,
+ 'Makefile.am' => 1,
+ 'Makefile.in' => 1,
+ 'Makefile' => 1,
+ );
+
+sub parse_guiscript_cpp {
+ local (*SRC);
+
+ my $fname = '';
+ my $desc = '';
+
+ open (SRC, "< $SRCFILE") || die "Can't open $SRCFILE: $!\n";
+
+ while (defined (my $line = <SRC>)) {
+ if ($line =~ /^PyDoc_STRVAR\s*\(\s*GemRB_(.*)__doc/g) {
+ $fname = $1;
+ }
+ elsif ($fname && $line =~ /^\"(.*)\"\s*$/g) {
+ $desc .= $1;
+ }
+ elsif ($fname && $line =~ /^\"(.*)\"\s*\);\s*$/g) {
+ $desc .= " : $1";
+ my $md5 = md5_hex ($1);
+ $fn_hash{$fname} = $md5;
+ $desc_hash{$fname} = $desc;
+ $fname = '';
+ $desc = '';
+ }
+ }
+
+ close (SRC);
+}
+
+sub parse_doc {
+ my ($file) = @_;
+ local (*SRC);
+
+ open (SRC, "< $TGTDIR/$file") || die "Can't open $TGTDIR/$file: $!\n";
+
+ my $md5 = '';
+ while (defined (my $line = <SRC>)) {
+ if ($line =~ /^MD5:\s*([0-9a-f]+)/o) {
+ $md5 = $1;
+ last;
+ }
+ }
+
+ $file_hash{$file} = $md5;
+
+ close (SRC);
+}
+
+&parse_guiscript_cpp ();
+
+opendir (DIR, $TGTDIR) || die "Can't open dir $TGTDIR: $!\n";
+my @files = grep { -f "$TGTDIR/$_" && ! exists ($file_ignore{$_}) } grep !/^\.\.?/, readdir (DIR);
+closedir (DIR);
+
+foreach my $f (@files) {
+ &parse_doc ($f);
+}
+
+foreach my $fn (sort keys %fn_hash) {
+ my $md5_1 = $fn_hash{$fn};
+ my $file = $fn . ".txt";
+
+ if (exists ($file_hash{$file})) {
+ my $md5_2 = $file_hash{$file};
+
+ if ($md5_1 eq $md5_2) {
+ print "= $fn\n";
+ } else {
+ print "! $fn: $md5_1 : $md5_2\n";
+ }
+ }
+ else {
+ print "+ $fn : $md5_1 : $desc_hash{$fn}\n";
+ }
+}
+
+foreach my $file (sort keys %file_hash) {
+ my $md5 = $file_hash{$file};
+ my $fn = $file;
+ $fn =~ s/\.[^\.]+$//o;
+
+ if (! exists ($fn_hash{$fn})) {
+ print "- $fn : $md5\n";
+ }
+}
diff --git a/admin/generate_formation_files.sh b/admin/generate_formation_files.sh
new file mode 100755
index 0000000..0ff6256
--- /dev/null
+++ b/admin/generate_formation_files.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+./make_formation.py bg1 > ../gemrb/override/bg1/formatio.2da
+./make_formation.py bg2 > ../gemrb/override/bg2/formatio.2da
+./make_formation.py iwd > ../gemrb/override/iwd/formatio.2da
+./make_formation.py iwd2 > ../gemrb/override/iwd2/formatio.2da
+./make_formation.py pst > ../gemrb/override/pst/formatio.2da
+./make_formation.py how > ../gemrb/override/how/formatio.2da
+
diff --git a/admin/guidoc_wikifier.sh b/admin/guidoc_wikifier.sh
new file mode 100755
index 0000000..13d6554
--- /dev/null
+++ b/admin/guidoc_wikifier.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+# 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.
+#
+#
+# this script takes the guiscript docs and prepares them to shine on dokuwiki
+
+docdir="${1:-$PWD/gemrb/docs/en/GUIScript}"
+out_dir="${2:-$PWD/guiscript-docs.wikified}"
+scriptdir="${3:-$docdir/../../../GUIScripts}"
+index_title="GUIScript function listing"
+see_also_str="See also:"
+
+mkdir -p "$out_dir"
+rm -f "$out_dir"/*.txt
+test -d "$docdir" || exit 1
+
+# intertwine the doc pages based on the "See also" entries
+echo Linking ...
+for txt_file in "$docdir"/*.txt; do
+ cp "$txt_file" "$out_dir/$(basename $txt_file)" || exit 2
+ # get the linked pages (all are in one line)
+ links=$(grep -m1 "$see_also_str" "$txt_file")
+ if [[ -n $links ]]; then
+ # we have some links and first we strip off the "See also:"
+ links=${links##*:}
+ for link in $links; do
+ # when there's multiple links, we have to deal with the commas
+ if [[ ! -f $docdir/${link//,/}.txt ]]; then
+ echo "Dangling link in $(basename $txt_file): ${link//,/}"
+ continue
+ fi
+ if [[ ${link##*,} == "" ]]; then
+ # has comma, which we strip and readd, so it doesn't affect the link
+ sed_cmd="/$see_also_str/ s@$link@[[guiscript:${link//,/}]], at g"
+ else
+ sed_cmd="/$see_also_str/ s@$link\>@[[guiscript:$link]]@g"
+ fi
+ sed -i "$sed_cmd" "$out_dir/$(basename $txt_file)"
+ done
+ fi
+done
+
+echo Formatting ...
+cd "$out_dir"
+echo > indexX
+# add some formatting to the doc pages
+for txt_file in *; do
+ # extra newlines
+ #sed -i "s,$,\n," $txt_file
+
+ # format the title
+ if grep -qR --include="*.py" "GemRB\.${txt_file%.*}" "$scriptdir"; then
+ # actual script command
+ sed -i "1 s,^,===== ${txt_file%.*} =====\n," $txt_file
+ if [[ ${txt_file%.*} == controls ]]; then echo juhu0; fi
+else
+ # miscellaneous doc
+ if [[ ${txt_file%.*} != indexX ]]; then
+ if [[ ${txt_file//_/} == $txt_file ]]; then
+ sed -i "1 s,^,===== ${txt_file%.*} =====\n," $txt_file
+ else
+ # misc doc with multiword title
+ sed -i "1 s,^\([^.]*\)\.*\s*$,===== \1 =====," $txt_file
+ fi
+ fi
+ fi
+
+ # bold the headlines, itemize the parameters and add some links
+ sed -i "/\[\[guiscript:/! s@^[^:]\{,20\}:@\n**&**@" $txt_file
+ sed -i "/^$see_also_str/ s@^[^:]\{,10\}:@\n**&**@" $txt_file
+ echo -e "\n\n[[guiscript:index|Function index]]" >> $txt_file
+ sed -i "/Parameters:/,/^[^:]*:\*\*/ { /^[^:]*:\*\*/!s,^\(\s*\S\S*\), * \1,}" $txt_file
+ sed -i '/^\s*$/ {N; /\n\s*$/D }' $txt_file # squeeze repeats of more than 2 newlines
+ echo " * [[guiscript:${txt_file%.*}]]" >> indexX
+
+ [[ ${txt_file%.*} == indexX ]] && continue
+ lo_file=$(tr '[[:upper:]]' '[[:lower:]]' <<< $txt_file)
+ if [[ $lo_file != $txt_file ]]; then
+ mv $txt_file $lo_file
+ fi
+done
+sort -o index.txt indexX
+rm indexX
+sed -i -e '/guiscript:index/d; /indexX/d' -e "1 s,^,===== $index_title =====\n," index.txt
+echo Done.
diff --git a/admin/make_formation.py b/admin/make_formation.py
new file mode 100755
index 0000000..10a0bec
--- /dev/null
+++ b/admin/make_formation.py
@@ -0,0 +1,367 @@
+#!/usr/bin/python
+# -*-python-*-
+# 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.
+
+# This generates a formatio.2da file for GemRB, because it was becoming
+# far too tedious to do this by hand. The output files have pairs of
+# coordinates in the order of the party members.
+
+# Run it as 'make_formation.py bg2 > override/bg2/formatio.2da' or similar.
+
+# written (badly, sorry!) by fuzzie, feb 2nd 2009
+
+# TODO: older games have focus points in different positions and different
+# spacing (ie, coordinates have different offsets)
+# eg, pst seems to usually have the focus point always on the lead char
+
+num_coords = 20
+
+def generate_header():
+ print "2DA V1.0"
+ print "-10"
+ print "# generated by make_formation.py, do not edit"
+
+ print "",
+ for i in range(num_coords):
+ print "X" + str(i + 1),
+ print "Y" + str(i + 1),
+ print
+
+# A simple spaced line formation.
+def generate_line(name):
+ print name,
+ yloc = 0
+ for i in range(num_coords):
+ print 0,
+ print yloc,
+ yloc += 36
+ print
+
+# A 'T' shape formation, with 3 actors at the front.
+def generate_t():
+ print "T",
+ for i in range(num_coords):
+ if i == 0:
+ # centre
+ print "0 0",
+ elif i == 1:
+ # right
+ print "48 0",
+ elif i == 2:
+ # left
+ print "-48 0",
+ elif i == 3:
+ # first line member (48 deeper)
+ print "0 48",
+ else:
+ # line member (36 deeper)
+ print 0,
+ print 48 + ((i - 3) * 36),
+ print
+
+# A 'gathered' formation around a centre point.
+def generate_gather():
+ print "GATHER",
+ for i in range(num_coords):
+ if i % 3 == 0:
+ if i < 3: # front row
+ xloc = 0 # centre
+ yloc = -36 # front
+ else:
+ xloc = 48 # right
+ yloc = 24 * (i / 3)
+ elif i % 3 == 1:
+ if i < 3: # front row
+ xloc = 48 # right
+ yloc = -24
+ else:
+ xloc = -48 # left
+ yloc = 24 * (i / 3)
+ else:
+ if i < 3: # front row
+ xloc = -48 # left
+ yloc = -24
+ else:
+ xloc = 0 # back
+ yloc = 36 * (i / 3)
+ print xloc,
+ print yloc,
+ print
+
+# A block formation which places 4 on a row - first two in the
+# middle (left then right), then two on the outside (left then right).
+# With a party size of 6 this results in 4 on the first row and 2 on the
+# second, hence the name.
+def generate_4and2():
+ print "4AND2",
+ for i in range(num_coords):
+ if i % 4 == 0:
+ xloc = 0
+ elif i % 4 == 1:
+ xloc = 64
+ elif i % 4 == 2:
+ xloc = -64
+ else:
+ xloc = 128
+ yloc = (i / 4) * 48
+ print xloc,
+ print yloc,
+ print
+
+# A wavy-line formation.
+def generate_s(bg2style):
+ print "S",
+ for i in range(num_coords):
+ # x coordinate: +/- 15 for bg2, 0/64 otherwise
+ if i % 2 == 0:
+ if bg2style: print 15, # on right
+ else: print 0, # on left
+ else:
+ if bg2style: print -15, # on left
+ else: print 64, # on right
+
+ # y coordinate: 24 each
+ print i * 24,
+ print
+
+# Returns the position in a formation of party member 'actorno',
+# if the lead character must be in position 'leadpos'.
+def corrected_position_for_actor(actorno, leadpos):
+ if actorno == 0: # lead
+ return leadpos
+ elif actorno < leadpos + 1: # in front of lead
+ return actorno - 1
+ else: # behind lead (normal position)
+ return actorno
+
+# A wavy-line formation, with the lead character in the 4th position and
+# the other characters in order (even if this leaves spaces!).
+def generate_wavyline():
+ print "WAVYLINE",
+ for i in range(num_coords):
+ pos = corrected_position_for_actor(i, 3)
+
+ # x coordinate: +/- 15
+ if pos % 2 == 0:
+ print 15, # on right
+ else:
+ print -15, # on left
+
+ # y coordinate: 24 each
+ print pos * 24,
+ print
+
+# A formation surrounding the main character. The next character goes
+# at the front, then one left, then one right, then the next left/right
+# at the back, then other characters trail behind the main one (it goes
+# completely chaotic in real BG2, apparently).
+def generate_protect():
+ print "PROTECT",
+ for i in range(num_coords):
+ if i == 0:
+ print "0 0", # centre
+ elif i == 1:
+ print "0 -36", # front
+ elif i == 2:
+ print "-64 0", # left
+ elif i == 3:
+ print "64 0", # right
+ elif i == 4:
+ print "-32 48", # back left
+ elif i == 5:
+ print "32 48", # back right
+ else:
+ print 0,
+ print 24 * (i - 5),
+ print
+
+# A simple 3-across block formation.
+def generate_3by2():
+ print "3BY2",
+ for i in range(num_coords):
+ # x coordinate
+ if i % 3 == 0:
+ print 0,
+ elif i % 3 == 1:
+ print 64,
+ else:
+ print -64,
+
+ # y coordinate
+ print (i / 3) * 48,
+ print
+
+# A simple 2-across block formation.
+def generate_2by3():
+ print "2BY3",
+ # left first, then right
+ left_side = True
+ yloc = 0
+ for i in range(num_coords):
+ if left_side: # left
+ print -24,
+ print yloc,
+ else: # right
+ print 24,
+ print yloc,
+ # first step back is 48, then 36
+ if yloc == 0:
+ yloc = 48
+ else:
+ yloc += 36
+ left_side = not left_side
+ print
+
+# A horizontal line formation, with each character being placed
+# alternately on right and left of the previous characters.
+def generate_rank():
+ print "RANK",
+ for i in range(num_coords):
+ # lead character placed at focal point -32, spacing is 64
+ if i % 2 == 0:
+ print -32 - ((i / 2) * 64),
+ else:
+ print -32 + (((i + 1) / 2) * 64),
+ print 0,
+ print
+
+def generate_v():
+ print "V",
+ for i in range(num_coords):
+ if i % 2 == 0:
+ xpos = (i / 2) * -15
+ else:
+ xpos = 64 + (i / 2) * -15
+ ypos = (i / 2) * 48
+ print xpos,
+ print ypos,
+ print
+
+# A triangle with the lead character at the back. Focal point is at the
+# front. Other characters are placed row-by-row starting at the front row,
+# middle then left then right, with the maximum width being 3.
+def generate_triangle():
+ print "TRIANGLE",
+ for i in range(num_coords):
+ pos = corrected_position_for_actor(i, 3)
+ if pos == 0:
+ print "0 0", # front
+ elif pos == 1:
+ print "-32 36", # middle left
+ elif pos == 2:
+ print "32 36", # middle right
+ else:
+ pos = pos - 3
+ # start 72 back, then move back 36 per row
+ ypos = 72 + ((pos / 3) * 36)
+
+ if pos % 3 == 0: # middle
+ xpos = 0
+ elif pos % 3 == 1: # left
+ xpos = -64
+ else: # right
+ xpos = 64
+
+ print xpos,
+ print ypos,
+ print
+
+# A wide triangle with the lead character at the front. Characters are placed
+# row-by-row. Second row: right then left. Third row: left then right then
+# middle. Other rows: Middle first, left, right.
+# TODO: the older games seem to have the third row ordered the same as the other ones
+def generate_wedge():
+ print "WEDGE",
+ for i in range(num_coords):
+ if i == 0:
+ print "0 0", # front
+ elif i == 1:
+ print "64 36", # second row: right
+ elif i == 2:
+ print "-64 36", # second row: left
+ elif i == 3:
+ print "-124 72", # third row: left
+ elif i == 4:
+ print "124 72", # third row: right
+ elif i == 5:
+ print "0 72", # third row: middle
+ else:
+ pos = i - 6
+ if pos % 3 == 0:
+ print 0, # middle
+ elif pos % 3 == 1:
+ print -124, # left
+ else:
+ print 124, # right
+ ypos = 72 + (i / 3) * 36
+ print ypos,
+ print
+
+from sys import argv,exit
+
+if len(argv) != 2:
+ print "pass a game name on the command line"
+ exit(1)
+
+generate_header()
+
+if argv[1] == "bg1" or argv[1] == "iwd" or argv[1] == "iwd2" or argv[1] == "how":
+ generate_line("FOLLOW") # TODO: should be hard-coded game logic
+ generate_t()
+ generate_gather()
+ generate_4and2()
+ generate_3by2()
+ generate_protect() # TODO: wrong formation for bg1 or others?
+ generate_2by3()
+ generate_rank()
+ generate_v()
+ generate_wedge()
+ generate_s(False)
+ generate_line("LINE")
+
+if argv[1] == "pst":
+ generate_line("FOLLOW") # TODO: should be hard-coded game logic
+ generate_t()
+ generate_gather()
+ generate_4and2()
+ generate_3by2()
+ generate_protect()
+ generate_2by3()
+ generate_rank()
+ generate_v()
+ generate_wedge()
+ generate_s(False)
+ generate_line("LINE")
+ print "NONE",
+ for i in range(num_coords):
+ print "0 0",
+ print
+
+if argv[1] == "bg2":
+ generate_line("FOLLOW") # TODO: should be hard-coded game logic
+ generate_t()
+ generate_gather()
+ generate_wavyline()
+ generate_3by2()
+ generate_protect()
+ generate_2by3()
+ generate_rank()
+ generate_triangle()
+ generate_wedge()
+ generate_s(True)
+ generate_line("LINE")
diff --git a/admin/restart_news.sh b/admin/restart_news.sh
new file mode 100755
index 0000000..6e4c817
--- /dev/null
+++ b/admin/restart_news.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# 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.
+#
+#
+# prepares NEWS for the restart of tracking bigger changes after release.
+
+if [[ ! -e NEWS ]]; then
+ echo 'Run me from the top gemrb dir that contains NEWS!'
+ exit 3
+fi
+
+# get the last revision that contains a change in the word git in NEWS
+# that's usually the final release update
+rev=$(git log -Sgit --pretty="format:%h" NEWS | head -n 1)
+rev="${rev:-missing revision}"
+
+cat - NEWS > NEWSNEWS << LILARCOR
+GemRB git ($rev):
+ New features:
+ -
+
+ Improved features:
+ -
+ - bugfixes
+
+ Applied patches:
+
+LILARCOR
+mv NEWSNEWS NEWS
diff --git a/artwork/gemrb-logo.png b/artwork/gemrb-logo.png
new file mode 100644
index 0000000..d85b678
Binary files /dev/null and b/artwork/gemrb-logo.png differ
diff --git a/artwork/logo04-8cog.svg b/artwork/logo04-8cog.svg
new file mode 100644
index 0000000..aa53908
--- /dev/null
+++ b/artwork/logo04-8cog.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0px" y="0px" width="1021px" height="411px" viewBox="0 0 1021 411" xml:space="preserve">
+<g id="Version_2b">
+ <path id="rb1" d="M727.579,321.447v-98.756c0-39.945,32.384-72.329,72.331-72.329v63.289c0,59.531,48.266,107.796,107.796,107.796
+ c59.533,0,107.796-48.265,107.796-107.796c0-59.534-48.263-107.796-107.796-107.796H844.42V5.011l-44.51,44.51v56.336
+ c-64.529,0-116.841,52.31-116.841,116.836v98.754H727.579z"/>
+ <g id="gem">
+ <path fill="#6156FF" d="M846.555,230.029c-1.394-5.226-2.137-10.713-2.137-16.378v-63.289l63.286,63.289L846.555,230.029z"/>
+ <path fill="#867DFF" d="M907.704,213.651l-16.382,61.146c-21.789-5.823-38.945-22.979-44.768-44.769L907.704,213.651z"/>
+ <path fill="#6156FF" d="M907.704,213.651l44.751,44.75c-11.453,11.453-27.273,18.539-44.751,18.539
+ c-5.665,0-11.156-0.747-16.382-2.143L907.704,213.651z"/>
+ <path fill="#3C2EFF" d="M968.851,197.266c1.396,5.226,2.144,10.718,2.144,16.386c0,17.478-7.086,33.297-18.539,44.75
+ l-44.751-44.75L968.851,197.266z"/>
+ <path fill="#2212FF" d="M907.704,213.651l16.386-61.147c21.785,5.823,38.938,22.976,44.761,44.761L907.704,213.651z"/>
+ <path fill="#3C2EFF" d="M844.418,150.363h63.286c5.665,0,11.16,0.746,16.386,2.142l-16.386,61.147L844.418,150.363z"/>
+ <polygon fill="#AAA4FF" points="874.113,222.65 883.116,189.059 916.707,180.06 941.295,204.648 932.296,238.239 898.705,247.242
+ "/>
+ </g>
+ <path id="e2b" d="M416.691,281.119l-22.85-21.763c2.838-8.699,4.392-17.978,4.392-27.624h-36.844
+ c0,28.809-23.353,52.162-52.16,52.162c-28.809,0-52.161-23.354-52.161-52.162c0-28.807,23.353-52.159,52.161-52.159
+ c14.399,0,27.437,5.834,36.876,15.27l-36.876,36.889h52.16l36.857-36.854l-18.645-18.644l13.726-27.655l-16.803-14.101
+ l-25.396,18.723c-7.289-3.897-15.181-6.809-23.504-8.558l-7.423-30.669h-21.934l-7.421,30.666
+ c-8.324,1.749-16.216,4.66-23.505,8.556l-25.4-18.724l-16.802,14.099l14.04,28.291c-5.128,6.538-9.36,13.808-12.514,21.633
+ l-31.499,1.985l-3.808,21.602l28.943,12.646c0.283,8.57,1.771,16.827,4.311,24.616l-22.85,21.767l10.968,18.994l30.284-8.909
+ c5.582,6.211,12.029,11.625,19.145,16.069l-3.515,31.374l20.613,7.501l17.471-26.291c4.087,0.572,8.257,0.89,12.503,0.89
+ c4.241,0,8.406-0.314,12.49-0.89l17.475,26.295l20.611-7.501l-3.517-31.371c7.118-4.443,13.565-9.856,19.148-16.068l30.288,8.912
+ L416.691,281.119z"/>
+ <path id="m" d="M420.784,231.734c0-49.158,39.848-89.005,89.004-89.005c13.854,0,26.966,3.166,38.656,8.812
+ c11.69-5.646,24.805-8.812,38.655-8.812c49.156,0,89.006,39.848,89.006,89.005v89.713h-36.844v-89.713
+ c0-28.809-23.354-52.161-52.162-52.161c-15.327,0-29.112,6.613-38.655,17.143c-9.544-10.53-23.325-17.143-38.656-17.143
+ c-28.808,0-52.16,23.353-52.16,52.161v89.713h-36.844V231.734z"/>
+ <g id="g_9_">
+ <path id="g" d="M97.207,283.895c-28.807,0-52.16-23.354-52.16-52.162c0-28.807,23.353-52.159,52.16-52.159
+ c28.807,0,52.16,23.353,52.16,52.159v52.162H97.207z M186.913,231.732c0-49.543-40.162-89.704-89.706-89.704
+ S7.501,182.189,7.501,231.732c0,49.544,40.162,89.708,89.706,89.708h52.16v21.821c0,14.406-5.836,27.447-15.275,36.887
+ l26.547,26.55c16.233-16.235,26.274-38.663,26.274-63.437V231.732z"/>
+ <rect id="square" x="149.367" y="283.895" fill="#FF4A2F" width="37.546" height="37.546"/>
+ </g>
+</g>
+</svg>
diff --git a/artwork/logo04-gem_only.svg b/artwork/logo04-gem_only.svg
new file mode 100644
index 0000000..ec8bfb3
--- /dev/null
+++ b/artwork/logo04-gem_only.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0px" y="0px" width="200px" height="200px" viewBox="0 0 200 200" xml:space="preserve">
+<g id="gem_1_">
+ <path fill="#6156FF" d="M5.3,124.798C3.147,116.851,2,108.502,2,99.883V3.593l97.692,96.291L5.3,124.798z"/>
+ <path fill="#867DFF" d="M99.692,99.883l-25.289,93.026c-33.634-8.854-60.119-34.958-69.103-68.111L99.692,99.883z"/>
+ <path fill="#6156FF" d="M99.692,99.883l69.077,68.082c-17.678,17.424-42.1,28.209-69.077,28.209
+ c-8.745,0-17.223-1.139-25.289-3.265L99.692,99.883z"/>
+ <path fill="#3C2EFF" d="M194.08,74.953c2.156,7.952,3.307,16.306,3.307,24.93c0,26.589-10.936,50.657-28.617,68.082L99.692,99.883
+ L194.08,74.953z"/>
+ <path fill="#2212FF" d="M99.692,99.883l25.292-93.033c33.627,8.859,60.109,34.955,69.096,68.103L99.692,99.883z"/>
+ <path fill="#3C2EFF" d="M2,3.593h97.692c8.745,0,17.226,1.134,25.292,3.258L99.692,99.883L2,3.593z"/>
+ <polygon fill="#AAA4FF" points="47.84,113.571 61.734,62.468 113.586,48.774 151.545,86.185 137.65,137.293 85.799,150.986 "/>
+</g>
+</svg>
diff --git a/artwork/logo04-outer_cog.svg b/artwork/logo04-outer_cog.svg
new file mode 100644
index 0000000..2390104
--- /dev/null
+++ b/artwork/logo04-outer_cog.svg
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0px" y="0px" width="1003px" height="411px" viewBox="0 0 1003 411" xml:space="preserve">
+<g id="Version_3">
+ <path id="rb1_5_" d="M708.579,319.846v-97.051c0-39.256,31.824-71.08,71.082-71.08v62.195c0,58.505,47.431,105.936,105.936,105.936
+ s105.936-47.431,105.936-105.936c0-58.506-47.431-105.934-105.936-105.934h-62.195V8.871l-43.742,43.742l0.002,55.364
+ c-63.415,0-114.823,51.407-114.823,114.82v97.049H708.579z"/>
+ <g id="gem_3_">
+ <path fill="#6156FF" d="M825.502,230.006c-1.37-5.136-2.101-10.528-2.101-16.096v-62.195l62.195,62.195L825.502,230.006z"/>
+ <path fill="#867DFF" d="M885.597,213.91l-16.101,60.091c-21.413-5.722-38.273-22.582-43.994-43.995L885.597,213.91z"/>
+ <path fill="#6156FF" d="M885.597,213.91l43.979,43.979c-11.256,11.255-26.804,18.219-43.979,18.219
+ c-5.567,0-10.966-0.734-16.101-2.106L885.597,213.91z"/>
+ <path fill="#3C2EFF" d="M945.688,197.808c1.372,5.136,2.104,10.533,2.104,16.103c0,17.177-6.962,32.723-18.217,43.979
+ l-43.979-43.979L945.688,197.808z"/>
+ <path fill="#2212FF" d="M885.597,213.91l16.101-60.091c21.409,5.722,38.269,22.579,43.99,43.988L885.597,213.91z"/>
+ <path fill="#3C2EFF" d="M823.401,151.715h62.195c5.567,0,10.966,0.732,16.101,2.104l-16.101,60.091L823.401,151.715z"/>
+ <polygon fill="#AAA4FF" points="852.586,222.754 861.431,189.743 894.442,180.899 918.607,205.063 909.763,238.074
+ 876.751,246.922 "/>
+ </g>
+ <g id="e4">
+ <path id="e_3_" d="M348.588,232.191l36.219-36.219l-25.632-25.632c-15.828-15.828-37.694-25.617-61.848-25.617
+ c-48.307,0-87.467,39.16-87.467,87.468c0,48.309,39.16,87.469,87.467,87.469c48.31,0,87.47-39.16,87.47-87.469H348.588
+ c0,28.312-22.95,51.261-51.261,51.261c-28.31,0-51.259-22.949-51.259-51.261c0-28.31,22.95-51.26,51.259-51.26
+ c14.152,0,26.965,5.734,36.241,15.006l-36.241,36.253H348.588z"/>
+ <path d="M372.281,297.306l15.481,28.236l-19.366,15.468l-24.144-21.356c-9.761,5.231-20.361,8.823-31.359,10.581l-6.295,31.599
+ l-24.776-0.601l-4.767-31.863c-5.345-1.124-10.667-2.675-15.922-4.732c-5.254-2.057-10.216-4.532-14.9-7.336l-25.135,20.16
+ l-18.595-16.386l16.83-27.473c-6.882-8.756-12.226-18.589-15.84-29.058l-32.227-0.711l-3.712-24.504l30.533-10.22
+ c0.336-10.83,2.461-21.779,6.557-32.455L180.421,175.4l12.905-21.159l29.953,11.797c7.477-8.367,16.251-15.322,25.888-20.662
+ l-4.892-31.836l23.488-7.912l15.375,28.315c10.819-1.575,21.989-1.367,33.092,0.799l16.725-27.543l23.077,9.036l-6.42,31.577
+ c9.623,5.947,17.962,13.379,24.836,21.878l30.516-10.35l11.869,21.759l-25.205,20.053c3.449,10.463,5.17,21.526,4.978,32.744
+ l30.003,11.675l-4.894,24.298l-32.214-0.84C385.258,279.646,379.385,289.128,372.281,297.306z M392.677,272.279l32.002,0.835
+ l5.914-29.387l-29.802-11.6c-0.01-10.434-1.602-20.717-4.645-30.522l25.042-19.924l-14.358-26.315l-30.269,10.264
+ c-6.559-7.822-14.336-14.736-23.188-20.425l6.369-31.324l-27.912-10.929l-16.591,27.322c-10.363-1.832-20.768-2.038-30.895-0.748
+ l-15.251-28.086l-28.408,9.571l4.857,31.63c-8.892,5.134-17.042,11.6-24.135,19.257l-29.755-11.718l-15.609,25.591l24.062,21.114
+ c-3.626,9.973-5.626,20.157-6.12,30.259l-30.325,10.148l4.491,29.638l31.996,0.708c3.54,9.696,8.5,18.854,14.766,27.121
+ l-16.701,27.26l22.49,19.816l24.941-20.003c4.417,2.54,9.074,4.791,13.964,6.705c4.888,1.914,9.835,3.423,14.806,4.559
+ l4.729,31.618l29.966,0.723l6.247-31.354c10.214-1.816,20.073-5.171,29.255-9.885l23.97,21.203l23.419-18.709l-15.374-28.042
+ C383.121,290.898,388.567,282.066,392.677,272.279z"/>
+ <path d="M326.795,232.191c0,16.273-13.193,29.467-29.468,29.467c-16.274,0-29.467-13.193-29.467-29.467
+ c0-16.274,13.193-29.468,29.467-29.468C313.602,202.724,326.795,215.917,326.795,232.191z M297.327,205.624
+ c-14.672,0-26.567,11.896-26.567,26.567s11.895,26.566,26.567,26.566c14.672,0,26.567-11.895,26.567-26.566
+ S312,205.624,297.327,205.624z"/>
+ <path d="M311.653,232.191c0,7.912-6.413,14.325-14.326,14.325c-7.91,0-14.325-6.413-14.325-14.325
+ c0-7.911,6.415-14.326,14.325-14.326C305.24,217.865,311.653,224.28,311.653,232.191z M297.327,219.478
+ c-7.022,0-12.713,5.691-12.713,12.714c0,7.021,5.691,12.713,12.713,12.713c7.021,0,12.713-5.692,12.713-12.713
+ C310.041,225.169,304.348,219.478,297.327,219.478z"/>
+ </g>
+ <path id="m_3_" d="M407.079,231.682c0-48.309,39.16-87.469,87.468-87.469c13.615,0,26.5,3.111,37.988,8.658
+ c11.489-5.547,24.375-8.658,37.989-8.658c48.308,0,87.47,39.16,87.47,87.469v88.164h-36.209v-88.164
+ c0-28.312-22.949-51.261-51.261-51.261c-15.064,0-28.61,6.501-37.989,16.847c-9.378-10.346-22.922-16.847-37.988-16.847
+ c-28.31,0-51.259,22.95-51.259,51.261v88.164h-36.208V231.682z"/>
+ <g id="g_6_">
+ <path id="g_7_" d="M99.627,282.941c-28.311,0-51.26-22.95-51.26-51.262c0-28.31,22.95-51.259,51.26-51.259
+ c28.312,0,51.261,22.95,51.261,51.259v51.262H99.627z M187.783,231.68c0-48.686-39.467-88.155-88.156-88.155
+ c-48.688,0-88.156,39.469-88.156,88.155c0,48.688,39.468,88.157,88.156,88.157h51.26l0.001,21.445
+ c0,14.158-5.739,26.974-15.015,36.25l26.09,26.092c15.953-15.955,25.819-37.995,25.819-62.342V231.68z"/>
+ <rect id="square_3_" x="150.887" y="282.941" fill="#FF4A2F" width="36.896" height="36.896"/>
+ </g>
+</g>
+</svg>
diff --git a/artwork/logo04-rb_only.svg b/artwork/logo04-rb_only.svg
new file mode 100644
index 0000000..91f664e
--- /dev/null
+++ b/artwork/logo04-rb_only.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0px" y="0px" width="200px" height="200px" viewBox="0 0 200 200" xml:space="preserve">
+<g>
+ <path id="rb1_3_" d="M27.873,193.766V135.24c0-23.674,19.191-42.865,42.866-42.865v37.508c0,35.279,28.603,63.883,63.883,63.883
+ c35.281,0,63.884-28.604,63.884-63.883c0-35.283-28.603-63.885-63.884-63.885H97.116V6.234L70.738,32.612l0.001,33.387
+ c-38.242,0-69.244,31-69.244,69.241v58.525H27.873z"/>
+ <g id="gem_1_">
+ <path fill="#6156FF" d="M98.383,139.588c-0.827-3.096-1.267-6.348-1.267-9.705V92.375l37.506,37.508L98.383,139.588z"/>
+ <path fill="#867DFF" d="M134.622,129.883l-9.709,36.236c-12.913-3.449-23.081-13.617-26.53-26.531L134.622,129.883z"/>
+ <path fill="#6156FF" d="M134.622,129.883l26.521,26.52c-6.787,6.787-16.163,10.988-26.521,10.988
+ c-3.357,0-6.612-0.443-9.709-1.271L134.622,129.883z"/>
+ <path fill="#3C2EFF" d="M170.859,120.172c0.828,3.098,1.27,6.352,1.27,9.711c0,10.357-4.198,19.732-10.986,26.52l-26.521-26.52
+ L170.859,120.172z"/>
+ <path fill="#2212FF" d="M134.622,129.883l9.71-36.239c12.91,3.451,23.077,13.616,26.527,26.528L134.622,129.883z"/>
+ <path fill="#3C2EFF" d="M97.116,92.375h37.506c3.357,0,6.613,0.442,9.71,1.269l-9.71,36.239L97.116,92.375z"/>
+ <polygon fill="#AAA4FF" points="114.715,135.215 120.049,115.309 139.956,109.975 154.529,124.547 149.195,144.455
+ 129.288,149.789 "/>
+ </g>
+</g>
+</svg>
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..52845e3
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,195 @@
+#!/bin/sh
+
+
+# Gentoo and Mandrake specific flags
+export WANT_AUTOMAKE="1.10"
+export WANT_AUTOCONF="2.5"
+
+# FreeBSD favourite paths
+#export CXXFLAGS="-ggdb -I/usr/local/include"
+#export LDFLAGS="-ggdb -L/usr/local/lib"
+
+echo "
+
+The autotools build system is NO LONGER MAINTAINED and may be dropped in
+future versions! Please switch to using cmake instead.
+
+"
+
+if [ "$1" = "" ]; then
+ dest=$HOME/GemRB
+else
+ if [ "${1:0:1}" == "/" ]; then
+ dest=$1
+ else
+ dest=$PWD/$1
+ fi
+ shift
+
+ # treat any other parameters as additional configure flags
+ if [ "$1" != "" ]; then
+ extra_args="$@"
+ fi
+fi
+
+if [ -n "$ACLOCAL" ];
+then
+ my_aclocal=$ACLOCAL
+else
+ for file in aclocal aclocal-1.7 aclocal-1.8 aclocal-1.9 aclocal-1.10 aclocal-1.11; do
+ version=`$file --version | sed -n '1 { s/^[^ ]* (.*) //; s/ .*$//; s,\.,,g; p; }'`
+ if [ "$version" -gt 17 ];
+ then
+ my_aclocal=$file
+ break
+ fi
+ done
+fi
+
+if [ -z "$my_aclocal" ];
+then
+ echo "***************************************************************"
+ echo
+ echo "Aclocal version >= 1.7 is required. If it is already installed,"
+ echo "set enviroment variable ACLOCAL to point to it."
+ echo "(for example in bash do: \"export ACLOCAL=/pathto/aclocal-1.7\")"
+ echo
+ echo "***************************************************************"
+ exit 1
+fi
+
+if [ -n "$AUTOHEADER" ];
+then
+ my_autoheader=$AUTOHEADER
+else
+ for file in autoheader autoheader-2.57 autoheader-2.58 autoheader-2.59 autoheader-2.60 autoheader-2.61 autoheader-2.62; do
+ version=`$file --version | sed -n '1 { s/^[^ ]* (.*) //; s/ .*$//; s,\.,,g; p; }'`
+ if [ "$version" -gt 257 ];
+ then
+ my_autoheader=$file
+ break
+ fi
+ done
+fi
+
+if [ -z "$my_autoheader" ];
+then
+ echo "*********************************************************************"
+ echo
+ echo "Autoheader version >= 2.57 is required. If it is already installed,"
+ echo "set enviroment variable AUTOHEADER to point to it."
+ echo "(for example in bash do: \"export AUTOHEADER=/pathto/autoheader-2.58\")"
+ echo
+ echo "*********************************************************************"
+ exit 1
+fi
+
+if [ -n "$LIBTOOLIZE" ];
+then
+ my_libtoolize=$LIBTOOLIZE
+else
+ for file in libtoolize; do
+ libtool_version=`$file --version | sed -n '1 { s/^[^ ]* (.*) //; s/ .*$//; s,,,g; p; }'`
+ libtool_version_major=`echo "$libtool_version" | cut -d. -f1`
+ libtool_version_minor=`echo "$libtool_version" | cut -d. -f2`
+ echo libtool version: "$libtool_version_major"."$libtool_version_minor"
+ if [ "$libtool_version_major" -gt 1 ] || [ "$libtool_version_minor" -gt 4 ];
+ then
+ my_libtoolize=$file
+ break
+ fi
+ done
+fi
+
+if [ -z "$my_libtoolize" ];
+then
+ echo "***************************************************************"
+ echo
+ echo "Libtool version >= 1.5 is required. If it is already installed,"
+ echo "set enviroment variable LIBTOOLIZE to point to it."
+ echo "(for example in bash do: \"export LIBTOOLIZE=/pathto/libtoolize\")"
+ echo
+ echo "***************************************************************"
+ exit 1
+fi
+
+if [ -n "$AUTOMAKE" ];
+then
+ my_automake=$AUTOMAKE
+else
+ for file in automake automake-1.7 automake-1.8 automake-1.9 automake-1.10 automake-1.11; do
+ version=`$file --version | sed -n '1 { s/^[^ ]* (.*) //; s/ .*$//; s,\.,,g; p; }'`
+ if [ "$version" -gt 17 ];
+ then
+ my_automake=$file
+ break
+ fi
+ done
+fi
+
+if [ -z "$my_automake" ];
+then
+ echo "***************************************************************"
+ echo
+ echo "Automake version >= 1.7 is required. If it is already installed,"
+ echo "set enviroment variable AUTOMAKE to point to it."
+ echo "(for example in bash do: \"export AUTOMAKE=/pathto/automake-1.7\")"
+ echo
+ echo "***************************************************************"
+ exit 1
+fi
+
+if [ -n "$AUTOCONF" ];
+then
+ my_autoconf=$AUTOCONF
+else
+ for file in autoconf autoconf-2.57 autoconf-2.58 autoconf-2.59 autoconf-2.60 autoconf-2.61 autoconf-2.62; do
+ version=`$file --version | sed -n '1 { s/^[^ ]* (.*) //; s/ .*$//; s,\.,,g; p; }'`
+ if [ "$version" -gt 257 ];
+ then
+ my_autoconf=$file
+ break
+ fi
+ done
+fi
+
+if [ -z "$my_autoconf" ];
+then
+ echo "*****************************************************************"
+ echo
+ echo "Autoconf version >= 2.58 is required. If it is already installed,"
+ echo "set enviroment variable AUTOCONF to point to it."
+ echo "(for example in bash do: \"export AUTOCONF=/pathto/autoconf-2.58\")"
+ echo
+ echo "*********************************************************************"
+ exit 1
+fi
+
+echo Running libtoolize
+if [ "$libtool_version_major" = "2" ]; then
+ $my_libtoolize --force --no-warn
+else
+ $my_libtoolize --force
+fi || exit 1
+
+echo Running aclocal
+$my_aclocal -W no-syntax || $my_aclocal || exit 1
+
+echo Running autoconf
+$my_autoconf || exit 1
+
+echo Running autoheader
+$my_autoheader || exit 1
+
+echo Running automake
+$my_automake --add-missing || exit 1
+
+if test -z "$NOCONFIGURE"; then
+ echo Running configure
+
+ cmd="./configure --prefix=$dest/ --bindir=$dest/ --sysconfdir=$dest/ --datadir=$dest/ --libdir=$dest --disable-subdirs"
+ $cmd "$extra_args"
+
+ echo
+ echo "Configure was invoked as: $cmd $extra_args"
+fi
diff --git a/cmake_config.h.in b/cmake_config.h.in
new file mode 100644
index 0000000..7f69962
--- /dev/null
+++ b/cmake_config.h.in
@@ -0,0 +1,16 @@
+#define PACKAGE "gemrb"
+
+#cmakedefine SIZEOF_INT ${SIZEOF_INT}
+#cmakedefine SIZEOF_LONG_INT ${SIZEOF_LONG_INT}
+#ifndef HAVE_SNPRINTF
+#cmakedefine HAVE_SNPRINTF 1
+#endif
+#ifndef HAVE_STRNDUP
+#cmakedefine HAVE_STRNDUP 1
+#endif
+#cmakedefine HAVE_FORBIDDEN_OBJECT_TO_FUNCTION_CAST 1
+#define PLUGINDIR "${PLUGIN_DIR}"
+#define DATADIR "${DATA_DIR}"
+#define SYSCONFDIR "${SYSCONF_DIR}"
+#cmakedefine NOCOLOR ${NOCOLOR}
+#cmakedefine TOUCHSCREEN ${TOUCHSCREEN}
diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in
new file mode 100644
index 0000000..df95fb9
--- /dev/null
+++ b/cmake_uninstall.cmake.in
@@ -0,0 +1,21 @@
+IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+ MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
+ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+
+FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
+STRING(REGEX REPLACE "\n" ";" files "${files}")
+FOREACH(file ${files})
+ MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
+ IF(EXISTS "$ENV{DESTDIR}${file}")
+ EXEC_PROGRAM(
+ "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+ OUTPUT_VARIABLE rm_out
+ RETURN_VALUE rm_retval
+ )
+ IF(NOT "${rm_retval}" STREQUAL 0)
+ MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
+ ENDIF(NOT "${rm_retval}" STREQUAL 0)
+ ELSE(EXISTS "$ENV{DESTDIR}${file}")
+ MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
+ ENDIF(EXISTS "$ENV{DESTDIR}${file}")
+ENDFOREACH(file)
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..2b13417
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,295 @@
+# -*-autoconf-*-
+# 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.
+#
+
+###################################################
+
+# I used fluxbox's configure.in as a template of sorts, and
+# Redhat's autotools book. Thanks to everyone involved with
+# both of these projects!
+# The build system was mostly done by edheldil & subvertir
+
+dnl Initialize autoconf and automake
+
+AC_PREREQ([2.57])
+AC_INIT(gemrb, 0.6.4)
+
+AC_MSG_WARN([
+
+
+The autotools build system is NO LONGER MAINTAINED and may be dropped in
+future versions! Please switch to using cmake instead.
+
+])
+
+AC_CONFIG_AUX_DIR([admin])
+AC_CONFIG_SRCDIR([gemrb/])
+AC_CANONICAL_SYSTEM
+AM_INIT_AUTOMAKE(AC_PACKAGE_NAME,AC_PACKAGE_VERSION)
+AM_CONFIG_HEADER(config.h)
+AC_USE_SYSTEM_EXTENSIONS
+
+for top_builddir in . .. ../.. $ac_auxdir $ac_auxdir/..; do
+ test -f $top_builddir/configure && break
+done
+
+dnl Check for required software.
+AC_DISABLE_STATIC
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+AC_LANG([C++])
+
+if test "x$docdir" = "x" ; then
+ docdir=$datadir/doc
+fi
+
+AC_ARG_ENABLE(subdirs, AS_HELP_STRING(--disable-subdirs,Install plugins and data directly to LIBDIR and DATADIR), gemrb_use_subdirs=$enableval, gemrb_use_subdirs=yes)
+if test "x$gemrb_use_subdirs" = "xno" ; then
+ plugindir=$libdir/plugins
+ moddir=$datadir
+else
+ plugindir=$libdir/gemrb/plugins
+ moddir=$datadir/gemrb
+fi
+
+AC_ARG_ENABLE(debug, AS_HELP_STRING(--enable-debug,Build in debug mode), gemrb_use_debug=$enableval, gemrb_use_debug=no)
+if test "x$gemrb_use_debug" = "xyes" ; then
+ CFLAGS="$CFLAGS -O0 -g"
+ CXXFLAGS="$CXXFLAGS -O0 -g"
+fi
+
+AC_SUBST(plugindir)
+AC_SUBST(moddir)
+AC_SUBST(docdir)
+
+
+dnl Check for recent STL with <vector>::at()
+AC_CHECK_STL_CONTAINER_AT([], [AC_MSG_ERROR([
+
+*** Your STL library does not define method container::at().
+*** It probably means that its version is too old.
+*** Please upgrade your standard C++ library to version 3.x.
+])])
+
+dnl Determine prefix.
+if test "x$prefix" = "xNONE"; then
+ prefix=$ac_default_prefix
+ ac_configure_args="$ac_configure_args --prefix $prefix"
+fi
+
+dnl Check various functions.
+AC_CHECK_SIZEOF([char])
+AC_CHECK_SIZEOF([short int])
+AC_CHECK_SIZEOF([int])
+AC_CHECK_SIZEOF([long int])
+AC_CHECK_SIZEOF([long long int])
+AC_CHECK_FUNCS([getcwd])
+AC_CHECK_FUNCS([gettimeofday])
+AC_CHECK_FUNCS([memchr])
+AC_CHECK_FUNCS([memmove])
+AC_CHECK_FUNCS([memset])
+AC_CHECK_FUNCS([mkdir])
+AC_CHECK_FUNCS([rmdir])
+AC_CHECK_FUNCS([sqrt])
+AC_CHECK_FUNCS([strcasecmp])
+AC_CHECK_FUNCS([strchr])
+AC_CHECK_FUNCS([strdup])
+AC_CHECK_FUNCS([strrchr])
+AC_CHECK_FUNCS([strstr])
+AC_CHECK_FUNCS([strtol])
+AC_CHECK_FUNCS([strtoul])
+AC_CHECK_FUNCS([strndup])
+AC_CHECK_FUNCS([strnlen])
+AC_CHECK_FUNCS([snprintf])
+AC_CHECK_HEADERS([fcntl.h])
+AC_CHECK_HEADERS([stddef.h])
+AC_CHECK_HEADERS([sys/time.h])
+AC_CHECK_HEADERS([sys/stat.h])
+AC_CHECK_LIB(dl, dlopen, [LIBDL="-ldl"], [LIBDL=""])
+AC_SUBST(LIBDL)
+
+AC_CHECK_PTHREADS
+
+dnl Check for zlib.
+CHECK_ZLIB()
+
+dnl Check for libSDL Version 1.2 or greater, fail if not found.
+AM_PATH_SDL(1.2.0, [], [AC_MSG_ERROR([
+
+*** You need LibSDL (www.libsdl.org) version 1.2.0 or greater to compile GemRB
+])])
+
+dnl Check for OpenAL, fail if not found.
+AM_PATH_OPENAL([], [AC_MSG_ERROR([
+
+*** You need OpenAL (www.openal.org) to compile GemRB
+])])
+
+#AC_CHECK_LIB([openal], [alGetError], [ $(which true) ], [AC_MSG_ERROR([
+#*** You need OpenAL (www.openal.org) to compile GemRB
+#])])
+##AC_CHECK_LIB([alut], [alutInit], [ $(which true) ], [AC_MSG_ERROR([
+##*** You need OpenAL (www.openal.org) to compile GemRB
+##])])
+#OPENAL_LIBS="`openal-config --libs`"
+
+##GEMRB_CHECK_ALUT()
+
+dnl Check for Python version >= 2.3.0, fail if not found.
+AM_PATH_PYTHON(2.3.0, [], [AC_MSG_ERROR([
+
+*** You need Python (www.python.org) version 2.3.0 or greater to compile GemRB
+])])
+
+dnl Check for Python headers and libraries, fail if not found.
+AM_CHECK_PYTHON_HEADERS([], [AC_MSG_ERROR([
+
+*** You need Python >=2.3.0 headers installed to compile GemRB
+*** See if your distribution offers -dev packages!
+])])
+
+dnl Check for Python libraries, fail if not found.
+AM_CHECK_PYTHON_LIBS([], [AC_MSG_ERROR([
+
+*** You need Python >=2.3.0 shared libraries installed to compile GemRB
+*** Maybe you need to recompile python with --shared-libs
+])])
+
+
+AC_CHECK_HEADER([vorbis/vorbisfile.h],
+ [AC_CHECK_LIB(vorbisfile, ov_info,
+ [AC_DEFINE( HAS_VORBIS_SUPPORT, 1, [Handling of .ogg soundfiles])
+ vorbis_ok=yes],[vorbis_ok=no], -lvorbis )],
+ [vorbis_ok=no])
+AM_CONDITIONAL([VORBIS], [test x$vorbis_ok = xyes])
+
+dnl FIXME: check for libpng properly, using libpng(12)-config
+AC_CHECK_HEADER(png.h,,AC_MSG_ERROR([*** libpng not found!]))
+AC_CHECK_LIB(png,png_create_read_struct,,AC_MSG_ERROR([*** libpng not found!]))
+AC_SUBST(LIBPNG,-lpng)
+
+AC_C_CONST
+AC_C_INLINE
+AC_C_RESTRICT
+AC_C_VOLATILE
+AC_FUNC_CLOSEDIR_VOID
+AC_FUNC_MALLOC
+AC_FUNC_MEMCMP
+AC_FUNC_REALLOC
+AC_FUNC_STAT
+AC_FUNC_STRFTIME
+AC_FUNC_STRNLEN
+AC_HEADER_DIRENT
+AC_HEADER_STDBOOL
+AC_HEADER_STAT
+AC_HEADER_TIME
+AC_REPLACE_FNMATCH
+AC_STRUCT_TM
+AC_TYPE_SIZE_T
+
+CPPFLAGS="$CPPFLAGS -DSYSCONFDIR=\\\"\$(sysconfdir)\\\" -DDATADIR=\\\"\$(moddir)\\\" -DPLUGINDIR=\\\"\$(plugindir)\\\""
+CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/gemrb/includes -I\$(top_srcdir)/gemrb/core"
+CXXFLAGS="$CXXFLAGS -Wall -W -Wpointer-arith -Wcast-align -pedantic -Wno-format-y2k -Wno-long-long -fno-strict-aliasing"
+
+AC_ARG_ENABLE(werror, AS_HELP_STRING(--disable-werror,Make compiler warning non-fatal.), enable_werror=$enableval, enable_werror=yes)
+if test "x$enable_werror" = "xyes" ; then
+ CXXFLAGS="$CXXFLAGS -Werror"
+fi
+
+# only export symbols explicitly marked to be exported
+CXXFLAGS="$CXXFLAGS -fvisibility=hidden"
+
+dnl Check for "ISO C++ forbids casting between pointer-to-function and pointer-to-object" as in GCC4
+AC_CHECK_OBJECT_TO_FUNCTION_CAST
+
+
+AC_SUBST(LIBS)
+AC_SUBST(OPENAL_LIBS)
+
+AC_CONFIG_FILES([
+Makefile
+gemrb/Makefile
+gemrb/core/Makefile
+gemrb/includes/Makefile
+gemrb/override/Makefile
+gemrb/override/bg1/Makefile
+gemrb/override/bg2/Makefile
+gemrb/override/how/Makefile
+gemrb/override/iwd/Makefile
+gemrb/override/iwd2/Makefile
+gemrb/override/pst/Makefile
+gemrb/override/shared/Makefile
+gemrb/GUIScripts/Makefile
+gemrb/GUIScripts/bg1/Makefile
+gemrb/GUIScripts/bg2/Makefile
+gemrb/GUIScripts/iwd/Makefile
+gemrb/GUIScripts/iwd2/Makefile
+gemrb/GUIScripts/pst/Makefile
+gemrb/plugins/Makefile
+gemrb/plugins/2DAImporter/Makefile
+gemrb/plugins/ACMReader/Makefile
+gemrb/plugins/OGGReader/Makefile
+gemrb/plugins/WAVReader/Makefile
+gemrb/plugins/AREImporter/Makefile
+gemrb/plugins/BAMImporter/Makefile
+gemrb/plugins/BIFImporter/Makefile
+gemrb/plugins/BIKPlayer/Makefile
+gemrb/plugins/BMPImporter/Makefile
+gemrb/plugins/BMPWriter/Makefile
+gemrb/plugins/PNGImporter/Makefile
+gemrb/plugins/CHUImporter/Makefile
+gemrb/plugins/CREImporter/Makefile
+gemrb/plugins/DLGImporter/Makefile
+gemrb/plugins/EFFImporter/Makefile
+gemrb/plugins/FXOpcodes/Makefile
+gemrb/plugins/IWDOpcodes/Makefile
+gemrb/plugins/PSTOpcodes/Makefile
+gemrb/plugins/GAMImporter/Makefile
+gemrb/plugins/GUIScript/Makefile
+gemrb/plugins/IDSImporter/Makefile
+gemrb/plugins/INIImporter/Makefile
+gemrb/plugins/ITMImporter/Makefile
+gemrb/plugins/KEYImporter/Makefile
+gemrb/plugins/DirectoryImporter/Makefile
+gemrb/plugins/MOSImporter/Makefile
+gemrb/plugins/MUSImporter/Makefile
+gemrb/plugins/MVEPlayer/Makefile
+gemrb/plugins/NullSound/Makefile
+gemrb/plugins/OpenALAudio/Makefile
+gemrb/plugins/PLTImporter/Makefile
+gemrb/plugins/PROImporter/Makefile
+gemrb/plugins/SDLVideo/Makefile
+gemrb/plugins/SPLImporter/Makefile
+gemrb/plugins/STOImporter/Makefile
+gemrb/plugins/TISImporter/Makefile
+gemrb/plugins/TLKImporter/Makefile
+gemrb/plugins/WEDImporter/Makefile
+gemrb/plugins/WMPImporter/Makefile
+gemrb/plugins/ZLibManager/Makefile
+gemrb/docs/Makefile
+gemrb/docs/en/Makefile
+gemrb/docs/en/GUIScript/Makefile
+gemrb/docs/en/Tables/Makefile
+gemrb/docs/en/Engine/Makefile
+])
+AC_CONFIG_FILES([gemrb.spec])
+AC_OUTPUT
diff --git a/gemrb.6.in b/gemrb.6.in
new file mode 100644
index 0000000..c337b85
--- /dev/null
+++ b/gemrb.6.in
@@ -0,0 +1,393 @@
+.\"Title and section
+.TH GEMRB 6
+
+.\"###################################################
+.SH NAME
+GemRB
+\- emulator for Infinity Engine-based games
+
+.\"###################################################
+.SH SYNOPSIS
+.B gemrb
+[\-c
+.IR CONFIG-FILE ]
+.br
+.B torment
+.br
+
+.\"###################################################
+.SH DESCRIPTION
+.B GemRB
+is an emulator for Infinity Engine-based games, fine RPGs like Baldur's Gate,
+Icewind Dale and Planescape: Torment.
+
+.B GemRB
+reimplements only the game engine. To actually play anything, you have to have
+the data from the original game(s), installed or unpackaged where GemRB can find it.
+See the
+.I GamePath
+and
+.I CDn
+settings in the engine configuration file below. A full install is recommended.
+
+.\"###################################################
+.SH OPTIONS
+.TP
+.BI \-c " FILE"
+Use the specified configuration file
+.IR FILE " instead"
+of the default
+.IR gemrb.cfg .
+
+.B Note:
+You can also use the program's name as a mean to select the configuration file.
+For example, if the program's name is
+.I torment
+instead of
+.IR gemrb ,
+the engine first seaches for
+.I torment.cfg
+and only if it's not found it searches for
+.IR gemrb.cfg .
+
+To use this feature, just create a symbolic link
+.I torment
+pointing to the
+.I gemrb
+binary and then run
+.IR torment
+instead.
+
+.\"###################################################
+.SH Configuration File
+.PD 0
+A configuration file consists of pairs
+.IR PARAMETER=VALUE ,
+each on its own line. The case of PARAMETER is not significant,
+GameType is the same as GAMETYPE. If the same PARAMETER is defined
+more than once, only the last occurence is used.
+
+Empty lines and lines starting with `#' (hash sign) are ignored.
+
+.B Parameters:
+
+.TP
+.BR GameType =(bg1|bg2|iwd|iwd2|how|pst)
+Type of the game. The value is a name of subdirectory in override/
+and GUIScripts/ directories
+with game type-specific files and scripts. It is at present also used
+for various tweaks hardcoded in the GemRB engine.
+
+.TP
+.BR GameName =STRING
+Arbitrary name of the game. It will be displayed in the window's title bar.
+
+.TP
+.BR GamePath =PATH
+Path to the directory where the original game is installed. If you can't
+install the game under MS Windows or with WINE, you can try to unpack the data files
+with the
+.I unshield
+program found at
+.IR http://synce.sourceforge.net .
+
+.TP
+.BR CD1 =PATH
+.TP
+.BR CD2 =PATH
+.TP
+.BR CDn =...
+Path to the data files for the game's particular CDs.
+
+.\".TP
+.\".BR INIConfig =FILE
+.\"Name of the game's INI file, relative to
+.\".IR GamePath .
+.\"E.g. for Baldur's Gate game it is baldur.ini.
+
+
+.TP
+.BR Width =INT
+Game window width (in pixels).
+
+.B WARNING:
+This is not arbitrary but depends on the game data. For custom resolutions
+the widescreen mod needs to be used.
+
+.TP
+.BR Height =INT
+Game window height (in pixels).
+
+.B WARNING:
+This is not arbitrary but depends on the game data. For custom resolutions
+the widescreen mod needs to be used.
+
+.TP
+.BR Bpp =INT
+Color depth of the game window (in bits per pixel).
+
+.TP
+.BR Fullscreen =(0|1)
+Whether the game should run in fullscreen mode.
+
+.\".TP
+.\".BR ForceStereo =(0|1)
+.\"Some older games such as BG1 and PST need this parameter to be
+.\".IR 1 .
+.\"For other game types set it to
+.\".IR 0 .
+
+.TP
+.BR TooltipDelay =INT
+Delay (in miliseconds) before tooltips are displayed when the mouse is not moving.
+A reasonable number for this option is e.g.
+.IR 500 .
+The default is
+.IR 100 .
+
+.TP
+.BR VolumeAmbients =INT
+Volume of ambient sounds in percents. The default is
+.IR 100 .
+
+.TP
+.BR VolumeMovie =INT
+Volume during movie playback in percents. The default is
+.IR 100 .
+
+.TP
+.BR VolumeMusic =INT
+Volume of background music in percents. The default is
+.IR 100 .
+
+.TP
+.BR VolumeSFX =INT
+Volume of sound effects in percents. The default is
+.IR 100 .
+
+.TP
+.BR VolumeVoices =INT
+Volume of PC or NPC voices in percents. The default is
+.IR 100 .
+
+
+.TP
+.BR SkipIntroVideos =(0|1)
+This parameter is meant for developers. If set to
+.IR 1 ,
+the intro and logo videos are skipped to save developer's nerves. The default is
+.IR 0 .
+
+.TP
+.BR DrawFPS =(0|1)
+This parameter is meant for developers. If set to
+.IR 1 ,
+the current FPS (Frames per Second) value is drawn in the top left window corner. The default is
+.IR 0 .
+
+.TP
+.BR FogOfWar =(0|1)
+If set to
+.IR 1 ,
+the unexplored parts of an area are blacked out. It is enabled by default.
+
+.TP
+.BR EnableCheatKeys =(0|1)
+This parameter is meant for developers. If set to
+.IR 1 ,
+certain keys allow you to inspect the internal state of objects, do forbidden things, etc.
+The keys are listed in the
+.I CheatKeys.txt
+file. Do NOT use this option unless you want to make your hands dirty :-). The default is
+.IR 0 .
+
+.TP
+.BR ScriptDebugMode =(n)
+This parameter is meant for developers. It is a combination of bit values
+
+.IR 1
+- count references,
+
+.IR 2
+- display cutscene warnings,
+
+.IR 4
+- display variable warnings,
+
+.IR 8
+- display action warnings,
+
+.IR 16
+- display trigger warnings.
+
+The default is
+.IR 0 .
+
+.TP
+.BR CaseSensitive =(0|1)
+When
+this parameter is set to
+.IR 1 ,
+the engine will try to find files regardless of their names' case at the cost
+of a slight speed penalty. This is especially important when using
+game files on CD-ROMs, where the files can't be renamed. Like
+many Windows programs, the original games use inconsistent mix
+of lower/upper case letters for naming and referencing their files.
+
+Set this parameter to
+.I 1
+on Unix-like systems.
+
+.TP
+.BR DelayPlugin =FILENAME
+Named plugin will be loaded after other (nondelayed) plugins were loaded.
+.IR FILENAME
+is a name without path, but with extension, for example
+.IR libNullSound.so
+or
+.IR libNullSound.dll .
+You can use this parameter more than once.
+
+.TP
+.BR SkipPlugin =FILENAME
+Named plugin will not be loaded.
+.IR FILENAME
+is a name without path, but with extension, for example
+.IR libNullSound.so
+or
+.IR libNullSound.dll .
+You can use this parameter more than once.
+
+.TP
+.BR AudioDriver =(openal|sdlaudio|none)
+Use the specified plugin as the audio driver. The default is openal, while
+.I none
+will disable all audio.
+
+.TP
+.BR SaveAsOriginal =(0|1)
+Set this parameter to
+.IR 1 ,
+if you want to keep the save game compatible with the original engine. It is enabled by default.
+
+.TP
+.BR CachePath =PATH
+User-writable directory where the engine caches uncompressed and temporary
+files.
+
+.TP
+.BR GemRBPath =PATH
+Path to the GemRB installation. (To be obsoleted; preset at build time).
+
+.TP
+.BR GUIScriptsPath =PATH
+Path to the directory with scripts controlling the game's GUI.
+
+.TP
+.BR SavePath =PATH
+Path to the directory with save games. Note that this directory has to
+contain
+.I save/
+and/or eventually
+.I mpsave/
+subdirectories and that only these subdirectories contain the actual
+saved games.
+
+.I SavePath
+defaults to
+.IR GamePath .
+
+.TP
+.BR GameOnCD =(0|1)
+defaults to
+.IR 0 .
+
+
+.TP
+.BR GameDataPath =PATH
+Path to the original game's installed data files, relative to GamePath.
+Usually it's
+.IR data .
+
+
+.TP
+.BR GameOverridePath =PATH
+Path to the original game's override dir, relative to GamePath.
+Usually it's
+.IR override .
+Original games use this directory to place last-minute patches.
+
+.TP
+.BR GemRBOverridePath =PATH
+Path where GemRB looks for its game override directory.
+It defaults to
+.IR GemRBPath .
+The original games hardcoded this information in the engine.
+
+
+.\"###################################################
+.SH FILES
+.PD 0
+.TP 20
+.B @SYSCONF_DIR@/gemrb.cfg
+system-wide configuration file
+
+.TP
+.B @DATA_DIR@/override/
+directory with
+.BR GemRB -distributed
+data files. These used to be hardcoded in the original engine. The files
+are sorted into directories for specific games.
+
+.TP
+.B @DATA_DIR@/GUIScripts/
+directory with Python scripts providing GUI setup and interaction. The files
+are sorted into directories for specific games.
+
+.TP
+.B ~/.gemrb/gemrb.cfg
+.TP
+.B ~/.gemrb/override/
+.TP
+.B ~/.gemrb/GUIScripts/
+user's own configuration and data files.
+.TP
+.B ~/.gemrb/cache/
+cache directory
+.PD
+
+.\"###################################################
+.SH BUGS
+Many.
+.B GemRB
+is still in an early development stage and not nearly complete.
+
+If you stumble over something which is supposed to work, either make a bug report
+at http://sourceforge.net/projects/gemrb or come to IRC channel
+.I #GemRB
+at FreeNode.
+
+.\"###################################################
+.SH AUTHOR
+The GemRB Project development team at http://gemrb.sourceforge.net
+
+.\"###################################################
+.SH COPYING
+Copyright (C) 2003-2008 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.
+
+.\"###################################################
+.\"End of file gemrb.man
diff --git a/gemrb.desktop b/gemrb.desktop
new file mode 100755
index 0000000..9849f49
--- /dev/null
+++ b/gemrb.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Version=1.0
+Name=GemRB
+GenericName=Game engine made with preRendered Background
+Comment=a portable opensource implementation of Bioware's Infinity Engine
+Type=Application
+Icon=gemrb
+Terminal=true
+Exec=gemrb
+Categories=Game;RolePlaying;Emulator;
diff --git a/gemrb.lsm b/gemrb.lsm
new file mode 100644
index 0000000..a186276
--- /dev/null
+++ b/gemrb.lsm
@@ -0,0 +1,14 @@
+Begin4
+Title: GemRB
+Version: 0.4.0
+Entered-date: 2009-05-25
+Description: OpenSource reimplementation of the InfinityEngine
+Keywords: Infinity Engine roleplaying game rpg
+Author: <avenger_teambg at users.sourceforge.net> GemRB Developement Team
+Maintained-by: <avenger_teambg at users.sourceforge.net> GemRB Developement Team
+Primary-site: http://gemrb.sourceforge.net
+Alternate-site:
+Original-site:
+Platforms: Linux and other Unices, Windows
+Copying-policy: GNU General Public License version 2 or later
+End
diff --git a/gemrb.spec.in b/gemrb.spec.in
new file mode 100644
index 0000000..17a59c9
--- /dev/null
+++ b/gemrb.spec.in
@@ -0,0 +1,72 @@
+Name: gemrb
+Version: @VERSION@
+Release: 1
+Summary: Port of the original Infinity (Game) Engine
+Group: Applications/Games
+License: GPL
+Source: %{name}-%{version}.tar.gz
+URL: http://gemrb.sourceforge.net/
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+Requires: zlib SDL >= 1.2 openal python >= 2.3 libvorbis libpng
+BuildRequires: zlib-devel SDL-devel openal-devel libvorbis-devel libpng-devel
+
+%description
+GemRB (Game engine made with preRendered Background) is a portable open-source
+implementation of Bioware's Infinity Engine which was written to support
+pseudo-3D role playing games based on the Dungeons & Dragons ruleset
+
+%prep
+%setup -q
+
+%build
+%configure --prefix=/usr --libdir=%{_libdir} --mandir=%{_mandir} --sysconfdir=%{_sysconfdir}
+make %{?_smp_mflags}
+
+%install
+rm -rf ${RPM_BUILD_ROOT}
+make DESTDIR=${RPM_BUILD_ROOT} install
+
+%clean
+rm -rf ${RPM_BUILD_ROOT}
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%defattr(644,root,root,755)
+%doc AUTHORS COPYING NEWS README
+%attr(755,root,root) %{_bindir}/gemrb
+%attr(755,root,root) %{_libdir}/*.so
+%attr(755,root,root) %{_libdir}/*.so.*
+%attr(755,root,root) %{_libdir}/gemrb/plugins/*.so
+%attr(755,root,root) %{_libdir}/gemrb/plugins/*.so.*
+%{_sysconfdir}/GemRB.*
+%{_mandir}/man6/*
+%{_datadir}/gemrb/*
+%{_datadir}/doc/gemrb/*
+
+### Exclude files
+%exclude %{_libdir}/*.la
+%attr(755,root,root) %{_libdir}/gemrb/plugins/*.la
+
+%changelog
+* Sat Jun 6 2009 Joachim Metz <jbmetz at users.sourceforge.net> 0.4.0-1
+- Added support for configure integration
+
+* Tue May 25 2009 Jaka Kranjc <lynxlupodian at users.sourceforge.net> 0.4.0-1
+- Update for gemrb 0.4.0
+
+* Sun Mar 15 2009 Joachim Metz <jbmetz at users.sourceforge.net> 0.3.2-2
+- Excluded .la files from rpm file
+
+* Tue Feb 17 2009 Joachim Metz <jbmetz at users.sourceforge.net> 0.3.2-1
+- Update for gemrb 0.3.2
+
+* Fri Feb 13 2009 Joachim Metz <jbmetz at users.sourceforge.net> 0.3.1-2
+- Merged default and devel package, gemrb requires .so files
+- Corrected use of legacy version of libpng
+
+* Sun Feb 8 2009 Joachim Metz <jbmetz at users.sourceforge.net> 0.3.1-1
+- Initial version
+
diff --git a/gemrb/CMakeLists.txt b/gemrb/CMakeLists.txt
new file mode 100644
index 0000000..0abc5d9
--- /dev/null
+++ b/gemrb/CMakeLists.txt
@@ -0,0 +1,41 @@
+ADD_SUBDIRECTORY( core )
+ADD_SUBDIRECTORY( plugins )
+ADD_SUBDIRECTORY( override )
+ADD_SUBDIRECTORY( GUIScripts )
+ADD_SUBDIRECTORY( docs )
+ADD_SUBDIRECTORY( tests )
+
+ADD_EXECUTABLE(gemrb GemRB.cpp )
+SET_TARGET_PROPERTIES(gemrb PROPERTIES INSTALL_RPATH ${LIB_DIR})
+INCLUDE_DIRECTORIES( ${SDL_INCLUDE_DIR} )
+IF(WIN32)
+ TARGET_LINK_LIBRARIES(gemrb gemrb_core)
+ELSE(WIN32)
+ IF(APPLE)
+ if (STATIC_LINK)
+ TARGET_LINK_LIBRARIES(gemrb ${SDL_LIBRARY}
+ ${SDL_MAIN_LIBRARY_PATH} ${COCOA_LIBRARY_PATH} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}
+ gemrb_core ${plugins} -Wl,-all_load)
+ else (STATIC_LINK)
+ TARGET_LINK_LIBRARIES(gemrb gemrb_core ${SDL_LIBRARY}
+ ${SDL_MAIN_LIBRARY_PATH} ${COCOA_LIBRARY_PATH} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
+ endif (STATIC_LINK)
+ ELSE(APPLE)
+ if (STATIC_LINK)
+ TARGET_LINK_LIBRARIES(gemrb ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}
+ -Wl,--whole-archive gemrb_core ${plugins} -Wl,--no-whole-archive)
+ else (STATIC_LINK)
+ TARGET_LINK_LIBRARIES(gemrb gemrb_core ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
+ endif (STATIC_LINK)
+ ENDIF(APPLE)
+ENDIF(WIN32)
+
+# preconfigure the sample config with the selected paths
+CONFIGURE_FILE(
+ "${CMAKE_CURRENT_SOURCE_DIR}/GemRB.cfg.sample.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/GemRB.cfg.sample"
+ IMMEDIATE @ONLY
+)
+
+INSTALL( TARGETS gemrb DESTINATION ${BIN_DIR} )
+INSTALL( FILES "${CMAKE_CURRENT_BINARY_DIR}/GemRB.cfg.sample" GemRB.cfg.noinstall.sample DESTINATION ${SYSCONF_DIR} )
diff --git a/gemrb/GUIScripts/Actor.py b/gemrb/GUIScripts/Actor.py
new file mode 100644
index 0000000..fd2e2cb
--- /dev/null
+++ b/gemrb/GUIScripts/Actor.py
@@ -0,0 +1,226 @@
+# -*-python-*-
+# 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.
+#
+
+# Actor.py: Contains the actor class.
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+#this import is primarily for the tables
+import CommonTables
+
+##############################################################################
+## GLOBALS TO BE INITIALIZED ONCE
+##############################################################################
+dualswap = None
+classcount = None
+levelslots = [IE_LEVEL, IE_LEVEL2, IE_LEVEL3]
+
+class Actor:
+ """Holds information of a PC."""
+
+ def __init__ (self, pc):
+ """Load up basic information."""
+
+ #setup our basic Actor
+ self.Reset (pc)
+
+ #setup globals if they are blank
+ if dualswap == None:
+ self.__setup_globals ()
+
+ def __setup_globals (self):
+ """Initializes all globals used for quick referencing.
+
+ Will only be called by the first Actor created."""
+
+ global classcount, dualswap
+ classcount = CommonTables.Classes.GetRowCount ()
+ dualswap = [0]*classcount
+
+ for i in range(classcount):
+ classid = CommonTables.Classes.GetValue (i, 5)
+ classnames = CommonTables.Classes.GetRowName(i).split("_")
+
+ #set the MC_WAS_ID of the first class
+ if len(classnames) == 2:
+ dualswap[classid-1] = CommonTables.Classes.GetValue (i, 8)
+
+ def Classes (self):
+ """Returns a list with all the class IDs."""
+
+ if self.__classes == None:
+ #already reversed in ClassNames
+ self.__classes = [CommonTables.Classes.GetValue (name, "ID", 1) for name in self.ClassNames()]
+ return self.__classes
+
+ def ClassNames (self):
+ """Returns a list will all the class names."""
+
+ if self.__classnames == None:
+ self.__classnames = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, self.classid) ).split("_")
+ if self.IsDualSwap():
+ self.__classnames.reverse()
+ return self.__classnames
+
+ def ClassTitle (self):
+ """Returns the class title as a displayable string."""
+
+ if self.__classtitle != None:
+ return self.__classtitle
+
+ self.__classtitle = GemRB.GetPlayerStat (self.pc, IE_TITLE1)
+
+ if self.__classtitle == 0:
+ if self.multiclass and self.isdual == 0:
+ self.__classtitle = CommonTables.Classes.GetValue (self.classindex, 2)
+ self.__classtitle = GemRB.GetString (self.__classtitle)
+ elif self.isdual:
+ # first (previous) kit or class of the dual class
+ self.Classes()
+ if self.KitIndex():
+ self.__classtitle = CommonTables.KitList.GetValue (self.__kitindex, 2)
+ else:
+ self.__classtitle = CommonTables.Classes.GetValue (CommonTables.Classes.FindValue \
+ (5, self.__classes[1]), 2)
+ self.__classtitle = GemRB.GetString (self.__classtitle) + " / " + \
+ GemRB.GetString (CommonTables.Classes.GetValue (CommonTables.Classes.FindValue \
+ (5, self.__classes[0]), 2) )
+ else: # ordinary class or kit
+ if self.KitIndex():
+ self.__classtitle = CommonTables.KitList.GetValue (self.__kitindex, 2)
+ else:
+ self.__classtitle = CommonTables.Classes.GetValue (self.classindex, 2)
+ self.__classtitle = GemRB.GetString (self.__classtitle)
+
+ if self.__classtitle == "*":
+ self.__classtitle = 0
+ return self.__classtitle
+
+ def IsDualSwap (self):
+ """Returns true if IE_LEVEL is opposite of expectations."""
+
+ if self.__dualswap == None:
+ self.__dualswap = (self.isdual & CommonTables.Classes.GetValue \
+ (self.ClassNames()[0], "MC_WAS_ID", 1)) > 0
+ return self.__dualswap
+
+ def KitIndex (self):
+ """Returns the kit index in relation to kitlist.2da."""
+
+ if self.__kitindex != None:
+ return self.__kitindex
+
+ Kit = GemRB.GetPlayerStat (self.pc, IE_KIT)
+ self.__kitindex = 0
+
+ if Kit & 0xc000 == 0x4000:
+ self.__kitindex = Kit & 0xfff
+
+ # carefully looking for kit by the usability flag
+ # since the barbarian kit id clashes with the no-kit value
+ if self.__kitindex == 0 and Kit != 0x4000:
+ self.__kitindex = CommonTables.KitList.FindValue (6, Kit)
+ if self.__kitindex == -1:
+ self.__kitindex = 0
+
+ return self.__kitindex
+
+ def LevelDiffs (self):
+ """Returns the differences between the current and next classes."""
+ return [(next-current) for current,next in zip(self.Levels(),
+ self.NextLevels())]
+
+ def Levels (self):
+ """Returns the current level of each class."""
+ if self.__levels == None:
+ self.__levels = [level for slot in levelslots for level \
+ in [GemRB.GetPlayerStat (self.pc, slot)] if level>0]
+ if self.IsDualSwap():
+ self.__levels.reverse()
+ return self.__levels
+
+ def NextLevelExp (self):
+ """Returns the experience required to level each class."""
+
+ #filtering the old dual class out seems unnecessary
+ #just be sure to use NumClasses() or isdual to check
+ return [CommonTables.NextLevel.GetValue (name, str(level+1)) for name,level \
+ in zip(self.ClassNames(), self.Levels())]
+
+ def NextLevels (self):
+ """Returns the next level for each class."""
+
+ if self.__nextlevels != None:
+ return self.__nextlevels
+
+ xp = GemRB.GetPlayerStat (self.pc, IE_XP) / self.NumClasses()
+
+ self.__nextlevels = []
+ for name, level in zip(self.ClassNames(), self.Levels() ):
+ next = level
+
+ #we only want the current level for the old part of a dual-class
+ if len(self.__nextlevels) < self.__numclasses:
+ for current in range(level+1, CommonTables.NextLevel.GetColumnCount () ):
+ if CommonTables.NextLevel.GetValue (name, str(current)) <= xp:
+ next = current
+ else:
+ break
+ self.__nextlevels.append(next)
+
+ return self.__nextlevels
+
+ def NumClasses (self):
+ """Returns the number of *active* classes."""
+ if self.__numclasses == None:
+ if self.isdual:
+ self.__numclasses = 1
+ else:
+ self.__numclasses = len(self.ClassNames() )
+ return self.__numclasses
+
+
+ def RaceName (self):
+ """Returns the race string."""
+ pass
+
+ def Reset (self, pc):
+ """Resets all internal variables.
+
+ This should be called after any fundemental changes to the pc.
+ This includes: dualclassing, leveling."""
+
+ #accessible variables
+ self.pc = pc
+ self.classid = GemRB.GetPlayerStat (self.pc, IE_CLASS)
+ self.classindex = CommonTables.Classes.FindValue (5, self.classid)
+ self.isdual = GemRB.GetPlayerStat (self.pc, IE_MC_FLAGS) & MC_WAS_ANY_CLASS
+ self.multiclass = CommonTables.Classes.GetValue (self.classindex, 4)
+
+ #internal variables - these are only intialized on the first
+ #call to their respective function, and stored thereafter
+ self.__classes = None
+ self.__classnames = None
+ self.__classtitle = None
+ self.__dualswap = None
+ self.__kitindex = None
+ self.__levels = None
+ self.__nextlevels = None
+ self.__numclasses = None
diff --git a/gemrb/GUIScripts/AutodetectCommon.py b/gemrb/GUIScripts/AutodetectCommon.py
new file mode 100644
index 0000000..b6b6be0
--- /dev/null
+++ b/gemrb/GUIScripts/AutodetectCommon.py
@@ -0,0 +1,51 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2010 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.
+#
+
+import os
+import GemRB
+
+
+fdict = {}
+
+# Create dict of files in GamePath and GamePath/data
+for file in os.listdir (GemRB.GamePath):
+ ufile = file.upper()
+ if ufile == 'DATA':
+ for file2 in os.listdir (os.path.join(GemRB.GamePath, file)):
+ fdict[file2.upper()] = 1
+ else:
+ fdict[ufile] = 1
+
+
+#
+# Return True if all given files/resrefs exist
+#
+# FILES is a list of tuples NAME, EXTENSION, TYPE
+#
+
+def CheckFiles(files):
+ for name, ext, type in files:
+ res = (name+'.'+ext).upper() in fdict or GemRB.HasResource (name, type)
+ #print name+'.'+ext, res
+ if not res:
+ return False
+
+ return True
+
diff --git a/gemrb/GUIScripts/BGCommon.py b/gemrb/GUIScripts/BGCommon.py
new file mode 100644
index 0000000..baf9e1c
--- /dev/null
+++ b/gemrb/GUIScripts/BGCommon.py
@@ -0,0 +1,40 @@
+# -*-python-*-
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2010 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.
+#
+# a library of any functions that are common to bg1 and bg2, but not useful for others
+
+import GemRB
+import CommonTables
+from ie_stats import IE_RACE, IE_CLASS, IE_SEX
+
+def RefreshPDoll(button, MinorColor, MajorColor, SkinColor, HairColor):
+ MyChar = GemRB.GetVar ("Slot")
+ AnimID = 0x6000
+ table = GemRB.LoadTable ("avprefr")
+ Race = GemRB.GetPlayerStat (MyChar, IE_RACE)
+ AnimID = AnimID + table.GetValue(Race, 0)
+ table = GemRB.LoadTable ("avprefc")
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ AnimID = AnimID + table.GetValue (Class, 0)
+ table = GemRB.LoadTable ("avprefg")
+ Gender = GemRB.GetPlayerStat (MyChar, IE_SEX)
+ AnimID = AnimID + table.GetValue (Gender, 0)
+ ResRef = CommonTables.Pdolls.GetValue (hex(AnimID), "LEVEL1")
+
+ button.SetPLT (ResRef, 0, MinorColor, MajorColor, SkinColor, 0, 0, HairColor, 0)
+ return
diff --git a/gemrb/GUIScripts/CMakeLists.txt b/gemrb/GUIScripts/CMakeLists.txt
new file mode 100644
index 0000000..811c581
--- /dev/null
+++ b/gemrb/GUIScripts/CMakeLists.txt
@@ -0,0 +1,11 @@
+FILE( GLOB FILES_TO_INSTALL *.py )
+
+INSTALL( FILES ${FILES_TO_INSTALL} DESTINATION ${DATA_DIR}/GUIScripts )
+
+ADD_SUBDIRECTORY( bg1 )
+ADD_SUBDIRECTORY( bg2 )
+ADD_SUBDIRECTORY( iwd )
+ADD_SUBDIRECTORY( iwd2 )
+ADD_SUBDIRECTORY( pst )
+
+INSTALL( DIRECTORY test DESTINATION ${DATA_DIR}/GUIScripts )
diff --git a/gemrb/GUIScripts/CommonTables.py b/gemrb/GUIScripts/CommonTables.py
new file mode 100644
index 0000000..cda3f95
--- /dev/null
+++ b/gemrb/GUIScripts/CommonTables.py
@@ -0,0 +1,47 @@
+# -*-python-*-
+# 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.
+#
+# the place for preloading the most commonly used tables
+import GemRB
+from ie_restype import RES_2DA
+
+# these two are only used in SetEncumbranceLabels, but that is called very often
+StrMod = StrModEx = None
+Classes = KitList = ClassSkills = Races = NextLevel = None
+Pdolls = None
+
+def Load():
+ global Classes, KitList, ClassSkills, Races, NextLevel
+ global Pdolls, StrModEx, StrMod
+
+ print # so the following output isn't appended to an existing line
+ if not Classes:
+ Classes = GemRB.LoadTable ("classes")
+ if not KitList and GemRB.HasResource("kitlist", RES_2DA):
+ KitList = GemRB.LoadTable ("kitlist")
+ if not ClassSkills:
+ ClassSkills= GemRB.LoadTable ("clskills")
+ if not Races:
+ Races = GemRB.LoadTable ("races")
+ if not NextLevel:
+ NextLevel = GemRB.LoadTable ("xplevel")
+ if not Pdolls and GemRB.HasResource("pdolls", RES_2DA):
+ Pdolls = GemRB.LoadTable ("pdolls")
+ if not StrMod:
+ StrMod = GemRB.LoadTable ("strmod")
+ StrModEx = GemRB.LoadTable ("strmodex")
diff --git a/gemrb/GUIScripts/CommonWindow.py b/gemrb/GUIScripts/CommonWindow.py
new file mode 100644
index 0000000..196008c
--- /dev/null
+++ b/gemrb/GUIScripts/CommonWindow.py
@@ -0,0 +1,328 @@
+# -*-python-*-
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2010 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.
+#
+import GemRB
+from GUIDefines import GS_DIALOGMASK, OP_SET
+
+# message window expansion
+def OnIncreaseSize():
+ GSFlags = GemRB.GetMessageWindowSize()
+ Expand = GSFlags&GS_DIALOGMASK
+ GSFlags = GSFlags-Expand
+ if Expand>2:
+ return
+ Expand = (Expand + 1)*2
+ GemRB.GameSetScreenFlags(Expand + GSFlags, OP_SET)
+
+# message window contraction
+def OnDecreaseSize():
+ GSFlags = GemRB.GetMessageWindowSize()
+ Expand = GSFlags&GS_DIALOGMASK
+ GSFlags = GSFlags-Expand
+ if Expand<2:
+ return
+ Expand = Expand/2 - 1 # 6->2, 2->0
+ GemRB.GameSetScreenFlags(Expand + GSFlags, OP_SET)
+
+
+##################################################################
+# functions dealing with containers
+##################################################################
+
+import GUICommon
+import GUIClasses
+import GUIWORLD
+from ie_stats import *
+from GUIDefines import *
+
+ContainerWindow = None
+Container = None
+if GUICommon.GameIsIWD2():
+ leftdiv = 5
+ ground_size = 10
+else:
+ leftdiv = 3
+ ground_size = 6
+
+if GUICommon.GameIsPST():
+ import GUICommonWindows
+
+def UpdateContainerWindow ():
+ global Container
+
+ Window = ContainerWindow
+
+ pc = GemRB.GameGetFirstSelectedPC ()
+ if GUICommon.GameIsPST():
+ GUICommon.SetEncumbranceLabels (Window, 54, None, pc, True)
+ else:
+ GUICommon.SetEncumbranceLabels (Window, 0x10000043, 0x10000044, pc)
+
+ party_gold = GemRB.GameGetPartyGold ()
+ Text = Window.GetControl (0x10000036)
+ Text.SetText (str (party_gold))
+
+ Container = GemRB.GetContainer (0) #will use first selected pc anyway
+ LeftCount = Container['ItemCount']
+ ScrollBar = Window.GetControl (52)
+ Count = LeftCount / leftdiv
+ if Count < 1:
+ Count = 1
+ ScrollBar.SetVarAssoc ("LeftTopIndex", Count)
+
+ inventory_slots = GemRB.GetSlots (pc, 0x8000)
+ RightCount = len(inventory_slots)
+ ScrollBar = Window.GetControl (53)
+ Count = RightCount / 2
+ if Count < 1:
+ Count = 1
+ ScrollBar.SetVarAssoc ("RightTopIndex", Count)
+
+ RedrawContainerWindow ()
+
+def RedrawContainerWindow ():
+ Window = ContainerWindow
+
+ LeftTopIndex = GemRB.GetVar ("LeftTopIndex") * 3
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ RightTopIndex = GemRB.GetVar ("RightTopIndex") * 2
+ RightIndex = GemRB.GetVar ("RightIndex")
+ LeftCount = Container['ItemCount']
+ pc = GemRB.GameGetFirstSelectedPC ()
+ inventory_slots = GemRB.GetSlots (pc, 0x8000)
+ RightCount = len(inventory_slots)
+
+ for i in range (ground_size):
+ #this is an autoselected container, but we could use PC too
+ Slot = GemRB.GetContainerItem (0, i+LeftTopIndex)
+ Button = Window.GetControl (i)
+ if Slot:
+ Button.SetVarAssoc ("LeftIndex", LeftTopIndex+i)
+ function = TakeItemContainer
+ else:
+ Button.SetVarAssoc ("LeftIndex", -1)
+ function = None
+ if GUICommon.GameIsPST():
+ GUICommonWindows.SetItemButton (Window, Button, Slot, function, None)
+ else:
+ GUICommon.UpdateInventorySlot (pc, Button, Slot, "container")
+
+ for i in range (4):
+ if i+RightTopIndex < RightCount:
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[i+RightTopIndex])
+ else:
+ Slot = None
+ Button = Window.GetControl (i+10)
+
+ #pst had a redundant call here, reenable if it turns out it isn't redundant:
+ #GUICommonWindows.SetItemButton (Window, Button, Slot, None, None)
+
+ if Slot:
+ Button.SetVarAssoc ("RightIndex", RightTopIndex+i)
+ function = DropItemContainer
+ else:
+ Button.SetVarAssoc ("RightIndex", -1)
+ function = None
+ if GUICommon.GameIsPST():
+ GUICommonWindows.SetItemButton (Window, Button, Slot, function, None)
+ else:
+ GUICommon.UpdateInventorySlot (pc, Button, Slot, "inventory")
+
+ # shade the inventory icon if it is full
+ if Window.HasControl (54):
+ Button = Window.GetControl (54)
+ free_slots = GemRB.GetSlots (pc, 0x8000, -1)
+ if free_slots == ():
+ Button.SetState (IE_GUI_BUTTON_PRESSED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+def OpenContainerWindow ():
+ global ContainerWindow, Container
+
+ if ContainerWindow:
+ return
+
+ hideflag = GemRB.HideGUI ()
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ContainerWindow = Window = GemRB.LoadWindow (8)
+
+
+ #stop gears from interfering
+ if GUICommon.GameIsPST():
+ GUIWORLD.OldPortraitWindow = GUIClasses.GWindow( GemRB.GetVar ("PortraitWindow") )
+ GUICommonWindows.DisableAnimatedWindows ()
+
+ if GUICommon.GameIsIWD2():
+ GUIWORLD.OldMessageWindow = GUIClasses.GWindow( GemRB.GetVar ("MessageWindow") )
+ GemRB.SetVar ("MessageWindow", Window.ID)
+ else:
+ GUIWORLD.OldActionsWindow = GUIClasses.GWindow( GemRB.GetVar ("ActionsWindow") )
+ GUIWORLD.OldMessageWindow = GUIClasses.GWindow( GemRB.GetVar ("MessageWindow") )
+ GemRB.SetVar ("MessageWindow", -1)
+ GemRB.SetVar ("ActionsWindow", Window.ID)
+
+ Container = GemRB.GetContainer(0)
+
+ # Gears (time) when options pane is down
+ if GUICommon.GameIsBG2():
+ Button = Window.GetControl (62)
+ Label = Button.CreateLabelOnButton (0x1000003e, "NORMAL", 0)
+
+ Label.SetAnimation ("CPEN")
+ Button.SetAnimation ("CGEAR")
+ Button.SetBAM ("CDIAL", 0, 0)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.GearsClicked)
+ GUICommon.SetGamedaysAndHourToken()
+ Button.SetTooltip(16041)
+
+ # 0-5 - Ground Item
+ for i in range (ground_size):
+ Button = Window.GetControl (i)
+ Button.SetVarAssoc ("LeftIndex", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, TakeItemContainer)
+ if GUICommon.GameIsPST():
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_BOTTOM, OP_OR)
+
+ # 10-13 - Personal Item
+ for i in range (4):
+ Button = Window.GetControl (i+10)
+ Button.SetVarAssoc ("RightIndex", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DropItemContainer)
+ if GUICommon.GameIsPST():
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_BOTTOM, OP_OR)
+
+ # left scrollbar (container)
+ ScrollBar = Window.GetControl (52)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawContainerWindow)
+
+ # right scrollbar (inventory)
+ ScrollBar = Window.GetControl (53)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawContainerWindow)
+
+ # encumbrance and inventory icon
+ # iwd has a handy button
+ if Window.HasControl (54):
+ Button = Window.GetControl (54)
+ if GUICommon.GameIsPST():
+ Button.SetFont ("NUMBER")
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.CreateLabelOnButton (0x10000043, "NUMBER", IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_TOP)
+ Button.CreateLabelOnButton (0x10000044, "NUMBER", IE_FONT_ALIGN_RIGHT|IE_FONT_ALIGN_BOTTOM)
+ else:
+ Label = Window.CreateLabel (0x10000043, 323,14,60,15,"NUMBER","0:",IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_TOP)
+ Label = Window.CreateLabel (0x10000044, 323,20,80,15,"NUMBER","0:",IE_FONT_ALIGN_RIGHT|IE_FONT_ALIGN_TOP)
+
+ # container icon
+ Button = Window.GetControl (50)
+ if GUICommon.GameIsPST():
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ if not GUICommon.GameIsPST():
+ Table = GemRB.LoadTable ("containr")
+ row = Container['Type']
+ tmp = Table.GetValue (row, 0)
+ if tmp!='*':
+ GemRB.PlaySound (tmp)
+ tmp = Table.GetValue (row, 1)
+ if tmp!='*':
+ Button.SetSprites (tmp, 0, 0, 0, 0, 0 )
+
+ # Done
+ Button = Window.GetControl (51)
+ if GUICommon.GameIsPST():
+ Button.SetText (1403)
+ else:
+ Button.SetText (11973)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LeaveContainer)
+
+ GemRB.SetVar ("LeftTopIndex", 0)
+ GemRB.SetVar ("RightTopIndex", 0)
+ UpdateContainerWindow ()
+ if hideflag:
+ GemRB.UnhideGUI ()
+
+def CloseContainerWindow ():
+ global ContainerWindow
+
+ if ContainerWindow == None:
+ return
+
+ hideflag = GemRB.HideGUI ()
+
+ if ContainerWindow:
+ ContainerWindow.Unload ()
+ ContainerWindow = None
+
+ if GUICommon.GameIsPST():
+ GUICommonWindows.EnableAnimatedWindows ()
+ GemRB.SetVar ("PortraitWindow", GUIWORLD.OldPortraitWindow.ID)
+
+ # FIXME: iwd2 bug or just bad naming?
+ if GUICommon.GameIsIWD2():
+ GemRB.SetVar ("MessageWindow", GUIWORLD.OldMessageWindow.ID)
+ else:
+ GemRB.SetVar ("ActionsWindow", GUIWORLD.OldActionsWindow.ID)
+ GemRB.SetVar ("MessageWindow", GUIWORLD.OldMessageWindow.ID)
+
+ Table = GemRB.LoadTable ("containr")
+ row = Container['Type']
+ tmp = Table.GetValue (row, 2)
+ #play closing sound if applicable
+ if tmp!='*':
+ GemRB.PlaySound (tmp)
+
+ #it is enough to close here
+
+ if hideflag:
+ GemRB.UnhideGUI ()
+
+#doing this way it will inform the core system too, which in turn will call
+#CloseContainerWindow ()
+def LeaveContainer ():
+ GemRB.LeaveContainer()
+
+def DropItemContainer ():
+ RightIndex = GemRB.GetVar ("RightIndex")
+ if RightIndex < 0:
+ return
+
+ #we need to get the right slot number
+ pc = GemRB.GameGetFirstSelectedPC ()
+ inventory_slots = GemRB.GetSlots (pc, 0x8000)
+ if RightIndex >= len(inventory_slots):
+ return
+
+ GemRB.ChangeContainerItem (0, inventory_slots[RightIndex], 0)
+ UpdateContainerWindow ()
+
+def TakeItemContainer ():
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ if LeftIndex < 0:
+ return
+
+ if LeftIndex >= Container['ItemCount']:
+ return
+
+ GemRB.ChangeContainerItem (0, LeftIndex, 1)
+ UpdateContainerWindow ()
diff --git a/gemrb/GUIScripts/DualClass.py b/gemrb/GUIScripts/DualClass.py
new file mode 100644
index 0000000..2c64bea
--- /dev/null
+++ b/gemrb/GUIScripts/DualClass.py
@@ -0,0 +1,597 @@
+# -*-python-*-
+# 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.
+#
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+from ie_restype import RES_2DA
+import GUICommon
+import CommonTables
+import LUSpellSelection
+import LUProfsSelection
+import LUSkillsSelection
+
+#######################
+pc = 0
+OldClassName = 0
+OldKitName = 0
+ClassName = 0
+NewMageSpells = 0
+NewPriestMask = 0
+NewClassId = 0
+DualClassTable = 0
+#######################
+DCMainWindow = 0
+DCMainClassButton = 0
+DCMainSkillsButton = 0
+DCMainDoneButton = 0
+DCMainStep = 0
+#######################
+DCClassWindow = 0
+DCClassDoneButton = 0
+DCClass = 0
+DCClasses = []
+#######################
+DCProfsWindow = 0
+DCProfsDoneButton = 0
+#######################
+DCSkillsWindow = 0
+DCSkillsDoneButton = 0
+#######################
+
+def DualClassWindow ():
+ """Opens the dual class selection window."""
+
+ global pc, OldClassName, NewMageSpells, NewPriestMask, NewClassId, OldKitName, DualClassTable
+ global DCMainWindow, DCMainClassButton, DCMainDoneButton, DCMainSkillsButton, DCMainStep
+
+ # get our basic globals
+ pc = GemRB.GameGetSelectedPCSingle ()
+ DCMainStep = 1
+
+ # make sure to nullify old values
+ NewPriestMask = 0
+ NewMageSpells = 0
+ NewClassId = 0
+
+ # set up our main window
+ DCMainWindow = GemRB.LoadWindow (5)
+
+ # done button (off)
+ DCMainDoneButton = DCMainWindow.GetControl (2)
+ DCMainDoneButton.SetText (11973)
+ DCMainDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCMainDonePress)
+ DCMainDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DCMainDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # cancel button (on)
+ DCMainCancelButton = DCMainWindow.GetControl (1)
+ DCMainCancelButton.SetText (13727)
+ DCMainCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCMainCancelPress)
+ DCMainCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ DCMainCancelButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # class button (on)
+ DCMainClassButton = DCMainWindow.GetControl (3)
+ DCMainClassButton.SetText (11959)
+ DCMainClassButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCMainClassPress)
+ DCMainClassButton.SetState (IE_GUI_BUTTON_ENABLED)
+ DCMainClassButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # skills button (off)
+ DCMainSkillsButton = DCMainWindow.GetControl (4)
+ DCMainSkillsButton.SetText (17372)
+ DCMainSkillsButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCMainSkillsPress)
+ DCMainSkillsButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DCMainSkillsButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # back button (on)
+ DCMainBackButton = DCMainWindow.GetControl (5)
+ if GUICommon.GameIsBG2():
+ DCMainBackButton.SetText (15416)
+ DCMainBackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCMainBackPress)
+ DCMainBackButton.SetState (IE_GUI_BUTTON_ENABLED)
+ DCMainBackButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # picture of character
+ DCMainPictureButton = DCMainWindow.GetControl (6)
+ DCMainPictureButton.SetState (IE_GUI_BUTTON_LOCKED)
+ DCMainPictureButton.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ DCMainPictureButton.SetPicture (GemRB.GetPlayerPortrait (pc, 0), "NOPORTMD")
+
+ # text area warning
+ DCTextArea = DCMainWindow.GetControl (7)
+ DCTextArea.SetText (10811)
+
+ # character name
+ DCLabel = DCMainWindow.GetControl (0x10000008)
+ DCLabel.SetText (GemRB.GetPlayerName (pc, 0))
+
+ # class name
+ Kit = GUICommon.GetKitIndex (pc)
+ OldClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, GemRB.GetPlayerStat (pc, IE_CLASS) ) )
+ if Kit:
+ OldKitName = CommonTables.KitList.GetValue (Kit, 0, 0)
+ else:
+ OldKitName = OldClassName
+ DCLabel = DCMainWindow.GetControl (0x10000009)
+ DCLabel.SetText (GUICommon.GetActorClassTitle (pc))
+
+ # get the names of the classes we can dual to
+ DualClassTable = GemRB.LoadTable ("dualclas")
+ for i in range (DualClassTable.GetColumnCount ()):
+ DCClasses.append (DualClassTable.GetColumnName (i))
+
+ # show our window
+ DCMainWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DCMainDonePress ():
+ """Saves our dualclass changes and closes the window.
+
+ This de-activates our old class and readjusts the basic actor stats, such as
+ THAC0, spell casting, proficiencies, and so forth, to the new class."""
+ import GUIREC
+
+ # save our proficiencies
+ LUProfsSelection.ProfsSave (pc, LUProfsSelection.LUPROFS_TYPE_DUALCLASS)
+
+ # remove old class abilities
+ KitIndex = GUICommon.GetKitIndex (pc)
+ if KitIndex:
+ ABTable = CommonTables.KitList.GetValue (str(KitIndex), "ABILITIES")
+ else:
+ ABTable = CommonTables.ClassSkills.GetValue (OldClassName, "ABILITIES")
+ if ABTable != "*" and GemRB.HasResource (ABTable, RES_2DA, 1):
+ GUICommon.RemoveClassAbilities (pc, ABTable, GemRB.GetPlayerStat (pc, IE_LEVEL))
+
+ # remove old class casting
+ if not NewMageSpells:
+ for i in range (9):
+ GemRB.SetMemorizableSpellsCount (pc, 0, IE_SPELL_TYPE_WIZARD, i)
+ GUICommon.RemoveKnownSpells (pc, IE_SPELL_TYPE_PRIEST, 1,7, 1)
+
+ # apply our class abilities
+ ABTable = CommonTables.ClassSkills.GetValue (ClassName, "ABILITIES")
+ if ABTable != "*" and GemRB.HasResource (ABTable, RES_2DA, 1):
+ GUICommon.AddClassAbilities (pc, ABTable)
+
+ # learn our new priest spells
+ if NewPriestMask:
+ GUICommon.LearnPriestSpells (pc, 1, NewPriestMask)
+ GemRB.SetMemorizableSpellsCount (pc, 1, IE_SPELL_TYPE_PRIEST, 0)
+
+ # save our thief skills if we have them
+ LUSkillsSelection.SkillsSave (pc)
+
+ # save our new class and say was multi
+ OldClassId = GemRB.GetPlayerStat (pc, IE_CLASS)
+ MultClassId = (1 << (NewClassId-1)) | (1 << (OldClassId-1))
+ MultClassId = CommonTables.Classes.FindValue (4, MultClassId)
+ MultClassId = CommonTables.Classes.GetValue (MultClassId, 5)
+ GemRB.SetPlayerStat (pc, IE_CLASS, MultClassId)
+ GemRB.SetPlayerStat (pc, IE_MC_FLAGS, CommonTables.Classes.GetValue (OldClassName, "MC_WAS_ID", 1))
+
+ # update our levels and xp
+ if GUICommon.IsDualSwap (pc):
+ GemRB.SetPlayerStat (pc, IE_LEVEL2, 1)
+ else:
+ GemRB.SetPlayerStat (pc, IE_LEVEL2, GemRB.GetPlayerStat (pc, IE_LEVEL), 0)
+ GemRB.SetPlayerStat (pc, IE_LEVEL, 1)
+ GemRB.SetPlayerStat (pc, IE_XP, 0)
+
+ # new thac0
+ ThacoTable = GemRB.LoadTable ("THAC0")
+ GemRB.SetPlayerStat (pc, IE_TOHIT, ThacoTable.GetValue (NewClassId-1, 0, 1))
+
+ # new saves
+ SavesTable = CommonTables.Classes.GetValue (CommonTables.Classes.FindValue (5, NewClassId), 3, 0)
+ SavesTable = GemRB.LoadTable (SavesTable)
+ for i in range (5):
+ GemRB.SetPlayerStat (pc, IE_SAVEVSDEATH+i, SavesTable.GetValue (i, 0))
+
+ # close our window
+ if DCMainWindow:
+ DCMainWindow.Unload ()
+ GUIREC.UpdateRecordsWindow()
+ return
+
+def DCMainCancelPress ():
+ """Revert all changes and close the dual class window."""
+
+ # simulate pressing the back button until we get back to DCMainStep = 1
+ # to unset all things from the new class
+ while DCMainStep > 1:
+ DCMainBackPress ()
+
+ # close our window
+ if DCMainWindow:
+ DCMainWindow.Unload ()
+
+ return
+
+def DCMainBackPress ():
+ """Reverts all current changes and reverts back to the previous step."""
+ global DCMainStep, DCClass, NewMageSpells
+ global NewPriestMask
+
+ if DCMainStep == 2: # class selected, wait to choose skills
+ # disable the skills button and re-enable the class button
+ # the class will be reset when the class button is clicked
+ DCMainSkillsButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DCMainClassButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # back a step
+ DCMainStep = 1
+ elif DCMainStep == 3: # skills selected
+ # re-enable our skills button
+ DCMainSkillsButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # un-learn our spells and skills
+ if NewMageSpells:
+ GUICommon.RemoveKnownSpells (pc, IE_SPELL_TYPE_WIZARD, 1,1, 1)
+
+ LUSkillsSelection.SkillsNullify ()
+ NewPriestMask = 0
+ NewMageSpells = 0
+
+ # go back a step
+ DCMainStep = 2
+ return
+
+def DCMainClassPress ():
+ """Opens the class selection window."""
+
+ global DCClassWindow, DCClassDoneButton, DCClass
+
+ # we default the class back down to -1
+ DCClass = -1
+ GemRB.SetVar ("DCClass", DCClass)
+
+ # open the window
+ DCClassWindow = GemRB.LoadWindow (6)
+
+ # string refs for the given classes
+ DCClassStrings = []
+ for classname in DCClasses:
+ DCClassStrings.append(CommonTables.Classes.GetValue (classname, "NAME_REF", 1))
+
+ # setup the class buttons
+ for i in range (6):
+ # get the button and associate it with the correct var
+ DCClassButton = DCClassWindow.GetControl (i+1)
+ DCClassButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCClassSelect)
+ DCClassButton.SetVarAssoc ("DCClass", i)
+ DCClassButton.SetText (DCClassStrings[i])
+ DCClassButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # enable only if we can dual into the given class
+ if CanDualInto (i):
+ DCClassButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ DCClassButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # done button (off)
+ DCClassDoneButton = DCClassWindow.GetControl (8)
+ DCClassDoneButton.SetText (11973)
+ DCClassDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCClassDonePress)
+ DCClassDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DCClassDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # back button (on)
+ DCClassBackButton = DCClassWindow.GetControl (7)
+ DCClassBackButton.SetText (15416)
+ DCClassBackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCClassBackPress)
+ DCClassBackButton.SetState (IE_GUI_BUTTON_ENABLED)
+ DCClassBackButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # setup the text area with default text
+ DCClassTextArea = DCClassWindow.GetControl (9)
+ DCClassTextArea.SetText (10949)
+
+ # show the window
+ DCClassWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+ return
+
+def DCClassSelect ():
+ """Updates the current class based on the button pressed."""
+
+ global DCClass
+
+ # un-select the old button and save and select the new one
+ if DCClass >= 0:
+ DCClassButton = DCClassWindow.GetControl (DCClass+1)
+ DCClassButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # if we clicked the same class twice, turn the done button off (toggled)
+ if DCClass == GemRB.GetVar ("DCClass"):
+ DCClassDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DCClass = -1
+
+ # don't need to worry about setting a new text area, as it would be the same
+ return
+
+ # save the new class, select it's button, and enable the done button
+ DCClass = GemRB.GetVar ("DCClass")
+ DCClassButton = DCClassWindow.GetControl (DCClass+1)
+ DCClassButton.SetState (IE_GUI_BUTTON_SELECTED)
+ DCClassDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # all the possible strrefs for the different classes
+ DCClassStrings = []
+ for classname in DCClasses:
+ DCClassStrings.append (CommonTables.Classes.GetValue (classname, "DESC_REF", 1))
+
+ # update the text are with the new string
+ DCClassTextArea = DCClassWindow.GetControl (9)
+ DCClassTextArea.SetText (DCClassStrings [DCClass])
+
+ return
+
+def DCClassDonePress ():
+ """Stores the selected class and moves to the next step."""
+
+ global DCMainStep, ClassName, NewClassId
+
+ # unload our class selection window
+ if DCClassWindow:
+ DCClassWindow.Unload ()
+
+ # enable the skills button and disable the class selection button
+ DCMainClassButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DCMainSkillsButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # save the class
+ ClassName = DCClasses[DCClass]
+ NewClassId = CommonTables.Classes.GetValue (ClassName, "ID", 1)
+
+ # set our step to 2 so that the back button knows where we are
+ DCMainStep = 2
+
+ return
+
+def CanDualInto (index):
+ """Determines if a given class can be dualed into.
+
+ Index defines the position within the DCClasses list."""
+
+ # make sure index isn't out of range
+ if index < 0 or index >= len (DCClasses):
+ return 0
+
+ # return 0 if we can't dual into the class
+ if not DualClassTable.GetValue (OldKitName, DCClasses[index], 1):
+ return 0
+
+ # make sure we aren't restricted by alignment
+ AlignmentTable = GemRB.LoadTable ("alignmnt")
+ AlignsTable = GemRB.LoadTable ("aligns")
+ Alignment = GemRB.GetPlayerStat (pc, IE_ALIGNMENT) # our alignment
+ Alignment = AlignsTable.FindValue (3, Alignment)
+ Alignment = AlignsTable.GetValue (Alignment, 4) # convert the alignment
+ if not AlignmentTable.GetValue (DCClasses[index], Alignment, 1): # check it
+ return 0
+
+ # make sure we have the minimum stats required for the next class
+ StatTable = GemRB.LoadTable ("abdcdsrq")
+ ClassStatIndex = StatTable.GetRowIndex (DCClasses[index])
+ for stat in range (6): # loop through each stat
+ minimum = StatTable.GetValue (ClassStatIndex, stat)
+ name = StatTable.GetColumnName (stat)
+ if GemRB.GetPlayerStat (pc, eval("IE_" + name[4:])) < minimum: # see if we're under the minimum
+ return 0
+
+ # if we made it here, we can dual to the class
+ return 1
+
+def DCClassBackPress ():
+ """Unloads the class selection window without making any changes."""
+ # close the class window
+ if DCClassWindow:
+ DCClassWindow.Unload ()
+ return
+
+def DCMainSkillsPress ():
+ """Opens the proficiency selection window.
+
+ Then follows the skills proficiency and spells windows, if required."""
+
+ global DCSkillsWindow, DCMainStep, DCHasProfs
+
+ # we're onto the next step
+ DCMainSkillsButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DCMainStep = 3
+
+ DCOpenProfsWindow ()
+ return
+
+def DCOpenProfsWindow ():
+ """Opens the proficiency selection window."""
+
+ global DCProfsWindow, DCProfsDoneButton
+
+ # load up our window and set some basic variables
+ DCProfsWindow = GemRB.LoadWindow (15)
+ NewClassId = CommonTables.Classes.GetValue (ClassName, "ID", 1)
+ if GUICommon.GameIsBG2():
+ LUProfsSelection.SetupProfsWindow (pc, \
+ LUProfsSelection.LUPROFS_TYPE_DUALCLASS, DCProfsWindow, DCProfsRedraw, classid=NewClassId)
+ else:
+ LUProfsSelection.SetupProfsWindow (pc, \
+ LUProfsSelection.LUPROFS_TYPE_DUALCLASS, DCProfsWindow, \
+ DCProfsRedraw, [0,0,0], [1,1,1], NewClassId, False, 0)
+
+ # setup the done and cancel
+ DCProfsDoneButton = DCProfsWindow.GetControl (76)
+ DCProfsDoneButton.SetText (11973)
+ DCProfsDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCProfsDonePress)
+ DCProfsDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DCProfsDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ DCProfsCancelButton = DCProfsWindow.GetControl (77)
+ DCProfsCancelButton.SetText (13727)
+ DCProfsCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DCProfsCancelPress)
+ DCProfsCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ DCProfsCancelButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # show the window and draw away
+ DCProfsWindow.ShowModal (MODAL_SHADOW_GRAY)
+ DCProfsRedraw ()
+ return
+
+def DCProfsRedraw ():
+ """Redraws the proficiency selection window.
+
+ Called whenever a proficiency is assigned or removed."""
+
+ ProfsPointsLeft = GemRB.GetVar ("ProfsPointsLeft")
+
+ if ProfsPointsLeft == 0:
+ DCProfsDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ DCProfsDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def DCProfsDonePress ():
+ """Goes to the next applicable step.
+
+ This is either skill selection, spell selection, or nothing."""
+
+ global NewMageSpells, NewPriestMask
+
+ # check for mage spells and thief skills
+ SpellTable = CommonTables.ClassSkills.GetValue (ClassName, "MAGESPELL")
+ ClericTable = CommonTables.ClassSkills.GetValue (ClassName, "CLERICSPELL")
+ DruidTable = CommonTables.ClassSkills.GetValue (ClassName, "DRUIDSPELL")
+ if SpellTable != "*":
+ # we go 2,2 to get an extra spell
+ # TODO: add a mod to the function instead?
+ LUSpellSelection.OpenSpellsWindow (pc, SpellTable, 2, 2, 0)
+ SpellTable = GemRB.LoadTable (SpellTable)
+ GemRB.SetMemorizableSpellsCount (pc, SpellTable.GetValue (0, 0), IE_SPELL_TYPE_WIZARD, 0)
+ NewMageSpells = 1
+ if ClericTable != "*":
+ print "Setting PriestMask"
+ if not GemRB.HasResource(ClericTable, RES_2DA, 1):
+ ClericTable = "MXSPLPRS" # iwd1 doesn't have a DRUIDSPELL column in the table
+ # make sure we can cast spells at this level (paladins)
+ ClericTable = GemRB.LoadTable (ClericTable)
+ if ClericTable.GetRowName (0) == "1":
+ NewPriestMask = 0x4000
+ elif DruidTable != "*":
+ # make sure we can cast spells at this level (rangers)
+ if GUICommon.HasTOB ():
+ DruidTable = GemRB.LoadTable (DruidTable)
+ if DruidTable.GetRowName (0) == "1":
+ NewPriestMask = 0x8000
+ else:
+ NewPriestMask = 0x8000
+
+ # open the thieves selection window
+ OpenSkillsWindow ()
+
+ # we can be done now
+ DCMainDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # close out the profs window (don't assign yet!)
+ if DCProfsWindow:
+ DCProfsWindow.Unload ()
+ return
+
+def DCProfsCancelPress ():
+ """Closes the profeciency selection window."""
+
+ # close out the profs window and go back a step
+ if DCProfsWindow:
+ DCProfsWindow.Unload ()
+
+ DCMainBackPress ()
+ return
+
+def OpenSkillsWindow ():
+ """Opens the skills selection window.
+
+ This will allow the selection of thief skills for appropriate classes, or
+ auto-assign them for classes that recieve skills based on level."""
+
+ global DCSkillsWindow, DCSkillsDoneButton
+
+ DCSkillsWindow = GemRB.LoadWindow (7)
+ if GUICommon.GameIsBG2():
+ LUSkillsSelection.SetupSkillsWindow (pc, \
+ LUSkillsSelection.LUSKILLS_TYPE_DUALCLASS, DCSkillsWindow, DCSkillsRedraw, classid=NewClassId)
+ else:
+ LUSkillsSelection.SetupSkillsWindow (pc, \
+ LUSkillsSelection.LUSKILLS_TYPE_DUALCLASS, DCSkillsWindow, \
+ DCSkillsRedraw, [0,0,0], [1,1,1], NewClassId, False)
+
+ #just go back if we can't assign skills
+ if GemRB.GetVar ("SkillPointsLeft") <= 0:
+ return
+
+ # setup the back and done buttons
+ BackButton = DCSkillsWindow.GetControl(24)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS,DCSkillsBackPress)
+
+ DCSkillsDoneButton = DCSkillsWindow.GetControl(25)
+ DCSkillsDoneButton.SetText(11973)
+ DCSkillsDoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DCSkillsDoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DCSkillsDonePress)
+ DCSkillsDoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ # setup the default text area
+ TextArea = DCSkillsWindow.GetControl(22)
+ TextArea.SetText(17248)
+
+ DCSkillsWindow.ShowModal (MODAL_SHADOW_GRAY)
+ DCSkillsRedraw ()
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+ return
+
+def DCSkillsRedraw ():
+ """Redraws the skills selection window.
+
+ Called whenever a skill is assigned or removed."""
+
+ # no points left? we can be done! :)
+ DCSkillsLeft = GemRB.GetVar ("SkillPointsLeft")
+ if DCSkillsLeft <= 0:
+ DCSkillsDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ DCSkillsDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+def DCSkillsBackPress ():
+ """Reverts all changes to this points."""
+
+ if DCSkillsWindow:
+ DCSkillsWindow.Unload ()
+ LUSkillsSelection.SkillsNullify ()
+ DCMainBackPress ()
+ return
+
+def DCSkillsDonePress ():
+ """Closes the skills selection window."""
+
+ if DCSkillsWindow:
+ DCSkillsWindow.Unload ()
+ GemRB.SetRepeatClickFlags (GEM_RK_DISABLE, OP_OR)
+ return
diff --git a/gemrb/GUIScripts/GUIClasses.py b/gemrb/GUIScripts/GUIClasses.py
new file mode 100644
index 0000000..6429342
--- /dev/null
+++ b/gemrb/GUIScripts/GUIClasses.py
@@ -0,0 +1,199 @@
+#-*-python-*-
+#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.
+
+
+import _GemRB
+
+from MetaClasses import metaIDWrapper, metaControl
+
+class GTable:
+ __metaclass__ = metaIDWrapper
+ methods = {
+ 'GetValue': _GemRB.Table_GetValue,
+ 'FindValue': _GemRB.Table_FindValue,
+ 'GetRowIndex': _GemRB.Table_GetRowIndex,
+ 'GetRowName': _GemRB.Table_GetRowName,
+ 'GetColumnIndex': _GemRB.Table_GetColumnIndex,
+ 'GetColumnName': _GemRB.Table_GetColumnName,
+ 'GetRowCount': _GemRB.Table_GetRowCount,
+ 'GetColumnCount': _GemRB.Table_GetColumnCount
+ }
+ def __del__(self):
+ # don't unload tables if the _GemRB module is already unloaded at exit
+ if self.ID != -1 and _GemRB:
+ pass #_GemRB.Table_Unload(self.ID)
+ def __nonzero__(self):
+ return self.ID != -1
+
+class GSymbol:
+ __metaclass__ = metaIDWrapper
+ methods = {
+ 'GetValue': _GemRB.Symbol_GetValue,
+ 'Unload': _GemRB.Symbol_Unload
+ }
+
+class GWindow:
+ __metaclass__ = metaIDWrapper
+ methods = {
+ 'SetSize': _GemRB.Window_SetSize,
+ 'SetFrame': _GemRB.Window_SetFrame,
+ 'SetPicture': _GemRB.Window_SetPicture,
+ 'SetPos': _GemRB.Window_SetPos,
+ 'HasControl': _GemRB.Window_HasControl,
+ 'DeleteControl': _GemRB.Window_DeleteControl,
+ 'SetupEquipmentIcons': _GemRB.Window_SetupEquipmentIcons,
+ 'SetupSpellIcons': _GemRB.Window_SetupSpellIcons,
+ 'SetupControls': _GemRB.Window_SetupControls,
+ 'SetVisible': _GemRB.Window_SetVisible,
+ 'ShowModal': _GemRB.Window_ShowModal,
+ 'Invalidate': _GemRB.Window_Invalidate
+ }
+ def __nonzero__(self):
+ return self.ID != -1
+ def Unload(self):
+ if self.ID != -1:
+ _GemRB.Window_Unload(self.ID)
+ self.ID = -1
+ def GetControl(self, control):
+ return _GemRB.Window_GetControl(self.ID, control)
+ def CreateWorldMapControl(self, control, *args):
+ _GemRB.Window_CreateWorldMapControl(self.ID, control, *args)
+ return _GemRB.Window_GetControl(self.ID, control)
+ def CreateMapControl(self, control, *args):
+ _GemRB.Window_CreateMapControl(self.ID, control, *args)
+ return _GemRB.Window_GetControl(self.ID, control)
+ def CreateLabel(self, control, *args):
+ _GemRB.Window_CreateLabel(self.ID, control, *args)
+ return _GemRB.Window_GetControl(self.ID, control)
+ def CreateButton(self, control, *args):
+ _GemRB.Window_CreateButton(self.ID, control, *args)
+ return _GemRB.Window_GetControl(self.ID, control)
+ def CreateScrollBar(self, control, *args):
+ _GemRB.Window_CreateScrollBar(self.ID, control, *args)
+ return _GemRB.Window_GetControl(self.ID, control)
+ def CreateTextEdit(self, control, *args):
+ _GemRB.Window_CreateTextEdit(self.ID, control, *args)
+ return _GemRB.Window_GetControl(self.ID, control)
+
+
+class GControl:
+ __metaclass__ = metaControl
+ methods = {
+ 'SetVarAssoc': _GemRB.Control_SetVarAssoc,
+ 'SetPos': _GemRB.Control_SetPos,
+ 'SetSize': _GemRB.Control_SetSize,
+ 'SetAnimationPalette': _GemRB.Control_SetAnimationPalette,
+ 'SetAnimation': _GemRB.Control_SetAnimation,
+ 'QueryText': _GemRB.Control_QueryText,
+ 'SetText': _GemRB.Control_SetText,
+ 'SetTooltip': _GemRB.Control_SetTooltip,
+ 'SetEvent': _GemRB.Control_SetEvent,
+ 'SetStatus': _GemRB.Control_SetStatus,
+ }
+ def AttachScrollBar(self, scrollbar):
+ if self.WinID != scrollbar.WinID:
+ raise RuntimeError, "Scrollbar must be in same Window as Control"
+ return _GemRB.Control_AttachScrollBar(self.WinID, self.ID, scrollbar.ID)
+
+class GLabel(GControl):
+ __metaclass__ = metaControl
+ methods = {
+ 'SetTextColor': _GemRB.Label_SetTextColor,
+ 'SetUseRGB': _GemRB.Label_SetUseRGB
+ }
+
+class GTextArea(GControl):
+ __metaclass__ = metaControl
+ methods = {
+ 'Rewind': _GemRB.TextArea_Rewind,
+ 'SetHistory': _GemRB.TextArea_SetHistory,
+ 'Append': _GemRB.TextArea_Append,
+ 'Clear': _GemRB.TextArea_Clear,
+ 'Scroll': _GemRB.TextArea_Scroll,
+ 'SelectText': _GemRB.TextArea_SelectText,
+ 'SetFlags': _GemRB.Control_TextArea_SetFlags,
+ 'GetCharSounds': _GemRB.TextArea_GetCharSounds,
+ 'GetCharacters': _GemRB.TextArea_GetCharacters,
+ 'GetPortraits': _GemRB.TextArea_GetPortraits
+ }
+ def MoveText(self, other):
+ _GemRB.TextArea_MoveText(self.WinID, self.ID, other.WinID, other.ID)
+
+class GTextEdit(GControl):
+ __metaclass__ = metaControl
+ methods = {
+ 'SetBufferLength': _GemRB.TextEdit_SetBufferLength
+ }
+ def ConvertEdit(self, ScrollBarID):
+ newID = _GemRB.TextEdit_ConvertEdit(self.WinID, self.ID, ScrollBarID)
+ return GTextArea(self.WinID, self.ID)
+
+class GScrollBar(GControl):
+ __metaclass__ = metaControl
+ methods = {
+ 'SetDefaultScrollBar': _GemRB.ScrollBar_SetDefaultScrollBar,
+ 'SetSprites': _GemRB.ScrollBar_SetSprites
+ }
+
+class GButton(GControl):
+ __metaclass__ = metaControl
+ methods = {
+ 'SetSprites': _GemRB.Button_SetSprites,
+ 'SetOverlay': _GemRB.Button_SetOverlay,
+ 'SetBorder': _GemRB.Button_SetBorder,
+ 'EnableBorder': _GemRB.Button_EnableBorder,
+ 'SetFont': _GemRB.Button_SetFont,
+ 'SetTextColor': _GemRB.Button_SetTextColor,
+ 'SetFlags': _GemRB.Button_SetFlags,
+ 'SetState': _GemRB.Button_SetState,
+ 'SetPictureClipping': _GemRB.Button_SetPictureClipping,
+ 'SetPicture': _GemRB.Button_SetPicture,
+ 'SetSprite2D': _GemRB.Button_SetSprite2D,
+ 'SetMOS': _GemRB.Button_SetMOS,
+ 'SetPLT': _GemRB.Button_SetPLT,
+ 'SetBAM': _GemRB.Button_SetBAM,
+ 'SetSpellIcon': _GemRB.Button_SetSpellIcon,
+ 'SetItemIcon': _GemRB.Button_SetItemIcon,
+ 'SetActionIcon': _GemRB.Button_SetActionIcon
+ }
+ def CreateLabelOnButton(self, control, *args):
+ _GemRB.Button_CreateLabelOnButton(self.WinID, self.ID, control, *args)
+ return _GemRB.Window_GetControl(self.WinID, control)
+
+class GWorldMap(GControl):
+ __metaclass__ = metaControl
+ methods = {
+ 'AdjustScrolling': _GemRB.WorldMap_AdjustScrolling,
+ 'GetDestinationArea': _GemRB.WorldMap_GetDestinationArea,
+ 'SetTextColor': _GemRB.WorldMap_SetTextColor
+ }
+
+class GSaveGame:
+ __metaclass__ = metaIDWrapper
+ methods = {
+ 'GetDate': _GemRB.SaveGame_GetDate,
+ 'GetGameDate': _GemRB.SaveGame_GetGameDate,
+ 'GetName': _GemRB.SaveGame_GetName,
+ 'GetPortrait': _GemRB.SaveGame_GetPortrait,
+ 'GetPreview': _GemRB.SaveGame_GetPreview,
+ 'GetSaveID': _GemRB.SaveGame_GetSaveID,
+ }
+
+class GSprite2D:
+ __metaclass__ = metaIDWrapper
+ methods = {}
diff --git a/gemrb/GUIScripts/GUICommon.py b/gemrb/GUIScripts/GUICommon.py
new file mode 100644
index 0000000..705ef52
--- /dev/null
+++ b/gemrb/GUIScripts/GUICommon.py
@@ -0,0 +1,911 @@
+# -*-python-*-
+# 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.
+#
+#
+# GUICommon.py - common functions for GUIScripts of all game types
+
+import GemRB
+import GUIClasses
+import CommonTables
+from ie_restype import RES_CHU, RES_2DA, RES_WMP, RES_ARE
+from ie_spells import LS_MEMO, LSR_KNOWN, LSR_LEVEL, LSR_STAT
+from GUIDefines import *
+from ie_stats import *
+from ie_action import ACT_QSLOT1, ACT_QSLOT2, ACT_QSLOT3, ACT_QSLOT4, ACT_QSLOT5
+from ie_slots import SLOT_ALL
+
+OtherWindowFn = None
+NextWindowFn = None
+
+CommonTables.Load ()
+
+def CloseOtherWindow (NewWindowFn):
+ global OtherWindowFn,NextWindowFn
+
+ GemRB.LeaveContainer()
+ if OtherWindowFn and OtherWindowFn != NewWindowFn:
+ # allow detection of 'next window'
+ NextWindowFn = NewWindowFn
+ # switching from a window to something else, call old function
+ OtherWindowFn ()
+ OtherWindowFn = NewWindowFn
+ return 0
+ elif OtherWindowFn:
+ # something is calling us with its own function, so
+ # it is closing down, return true
+ OtherWindowFn = None
+ return 1
+ else:
+ # new window, no need to do setup
+ OtherWindowFn = NewWindowFn
+ NextWindowFn = None
+ return 0
+
+def GetWindowPack():
+ width = GemRB.GetSystemVariable (SV_WIDTH)
+ height = GemRB.GetSystemVariable (SV_HEIGHT)
+
+ if GemRB.GameType == "pst":
+ default = "GUIWORLD"
+ else:
+ default = "GUIW"
+
+ # use a custom gui if there is one
+ gui = "CGUI" + str(width)[:2] + str(height)[:2]
+ if GemRB.HasResource (gui, RES_CHU, 1):
+ return gui
+
+ gui = None
+ if width == 640:
+ gui = default
+ elif width == 800:
+ gui = "GUIW08"
+ elif width == 1024:
+ gui = "GUIW10"
+ elif width == 1280:
+ gui = "GUIW12"
+ if gui:
+ if GemRB.HasResource (gui, RES_CHU, 1):
+ return gui
+
+ # fallback to the smallest resolution
+ return default
+
+def RestPress ():
+ GemRB.RestParty(0,0,0)
+ return
+
+def SelectFormation ():
+ GemRB.GameSetFormation (GemRB.GetVar ("Formation"))
+ return
+
+def OpenFloatMenuWindow ():
+ if GameIsPST():
+ import FloatMenuWindow
+ FloatMenuWindow.OpenFloatMenuWindow()
+ else:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_NONE)
+
+def GetActorPaperDoll (actor):
+ anim_id = GemRB.GetPlayerStat (actor, IE_ANIMATION_ID)
+ level = GemRB.GetPlayerStat (actor, IE_ARMOR_TYPE)
+ row = "0x%04X" %anim_id
+ which = "LEVEL%d" %(level+1)
+ doll = CommonTables.Pdolls.GetValue (row, which)
+ if doll == "*":
+ print "GetActorPaperDoll: Missing paper doll for animation", row, which
+ return doll
+
+def SelectAllOnPress ():
+ GemRB.GameSelectPC (0, 1)
+
+def GearsClicked ():
+ #GemRB.SetPlayerStat(GemRB.GameGetFirstSelectedPC (),44,249990)
+ GemRB.GamePause (2, 0)
+
+def GetMageSpells (Kit, Alignment, Level):
+ MageSpells = []
+ SpellType = 99
+ Table = GemRB.LoadTable ("aligns")
+ v = Table.FindValue (3, Alignment)
+ Usability = Kit | Table.GetValue(v, 5)
+
+ SpellsTable = GemRB.LoadTable ("spells")
+ for i in range(SpellsTable.GetValue ("MAGE", str(Level), 1) ):
+ SpellName = "SPWI%d%02d"%(Level,i+1)
+ ms = GemRB.GetSpell (SpellName, 1)
+ if ms == None:
+ continue
+
+ if Usability & ms['SpellExclusion']:
+ SpellType = 0
+ else:
+ SpellType = 1
+ if Kit & (1 << ms['SpellSchool']+5): # of matching specialist school
+ SpellType = 2
+ # Wild mage spells are of normal schools, so we have to find them
+ # separately. Generalists can learn any spell but the wild ones, so
+ # we check if the mage is wild and if a generalist wouldn't be able
+ # to learn the spell.
+ if Kit == 0x8000 and (0x4000 & ms['SpellExclusion']):
+ SpellType = 2
+ MageSpells.append ([SpellName, SpellType])
+
+ return MageSpells
+
+def GetLearnableMageSpells (Kit, Alignment, Level):
+ Learnable = []
+
+ for Spell in GetMageSpells (Kit, Alignment, Level):
+ if Spell[1]:
+ Learnable.append (Spell[0])
+ return Learnable
+
+def GetLearnablePriestSpells (Class, Alignment, Level):
+ Learnable =[]
+
+ Table=GemRB.LoadTable("aligns")
+ v = Table.FindValue(3, Alignment)
+ #usability is the bitset we look for
+ Usability=Table.GetValue(v, 5)
+
+ SpellsTable = GemRB.LoadTable ("spells")
+ for i in range(SpellsTable.GetValue ("PRIEST", str (Level), 1) ):
+ SpellName = "SPPR%d%02d"%(Level,i+1)
+ ms = GemRB.GetSpell(SpellName, 1)
+ if ms == None:
+ continue
+ if Class & ms['SpellDivine']:
+ continue
+ if Usability & ms['SpellExclusion']:
+ continue
+ Learnable.append (SpellName)
+ return Learnable
+
+def SetupSpellLevels (pc, TableName, Type, Level):
+ #don't die on a missing reference
+ #FIXME: try to do this in a non-hard way?
+ if not GemRB.HasResource (TableName, RES_2DA):
+ if TableName == "MXSPLDRU":
+ SetupSpellLevels (pc, "MXSPLPRS", Type, Level)
+ return
+
+ Table = GemRB.LoadTable (TableName)
+ for i in range(Table.GetColumnCount ()):
+ # do a string lookup since some tables don't have entries for all levels
+ value = Table.GetValue (str(Level), str(i+1), 1)
+ # specialist mages get an extra spell if they already know that level
+ # FIXME: get a general routine to find specialists
+ school = GemRB.GetVar("MAGESCHOOL")
+ if Type == IE_SPELL_TYPE_WIZARD and school != 0:
+ if value > 0:
+ value += 1
+ GemRB.SetMemorizableSpellsCount (pc, value, Type, i)
+ return
+
+def UnsetupSpellLevels (pc, TableName, Type, Level):
+ #don't die on a missing reference
+ #FIXME: try to do this in a non-hard way?
+ if not GemRB.HasResource (TableName, RES_2DA):
+ if TableName == "MXSPLDRU":
+ UnsetupSpellLevels (pc, "MXSPLPRS", Type, Level)
+ return
+
+ Table = GemRB.LoadTable (TableName)
+ for i in range(Table.GetColumnCount ()):
+ GemRB.SetMemorizableSpellsCount (pc, 0, Type, i)
+ return
+
+def SetColorStat (Actor, Stat, Value):
+ t = Value & 0xFF
+ t |= t << 8
+ t |= t << 16
+ GemRB.SetPlayerStat (Actor, Stat, t)
+ return
+
+def CheckStat100 (Actor, Stat, Diff):
+ mystat = GemRB.GetPlayerStat (Actor, Stat)
+ goal = GemRB.Roll (1,100, Diff)
+ if mystat>=goal:
+ return True
+ return False
+
+def CheckStat20 (Actor, Stat, Diff):
+ mystat = GemRB.GetPlayerStat (Actor, Stat)
+ goal = GemRB.Roll (1,20, Diff)
+ if mystat>=goal:
+ return True
+ return False
+
+def GameIsPST ():
+ return GemRB.GameType == "pst"
+
+def GameIsIWD ():
+ return GemRB.GameType == "iwd"
+
+def GameIsHOW ():
+ return GemRB.GameType == "how"
+
+def GameIsIWD1 ():
+ return GemRB.GameType == "iwd" or GemRB.GameType == "how"
+
+def GameIsIWD2 ():
+ return GemRB.GameType == "iwd2"
+
+def GameIsBG1 ():
+ return GemRB.GameType == "bg1"
+
+def GameIsBG2 ():
+ return GemRB.GameType == "bg2"
+
+def GameIsBG2Demo ():
+ return ('BG2Demo' in GemRB.__dict__) and (GemRB.BG2Demo == True)
+
+def GameIsTOB ():
+ return GemRB.HasResource ("worldm25", RES_WMP) and GemRB.GetVar("oldgame") == 0
+
+def HasTOB ():
+ return GemRB.HasResource ("worldm25", RES_WMP)
+
+def HasHOW ():
+ return GemRB.HasResource ("expmap", RES_WMP)
+
+def HasTOTL ():
+ return GemRB.HasResource ("ar9700", RES_ARE)
+
+def GetIWDSpellButtonCount ():
+ if HasHOW():
+ return 24
+ else:
+ return 20
+
+def SetGamedaysAndHourToken ():
+ currentTime = GemRB.GetGameTime()
+ days = currentTime / 7200
+ hours = (currentTime % 7200) / 300
+ GemRB.SetToken ('GAMEDAYS', str (days))
+ GemRB.SetToken ('HOUR', str (hours))
+
+# Returns -1 if not found; otherwise, the index of the spell
+def HasSpell (Actor, SpellType, Level, Ref):
+ # loop through each spell in the spell level and check for a matching ref
+ for i in range (GemRB.GetKnownSpellsCount (Actor, SpellType, Level)):
+ Spell = GemRB.GetKnownSpell(Actor, SpellType, Level, i)
+ if Spell["SpellResRef"].upper() == Ref.upper(): # ensure case is the same
+ return i
+
+ # not found
+ return -1
+
+# Adds class/kit abilities
+def AddClassAbilities (pc, table, Level=1, LevelDiff=1, align=-1):
+ TmpTable = GemRB.LoadTable (table)
+
+ # gotta stay positive
+ if Level-LevelDiff < 0:
+ return
+
+ # we're doing alignment additions
+ if align == -1:
+ iMin = 0
+ iMax = TmpTable.GetRowCount ()
+ else:
+ # alignment is expected to be the row required
+ iMin = align
+ iMax = align+1
+
+ # make sure we don't go out too far
+ jMin = Level-LevelDiff
+ jMax = Level
+ if jMax > TmpTable.GetColumnCount ():
+ jMax = TmpTable.GetColumnCount ()
+
+ for i in range(iMin, iMax):
+ # apply each spell from each new class
+ for j in range (jMin, jMax):
+ ab = TmpTable.GetValue (i, j, 0)
+ if ab and ab != "****":
+ # seems all SPINs act like GA_*
+ if ab[:4] == "SPIN":
+ ab = "GA_" + ab
+
+ # apply spell (AP_) or gain spell (GA_)
+ if ab[:2] == "AP":
+ GemRB.ApplySpell (pc, ab[3:])
+ elif ab[:2] == "GA":
+ SpellIndex = HasSpell (pc, IE_SPELL_TYPE_INNATE, 0, ab[3:])
+ if SpellIndex == -1:
+ GemRB.LearnSpell (pc, ab[3:], LS_MEMO)
+ else:
+ # make room for one more memorization
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_INNATE, 0, 0)
+ GemRB.SetMemorizableSpellsCount (pc, max_mem_cnt+1, IE_SPELL_TYPE_INNATE, 0)
+ # memorize another spell instance
+ GemRB.MemorizeSpell (pc, IE_SPELL_TYPE_INNATE, 0, SpellIndex)
+ else:
+ print "ERROR, unknown class ability (type): ", ab
+
+# remove all class abilities up to the given level
+# for dual-classing mainly
+def RemoveClassAbilities (pc, table, Level):
+ TmpTable = GemRB.LoadTable (table)
+
+ # gotta stay positive
+ if Level < 0:
+ return
+
+ # make sure we don't go out too far
+ jMax = Level
+ if jMax > TmpTable.GetColumnCount ():
+ jMax = TmpTable.GetColumnCount ()
+
+ for i in range(TmpTable.GetRowCount ()):
+ for j in range (jMax):
+ ab = TmpTable.GetValue (i, j, 0)
+ if ab and ab != "****":
+ # get the index
+ SpellIndex = HasSpell (pc, IE_SPELL_TYPE_INNATE, 0, ab[3:])
+
+ # seems all SPINs act like GA_*
+ if ab[:4] == "SPIN":
+ ab = "GA_" + ab
+
+ # apply spell (AP_) or gain spell (GA_)?
+ if ab[:2] == "AP":
+ GemRB.RemoveEffects (pc, ab[3:])
+ elif ab[:2] == "GA":
+ if SpellIndex >= 0:
+ # TODO: get the correct counts to avoid removing an innate ability
+ # given by more than one thing?
+ # RemoveSpell will unmemorize them all too
+ GemRB.RemoveSpell (pc, IE_SPELL_TYPE_INNATE, 0, SpellIndex)
+ else:
+ print "ERROR, unknown class ability (type): ", ab
+
+def CannotLearnSlotSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # disqualify sorcerors immediately
+ if GemRB.GetPlayerStat (pc, IE_CLASS) == 19:
+ return LSR_STAT
+
+ slot_item = GemRB.GetSlotItem (pc, GemRB.GetVar ("ItemButton"))
+ spell_ref = GemRB.GetItem (slot_item['ItemResRef'], pc)['Spell']
+ spell = GemRB.GetSpell (spell_ref)
+
+ # maybe she already knows this spell
+ if HasSpell (pc, IE_SPELL_TYPE_WIZARD, spell['SpellLevel']-1, spell_ref) != -1:
+ return LSR_KNOWN
+
+ # level check (needs enough intelligence for this level of spell)
+ dumbness = GemRB.GetPlayerStat (pc, IE_INT)
+ if spell['SpellLevel'] > GemRB.GetAbilityBonus (IE_INT, 1, dumbness):
+ return LSR_LEVEL
+
+ return 0
+
+def UpdateInventorySlot (pc, Button, Slot, Type, Equipped=False):
+ Button.SetFont ("NUMBER")
+ Button.SetBorder (0, 0,0,0,0, 128,128,255,64, 0,1)
+ Button.SetBorder (1, 2,2,2,2, 32,32,255,0, 0,0)
+ Button.SetBorder (2, 0,0,0,0, 255,128,128,64, 0,1)
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_TOP | IE_GUI_BUTTON_PICTURE, OP_OR)
+ Button.SetText ("")
+
+ if Slot == None:
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ if Type == "inventory":
+ Button.SetTooltip (12013) # Personal Item
+ elif Type == "ground":
+ Button.SetTooltip (12011) # Ground Item
+ else:
+ Button.SetTooltip ("")
+ Button.EnableBorder (0, 0)
+ Button.EnableBorder (1, 0)
+ Button.EnableBorder (2, 0)
+ else:
+ item = GemRB.GetItem (Slot['ItemResRef'])
+ identified = Slot["Flags"] & IE_INV_ITEM_IDENTIFIED
+ magical = Slot["Flags"] & IE_INV_ITEM_MAGICAL
+
+ # StackAmount holds the *maximum* item count in the stack while Usages0 holds the actual
+ if item["StackAmount"] > 1:
+ Button.SetText (str (Slot["Usages0"]))
+ else:
+ Button.SetText ("")
+
+ # auto-identify mundane items; the actual indentification will happen on transfer
+ if not identified and item["LoreToID"] == 0:
+ identified = True
+
+ if not identified or item["ItemNameIdentified"] == -1:
+ Button.SetTooltip (item["ItemName"])
+ Button.EnableBorder (0, 1)
+ Button.EnableBorder (1, 0)
+ else:
+ Button.SetTooltip (item["ItemNameIdentified"])
+ Button.EnableBorder (0, 0)
+ if magical:
+ Button.EnableBorder (1, 1)
+ else:
+ Button.EnableBorder (1, 0)
+
+ if GemRB.CanUseItemType (SLOT_ALL, Slot['ItemResRef'], pc, Equipped):
+ Button.EnableBorder (2, 0)
+ else:
+ Button.EnableBorder (2, 1)
+
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+
+ return
+
+def LearnPriestSpells (pc, level, mask):
+ """Learns all the priest spells through the given spell level.
+
+ Mask distinguishes clerical and druidic spells."""
+ if level > 7: # make sure we don't have too high a level
+ level = 7
+
+ # go through each level
+ alignment = GemRB.GetPlayerStat (pc, IE_ALIGNMENT)
+ for i in range (level):
+ learnable = GetLearnablePriestSpells (mask, alignment, i+1)
+
+ for spell in learnable:
+ # if the spell isn't learned, learn it
+ if HasSpell (pc, IE_SPELL_TYPE_PRIEST, i, spell) < 0:
+ GemRB.LearnSpell (pc, spell)
+ return
+
+# PST uses a button, IWD2 two types, the rest are the same with two labels
+def SetEncumbranceLabels (Window, ControlID, Control2ID, pc, invert_colors = False):
+ """Displays the encumbrance as a ratio of current to maximum."""
+
+ # Getting the character's strength
+ sstr = GemRB.GetPlayerStat (pc, IE_STR)
+ ext_str = GemRB.GetPlayerStat (pc, IE_STREXTRA)
+
+ # encumbrance
+ max_encumb = CommonTables.StrMod.GetValue (sstr, 3) + CommonTables.StrModEx.GetValue (ext_str, 3)
+ encumbrance = GemRB.GetPlayerStat (pc, IE_ENCUMBRANCE)
+
+ Control = Window.GetControl (ControlID)
+ if GameIsPST():
+ # FIXME: there should be a space before LB symbol (':')
+ Control.SetText (str (encumbrance) + ":\n\n\n\n" + str (max_encumb) + ":")
+ elif GameIsIWD2() and not Control2ID:
+ Control.SetText (str (encumbrance) + "/" + str(max_encumb) + GemRB.GetString(39537))
+ else:
+ Control.SetText (str (encumbrance) + ":")
+ if not Control2ID: # shouldn't happen
+ print "Missing second control parameter to SetEncumbranceLabels!"
+ return
+ Control2 = Window.GetControl (Control2ID)
+ Control2.SetText (str (max_encumb) + ":")
+
+ ratio = (0.0 + encumbrance) / max_encumb
+ if ratio > 1.0:
+ if invert_colors:
+ Control.SetTextColor (255, 0, 0, True)
+ else:
+ Control.SetTextColor (255, 0, 0)
+ elif ratio > 0.8:
+ if invert_colors:
+ Control.SetTextColor (255, 255, 0, True)
+ else:
+ Control.SetTextColor (255, 255, 0)
+ else:
+ if invert_colors:
+ Control.SetTextColor (255, 255, 255, True)
+ else:
+ Control.SetTextColor (255, 255, 255)
+
+ if Control2ID:
+ Control2.SetTextColor (255, 0, 0)
+
+ return
+
+def GetActorClassTitle (actor):
+ """Returns the string representation of the actors class."""
+
+ ClassTitle = GemRB.GetPlayerStat (actor, IE_TITLE1)
+
+ if ClassTitle == 0:
+ Class = GemRB.GetPlayerStat (actor, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue ( 5, Class )
+ KitIndex = GetKitIndex (actor)
+ Multi = CommonTables.Classes.GetValue (ClassIndex, 4)
+ Dual = IsDualClassed (actor, 1)
+
+ if Multi and Dual[0] == 0: # true multi class
+ ClassTitle = CommonTables.Classes.GetValue (ClassIndex, 2)
+ ClassTitle = GemRB.GetString (ClassTitle)
+ else:
+ if Dual[0]: # dual class
+ # first (previous) kit or class of the dual class
+ if Dual[0] == 1:
+ ClassTitle = CommonTables.KitList.GetValue (Dual[1], 2)
+ elif Dual[0] == 2:
+ ClassTitle = CommonTables.Classes.GetValue (Dual[1], 2)
+ ClassTitle = GemRB.GetString (ClassTitle) + " / "
+ ClassTitle += GemRB.GetString (CommonTables.Classes.GetValue (Dual[2], 2))
+ else: # ordinary class or kit
+ if KitIndex:
+ ClassTitle = CommonTables.KitList.GetValue (KitIndex, 2)
+ else:
+ ClassTitle = CommonTables.Classes.GetValue (ClassIndex, 2)
+ ClassTitle = GemRB.GetString (ClassTitle)
+ else:
+ ClassTitle = GemRB.GetString (ClassTitle)
+
+ #GetActorClassTitle returns string now...
+ #if ClassTitle == "*":
+ # return 0
+
+ return ClassTitle
+
+
+def GetKitIndex (actor):
+ """Return the index of the actors kit from KITLIST.2da.
+
+ Returns 0 if the class is not kitted."""
+
+ Class = GemRB.GetPlayerStat (actor, IE_CLASS)
+ Kit = GemRB.GetPlayerStat (actor, IE_KIT)
+ KitIndex = 0
+
+ if Kit & 0xc000 == 0x4000:
+ KitIndex = Kit & 0xfff
+
+ # carefully looking for kit by the usability flag
+ # since the barbarian kit id clashes with the no-kit value
+ if KitIndex == 0 and Kit != 0x4000:
+ KitIndex = CommonTables.KitList.FindValue (6, Kit)
+ if KitIndex == -1:
+ KitIndex = 0
+
+ return KitIndex
+
+def IsDualClassed(actor, verbose):
+ """Returns an array containing the dual class information.
+
+ Return[0] is 0 if not dualclassed, 1 if the old class is a kit, 2 otherwise.
+ Return[1] contains either the kit or class index of the old class.
+ Return[2] contains the class index of the new class.
+ If verbose is false, only Return[0] contains useable data."""
+
+ if GameIsIWD2():
+ return (0,-1,-1)
+
+ DualedFrom = GemRB.GetPlayerStat (actor, IE_MC_FLAGS) & MC_WAS_ANY_CLASS
+
+ if verbose:
+ Class = GemRB.GetPlayerStat (actor, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ Multi = CommonTables.Classes.GetValue (ClassIndex, 4)
+ DualInfo = []
+ KitIndex = GetKitIndex (actor)
+
+ if DualedFrom > 0: # first (previous) class of the dual class
+ MCColumn = CommonTables.Classes.GetColumnIndex ("MC_WAS_ID")
+ FirstClassIndex = CommonTables.Classes.FindValue (MCColumn, DualedFrom)
+ if KitIndex:
+ DualInfo.append (1)
+ DualInfo.append (KitIndex)
+ else:
+ DualInfo.append (2)
+ DualInfo.append (FirstClassIndex)
+
+ # use the first class of the multiclass bunch that isn't the same as the first class
+ Mask = 1
+ for i in range (1,16):
+ if Multi & Mask:
+ ClassIndex = CommonTables.Classes.FindValue (5, i)
+ if ClassIndex == FirstClassIndex:
+ Mask = 1 << i
+ continue
+ DualInfo.append (ClassIndex)
+ break
+ Mask = 1 << i
+ if len(DualInfo) != 3:
+ print "WARNING: Invalid dualclass combination, treating as a single class!"
+ print DualedFrom, Class, Multi, KitIndex, DualInfo
+ return (0,-1,-1)
+
+ return DualInfo
+ else:
+ return (0,-1,-1)
+ else:
+ if DualedFrom > 0:
+ return (1,-1,-1)
+ else:
+ return (0,-1,-1)
+
+def IsDualSwap (actor):
+ """Returns true if the dualed classes are reverse of expection.
+
+ This can happen, because the engine gives dualclass characters the same ID as
+ their multiclass counterpart (eg. FIGHTER_MAGE = 3). Logic would dictate that
+ the new and old class levels would be stored in IE_LEVEL and IE_LEVEL2,
+ respectively; however, if one duals from a fighter to a mage in the above
+ example, the levels would actually be in reverse of expectation."""
+
+ Dual = IsDualClassed (actor, 1)
+
+ # not dual classed
+ if Dual[0] == 0:
+ return 0
+
+ # split the full class name into its individual parts
+ # i.e FIGHTER_MAGE becomes [FIGHTER, MAGE]
+ Class = GemRB.GetPlayerStat (actor, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ Class = CommonTables.Classes.GetRowName (Class)
+ Class = Class.split("_")
+
+ # get our old class name
+ if Dual[0] == 2:
+ BaseClass = CommonTables.Classes.GetRowName (Dual[1])
+ else:
+ BaseClass = GetKitIndex (actor)
+ BaseClass = CommonTables.KitList.GetValue (BaseClass, 7)
+ BaseClass = CommonTables.Classes.FindValue (5, BaseClass)
+ BaseClass = CommonTables.Classes.GetRowName (BaseClass)
+
+ # if our old class is the first class, we need to swap
+ if Class[0] == BaseClass:
+ return 1
+
+ return 0
+
+def IsMultiClassed (actor, verbose):
+ """Returns a tuple containing the multiclass information.
+
+ Return[0] contains the total number of classes.
+ Return[1-3] contain the ID of their respective classes.
+ If verbose is false, only Return[0] has useable data."""
+
+ # change this if it will ever be needed
+ if GameIsIWD2():
+ return (0,-1,-1,-1)
+
+ # get our base class
+ ClassIndex = CommonTables.Classes.FindValue (5, GemRB.GetPlayerStat (actor, IE_CLASS))
+ IsMulti = CommonTables.Classes.GetValue (ClassIndex, 4) # 0 if not multi'd
+ IsDual = IsDualClassed (actor, 0)
+
+ # dual-class char's look like multi-class chars
+ if (IsMulti == 0) or (IsDual[0] > 0):
+ return (0,-1,-1,-1)
+ elif verbose == 0:
+ return (IsMulti,-1,-1,-1)
+
+ # get all our classes (leave space for our number of classes in the return array)
+ Classes = [0]*3
+ NumClasses = 0
+ Mask = 1 # we're looking at multiples of 2
+ ClassNames = CommonTables.Classes.GetRowName(ClassIndex).split("_")
+
+ # loop through each class and test it as a mask
+ # TODO: make 16 dynamic? -- allows for custom classes (not just kits)
+ for i in range (1, 16):
+ if IsMulti&Mask: # it's part of this class
+ #we need to place the classes in the array based on their order in the name,
+ #NOT the order they are detected in
+ CurrentName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, i));
+ for j in range(len(ClassNames)):
+ if ClassNames[j] == CurrentName:
+ Classes[j] = i # mask is (i-1)^2 where i is class id
+ NumClasses = NumClasses+1
+ Mask = 1 << i # shift to the next multiple of 2 for testing
+
+ # in case we couldn't figure out to which classes the multi belonged
+ if NumClasses < 2:
+ return (0,-1,-1,-1)
+
+ # return the tuple
+ return (NumClasses, Classes[0], Classes[1], Classes[2])
+
+def RemoveKnownSpells (pc, type, level1=1, level2=1, noslots=0, kit=0):
+ """Removes all known spells of a given type between two spell levels.
+
+ If noslots is true, all memorization counts are set to 0.
+ Kit is used to identify the priest spell mask of the spells to be removed;
+ this is only used when removing spells in a dualclass."""
+
+ # choose the correct limit based upon class type
+ if type == IE_SPELL_TYPE_WIZARD:
+ limit = 9
+ elif type == IE_SPELL_TYPE_PRIEST:
+ limit = 7
+
+ # make sure that we get the original kit, if we have one
+ if kit:
+ originalkit = GetKitIndex (pc)
+
+ if originalkit: # kitted; find the class value
+ originalkit = CommonTables.KitList.GetValue (originalkit, 7)
+ else: # just get the class value
+ originalkit = GemRB.GetPlayerStat (pc, IE_CLASS)
+
+ # this is is specifically for dual-classes and will not work to remove only one
+ # spell type from a ranger/cleric multi-class
+ if CommonTables.ClassSkills.GetValue (originalkit, 0, 0) != "*": # knows druid spells
+ originalkit = 0x8000
+ elif CommonTables.ClassSkills.GetValue (originalkit, 1, 0) != "*": # knows cleric spells
+ originalkit = 0x4000
+ else: # don't know any other spells
+ originalkit = 0
+
+ # don't know how this would happen, but better to be safe
+ if originalkit == kit:
+ originalkit = 0
+ elif type == IE_SPELL_TYPE_INNATE:
+ limit = 1
+ else: # can't do anything if an improper spell type is sent
+ return 0
+
+ # make sure we're within parameters
+ if level1 < 1 or level2 > limit or level1 > level2:
+ return 0
+
+ # remove all spells for each level
+ for level in range (level1-1, level2):
+ # we need the count because we remove each spell in reverse order
+ count = GemRB.GetKnownSpellsCount (pc, type, level)
+ mod = count-1
+
+ for spell in range (count):
+ # see if we need to check for kit
+ if type == IE_SPELL_TYPE_PRIEST and kit:
+ # get the spell's ref data
+ ref = GemRB.GetKnownSpell (pc, type, level, mod-spell)
+ ref = GemRB.GetSpell (ref['SpellResRef'], 1)
+
+ # we have to look at the originalkit as well specifically for ranger/cleric dual-classes
+ # we wouldn't want to remove all cleric spells and druid spells if we lost our cleric class
+ # only the cleric ones
+ if kit&ref['SpellDivine'] or (originalkit and not originalkit&ref['SpellDivine']):
+ continue
+
+ # remove the spell
+ GemRB.RemoveSpell (pc, type, level, mod-spell)
+
+ # remove memorization counts if desired
+ if noslots:
+ GemRB.SetMemorizableSpellsCount (pc, 0, type, level)
+
+ # return success
+ return 1
+
+def CanDualClass(actor):
+ # human
+ if GemRB.GetPlayerStat (actor, IE_RACE) != 1:
+ return 1
+
+ # already dualclassed
+ Dual = IsDualClassed (actor,0)
+ if Dual[0] > 0:
+ return 1
+
+ DualClassTable = GemRB.LoadTable ("dualclas")
+ CurrentStatTable = GemRB.LoadTable ("abdcscrq")
+ Class = GemRB.GetPlayerStat (actor, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ ClassName = CommonTables.Classes.GetRowName (ClassIndex)
+ KitIndex = GetKitIndex (actor)
+ if KitIndex == 0:
+ ClassTitle = ClassName
+ else:
+ ClassTitle = CommonTables.KitList.GetValue (KitIndex, 0)
+ Row = DualClassTable.GetRowIndex (ClassTitle)
+
+ # a lookup table for the DualClassTable columns
+ classes = [ "FIGHTER", "CLERIC", "MAGE", "THIEF", "DRUID", "RANGER" ]
+ matches = []
+ Sum = 0
+ for col in range (0, DualClassTable.GetColumnCount ()):
+ value = DualClassTable.GetValue (Row, col)
+ Sum += value
+ if value == 1:
+ matches.append (classes[col])
+
+ # cannot dc if all the columns of the DualClassTable are 0
+ if Sum == 0:
+ print "CannotDualClass: all the columns of the DualClassTable are 0"
+ return 1
+
+ # if the only choice for dc is already the same as the actors base class
+ if Sum == 1 and ClassName in matches and KitIndex == 0:
+ print "CannotDualClass: the only choice for dc is already the same as the actors base class"
+ return 1
+
+ AlignmentTable = GemRB.LoadTable ("alignmnt")
+ AlignsTable = GemRB.LoadTable ("aligns")
+ Alignment = GemRB.GetPlayerStat (actor, IE_ALIGNMENT)
+ AlignmentColName = AlignsTable.FindValue (3, Alignment)
+ AlignmentColName = AlignsTable.GetValue (AlignmentColName, 4)
+ Sum = 0
+ for classy in matches:
+ Sum += AlignmentTable.GetValue (classy, AlignmentColName)
+
+ # cannot dc if all the available classes forbid the chars alignment
+ if Sum == 0:
+ print "CannotDualClass: all the available classes forbid the chars alignment"
+ return 1
+
+ # check current class' stat limitations
+ ClassStatIndex = CurrentStatTable.GetRowIndex (ClassTitle)
+ for stat in range (6):
+ minimum = CurrentStatTable.GetValue (ClassStatIndex, stat)
+ name = CurrentStatTable.GetColumnName (stat)
+ if GemRB.GetPlayerStat (actor, eval ("IE_" + name[4:])) < minimum:
+ print "CannotDualClass: current class' stat limitations are too big"
+ return 1
+
+ # check new class' stat limitations - make sure there are any good class choices
+ TargetStatTable = GemRB.LoadTable ("abdcdsrq")
+ for match in matches:
+ ClassStatIndex = TargetStatTable.GetRowIndex (match)
+ for stat in range (6):
+ minimum = TargetStatTable.GetValue (ClassStatIndex, stat)
+ name = TargetStatTable.GetColumnName (stat)
+ if GemRB.GetPlayerStat (actor, eval ("IE_" + name[4:])) < minimum:
+ matches.remove (match)
+ break
+ if len(matches) == 0:
+ print "CannotDualClass: no good new class choices"
+ return 1
+
+ # must be at least level 2
+ if GemRB.GetPlayerStat (actor, IE_LEVEL) == 1:
+ print "CannotDualClass: level 1"
+ return 1
+ return 0
+
+def SetupDamageInfo (pc, Button):
+ hp = GemRB.GetPlayerStat (pc, IE_HITPOINTS)
+ hp_max = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)
+ state = GemRB.GetPlayerStat (pc, IE_STATE_ID)
+
+ if hp_max < 1:
+ ratio = 0.0
+ else:
+ ratio = (hp+0.0) / hp_max
+
+ if hp < 1 or (state & STATE_DEAD):
+ Button.SetOverlay (0, 64,64,64,200, 64,64,64,200)
+ else:
+ Button.SetOverlay (ratio, 140,0,0,205, 128,0,0,200)
+ ratio_str = "\n%d/%d" %(hp, hp_max)
+ Button.SetTooltip (GemRB.GetPlayerName (pc, 1) + ratio_str)
+
+ return ratio_str
+
+# return ceil(n/d)
+#
+def ceildiv (n, d):
+ if d == 0:
+ raise ZeroDivisionError("ceildiv by zero")
+ elif d < 0:
+ return (n+d+1)/d
+ else:
+ return (n+d-1)/d
+
+GameWindow = GUIClasses.GWindow(0)
+GameControl = GUIClasses.GControl(0,0)
diff --git a/gemrb/GUIScripts/GUIDefines.py b/gemrb/GUIScripts/GUIDefines.py
new file mode 100644
index 0000000..2612e2f
--- /dev/null
+++ b/gemrb/GUIScripts/GUIDefines.py
@@ -0,0 +1,292 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIDefines.py - common definitions of GUI-related constants for GUIScripts
+
+#button flags
+IE_GUI_BUTTON_NORMAL = 0x00000004 #default button, doesn't stick
+IE_GUI_BUTTON_NO_IMAGE = 0x00000001
+IE_GUI_BUTTON_PICTURE = 0x00000002
+IE_GUI_BUTTON_SOUND = 0x00000004
+IE_GUI_BUTTON_ALT_SOUND = 0x00000008
+IE_GUI_BUTTON_CHECKBOX = 0x00000010 #or radio button
+IE_GUI_BUTTON_RADIOBUTTON= 0x00000020 #sticks in a state
+IE_GUI_BUTTON_DEFAULT = 0x00000040 #enter key triggers it
+IE_GUI_BUTTON_ANIMATED = 0x00000080 # the button is animated
+
+#these bits are hardcoded in the .chu structure, don't move them
+IE_GUI_BUTTON_ALIGN_LEFT = 0x00000100
+IE_GUI_BUTTON_ALIGN_RIGHT= 0x00000200
+IE_GUI_BUTTON_ALIGN_TOP = 0x00000400
+IE_GUI_BUTTON_ALIGN_BOTTOM = 0x00000800
+IE_GUI_BUTTON_ALIGN_ANCHOR = 0x00001000
+IE_GUI_BUTTON_LOWERCASE = 0x00002000
+IE_GUI_BUTTON_MULTILINE = 0x00004000 # don't set the SINGLE_LINE font rendering flag
+#end of hardcoded section
+
+IE_GUI_BUTTON_DRAGGABLE = 0x00008000
+IE_GUI_BUTTON_NO_TEXT = 0x00010000 # don't draw button label
+IE_GUI_BUTTON_PLAYRANDOM = 0x00020000 # the button animation is random
+IE_GUI_BUTTON_PLAYONCE = 0x00040000 # the button animation won't restart
+
+IE_GUI_BUTTON_CENTER_PICTURES = 0x00080000 # center the button's PictureList
+IE_GUI_BUTTON_BG1_PAPERDOLL = 0x00100000 # BG1-style paperdoll
+IE_GUI_BUTTON_HORIZONTAL = 0x00200000 # horizontal clipping of overlay
+IE_GUI_BUTTON_CANCEL = 0x00400000 # escape key triggers it
+IE_GUI_BUTTON_CAPS = 0x00800000 # capitalize all the text (default for bg2)
+
+#scrollbar flags
+IE_GUI_SCROLLBAR_DEFAULT = 0x00000040 # mousewheel triggers it (same value as default button)
+
+#textarea flags
+IE_GUI_TEXTAREA_SELECTABLE = 0x05000001
+IE_GUI_TEXTAREA_AUTOSCROLL = 0x05000002
+IE_GUI_TEXTAREA_SMOOTHSCROLL = 0x05000004
+IE_GUI_TEXTAREA_HISTORY = 0x05000008
+IE_GUI_TEXTAREA_SPEAKER = 0x05000010
+IE_GUI_TEXTAREA_EDITABLE = 0x05000040
+
+#gui control types
+IE_GUI_BUTTON = 0
+IE_GUI_PROGRESS = 1
+IE_GUI_SLIDER = 2
+IE_GUI_EDIT = 3
+IE_GUI_TEXTAREA = 5
+IE_GUI_LABEL = 6
+IE_GUI_SCROLLBAR = 7
+IE_GUI_WORLDMAP = 8
+IE_GUI_MAP = 9
+
+#events
+IE_GUI_BUTTON_ON_PRESS = 0x00000000
+IE_GUI_MOUSE_OVER_BUTTON = 0x00000001
+IE_GUI_MOUSE_ENTER_BUTTON = 0x00000002
+IE_GUI_MOUSE_LEAVE_BUTTON = 0x00000003
+IE_GUI_BUTTON_ON_SHIFT_PRESS= 0x00000004
+IE_GUI_BUTTON_ON_RIGHT_PRESS= 0x00000005
+IE_GUI_BUTTON_ON_DRAG_DROP = 0x00000006
+IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT = 0x00000007
+IE_GUI_BUTTON_ON_DRAG = 0x00000008
+IE_GUI_BUTTON_ON_DOUBLE_PRESS = 0x00000009
+IE_GUI_PROGRESS_END_REACHED = 0x01000000
+IE_GUI_SLIDER_ON_CHANGE = 0x02000000
+IE_GUI_EDIT_ON_CHANGE = 0x03000000
+IE_GUI_EDIT_ON_DONE = 0x03000001
+IE_GUI_EDIT_ON_CANCEL = 0x03000002
+IE_GUI_TEXTAREA_ON_CHANGE = 0x05000000
+IE_GUI_TEXTAREA_OUT_OF_TEXT = 0x05000001
+IE_GUI_LABEL_ON_PRESS = 0x06000000
+IE_GUI_SCROLLBAR_ON_CHANGE = 0x07000000
+IE_GUI_WORLDMAP_ON_PRESS = 0x08000000
+IE_GUI_MOUSE_ENTER_WORLDMAP = 0x08000002
+IE_GUI_MAP_ON_PRESS = 0x09000000
+IE_GUI_MAP_ON_RIGHT_PRESS = 0x09000005
+IE_GUI_MAP_ON_DOUBLE_PRESS = 0x09000008
+
+#common states
+IE_GUI_CONTROL_FOCUSED = 0x7f000080
+
+#button states
+IE_GUI_BUTTON_ENABLED = 0x00000000
+IE_GUI_BUTTON_UNPRESSED = 0x00000000
+IE_GUI_BUTTON_PRESSED = 0x00000001
+IE_GUI_BUTTON_SELECTED = 0x00000002
+IE_GUI_BUTTON_DISABLED = 0x00000003
+# Like DISABLED, but processes MouseOver events and draws UNPRESSED bitmap
+IE_GUI_BUTTON_LOCKED = 0x00000004
+# Draws DISABLED bitmap, but it isn't disabled
+IE_GUI_BUTTON_THIRD = 0x00000005
+# Draws PRESSED bitmap, but it isn't shifted
+IE_GUI_BUTTON_SECOND = 0x00000006
+
+#edit field states
+IE_GUI_EDIT_NUMBER = 0x030000001
+
+#mapcontrol states (add 0x090000000 if used with SetControlStatus)
+IE_GUI_MAP_NO_NOTES = 0
+IE_GUI_MAP_VIEW_NOTES = 1
+IE_GUI_MAP_SET_NOTE = 2
+IE_GUI_MAP_REVEAL_MAP = 3
+
+# !!! Keep these synchronized with WorldMapControl.h !!!
+# WorldMap label colors
+IE_GUI_WMAP_COLOR_BACKGROUND = 0
+IE_GUI_WMAP_COLOR_NORMAL = 1
+IE_GUI_WMAP_COLOR_SELECTED = 2
+IE_GUI_WMAP_COLOR_NOTVISITED = 3
+
+# !!! Keep these synchronized with Font.h !!!
+IE_FONT_ALIGN_LEFT = 0x00
+IE_FONT_ALIGN_CENTER = 0x01
+IE_FONT_ALIGN_RIGHT = 0x02
+IE_FONT_ALIGN_BOTTOM = 0x04
+IE_FONT_ALIGN_TOP = 0x10 # Single-Line and Multi-Line Text
+IE_FONT_ALIGN_MIDDLE = 0x20 #Only for single line Text
+IE_FONT_SINGLE_LINE = 0x40
+
+OP_SET = 0
+OP_AND = 1
+OP_OR = 2
+OP_XOR = 3
+OP_NAND = 4
+
+# Window position anchors/alignments
+# !!! Keep these synchronized with Window.h !!!
+WINDOW_TOPLEFT = 0x00
+WINDOW_CENTER = 0x01
+WINDOW_ABSCENTER = 0x02
+WINDOW_RELATIVE = 0x04
+WINDOW_SCALE = 0x08
+WINDOW_BOUNDED = 0x10
+
+# GameScreen flags
+GS_PARTYAI = 1
+GS_SMALLDIALOG = 0
+GS_MEDIUMDIALOG = 2
+GS_LARGEDIALOG = 6
+GS_DIALOGMASK = 6
+GS_DIALOG = 8
+GS_HIDEGUI = 16
+GS_OPTIONPANE = 32
+GS_PORTRAITPANE = 64
+GS_MAPNOTE = 128
+
+# GameControl screen flags
+# !!! Keep these synchronized with GameControl.h !!!
+SF_DISABLEMOUSE = 1
+SF_CENTERONACTOR = 2
+SF_ALWAYSCENTER = 4
+SF_GUIENABLED = 8
+SF_LOCKSCROLL = 16
+
+# GameControltarget modes
+# !!! Keep these synchronized with GameControl.h !!!
+TARGET_MODE_NONE = 0
+TARGET_MODE_TALK = 1
+TARGET_MODE_ATTACK = 2
+TARGET_MODE_CAST = 3
+TARGET_MODE_DEFEND = 4
+TARGET_MODE_PICK = 5
+
+GA_SELECT = 16
+GA_NO_DEAD = 32
+GA_POINT = 64
+GA_NO_HIDDEN = 128
+GA_NO_ALLY = 256
+GA_NO_ENEMY = 512
+GA_NO_NEUTRAL = 1024
+GA_NO_SELF = 2048
+
+# Shadow color for ShowModal()
+# !!! Keep these synchronized with Interface.h !!!
+MODAL_SHADOW_NONE = 0
+MODAL_SHADOW_GRAY = 1
+MODAL_SHADOW_BLACK = 2
+
+# Flags for SetVisible()
+# !!! Keep these synchronized with Interface.h !!!
+#WINDOW_INVALID = -1
+WINDOW_INVISIBLE = 0
+WINDOW_VISIBLE = 1
+WINDOW_GRAYED = 2
+WINDOW_FRONT = 3
+
+# Flags for GameSelectPC()
+# !!! Keep these synchronized with Game.h !!!
+SELECT_NORMAL = 0x00
+SELECT_REPLACE = 0x01
+SELECT_QUIET = 0x02
+
+# Spell types
+# !!! Keep these synchronized with Spellbook.h !!!
+IE_SPELL_TYPE_PRIEST = 0
+IE_SPELL_TYPE_WIZARD = 1
+IE_SPELL_TYPE_INNATE = 2
+
+# Item Flags bits
+# !!! Keep these synchronized with Item.h !!!
+IE_ITEM_CRITICAL = 0x00000001
+IE_ITEM_TWO_HANDED = 0x00000002
+IE_ITEM_MOVABLE = 0x00000004
+IE_ITEM_DISPLAYABLE = 0x00000008
+IE_ITEM_CURSED = 0x00000010
+IE_ITEM_NOT_COPYABLE = 0x00000020
+IE_ITEM_MAGICAL = 0x00000040
+IE_ITEM_BOW = 0x00000080
+IE_ITEM_SILVER = 0x00000100
+IE_ITEM_COLD_IRON = 0x00000200
+IE_ITEM_STOLEN = 0x00000400
+IE_ITEM_CONVERSABLE = 0x00000800
+IE_ITEM_PULSATING = 0x00001000
+IE_ITEM_UNSELLABLE = (IE_ITEM_CRITICAL | IE_ITEM_STOLEN)
+
+# CREItem (SlotItem) Flags bits
+# !!! Keep these synchronized with Inventory.h !!!
+IE_INV_ITEM_IDENTIFIED = 0x01
+IE_INV_ITEM_UNSTEALABLE = 0x02
+IE_INV_ITEM_STOLEN = 0x04
+IE_INV_ITEM_UNDROPPABLE = 0x08
+# GemRB extensions
+IE_INV_ITEM_ACQUIRED = 0x10
+IE_INV_ITEM_DESTRUCTIBLE = 0x20
+IE_INV_ITEM_EQUIPPED = 0x40
+IE_INV_ITEM_STACKED = 0x80
+# these come from the original item bits
+IE_INV_ITEM_CRITICAL = 0x100
+IE_INV_ITEM_TWOHANDED = 0x200
+IE_INV_ITEM_MOVABLE = 0x400
+IE_INV_ITEM_UNKNOWN800 = 0x800
+IE_INV_ITEM_CURSED = 0x1000
+IE_INV_ITEM_UNKNOWN2000 = 0x2000
+IE_INV_ITEM_MAGICAL = 0x4000
+IE_INV_ITEM_BOW = 0x8000
+IE_INV_ITEM_SILVER = 0x10000
+IE_INV_ITEM_COLDIRON = 0x20000
+IE_INV_ITEM_STOLEN2 = 0x40000
+IE_INV_ITEM_CONVERSIBLE = 0x80000
+IE_INV_ITEM_PULSATING = 0x100000
+
+#repeat key flags
+GEM_RK_DOUBLESPEED = 1
+GEM_RK_DISABLE = 2
+GEM_RK_QUADRUPLESPEED = 4
+
+SHOP_BUY = 1
+SHOP_SELL = 2
+SHOP_ID = 4
+SHOP_STEAL = 8
+SHOP_SELECT = 0x40
+
+#game constants
+PARTY_SIZE = 6
+
+# !!! Keep this synchronized with Video.h !!!
+TOOLTIP_DELAY_FACTOR = 250
+
+#game strings
+STR_LOADMOS = 0
+STR_AREANAME = 1
+
+#game integers
+SV_BPP = 0
+SV_WIDTH = 1
+SV_HEIGHT = 2
+global GEMRB_VERSION
+GEMRB_VERSION = -1
diff --git a/gemrb/GUIScripts/GUISTORE.py b/gemrb/GUIScripts/GUISTORE.py
new file mode 100644
index 0000000..c0d9eb7
--- /dev/null
+++ b/gemrb/GUIScripts/GUISTORE.py
@@ -0,0 +1,1526 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUISTORE.py - script to open the store/inn/temple windows
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+from ie_slots import *
+
+StoreWindow = None
+MessageWindow = None
+ActionWindow = None
+PortraitWindow = None
+StoreShoppingWindow = None
+StoreIdentifyWindow = None
+StoreStealWindow = None
+StoreDonateWindow = None
+StoreHealWindow = None
+StoreRumourWindow = None
+StoreRentWindow = None
+OldPortraitWindow = None
+RentConfirmWindow = None
+LeftButton = None
+RightButton = None
+
+ITEM_PC = 0
+ITEM_STORE = 1
+
+Inventory = None
+RentIndex = -1
+Store = None
+Buttons = [-1,-1,-1,-1]
+inventory_slots = ()
+total_price = 0
+total_income = 0
+if GUICommon.GameIsIWD2():
+ ItemButtonCount = 6
+else:
+ ItemButtonCount = 4
+RepModTable = None
+PreviousPC = 0
+BarteringPC = 0
+
+# 0 - Store
+# 1 - Tavern
+# 2 - Inn
+# 3 - Temple
+# 4 - Container
+# 5 - Container
+
+# 0 - buy/sell
+# 1 - identify
+# 2 - steal
+# 3 - heal
+# 4 - donate
+# 5 - drink
+# 6 - rent
+
+if GUICommon.GameIsIWD1():
+ # no bam for bags
+ storebams = ("STORSTOR","STORTVRN","STORINN","STORTMPL","STORSTOR","STORSTOR")
+else:
+ storebams = ("STORSTOR","STORTVRN","STORINN","STORTMPL","STORBAG","STORBAG")
+storetips = (14288,14292,14291,12138,15013,14289,14287)
+roomtypes = (17389,17517,17521,17519)
+store_funcs = None
+
+def CloseWindows ():
+ global StoreShoppingWindow, StoreIdentifyWindow, StoreStealWindow
+ global StoreHealWindow, StoreDonateWindow, StoreRumourWindow, StoreRentWindow
+
+ for win in StoreShoppingWindow, StoreIdentifyWindow, StoreStealWindow, StoreHealWindow, StoreDonateWindow, StoreRumourWindow, StoreRentWindow:
+ if win:
+ win.Unload ()
+
+ StoreShoppingWindow = StoreIdentifyWindow = StoreStealWindow = StoreHealWindow = StoreDonateWindow = StoreRumourWindow = StoreRentWindow = None
+ return
+
+def CloseStoreWindow ():
+ import GUIINV
+ global StoreWindow, ActionWindow, PortraitWindow
+ global OldPortraitWindow
+
+ GemRB.SetVar ("Inventory", 0)
+ CloseWindows ()
+ if StoreWindow:
+ StoreWindow.Unload ()
+ if ActionWindow:
+ ActionWindow.Unload ()
+ if not GUICommon.GameIsBG1():
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ StoreWindow = None
+ GemRB.LeaveStore ()
+ if not GUICommon.GameIsBG1():
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ if Inventory:
+ GUIINV.OpenInventoryWindow ()
+ else:
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE) #enabling the game control screen
+ GemRB.UnhideGUI () #enabling the other windows
+ GUICommonWindows.SetSelectionChangeHandler( None )
+ return
+
+def OpenStoreWindow ():
+ global Store
+ global StoreWindow, ActionWindow, PortraitWindow
+ global OldPortraitWindow
+ global store_funcs
+ global Inventory, RepModTable, BarteringPC
+
+ #these are function pointers, not strings
+ #can't put this in global init, doh!
+ store_funcs = (OpenStoreShoppingWindow,
+ OpenStoreIdentifyWindow,OpenStoreStealWindow,
+ OpenStoreHealWindow, OpenStoreDonateWindow,
+ OpenStoreRumourWindow,OpenStoreRentWindow )
+
+ RepModTable = GemRB.LoadTable ("repmodst")
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE) #removing the game control screen
+
+ if GemRB.GetVar ("Inventory"):
+ Inventory = 1
+ else:
+ Inventory = None
+
+ GemRB.SetVar ("Action", 0)
+ if GUICommon.GameIsIWD2():
+ GemRB.LoadWindowPack ("GUISTORE", 800, 600)
+ else:
+ GemRB.LoadWindowPack ("GUISTORE", 640, 480)
+ StoreWindow = Window = GemRB.LoadWindow (3)
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ if GUICommon.GameIsIWD2() or GUICommon.GameIsBG1():
+ #PortraitWindow = GUICommonWindows.OpenPortraitWindow ()
+ pass
+ else:
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ ActionWindow = GemRB.LoadWindow (0)
+ #this window is static and grey, but good to stick the frame onto
+ ActionWindow.SetFrame ()
+
+ Store = GemRB.GetStore ()
+ BarteringPC = GemRB.GameGetFirstSelectedPC ()
+
+ # Done
+ Button = Window.GetControl (0)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseStoreWindow)
+
+ #Store type icon
+ if not GUICommon.GameIsIWD2():
+ Button = Window.GetControl (5)
+ Button.SetSprites (storebams[Store['StoreType']],0,0,0,0,0)
+
+ #based on shop type, these buttons will change
+ store_type = Store['StoreType']
+ store_buttons = Store['StoreButtons']
+ for i in range (4):
+ Buttons[i] = Button = Window.GetControl (i+1)
+ Action = store_buttons[i]
+ Button.SetVarAssoc ("Action", i)
+ if Action>=0:
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ if GUICommon.GameIsIWD1() or GUICommon.GameIsIWD2():
+ Button.SetSprites ("GUISTBBC", Action, 1,2,0,0)
+ else:
+ Button.SetSprites ("GUISTBBC", Action, 0,1,2,0)
+ Button.SetTooltip (storetips[Action])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, store_funcs[Action])
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetTooltip ("")
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ActionWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ store_funcs[store_buttons[0]] ()
+ if not GUICommon.GameIsIWD2():
+ if GUICommon.GameIsBG1():
+ GUICommonWindows.PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ else:
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreShoppingWindow ():
+ global StoreShoppingWindow
+ global LeftButton, RightButton
+
+ CloseWindows()
+
+ StoreShoppingWindow = Window = GemRB.LoadWindow (2)
+
+ # left scrollbar
+ ScrollBarLeft = Window.GetControl (11)
+ ScrollBarLeft.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreShoppingWindow)
+
+ # right scrollbar
+ ScrollBarRight = Window.GetControl (12)
+ ScrollBarRight.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreShoppingWindow)
+
+ if Inventory:
+ # Title
+ Label = Window.GetControl (0xfffffff)
+ if GUICommon.GameIsIWD1() or GUICommon.GameIsIWD2():
+ Label.SetText (26291)
+ elif GUICommon.GameIsBG2():
+ Label.SetText (51881)
+ else:
+ Label.SetText ("")
+ # buy price ...
+ Label = Window.GetControl (0x1000002b)
+ Label.SetText ("")
+ # sell price ...
+ Label = Window.GetControl (0x1000002c)
+ Label.SetText ("")
+ # buy price ...
+ Label = Window.GetControl (0x1000002f)
+ Label.SetText ("")
+ # sell price ...
+ Label = Window.GetControl (0x10000030)
+ Label.SetText ("")
+ else:
+ # buy price ...
+ Label = Window.GetControl (0x1000002b)
+ Label.SetText ("0")
+
+ # sell price ...
+ Label = Window.GetControl (0x1000002c)
+ Label.SetText ("0")
+
+ for i in range (ItemButtonCount):
+ Button = Window.GetControl (i+5)
+ if GUICommon.GameIsBG2():
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ else:
+ Button.SetBorder (0,0,0,0,0,32,32,192,128,0,1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectBuy)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoLeftWindow)
+ Button.SetFont ("NUMBER")
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT|IE_GUI_BUTTON_ALIGN_TOP, OP_OR)
+ Button.AttachScrollBar (ScrollBarLeft)
+
+ Button = Window.GetControl (i+13)
+ if GUICommon.GameIsBG2():
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ Button.SetSprites ("GUIBTBUT", 0, 0,1,2,5)
+ else:
+ Button.SetBorder (0,0,0,0,0,32,32,192,128,0,1)
+ if Store['StoreType'] != 3: # can't sell to temples
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectSell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoRightWindow)
+ Button.SetFont ("NUMBER")
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT|IE_GUI_BUTTON_ALIGN_TOP, OP_OR)
+ Button.AttachScrollBar (ScrollBarRight)
+
+ # Buy
+ LeftButton = Button = Window.GetControl (2)
+ if Inventory:
+ if GUICommon.GameIsIWD2():
+ Button.SetText (26287)
+ elif GUICommon.GameIsIWD1():
+ Button.SetText (26288)
+ elif GUICommon.GameIsBG2():
+ Button.SetText (51882)
+ else:
+ Button.SetText ("")
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ToBackpackPressed)
+ else:
+ Button.SetText (13703)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, BuyPressed)
+
+ # Sell
+ RightButton = Button = Window.GetControl (3)
+ if Inventory:
+ if GUICommon.GameIsIWD1() or GUICommon.GameIsIWD2():
+ Button.SetText (26288)
+ elif GUICommon.GameIsBG2():
+ Button.SetText (51883)
+ else:
+ Button.SetText ("")
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ToBagPressed)
+ else:
+ Button.SetText (13704)
+ if Store['StoreType'] != 3: # can't sell to temples
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SellPressed)
+
+ # inactive button
+ if GUICommon.GameIsBG2():
+ Button = Window.GetControl (50)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+
+ #backpack
+ Button = Window.GetControl (44)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # encumbrance
+ Label = Window.CreateLabel (0x10000043, 15,325,60,15,"NUMBER","0:",IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_TOP)
+ Label = Window.CreateLabel (0x10000044, 15,365,80,15,"NUMBER","0:",IE_FONT_ALIGN_RIGHT|IE_FONT_ALIGN_TOP)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreShoppingWindow )
+ UpdateStoreShoppingWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreIdentifyWindow ():
+ global StoreIdentifyWindow
+ global LeftButton
+
+ GemRB.SetVar ("Index", -1)
+ GemRB.SetVar ("TopIndex", 0)
+ CloseWindows()
+
+ StoreIdentifyWindow = Window = GemRB.LoadWindow (4)
+
+ ScrollBar = Window.GetControl (7)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreIdentifyWindow)
+
+ TextArea = Window.GetControl (23)
+ TextArea.SetFlags (IE_GUI_TEXTAREA_AUTOSCROLL)
+
+ # Identify
+ LeftButton = Button = Window.GetControl (5)
+ Button.SetText (14133)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, IdentifyPressed)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoIdentifyWindow)
+
+ # price ...
+ Label = Window.GetControl (0x10000003)
+ Label.SetText ("0")
+
+ # 8-11 item slots, 0x1000000c-f labels
+ for i in range (ItemButtonCount):
+ Button = Window.GetControl (i+8)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ if GUICommon.GameIsIWD1() or GUICommon.GameIsIWD2():
+ Button.SetSprites ("GUISTMSC", 0, 1,2,0,3)
+ Button.SetBorder (0,0,0,0,0,32,32,192,128,0,1)
+ elif GUICommon.GameIsBG1():
+ Button.SetBorder (0,0,0,0,0,32,32,192,128,0,1)
+ else:
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectID)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoIdentifyWindow)
+ Button.SetFont ("NUMBER")
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT|IE_GUI_BUTTON_ALIGN_TOP, OP_OR)
+ Button.AttachScrollBar (ScrollBar)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreIdentifyWindow )
+ UpdateStoreIdentifyWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreStealWindow ():
+ global StoreStealWindow
+ global LeftButton
+
+ GemRB.SetVar ("RightIndex", 0)
+ GemRB.SetVar ("LeftIndex", 0)
+ GemRB.SetVar ("RightTopIndex", 0)
+ GemRB.SetVar ("LeftTopIndex", 0)
+ CloseWindows()
+
+ StoreStealWindow = Window = GemRB.LoadWindow (6)
+
+ # left scrollbar
+ ScrollBarLeft = Window.GetControl (9)
+ ScrollBarLeft.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreStealWindow)
+
+ # right scrollbar
+ ScrollBarRight = Window.GetControl (10)
+ ScrollBarRight.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreStealWindow)
+
+ for i in range (ItemButtonCount):
+ Button = Window.GetControl (i+4)
+ if GUICommon.GameIsBG2():
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ else:
+ Button.SetBorder (0,0,0,0,0,32,32,192,128,0,1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RedrawStoreStealWindow)
+ Button.AttachScrollBar (ScrollBarLeft)
+
+ Button = Window.GetControl (i+11)
+ if GUICommon.GameIsBG2():
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ else:
+ Button.SetBorder (0,0,0,0,0,32,32,192,128,0,1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoRightWindow)
+ Button.SetFont ("NUMBER")
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT|IE_GUI_BUTTON_ALIGN_TOP, OP_OR)
+ Button.AttachScrollBar (ScrollBarRight)
+
+ # Steal
+ LeftButton = Button = Window.GetControl (1)
+ Button.SetText (14179)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, StealPressed)
+
+ Button = Window.GetControl (37)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # encumbrance
+ Label = Window.CreateLabel (0x10000043, 15,325,60,15,"NUMBER","0:",IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_TOP)
+ Label = Window.CreateLabel (0x10000044, 15,365,80,15,"NUMBER","0:",IE_FONT_ALIGN_RIGHT|IE_FONT_ALIGN_TOP)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreStealWindow )
+ UpdateStoreStealWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreDonateWindow ():
+ global StoreDonateWindow
+
+ CloseWindows ()
+
+ StoreDonateWindow = Window = GemRB.LoadWindow (9)
+
+ # graphics
+ Button = Window.GetControl (10)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED|IE_GUI_BUTTON_PLAYONCE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Donate
+ Button = Window.GetControl (3)
+ Button.SetText (15101)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonateGold)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Entry
+ Field = Window.GetControl (5)
+ Field.SetText ("0")
+ Field.SetEvent (IE_GUI_EDIT_ON_CHANGE, UpdateStoreDonateWindow)
+ Field.SetStatus (IE_GUI_EDIT_NUMBER|IE_GUI_CONTROL_FOCUSED)
+
+ # +
+ Button = Window.GetControl (6)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, IncrementDonation)
+ # -
+ Button = Window.GetControl (7)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DecrementDonation)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreDonateWindow )
+ UpdateStoreDonateWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreHealWindow ():
+ global StoreHealWindow
+
+ GemRB.SetVar ("Index", -1)
+ GemRB.SetVar ("TopIndex", 0)
+ CloseWindows()
+
+ StoreHealWindow = Window = GemRB.LoadWindow (5)
+
+ ScrollBar = Window.GetControl (7)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, UpdateStoreHealWindow)
+
+ #spell buttons
+ for i in range (ItemButtonCount):
+ Button = Window.GetControl (i+8)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateStoreHealWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoHealWindow)
+ #Button.AttachScrollBar (ScrollBar)
+
+ # price tag
+ Label = Window.GetControl (0x10000003)
+ Label.SetText ("0")
+
+ # Heal
+ Button = Window.GetControl (5)
+ Button.SetText (13703)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, BuyHeal)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Count = Store['StoreCureCount']
+ if Count>4:
+ Count = Count-4
+ else:
+ Count = 0
+ ScrollBar.SetVarAssoc ("TopIndex", Count+1)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreHealWindow )
+ UpdateStoreHealWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreRumourWindow ():
+ global StoreRumourWindow
+
+ GemRB.SetVar ("TopIndex", 0)
+ CloseWindows()
+
+ StoreRumourWindow = Window = GemRB.LoadWindow (8)
+
+ #removing those pesky labels
+ for i in range (5):
+ Window.DeleteControl (0x10000005+i)
+
+ TextArea = Window.GetControl (11)
+ TextArea.SetText (14144)
+
+ #tavern quality image
+ if GUICommon.GameIsBG1() or GUICommon.GameIsBG2():
+ BAM = "TVRNQUL%d"% ((Store['StoreFlags']>>9)&3)
+ Button = Window.GetControl (12)
+ Button.SetSprites (BAM, 0, 0, 0, 0, 0)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ ScrollBar = Window.GetControl (5)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, UpdateStoreRumourWindow)
+ Count = Store['StoreDrinkCount']
+ if Count>4:
+ Count = Count-4
+ else:
+ Count = 0
+ ScrollBar.SetVarAssoc ("TopIndex", Count+1)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreRumourWindow )
+ UpdateStoreRumourWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreRentWindow ():
+ global StoreRentWindow, RentIndex
+
+ CloseWindows()
+
+ StoreRentWindow = Window = GemRB.LoadWindow (7)
+
+ # room types
+ RentIndex = -1
+ for i in range (4):
+ ok = Store['StoreRoomPrices'][i]
+ Button = Window.GetControl (i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateStoreRentWindow)
+ if ok<0:
+ Button.SetState (IE_GUI_BUTTON_DISABLED) #disabled room icons are selected, not disabled
+ else:
+ Button.SetVarAssoc ("RentIndex", i)
+ if RentIndex==-1:
+ RentIndex = i
+
+ Button = Window.GetControl (i+4)
+ Button.SetText (14294+i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateStoreRentWindow)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("RentIndex", i)
+ if GUICommon.GameIsBG1():
+ #these bioware guys screw up everything possible
+ #remove this line if you fixed guistore
+ Button.SetSprites ("GUISTROC",0, 1,2,0,3)
+ if ok<0:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # Rent
+ Button = Window.GetControl (11)
+ Button.SetText (14293)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RentRoom)
+
+ GemRB.SetVar ("RentIndex", RentIndex)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreRentWindow )
+ UpdateStoreRentWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def UpdateStoreCommon (Window, title, name, gold):
+
+ Label = Window.GetControl (title)
+ Label.SetText (Store['StoreName'])
+
+ if name:
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Label = Window.GetControl (name)
+ Label.SetText (GemRB.GetPlayerName (pc, 0) )
+
+ Label = Window.GetControl (gold)
+ Label.SetText (str(GemRB.GameGetPartyGold ()))
+ return
+
+def GetPC():
+ global PreviousPC
+
+ if PreviousPC:
+ pc = GemRB.GameGetSelectedPCSingle ()
+ if PreviousPC != pc:
+ PreviousPC = pc
+ # reset the store indices, to prevent overscrolling
+ GemRB.SetVar ("RightIndex", 0)
+ GemRB.SetVar ("LeftIndex", 0)
+ GemRB.SetVar ("RightTopIndex", 0)
+ GemRB.SetVar ("LeftTopIndex", 0)
+ GemRB.SetVar ("Index", 0)
+ GemRB.SetVar ("TopIndex", 0)
+ else:
+ PreviousPC = GemRB.GameGetSelectedPCSingle ()
+ pc = PreviousPC
+
+ return pc
+
+def UpdateStoreShoppingWindow ():
+ global Store, inventory_slots
+
+ Window = StoreShoppingWindow
+ #reget store in case of a change
+ Store = GemRB.GetStore ()
+ pc = GetPC()
+
+ LeftCount = Store['StoreItemCount'] - ItemButtonCount + 1
+ if LeftCount<0:
+ LeftCount=0
+ ScrollBar = Window.GetControl (11)
+ ScrollBar.SetVarAssoc ("LeftTopIndex", LeftCount)
+ LeftTopIndex = GemRB.GetVar ("LeftTopIndex")
+ if LeftTopIndex>LeftCount:
+ GemRB.SetVar ("LeftTopIndex", LeftCount)
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ inventory_slots = GemRB.GetSlots (pc, SLOT_INVENTORY)
+ RightCount = len(inventory_slots) - ItemButtonCount + 1
+ if RightCount<0:
+ RightCount=0
+ ScrollBar = Window.GetControl (12)
+ ScrollBar.SetVarAssoc ("RightTopIndex", RightCount)
+ RightTopIndex = GemRB.GetVar ("RightTopIndex")
+ if RightTopIndex>RightCount:
+ GemRB.SetVar ("RightTopIndex", RightCount)
+
+ RedrawStoreShoppingWindow ()
+ return
+
+def SelectID ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Index = GemRB.GetVar ("Index")
+ GemRB.ChangeStoreItem (pc, inventory_slots[Index], SHOP_ID|SHOP_SELECT)
+ RedrawStoreIdentifyWindow ()
+ return
+
+def SelectBuy ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ GemRB.ChangeStoreItem (pc, LeftIndex, SHOP_BUY|SHOP_SELECT)
+ RedrawStoreShoppingWindow ()
+ return
+
+def ToBackpackPressed ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ LeftCount = Store['StoreItemCount']
+ #going backwards because removed items shift the slots
+ for i in range (LeftCount, 0, -1):
+ Flags = GemRB.IsValidStoreItem (pc, i-1, ITEM_STORE)&SHOP_SELECT
+ if Flags:
+ GemRB.ChangeStoreItem (pc, i-1, SHOP_BUY)
+
+ UpdateStoreShoppingWindow ()
+ return
+
+def BuyPressed ():
+ Window = StoreShoppingWindow
+
+ if (BuySum>GemRB.GameGetPartyGold ()):
+ ErrorWindow (11047)
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ LeftCount = Store['StoreItemCount']
+ #going backwards because removed items shift the slots
+ for i in range (LeftCount, 0, -1):
+ Flags = GemRB.IsValidStoreItem (pc, i-1, ITEM_STORE)&SHOP_SELECT
+ if Flags:
+ Slot = GemRB.GetStoreItem (i-1)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ Price = GetRealPrice (pc, "sell", Item, Slot)
+ if Price <= 0:
+ Price = 1
+
+ if GemRB.ChangeStoreItem (pc, i-1, SHOP_BUY):
+ GemRB.GameSetPartyGold (GemRB.GameGetPartyGold ()-Price)
+ UpdateStoreShoppingWindow ()
+ return
+
+def SelectSell ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ RightIndex = GemRB.GetVar ("RightIndex")
+ GemRB.ChangeStoreItem (pc, inventory_slots[RightIndex], SHOP_SELL|SHOP_SELECT)
+ RedrawStoreShoppingWindow ()
+ return
+
+def ToBagPressed ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ RightCount = len (inventory_slots)
+ #no need to go reverse
+ for Slot in range (RightCount):
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[Slot], ITEM_PC)
+ if Flags & SHOP_SELECT:
+ GemRB.ChangeStoreItem (pc, inventory_slots[Slot], SHOP_SELL)
+ UpdateStoreShoppingWindow ()
+ return
+
+def SellPressed ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ RightCount = len (inventory_slots)
+ #no need to go reverse
+ for Slot in range (RightCount):
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[Slot], ITEM_PC) & SHOP_SELECT
+ if Flags:
+ GemRB.ChangeStoreItem (pc, inventory_slots[Slot], SHOP_SELL)
+
+ GemRB.GameSetPartyGold (GemRB.GameGetPartyGold ()+SellSum)
+ UpdateStoreShoppingWindow ()
+ return
+
+def RedrawStoreShoppingWindow ():
+ global BuySum, SellSum
+
+ Window = StoreShoppingWindow
+
+ UpdateStoreCommon (Window, 0x10000003, 0x1000002e, 0x1000002a)
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ LeftTopIndex = GemRB.GetVar ("LeftTopIndex")
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ RightTopIndex = GemRB.GetVar ("RightTopIndex")
+ RightIndex = GemRB.GetVar ("RightIndex")
+ idx = [ LeftTopIndex, RightTopIndex, LeftIndex, RightIndex ]
+ LeftCount = Store['StoreItemCount']
+ BuySum = 0
+ selected_count = 0
+ for i in range (LeftCount):
+ if GemRB.IsValidStoreItem (pc, i, ITEM_STORE) & SHOP_SELECT:
+ Slot = GemRB.GetStoreItem (i)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ selected_count += 1
+ if Inventory:
+ Price = 1
+ else:
+ Price = GetRealPrice (pc, "sell", Item, Slot)
+ if Price <= 0:
+ Price = 1
+ BuySum = BuySum + Price
+
+ RightCount = len(inventory_slots)
+ SellSum = 0
+ for i in range (RightCount):
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[i], ITEM_PC)
+ if Flags & SHOP_SELECT:
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[i])
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ if Inventory:
+ Price = 1
+ else:
+ Price = GetRealPrice (pc, "buy", Item, Slot)
+ if Flags & SHOP_ID:
+ Price = 1
+ SellSum = SellSum + Price
+
+ Label = Window.GetControl (0x1000002b)
+ if Inventory:
+ Label.SetText ("")
+ else:
+ Label.SetText (str(BuySum) )
+ # also disable the button if the inventory is full
+ if BuySum and selected_count <= len(GemRB.GetSlots (pc, SLOT_INVENTORY, -1)):
+ LeftButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ LeftButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Label = Window.GetControl (0x1000002c)
+ if Inventory:
+ Label.SetText ("")
+ else:
+ Label.SetText (str(SellSum) )
+ if SellSum:
+ RightButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ RightButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ for i in range (ItemButtonCount):
+ if i+LeftTopIndex<LeftCount:
+ Slot = GemRB.GetStoreItem (i+LeftTopIndex)
+ else:
+ Slot = None
+ Button = Window.GetControl (i+5)
+ Label = Window.GetControl (0x10000012+i)
+ Button.SetVarAssoc ("LeftIndex", LeftTopIndex+i)
+ SetupItems (pc, Slot, Button, Label, i, ITEM_STORE, idx)
+
+ if i+RightTopIndex<RightCount:
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[i+RightTopIndex])
+ else:
+ Slot = None
+ Button = Window.GetControl (i+13)
+ Label = Window.GetControl (0x1000001e+i)
+ Button.SetVarAssoc ("RightIndex", RightTopIndex+i)
+ SetupItems (pc, Slot, Button, Label, i, ITEM_PC, idx)
+
+ return
+
+def UpdateStoreIdentifyWindow ():
+ global inventory_slots
+
+ Window = StoreIdentifyWindow
+
+ pc = GetPC()
+ inventory_slots = GemRB.GetSlots (pc, SLOT_INVENTORY)
+ Count = len(inventory_slots)
+ ScrollBar = Window.GetControl (7)
+ ScrollBar.SetVarAssoc ("TopIndex", Count-ItemButtonCount+1)
+ GemRB.SetVar ("Index", -1)
+ RedrawStoreIdentifyWindow ()
+ return
+
+def RedrawStoreIdentifyWindow ():
+ Window = StoreIdentifyWindow
+
+ UpdateStoreCommon (Window, 0x10000000, 0x10000005, 0x10000001)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Index = GemRB.GetVar ("Index")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Count = len(inventory_slots)
+ IDPrice = Store['IDPrice']
+
+ Selected = 0
+ for Slot in range (0, Count):
+ flags = GemRB.IsValidStoreItem (pc, inventory_slots[Slot], ITEM_PC)
+ if flags & SHOP_ID and flags & SHOP_SELECT:
+ Selected += 1
+
+ for i in range (ItemButtonCount):
+ if TopIndex+i<Count:
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[TopIndex+i])
+ else:
+ Slot = None
+ Button = Window.GetControl (i+8)
+ # TODO: recheck they really differ
+ if GUICommon.GameIsIWD2():
+ Label = Window.GetControl (0x1000000d+i)
+ else:
+ Label = Window.GetControl (0x1000000c+i)
+ Button.SetVarAssoc ("Index", TopIndex+i)
+ if Slot:
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[TopIndex+i], ITEM_PC)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+ if Item['StackAmount'] > 1:
+ Button.SetText (str(Slot['Usages0']))
+ else:
+ Button.SetText ("")
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if Flags & SHOP_ID:
+ if Flags & SHOP_SELECT:
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemName']))
+ GemRB.SetToken ("ITEMCOST", str(IDPrice) )
+ Button.EnableBorder (0, 1)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemNameIdentified']))
+ GemRB.SetToken ("ITEMCOST", str(0) )
+ Button.EnableBorder (0, 0)
+
+ Label.SetText (10162)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Label.SetText ("")
+
+ Button = Window.GetControl (5)
+ Label = Window.GetControl (0x10000003)
+ if Selected:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Label.SetText (str(IDPrice * Selected) )
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Label.SetText (str(0) )
+ return
+
+def IdentifyPressed ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Count = len(inventory_slots)
+
+ # get all the selected items
+ toID = []
+ for Slot in range (0, Count):
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[Slot], ITEM_PC)
+ if Flags & SHOP_SELECT and Flags & SHOP_ID:
+ toID.append(Slot)
+
+ # enough gold?
+ EndGold = GemRB.GameGetPartyGold () - Store['IDPrice'] * len(toID)
+ if EndGold < 0:
+ return
+
+ # identify
+ Window = StoreIdentifyWindow
+ TextArea = Window.GetControl (23)
+ for i in toID:
+ GemRB.ChangeStoreItem (pc, inventory_slots[i], SHOP_ID)
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[i])
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ # FIXME: some items have the title, some don't - figure it out
+ TextArea.Append(Item['ItemNameIdentified'])
+ TextArea.Append("\n\n")
+ TextArea.Append(Item['ItemDescIdentified'])
+ TextArea.Append("\n\n\n")
+ GemRB.GameSetPartyGold (EndGold)
+
+ UpdateStoreIdentifyWindow ()
+ return
+
+def InfoIdentifyWindow ():
+ Index = GemRB.GetVar ("Index")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Count = len(inventory_slots)
+ if Index >= Count:
+ return
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[Index])
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ InfoWindow (Slot, Item)
+ return
+
+def InfoLeftWindow ():
+ Index = GemRB.GetVar ("LeftIndex")
+ Slot = GemRB.GetStoreItem (Index)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ InfoWindow (Slot, Item)
+ return
+
+def InfoRightWindow ():
+ Index = GemRB.GetVar ("RightIndex")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Count = len(inventory_slots)
+ if Index >= Count:
+ return
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[Index])
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ InfoWindow (Slot, Item)
+ return
+
+def InfoWindow (Slot, Item):
+ global MessageWindow
+
+ Identify = Slot['Flags'] & IE_INV_ITEM_IDENTIFIED
+
+ MessageWindow = Window = GemRB.LoadWindow (12)
+
+ #fake label
+ Label = Window.GetControl (0x10000000)
+ Label.SetText ("")
+
+ #description bam
+ if GUICommon.GameIsBG1() or GUICommon.GameIsBG2():
+ Button = Window.GetControl (7)
+ Button.SetItemIcon (Slot['ItemResRef'], 2)
+
+ #slot bam
+ Button = Window.GetControl (2)
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+
+ Label = Window.GetControl (0x10000007)
+ TextArea = Window.GetControl (5)
+ if Identify:
+ Label.SetText (Item['ItemNameIdentified'])
+ TextArea.SetText (Item['ItemDescIdentified'])
+ else:
+ Label.SetText (Item['ItemName'])
+ TextArea.SetText (Item['ItemDesc'])
+
+ #Done
+ Button = Window.GetControl (4)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ErrorDone)
+
+ # hide the empty button
+ if GUICommon.GameIsBG2() or GUICommon.GameIsIWD2():
+ Window.DeleteControl (9)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def UpdateStoreStealWindow ():
+ global Store, inventory_slots
+
+ Window = StoreStealWindow
+
+ #reget store in case of a change
+ Store = GemRB.GetStore ()
+ LeftCount = Store['StoreItemCount']
+ ScrollBar = Window.GetControl (9)
+ ScrollBar.SetVarAssoc ("LeftTopIndex", LeftCount-ItemButtonCount+1)
+
+ pc = GetPC()
+ inventory_slots = GemRB.GetSlots (pc, SLOT_INVENTORY)
+ RightCount = len(inventory_slots)
+ ScrollBar = Window.GetControl (10)
+ ScrollBar.SetVarAssoc ("RightTopIndex", RightCount-ItemButtonCount+1)
+ GemRB.SetVar ("LeftIndex", -1)
+ LeftButton.SetState (IE_GUI_BUTTON_DISABLED)
+ RedrawStoreStealWindow ()
+ return
+
+def StealPressed ():
+ Window = StoreShoppingWindow
+
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #percentage skill check, if fails, trigger StealFailed
+ #if difficulty = 0 and skill=100, automatic success
+ #if difficulty = 0 and skill=50, 50% success
+ #if difficulty = 50 and skill=50, 0% success
+ #if skill>random(100)+difficulty - success
+ if GUICommon.CheckStat100 (pc, IE_PICKPOCKET, Store['StealFailure']):
+ GemRB.ChangeStoreItem (pc, LeftIndex, SHOP_STEAL)
+ UpdateStoreStealWindow ()
+ else:
+ GemRB.StealFailed ()
+ CloseStoreWindow ()
+ return
+
+def RedrawStoreStealWindow ():
+ Window = StoreStealWindow
+
+ UpdateStoreCommon (Window, 0x10000002, 0x10000027, 0x10000023)
+ LeftTopIndex = GemRB.GetVar ("LeftTopIndex")
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ RightTopIndex = GemRB.GetVar ("RightTopIndex")
+ RightIndex = GemRB.GetVar ("RightIndex")
+ idx = [ LeftTopIndex, RightTopIndex, LeftIndex, RightIndex ]
+ LeftCount = Store['StoreItemCount']
+ pc = GemRB.GameGetSelectedPCSingle ()
+ RightCount = len(inventory_slots)
+ for i in range (ItemButtonCount):
+ Slot = GemRB.GetStoreItem (i+LeftTopIndex)
+ Button = Window.GetControl (i+4)
+ Label = Window.GetControl (0x1000000f+i)
+ Button.SetVarAssoc ("LeftIndex", LeftTopIndex+i)
+ SetupItems (pc, Slot, Button, Label, i, ITEM_STORE, idx, 1)
+
+ if i+RightTopIndex<RightCount:
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[i+RightTopIndex])
+ else:
+ Slot = None
+ Button = Window.GetControl (i+11)
+ Label = Window.GetControl (0x10000019+i)
+ Button.SetVarAssoc ("RightIndex", RightTopIndex+i)
+ SetupItems (pc, Slot, Button, Label, i, ITEM_PC, idx, 1)
+
+ selected_count = 0
+ for i in range (LeftCount):
+ Flags = GemRB.IsValidStoreItem (pc, i, ITEM_STORE)
+ if Flags & SHOP_SELECT:
+ selected_count += 1
+
+ # also disable the button if the inventory is full
+ if LeftIndex>=0 and selected_count <= len(GemRB.GetSlots (pc, SLOT_INVENTORY, -1)):
+ LeftButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ LeftButton.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def SetupItems (pc, Slot, Button, Label, i, type, idx, steal=0):
+ if Slot == None:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Label.SetText ("")
+ Button.SetText ("")
+ else:
+ LeftTopIndex = idx[0]
+ RightTopIndex = idx[1]
+ LeftIndex = idx[2]
+
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+ if Item['StackAmount']>1:
+ Button.SetText ( str(Slot['Usages0']) )
+ else:
+ Button.SetText ("")
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+
+ if type == ITEM_STORE:
+ Price = GetRealPrice (pc, "buy", Item, Slot)
+ Flags = GemRB.IsValidStoreItem (pc, i+LeftTopIndex, type)
+ if steal:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ if Flags & SHOP_BUY:
+ if Flags & SHOP_SELECT:
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if not Inventory:
+ Price = GetRealPrice (pc, "sell", Item, Slot)
+ if Price <= 0:
+ Price = 1
+ else:
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[i+RightTopIndex], type)
+ if Flags & SHOP_STEAL:
+ if LeftIndex == LeftTopIndex + i:
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if Inventory:
+ Price = 1
+ else:
+ Price = GetRealPrice (pc, "buy", Item, Slot)
+
+ if (Price>0) and (Flags & SHOP_SELL):
+ if Flags & SHOP_SELECT:
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if Flags & SHOP_ID:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemName']))
+ Button.EnableBorder (0, 1)
+ if not steal and type == ITEM_PC:
+ Price = 1
+ else:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemNameIdentified']))
+ Button.EnableBorder (0, 0)
+
+ if Inventory:
+ if GUICommon.GameIsIWD1() or GUICommon.GameIsIWD2():
+ Label.SetText (24890)
+ elif GUICommon.GameIsBG2():
+ Label.SetText (28337)
+ else:
+ Label.SetText ("")
+ else:
+ GemRB.SetToken ("ITEMCOST", str(Price) )
+ Label.SetText (10162)
+ return
+
+def GetRealPrice (pc, mode, Item, Slot):
+ # get the base from the item
+ price = Item['Price']
+
+ # FIXME: actually, the price for wands depends on their charge
+ # how ...
+ if Item['StackAmount']>1:
+ price = price * Slot['Usages0']
+
+ # modifier from store properties (in percent)
+ if mode == "buy":
+ mod = Store['BuyMarkup']
+ else:
+ mod = Store['SellMarkup']
+
+ # depreciation works like this:
+ # - if you sell the item the first time, SellMarkup is used;
+ # - if you sell the item the second time, SellMarkup-DepreciationRate is used;
+ # - if you sell the item any more times, SellMarkup-2*DepreciationRate is used.
+ # If the storekeep has an infinite amount of the item, only SellMarkup is used.
+ # The amount of items sold at the same time doesn't matter! Selling three bows
+ # separately will produce less gold then selling them at the same time.
+ # We don't care who is the seller, so if the store already has 2 items, there'll be no gain
+ if mode == "buy":
+ count = GemRB.FindStoreItem (Slot["ItemResRef"])
+ if count:
+ oc = count
+ if count > 2:
+ count = 2
+ mod -= count * Store['Depreciation']
+
+ # charisma modifier (in percent)
+ mod += GemRB.GetAbilityBonus (IE_CHR, GemRB.GetPlayerStat (BarteringPC, IE_CHR) - 1, 0)
+
+ # reputation modifier (in percent, but absolute)
+ mod = mod * RepModTable.GetValue (0, GemRB.GameGetReputation()/10 - 1) / 100
+
+ return price * mod / 100
+
+def UpdateStoreDonateWindow ():
+ Window = StoreDonateWindow
+
+ UpdateStoreCommon (Window, 0x10000007, 0, 0x10000008)
+ Field = Window.GetControl (5)
+ donation = int("0"+Field.QueryText ())
+ gold = GemRB.GameGetPartyGold ()
+ if donation>gold:
+ donation = gold
+ Field.SetText (str(gold) )
+
+ Button = Window.GetControl (3)
+ if donation:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def IncrementDonation ():
+ Window = StoreDonateWindow
+
+ Field = Window.GetControl (5)
+ donation = int("0"+Field.QueryText ())
+ if donation<GemRB.GameGetPartyGold ():
+ Field.SetText (str(donation+1) )
+ else:
+ Field.SetText (str(GemRB.GameGetPartyGold ()) )
+ UpdateStoreDonateWindow ()
+ return
+
+def DecrementDonation ():
+ Window = StoreDonateWindow
+
+ Field = Window.GetControl (5)
+ donation = int("0"+Field.QueryText ())
+ if donation>0:
+ Field.SetText (str(donation-1) )
+ else:
+ Field.SetText (str(0) )
+ UpdateStoreDonateWindow ()
+ return
+
+def DonateGold ():
+ Window = StoreDonateWindow
+
+ TextArea = Window.GetControl (0)
+ TextArea.SetFlags (IE_GUI_TEXTAREA_AUTOSCROLL)
+
+ Button = Window.GetControl (10)
+ Button.SetAnimation ("DONATE")
+
+ Field = Window.GetControl (5)
+ donation = int("0"+Field.QueryText ())
+ GemRB.GameSetPartyGold (GemRB.GameGetPartyGold ()-donation)
+ if GemRB.IncreaseReputation (donation):
+ TextArea.Append (10468, -1)
+ GemRB.PlaySound ("act_03")
+ UpdateStoreDonateWindow ()
+ return
+
+ TextArea.Append (10469, -1)
+ GemRB.PlaySound ("act_03e")
+ UpdateStoreDonateWindow ()
+ return
+
+def UpdateStoreHealWindow ():
+ Window = StoreHealWindow
+
+ UpdateStoreCommon (Window, 0x10000000, 0, 0x10000001)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Index = GemRB.GetVar ("Index")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ for i in range (ItemButtonCount):
+ Cure = GemRB.GetStoreCure (TopIndex+i)
+
+ Button = Window.GetControl (i+8)
+ Label = Window.GetControl (0x1000000c+i)
+ Button.SetVarAssoc ("Index", TopIndex+i)
+ if Cure:
+ Spell = GemRB.GetSpell (Cure['CureResRef'])
+ Button.SetSpellIcon (Cure['CureResRef'], 1)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ dead = GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD
+ # toggle raise dead/resurrect based on state
+ # unfortunately the flags are not set properly in iwd
+ if (dead and Spell["SpellTargetType"] != 3) or \
+ (not dead and Spell["SpellTargetType"] == 3): # 3 - non-living
+ # locked and shaded
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetBorder (0, 0,0, 0,0, 200,0,0,100, 1,1)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetBorder (0, 0,0, 0,0, 0,0,0,0, 0,0)
+
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Spell['SpellName']))
+ GemRB.SetToken ("ITEMCOST", str(Cure['Price']) )
+ Label.SetText (10162)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetBorder (0, 0,0, 0,0, 0,0,0,0, 0,0)
+ Label.SetText ("")
+
+ if TopIndex+i==Index:
+ TextArea = Window.GetControl (23)
+ TextArea.SetText (Cure['Description'])
+ Label = Window.GetControl (0x10000003)
+ Label.SetText (str(Cure['Price']) )
+ Button = Window.GetControl (5)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def InfoHealWindow ():
+ global MessageWindow
+
+ UpdateStoreHealWindow ()
+ Index = GemRB.GetVar ("Index")
+ Cure = GemRB.GetStoreCure (Index)
+ Spell = GemRB.GetSpell (Cure['CureResRef'])
+
+ MessageWindow = Window = GemRB.LoadWindow (14)
+
+ Label = Window.GetControl (0x10000000)
+ Label.SetText (Spell['SpellName'])
+
+ Button = Window.GetControl (2)
+ Button.SetSpellIcon (Cure['CureResRef'], 1)
+
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (Spell['SpellDesc'])
+
+ #Done
+ Button = Window.GetControl (5)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ErrorDone)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def BuyHeal ():
+ Index = GemRB.GetVar ("Index")
+ Cure = GemRB.GetStoreCure (Index)
+ gold = GemRB.GameGetPartyGold ()
+ if gold < Cure['Price']:
+ ErrorWindow (11048)
+ return
+
+ GemRB.GameSetPartyGold (gold-Cure['Price'])
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.ApplySpell (pc, Cure['CureResRef'], Store['StoreOwner'])
+ UpdateStoreHealWindow ()
+ return
+
+def UpdateStoreRumourWindow ():
+ Window = StoreRumourWindow
+
+ UpdateStoreCommon (Window, 0x10000011, 0, 0x10000012)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ for i in range (5):
+ Drink = GemRB.GetStoreDrink (TopIndex+i)
+ Button = Window.GetControl (i)
+ Button.SetVarAssoc ("Index", i)
+ if Drink:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Drink['DrinkName']))
+ GemRB.SetToken ("ITEMCOST", str(Drink['Price']) )
+ Button.SetText (10162)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GulpDrink)
+ else:
+ Button.SetText ("")
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def GulpDrink ():
+ Window = StoreRumourWindow
+
+ TextArea = Window.GetControl (13)
+ TextArea.SetFlags (IE_GUI_TEXTAREA_AUTOSCROLL)
+ pc = GemRB.GameGetSelectedPCSingle ()
+ intox = GemRB.GetPlayerStat (pc, IE_INTOXICATION)
+ intox = 0
+ if intox > 80:
+ TextArea.Append (10832, -1)
+ return
+
+ gold = GemRB.GameGetPartyGold ()
+ Index = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("Index")
+ Drink = GemRB.GetStoreDrink (Index)
+ if gold < Drink['Price']:
+ ErrorWindow (11049)
+ return
+
+ GemRB.GameSetPartyGold (gold-Drink['Price'])
+ GemRB.SetPlayerStat (pc, IE_INTOXICATION, intox+Drink['Strength'])
+ text = GemRB.GetRumour (Drink['Strength'], Store['TavernRumour'])
+ TextArea.Append (text, -1)
+ GemRB.PlaySound ("gam_07")
+ UpdateStoreRumourWindow ()
+ return
+
+def UpdateStoreRentWindow ():
+ global RentIndex
+
+ Window = StoreRentWindow
+ UpdateStoreCommon (Window, 0x10000008, 0, 0x10000009)
+ RentIndex = GemRB.GetVar ("RentIndex")
+ Button = Window.GetControl (11)
+ Label = Window.GetControl (0x1000000d)
+ if RentIndex>=0:
+ TextArea = Window.GetControl (12)
+ TextArea.SetText (roomtypes[RentIndex] )
+ price = Store['StoreRoomPrices'][RentIndex]
+ Label.SetText (str(price) )
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Label.SetText ("0" )
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def RentConfirm ():
+ RentIndex = GemRB.GetVar ("RentIndex")
+ price = Store['StoreRoomPrices'][RentIndex]
+ Gold = GemRB.GameGetPartyGold ()
+ GemRB.GameSetPartyGold (Gold-price)
+ GemRB.RestParty (13, 1, RentIndex+1)
+ if RentConfirmWindow:
+ RentConfirmWindow.Unload ()
+ Window = StoreRentWindow
+ TextArea = Window.GetControl (12)
+ #is there any way to change this???
+ GemRB.SetToken ("HOUR", "8")
+ GemRB.SetToken ("HP", "%d"%(RentIndex+1))
+ TextArea.SetText (16476)
+ GemRB.SetVar ("RentIndex", -1)
+ Button = Window.GetControl (RentIndex+4)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ UpdateStoreRentWindow ()
+ return
+
+def RentDeny () :
+ if RentConfirmWindow:
+ RentConfirmWindow.Unload ()
+ UpdateStoreRentWindow ()
+ return
+
+def RentRoom ():
+ global RentIndex, RentConfirmWindow
+
+ RentIndex = GemRB.GetVar ("RentIndex")
+ price = Store['StoreRoomPrices'][RentIndex]
+ Gold = GemRB.GameGetPartyGold ()
+ if Gold<price:
+ ErrorWindow (11051)
+ return
+
+ RentConfirmWindow = Window = GemRB.LoadWindow (11)
+ #confirm
+ Button = Window.GetControl (0)
+ Button.SetText (17199)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RentConfirm)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ #deny
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RentDeny)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ #textarea
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (15358)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ErrorWindow (strref):
+ global MessageWindow
+
+ MessageWindow = Window = GemRB.LoadWindow (10)
+
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (strref)
+
+ #done
+ Button = Window.GetControl (0)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ErrorDone)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ErrorDone ():
+ if MessageWindow:
+ MessageWindow.Unload ()
+ return
+
+###################################################
+# End of file GUISTORE.py
diff --git a/gemrb/GUIScripts/InventoryCommon.py b/gemrb/GUIScripts/InventoryCommon.py
new file mode 100644
index 0000000..9c78074
--- /dev/null
+++ b/gemrb/GUIScripts/InventoryCommon.py
@@ -0,0 +1,910 @@
+# -*-python-*-
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2010 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.
+#
+
+import GemRB
+import GUICommon
+from GUIDefines import *
+from ie_stats import *
+from ie_slots import *
+from ie_spells import *
+
+OverSlot = None
+UsedSlot = None
+ItemInfoWindow = None
+ItemAmountWindow = None
+ItemIdentifyWindow = None
+ItemAbilitiesWindow = None
+ErrorWindow = None
+StackAmount = 0
+
+def OnDragItemGround ():
+ """Drops and item to the ground."""
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("GroundItemButton") + GemRB.GetVar ("TopIndex")
+
+ if GemRB.IsDraggingItem ()==0:
+ slot_item = GemRB.GetContainerItem (pc, slot)
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ GemRB.DragItem (pc, slot, item["ItemIcon"], 0, 1) #container
+ if GUICommon.GameIsPST():
+ GemRB.PlaySound (item["DescIcon"])
+ else:
+ GemRB.DropDraggedItem (pc, -2) #dropping on ground
+
+ GUIINV.UpdateInventoryWindow ()
+ return
+
+def OnAutoEquip ():
+ """Auto-equips equipment if possible."""
+
+ if GemRB.IsDraggingItem ()!=1:
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ #-1 : drop stuff in equipable slots (but not inventory)
+ GemRB.DropDraggedItem (pc, -1)
+
+ if GemRB.IsDraggingItem ()==1:
+ GemRB.PlaySound("GAM_47") #failed equip
+
+ GUIINV.UpdateInventoryWindow ()
+ return
+
+def OnDragItem ():
+ """Updates dragging."""
+
+ if GemRB.IsDraggingItem()==2:
+ return
+
+ #don't call when splitting items
+ if ItemAmountWindow != None:
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ slot_item = GemRB.GetSlotItem (pc, slot)
+
+ if not GemRB.IsDraggingItem ():
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ GemRB.DragItem (pc, slot, item["ItemIcon"], 0, 0)
+ else:
+ SlotType = GemRB.GetSlotType (slot, pc)
+ #special monk check
+ if GemRB.GetPlayerStat (pc, IE_CLASS)==0x14:
+ #offhand slot mark
+ if SlotType["Effects"]==TYPE_OFFHAND:
+ SlotType["ResRef"]=""
+ GemRB.DisplayString (61355, 0xffffff)
+
+ if SlotType["ResRef"]!="":
+ if slot_item:
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ #drag items into a bag
+ if item["Function"]&4:
+ #first swap them
+ GemRB.DropDraggedItem (pc, slot)
+ #enter the store
+ GemRB.EnterStore (slot_item["ItemResRef"])
+ #if it is possible to add, then do it
+ if GemRB.IsValidStoreItem (pc, slot, 0)&SHOP_SELL:
+ GemRB.ChangeStoreItem (pc, slot, SHOP_SELL)
+ else:
+ GemRB.DisplayString( 9375, 0xffffff)
+ #leave (save) store
+ GemRB.LeaveStore()
+
+ GemRB.DropDraggedItem (pc, slot)
+
+ GUIINV.UpdateInventoryWindow ()
+ return
+
+def OnDropItemToPC ():
+ """Gives an item to another character."""
+
+ pc = GemRB.GetVar ("PressedPortrait")
+
+ #-3 : drop stuff in inventory (but not equippable slots)
+ GemRB.DropDraggedItem (pc, -3)
+ if GemRB.IsDraggingItem ()==1:
+ if GUICommon.HasTOB ():
+ GemRB.DisplayString (61794, 0xffffff)
+ elif GUICommon.GameIsPST ():
+ GemRB.DisplayString (19257, 0xffffff)
+ else:
+ GemRB.DisplayString (17999, 0xffffff)
+
+ GUIINV.UpdateInventoryWindow ()
+ return
+
+def DecreaseStackAmount ():
+ """Decreases the split size."""
+
+ Text = ItemAmountWindow.GetControl (6)
+ Amount = Text.QueryText ()
+ number = int ("0"+Amount)-1
+ if number<1:
+ number=1
+ Text.SetText (str (number))
+ return
+
+def IncreaseStackAmount ():
+ """Increases the split size."""
+
+ Text = ItemAmountWindow.GetControl (6)
+ Amount = Text.QueryText ()
+ number = int ("0"+Amount)+1
+ if number>StackAmount:
+ number=StackAmount
+ Text.SetText (str (number))
+ return
+
+def DragItemAmount ():
+ """Drag a split item."""
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ #emergency dropping
+ if GemRB.IsDraggingItem()==1:
+ GemRB.DropDraggedItem (pc, UsedSlot)
+ UpdateSlot (pc, UsedSlot-1)
+
+ slot_item = GemRB.GetSlotItem (pc, UsedSlot)
+
+ #if dropping didn't help, don't die if slot_item isn't here
+ if slot_item:
+ Text = ItemAmountWindow.GetControl (6)
+ Amount = Text.QueryText ()
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ GemRB.DragItem (pc, UsedSlot, item["ItemIcon"], int ("0"+Amount), 0)
+ OpenItemAmountWindow ()
+ return
+
+def MouseEnterSlot ():
+ global OverSlot
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ OverSlot = GemRB.GetVar ("ItemButton")
+ if GemRB.IsDraggingItem ()==1:
+ UpdateSlot (pc, OverSlot-1)
+ return
+
+def MouseLeaveSlot ():
+ global OverSlot
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ if slot == OverSlot or not GemRB.IsDraggingItem ():
+ OverSlot = None
+ UpdateSlot (pc, slot-1)
+ return
+
+def MouseEnterGround ():
+ Window = GUIINV.InventoryWindow
+
+ if GUICommon.GameIsPST():
+ offset = 47
+ else:
+ offset = 68
+ i = GemRB.GetVar ("GroundItemButton")
+ Button = Window.GetControl (i+offset)
+
+ if GemRB.IsDraggingItem ()==1:
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ return
+
+def MouseLeaveGround ():
+ Window = GUIINV.InventoryWindow
+
+ if GUICommon.GameIsPST():
+ offset = 47
+ else:
+ offset = 68
+ i = GemRB.GetVar ("GroundItemButton")
+ Button = Window.GetControl (i+offset)
+
+ if GemRB.IsDraggingItem ()==1:
+ Button.SetState (IE_GUI_BUTTON_SECOND)
+ return
+
+def CloseItemInfoWindow ():
+ if ItemInfoWindow:
+ ItemInfoWindow.Unload ()
+ GUIINV.UpdateInventoryWindow ()
+ return
+
+def DisplayItem (itemresref, type):
+ global ItemInfoWindow
+
+ item = GemRB.GetItem (itemresref)
+ ItemInfoWindow = Window = GemRB.LoadWindow (5)
+
+ if GUICommon.GameIsPST():
+ strrefs = [ 1403, 4256, 4255, 4251, 4252, 4254, 4279 ]
+ else:
+ strrefs = [ 11973, 14133, 11960, 19392, 17104, item["DialogName"], 17108 ]
+
+ # item name
+ Label = Window.GetControl (0x10000000)
+ if (type&2):
+ text = item["ItemName"]
+ else:
+ text = item["ItemNameIdentified"]
+ Label.SetText (text)
+
+ #item icon
+ Button = Window.GetControl (2)
+ if GUICommon.GameIsPST():
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetItemIcon (itemresref)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Button.SetItemIcon (itemresref,0)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ #middle button
+ Button = Window.GetControl (4)
+ Button.SetText (strrefs[0])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseItemInfoWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ #textarea
+ Text = Window.GetControl (5)
+ if (type&2):
+ text = item["ItemDesc"]
+ else:
+ text = item["ItemDescIdentified"]
+ Text.SetText (text)
+
+ #left button
+ Button = Window.GetControl(8)
+ select = (type&1) and (item["Function"]&8)
+
+ if type&2:
+ Button.SetText (strrefs[1])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, IdentifyItemWindow)
+ elif select and not GUICommon.GameIsPST():
+ Button.SetText (strrefs[2])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesItemWindow)
+ else:
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+
+ # description icon (not present in iwds)
+ if not GUICommon.GameIsIWD1() and not GUICommon.GameIsIWD2():
+ Button = Window.GetControl (7)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_CENTER_PICTURES | IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ if GUICommon.GameIsPST():
+ Button.SetItemIcon (itemresref, 1) # no DescIcon
+ else:
+ Button.SetItemIcon (itemresref, 2)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ #right button
+ Button = Window.GetControl(9)
+ drink = (type&1) and (item["Function"]&1)
+ read = (type&1) and (item["Function"]&2)
+ #sorcerors cannot learn spells
+ # FIXME: unhardcode
+ pc = GemRB.GameGetSelectedPCSingle ()
+ if GemRB.GetPlayerStat (pc, IE_CLASS) == 19:
+ read = 0
+ container = (type&1) and (item["Function"]&4)
+ dialog = (type&1) and (item["Dialog"]!="")
+ familiar = (type&1) and (item["Type"] == 38)
+ if drink:
+ Button.SetText (strrefs[3])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DrinkItemWindow)
+ elif read:
+ Button.SetText (strrefs[4])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ReadItemWindow)
+ elif container:
+ if GUICommon.GameIsIWD2() or GUICommon.GameIsHOW():
+ Button.SetText (24891) # Open Container
+ elif GUICommon.GameIsBG2():
+ Button.SetText (44002) # open container
+ else:
+ # a fallback, since the originals have nothing appropriate
+ # FIXME: where do mods add the new string? This is untranslatable
+ Button.SetText ("Open container")
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenItemWindow)
+ elif dialog:
+ Button.SetText (strrefs[5])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DialogItemWindow)
+ elif familiar:
+ Button.SetText (4373)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ReleaseFamiliar)
+ else:
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+
+ Label = Window.HasControl(0x1000000b)
+ if Label:
+ Label = Window.GetControl (0x1000000b)
+ if (type&2):
+ # NOT IDENTIFIED
+ Label.SetText (strrefs[6])
+ else:
+ Label.SetText ("")
+
+ # in pst one can cycle through all the items from the description window
+ if Window.HasControl (14):
+ #left scroll
+ Button = Window.GetControl (13)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIINV.LeftItemScroll)
+
+ #right scroll
+ Button = Window.GetControl (14)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIINV.RightItemScroll)
+
+ ItemInfoWindow.ShowModal(MODAL_SHADOW_GRAY)
+ return
+
+def OpenItemInfoWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ if GUICommon.GameIsPST():
+ slot, slot_item = GUIINV.ItemHash[GemRB.GetVar ('ItemButton')]
+ else:
+ slot = GemRB.GetVar ("ItemButton")
+ slot_item = GemRB.GetSlotItem (pc, slot)
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+
+ #auto identify when lore is high enough
+ if item["LoreToID"]<=GemRB.GetPlayerStat (pc, IE_LORE):
+ GemRB.ChangeItemFlag (pc, slot, IE_INV_ITEM_IDENTIFIED, OP_OR)
+ slot_item["Flags"] |= IE_INV_ITEM_IDENTIFIED
+ GUIINV.UpdateInventoryWindow ()
+
+ if slot_item["Flags"] & IE_INV_ITEM_IDENTIFIED:
+ value = 1
+ else:
+ value = 3
+ DisplayItem (slot_item["ItemResRef"], value)
+ return
+
+def OpenGroundItemInfoWindow ():
+ global ItemInfoWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar("TopIndex")+GemRB.GetVar("GroundItemButton")
+ slot_item = GemRB.GetContainerItem (pc, slot)
+
+ #the ground items are only displayable
+ if slot_item["Flags"] & IE_INV_ITEM_IDENTIFIED:
+ value = 0
+ else:
+ value = 2
+ DisplayItem(slot_item["ItemResRef"], value)
+ return
+
+def OpenItemAmountWindow ():
+ """Open the split window."""
+
+ global UsedSlot, OverSlot
+ global ItemAmountWindow, StackAmount
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ if ItemAmountWindow != None:
+ if ItemAmountWindow:
+ ItemAmountWindow.Unload ()
+ ItemAmountWindow = None
+ GemRB.SetRepeatClickFlags (GEM_RK_DISABLE, OP_OR)
+ UsedSlot = None
+ OverSlot = None
+ GUIINV.UpdateInventoryWindow()
+ return
+
+ UsedSlot = GemRB.GetVar ("ItemButton")
+ if GemRB.IsDraggingItem ()==1:
+ GemRB.DropDraggedItem (pc, UsedSlot)
+ #redraw slot
+ UpdateSlot (pc, UsedSlot-1)
+ # disallow splitting while holding split items (double splitting)
+ if GemRB.IsDraggingItem () == 1:
+ return
+
+ slot_item = GemRB.GetSlotItem (pc, UsedSlot)
+
+ if slot_item:
+ StackAmount = slot_item["Usages0"]
+ else:
+ StackAmount = 0
+ if StackAmount<=1:
+ UpdateSlot (pc, UsedSlot-1)
+ return
+
+ ResRef = slot_item['ItemResRef']
+ item = GemRB.GetItem (ResRef)
+
+ ItemAmountWindow = Window = GemRB.LoadWindow (4)
+
+ # item icon
+ Icon = Window.GetControl (0)
+ Icon.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Icon.SetItemIcon (ResRef)
+
+ # item amount
+ Text = Window.GetControl (6)
+ # FIXME: use a proper size
+ # FIXME: fix it for all the games
+ if GUICommon.GameIsIWD2():
+ Text.SetSize (40, 40)
+ Text.SetText (str (StackAmount//2))
+ Text.SetStatus (IE_GUI_EDIT_NUMBER|IE_GUI_CONTROL_FOCUSED)
+
+ # Decrease
+ Button = Window.GetControl (4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DecreaseStackAmount)
+
+ # Increase
+ Button = Window.GetControl (3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, IncreaseStackAmount)
+
+ # Done
+ Button = Window.GetControl (2)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DragItemAmount)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenItemAmountWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ #GemRB.UnhideGUI ()
+ GemRB.SetRepeatClickFlags (GEM_RK_DISABLE, OP_NAND)
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def UpdateSlot (pc, slot):
+ """Updates a specific slot."""
+
+ Window = GUIINV.InventoryWindow
+ SlotType = GemRB.GetSlotType (slot+1, pc)
+ ControlID = SlotType["ID"]
+
+ if not ControlID:
+ return
+
+ if GemRB.IsDraggingItem ()==1:
+ #get dragged item
+ drag_item = GemRB.GetSlotItem (0,0)
+ itemname = drag_item["ItemResRef"]
+ drag_item = GemRB.GetItem (itemname)
+ else:
+ itemname = ""
+
+ Button = Window.GetControl (ControlID)
+ slot_item = GemRB.GetSlotItem (pc, slot+1)
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, OnDragItem)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ GUICommon.UpdateInventorySlot (pc, Button, slot_item, "inventory", SlotType["Type"]&SLOT_INVENTORY == 0)
+
+ if slot_item:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnDragItem)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenItemInfoWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, OpenItemAmountWindow)
+ else:
+ if SlotType["ResRef"]=="*":
+ Button.SetBAM ("",0,0)
+ Button.SetTooltip (SlotType["Tip"])
+ itemname = ""
+ elif SlotType["ResRef"]=="":
+ Button.SetBAM ("",0,0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetTooltip ("")
+ itemname = ""
+ else:
+ Button.SetBAM (SlotType["ResRef"],0,0)
+ Button.SetTooltip (SlotType["Tip"])
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DOUBLE_PRESS, OpenItemAmountWindow)
+
+ if OverSlot == slot+1:
+ if GemRB.CanUseItemType (SlotType["Type"], itemname):
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ if (SlotType["Type"]&SLOT_INVENTORY) or not GemRB.CanUseItemType (SlotType["Type"], itemname):
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_SECOND)
+
+ if slot_item and (GemRB.GetEquippedQuickSlot (pc)==slot+1 or GemRB.GetEquippedAmmunition (pc)==slot+1):
+ Button.SetState (IE_GUI_BUTTON_THIRD)
+
+ return
+
+def CancelColor():
+ if ColorPicker:
+ ColorPicker.Unload ()
+ GUIINV.InventoryWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ColorDonePress():
+ """Saves the selected colors."""
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ if ColorPicker:
+ ColorPicker.Unload ()
+
+ ColorTable = GemRB.LoadTable ("clowncol")
+ PickedColor=ColorTable.GetValue (ColorIndex, GemRB.GetVar ("Selected"))
+ if ColorIndex==0:
+ GUICommon.SetColorStat (pc, IE_HAIR_COLOR, PickedColor)
+ elif ColorIndex==1:
+ GUICommon.SetColorStat (pc, IE_SKIN_COLOR, PickedColor)
+ elif ColorIndex==2:
+ GUICommon.SetColorStat (pc, IE_MAJOR_COLOR, PickedColor)
+ else:
+ GUICommon.SetColorStat (pc, IE_MINOR_COLOR, PickedColor)
+ GUIINV.UpdateInventoryWindow ()
+ return
+
+def HairPress():
+ global ColorIndex, PickedColor
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ ColorIndex = 0
+ PickedColor = GemRB.GetPlayerStat (pc, IE_HAIR_COLOR, 1) & 0xFF
+ GetColor()
+ return
+
+def SkinPress():
+ global ColorIndex, PickedColor
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ ColorIndex = 1
+ PickedColor = GemRB.GetPlayerStat (pc, IE_SKIN_COLOR, 1) & 0xFF
+ GetColor()
+ return
+
+def MajorPress():
+ """Selects the major color."""
+ global ColorIndex, PickedColor
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ ColorIndex = 2
+ PickedColor = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR, 1) & 0xFF
+ GetColor()
+ return
+
+def MinorPress():
+ """Selects the minor color."""
+ global ColorIndex, PickedColor
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ ColorIndex = 3
+ PickedColor = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR, 1) & 0xFF
+ GetColor()
+ return
+
+def GetColor():
+ """Opens the color selection window."""
+
+ global ColorPicker
+
+ ColorTable = GemRB.LoadTable ("clowncol")
+ GUIINV.InventoryWindow.SetVisible (WINDOW_GRAYED) #darken it
+ ColorPicker=GemRB.LoadWindow (3)
+ GemRB.SetVar ("Selected",-1)
+ if GUICommon.GameIsIWD2():
+ Button = ColorPicker.GetControl (35)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelColor)
+ Button.SetText(13727)
+
+ for i in range (34):
+ Button = ColorPicker.GetControl (i)
+ MyColor = ColorTable.GetValue (ColorIndex, i)
+ if MyColor == "*":
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ continue
+ if PickedColor == MyColor:
+ GemRB.SetVar ("Selected",i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ else:
+ Button.SetBAM ("COLGRAD", 2, 0, MyColor)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetVarAssoc ("Selected",i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ColorDonePress)
+ ColorPicker.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ReleaseFamiliar ():
+ """Simple Use Item"""
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ # the header is always the first, target is always self
+ GemRB.UseItem (pc, slot, 0, 5)
+ CloseItemInfoWindow ()
+ return
+
+def DrinkItemWindow ():
+ """Drink the potion"""
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ # the drink item header is always the first
+ GemRB.UseItem (pc, slot, 0)
+ CloseItemInfoWindow ()
+ return
+
+def OpenErrorWindow (strref):
+ """Opens the error window and displays the string."""
+
+ global ErrorWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ ErrorWindow = Window = GemRB.LoadWindow (7)
+ Button = Window.GetControl (0)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseErrorWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (strref)
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseErrorWindow ():
+ if ErrorWindow:
+ ErrorWindow.Unload ()
+ GUIINV.UpdateInventoryWindow ()
+ return
+
+def ReadItemWindow ():
+ global level, spell_ref
+ """Tries to learn the mage scroll."""
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ ret = GUICommon.CannotLearnSlotSpell()
+
+ if ret:
+ strref = 72873
+ CloseItemInfoWindow ()
+ if GUICommon.HasTOB():
+ OpenErrorWindow (strref)
+ return
+
+ slot_item = GemRB.GetSlotItem (pc, slot)
+ spell_ref = GemRB.GetItem (slot_item['ItemResRef'], pc)['Spell']
+ spell = GemRB.GetSpell (spell_ref)
+ if spell:
+ # can we learn more spells of this level?
+ level = spell['SpellLevel']-1
+ spell_count = GemRB.GetKnownSpellsCount (pc, IE_SPELL_TYPE_WIZARD, level)
+ if spell_count > GemRB.GetAbilityBonus (IE_INT, 2, GemRB.GetPlayerStat (pc, IE_INT)):
+ ret = LSR_FULL
+ if GUICommon.GameIsBG2():
+ strref = 32097
+ else:
+ strref = -1
+ else:
+ GemRB.UseItem (pc, slot, 1, 5)
+ GemRB.SetTimedEvent(DelayedReadItemWindow, 1)
+ return
+ else:
+ print "WARNING: invalid spell header in item", slot_item['ItemResRef']
+ CloseItemInfoWindow ()
+ return -1
+
+ CloseItemInfoWindow ()
+ OpenErrorWindow (strref)
+
+ return ret
+
+def DelayedReadItemWindow ():
+ global level, spell_ref
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ if GUICommon.HasSpell (pc, IE_SPELL_TYPE_WIZARD, level, spell_ref):
+ strref = 10830
+ else:
+ ret = LSR_FAILED
+ strref = 10831
+ CloseItemInfoWindow ()
+ if not GUICommon.GameIsPST():
+ OpenErrorWindow (strref)
+ return
+
+def OpenItemWindow ():
+ """Displays information about the item."""
+
+ #close inventory
+ GemRB.SetVar ("Inventory", 1)
+ if ItemInfoWindow:
+ ItemInfoWindow.Unload ()
+ GUIINV.OpenInventoryWindow ()
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ slot_item = GemRB.GetSlotItem (pc, slot)
+ ResRef = slot_item['ItemResRef']
+ #the store will have to reopen the inventory
+ GemRB.EnterStore (ResRef)
+ return
+
+def DialogItemWindow ():
+ """Converse with an item."""
+
+ if ItemInfoWindow:
+ ItemInfoWindow.Unload ()
+ GUIINV.OpenInventoryWindow ()
+ pc = GemRB.GameGetSelectedPCSingle ()
+ if GUICommon.GameIsPST():
+ slot, slot_item = GUIINV.ItemHash[GemRB.GetVar ('ItemButton')]
+ else:
+ slot = GemRB.GetVar ("ItemButton")
+ slot_item = GemRB.GetSlotItem (pc, slot)
+ ResRef = slot_item['ItemResRef']
+ item = GemRB.GetItem (ResRef)
+ dialog=item["Dialog"]
+ GemRB.ExecuteString ("StartDialogOverride(\""+dialog+"\",Myself,0,0,1)", pc)
+ return
+
+def IdentifyUseSpell ():
+ """Identifies the item with a memorized spell."""
+
+ global ItemIdentifyWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ if ItemIdentifyWindow:
+ ItemIdentifyWindow.Unload ()
+ GemRB.HasSpecialSpell (pc, 1, 1)
+ if ItemInfoWindow:
+ ItemInfoWindow.Unload ()
+ GemRB.ChangeItemFlag (pc, slot, IE_INV_ITEM_IDENTIFIED, OP_OR)
+ OpenItemInfoWindow()
+ return
+
+def IdentifyUseScroll ():
+ """Identifies the item with a scroll or other item."""
+
+ global ItemIdentifyWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ if ItemIdentifyWindow:
+ ItemIdentifyWindow.Unload ()
+ if ItemInfoWindow:
+ ItemInfoWindow.Unload ()
+ if GemRB.HasSpecialItem (pc, 1, 1):
+ GemRB.ChangeItemFlag (pc, slot, IE_INV_ITEM_IDENTIFIED, OP_OR)
+ OpenItemInfoWindow()
+ return
+
+def CloseIdentifyItemWindow ():
+ global ItemIdentifyWindow
+
+ if ItemIdentifyWindow:
+ ItemIdentifyWindow.Unload ()
+ ItemIdentifyWindow = None
+ return
+
+def IdentifyItemWindow ():
+ global ItemIdentifyWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ ItemIdentifyWindow = Window = GemRB.LoadWindow (9)
+ Button = Window.GetControl (0)
+ if GUICommon.GameIsPST():
+ Button.SetText (4259)
+ else:
+ Button.SetText (17105)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, IdentifyUseSpell)
+ if not GemRB.HasSpecialSpell (pc, 1, 0):
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Button = Window.GetControl (1)
+ if GUICommon.GameIsPST():
+ Button.SetText (4260)
+ else:
+ Button.SetText (17106)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, IdentifyUseScroll)
+ if not GemRB.HasSpecialItem (pc, 1, 0):
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Button = Window.GetControl (2)
+ if GUICommon.GameIsPST():
+ Button.SetText (4196)
+ else:
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseIdentifyItemWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ TextArea = Window.GetControl (3)
+ if GUICommon.GameIsPST():
+ TextArea.SetText (4258)
+ else:
+ TextArea.SetText (19394)
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DoneAbilitiesItemWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ GemRB.SetupQuickSlot (pc, 0, slot, GemRB.GetVar ("Ability") )
+ CloseAbilitiesItemWindow ()
+ return
+
+def CloseAbilitiesItemWindow ():
+ global ItemAbilitiesWindow
+
+ if ItemAbilitiesWindow:
+ ItemAbilitiesWindow.Unload ()
+ ItemAbilitiesWindow = None
+ return
+
+def AbilitiesItemWindow ():
+ global ItemAbilitiesWindow
+
+ ItemAbilitiesWindow = Window = GemRB.LoadWindow (6)
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ("ItemButton")
+ slot_item = GemRB.GetSlotItem (pc, slot)
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ Tips = item["Tooltips"]
+
+ GemRB.SetVar ("Ability", slot_item["Header"])
+ for i in range(3):
+ Button = Window.GetControl (i+1)
+ Button.SetSprites ("GUIBTBUT",i,0,1,2,0)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("Ability",i)
+ Text = Window.GetControl (i+0x10000003)
+ if i<len(Tips):
+ Button.SetItemIcon (slot_item['ItemResRef'],i+6)
+ Text.SetText (Tips[i])
+ else:
+ #disable button
+ Button.SetItemIcon ("",3)
+ Text.SetText ("")
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ TextArea = Window.GetControl (8)
+ TextArea.SetText (11322)
+
+ Button = Window.GetControl (7)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneAbilitiesItemWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ Button = Window.GetControl (10)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseAbilitiesItemWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+import GUIINV
diff --git a/gemrb/GUIScripts/LUCommon.py b/gemrb/GUIScripts/LUCommon.py
new file mode 100644
index 0000000..11c5432
--- /dev/null
+++ b/gemrb/GUIScripts/LUCommon.py
@@ -0,0 +1,386 @@
+# -*-python-*-
+# 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.
+#
+#
+# LUCommon.py - common functions related to leveling up
+
+import GemRB
+import GUICommon
+import CommonTables
+from ie_stats import *
+
+def GetNextLevelExp (Level, Class):
+ """Returns the amount of XP required to gain the next level."""
+ Row = CommonTables.NextLevel.GetRowIndex (Class)
+ if Level < CommonTables.NextLevel.GetColumnCount (Row):
+ return str (CommonTables.NextLevel.GetValue (Row, Level) )
+
+ return 0
+
+def CanLevelUp(actor):
+ """Returns true if the actor can level up."""
+
+ # get our class and placements for Multi'd and Dual'd characters
+ Class = GemRB.GetPlayerStat (actor, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ Class = CommonTables.Classes.GetRowName (Class)
+ Multi = GUICommon.IsMultiClassed (actor, 1)
+ Dual = GUICommon.IsDualClassed (actor, 1)
+
+ # get all the levels and overall xp here
+ xp = GemRB.GetPlayerStat (actor, IE_XP)
+ Levels = [GemRB.GetPlayerStat (actor, IE_LEVEL), GemRB.GetPlayerStat (actor, IE_LEVEL2),\
+ GemRB.GetPlayerStat (actor, IE_LEVEL3)]
+
+ #TODO: double-check this
+ if GemRB.GetPlayerStat(actor, IE_LEVELDRAIN)>0:
+ return 0
+
+ if Multi[0] > 1: # multiclassed
+ xp = xp/Multi[0] # divide the xp evenly between the classes
+ for i in range (Multi[0]):
+ # if any class can level, return 1
+ ClassIndex = CommonTables.Classes.FindValue (5, Multi[i+1])
+ tmpNext = int(GetNextLevelExp (Levels[i], CommonTables.Classes.GetRowName (ClassIndex) ) )
+ if tmpNext != 0 and tmpNext <= xp:
+ return 1
+
+ # didn't find a class that could level
+ return 0
+ elif Dual[0] > 0: # dual classed
+ # get the class we can level
+ Class = CommonTables.Classes.GetRowName (Dual[2])
+ if GUICommon.IsDualSwap(actor):
+ Levels = [Levels[1], Levels[0], Levels[2]]
+
+ # check the class that can be level (single or dual)
+ tmpNext = int(GetNextLevelExp (Levels[0], Class) )
+ return (tmpNext != 0 and tmpNext <= xp)
+
+def SetupSavingThrows (pc, Level=None):
+ """Updates an actors saving throws based upon level.
+
+ Level should contain the actors current level.
+ If Level is None, it is filled with the actors current level."""
+
+ #storing levels as an array makes them easier to deal with
+ if not Level:
+ Levels = [GemRB.GetPlayerStat (pc, IE_LEVEL)-1, \
+ GemRB.GetPlayerStat (pc, IE_LEVEL2)-1, \
+ GemRB.GetPlayerStat (pc, IE_LEVEL3)-1]
+ else:
+ Levels = []
+ for level in Level:
+ Levels.append (level-1)
+
+ #get some basic values
+ Class = [GemRB.GetPlayerStat (pc, IE_CLASS)]
+ Race = GemRB.GetPlayerStat (pc, IE_RACE)
+
+ #adjust the class for multi/dual chars
+ Multi = GUICommon.IsMultiClassed (pc, 1)
+ Dual = GUICommon.IsDualClassed (pc, 1)
+ NumClasses = 1
+ if Multi[0]>1: #get each of the multi-classes
+ NumClasses = Multi[0]
+ Class = [Multi[1], Multi[2], Multi[3]]
+ elif Dual[0]: #only worry about the newer class
+ Class = [CommonTables.Classes.GetValue (Dual[2], 5)]
+ #assume Level is correct if passed
+ if GUICommon.IsDualSwap(pc) and not Level:
+ Levels = [Levels[1], Levels[0], Levels[2]]
+ if NumClasses>len(Levels):
+ return
+
+ #see if we can add racial bonuses to saves
+ Race = CommonTables.Races.GetRowName (CommonTables.Races.FindValue (3, Race) )
+ RaceSaveTableName = CommonTables.Races.GetValue (Race, "SAVE", 0)
+ RaceSaveTable = None
+ if RaceSaveTableName != "-1" and RaceSaveTableName != "*":
+ Con = GemRB.GetPlayerStat (pc, IE_CON, 1)-1
+ RaceSaveTable = GemRB.LoadTable (RaceSaveTableName)
+ if Con >= RaceSaveTable.GetRowCount ():
+ Con = RaceSaveTable.GetRowCount ()-1
+
+ #preload our tables to limit multi-classed lookups
+ SaveTables = []
+ ClassBonus = 0
+ for i in range (NumClasses):
+ Row = CommonTables.Classes.FindValue (5, Class[i])
+ RowName = CommonTables.Classes.GetRowName (Row)
+ SaveName = CommonTables.Classes.GetValue (RowName, "SAVE", 0)
+ SaveTables.append (GemRB.LoadTable (SaveName) )
+ #use numeric value
+ ClassBonus += CommonTables.ClassSkills.GetValue (RowName, "SAVEBONUS", 1)
+
+ if not len (SaveTables):
+ return
+
+ #make sure to limit the levels to the table allowable
+ MaxLevel = SaveTables[0].GetColumnCount ()-1
+ for i in range (len(Levels)):
+ if Levels[i] > MaxLevel:
+ Levels[i] = MaxLevel
+
+ #save the saves
+ for row in range (5):
+ CurrentSave = GemRB.GetPlayerStat(pc, IE_SAVEVSDEATH+i, 1)
+ for i in range (NumClasses):
+ #loop through each class and update the save value if we have
+ #a better save
+ TmpSave = SaveTables[i].GetValue (row, Levels[i])
+ if TmpSave and (TmpSave < CurrentSave or i == 0):
+ CurrentSave = TmpSave
+
+ #add racial bonuses if applicable (small pc's)
+ if RaceSaveTable:
+ CurrentSave -= RaceSaveTable.GetValue (row, Con)
+
+ #add class bonuses if applicable (paladin)
+ CurrentSave -= ClassBonus
+ GemRB.SetPlayerStat (pc, IE_SAVEVSDEATH+row, CurrentSave)
+ return
+
+def GetNextLevelFromExp (XP, Class):
+ """Gets the next level based on current experience."""
+
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ ClassName = CommonTables.Classes.GetRowName (ClassIndex)
+ Row = CommonTables.NextLevel.GetRowIndex (ClassName)
+ for i in range(1, CommonTables.NextLevel.GetColumnCount()-1):
+ if XP < CommonTables.NextLevel.GetValue (Row, i):
+ return i
+ # fix hacked characters that have more xp than the xp cap
+ return 40
+
+def SetupThaco (pc, Level=None):
+ """Updates an actors THAC0 based upon level.
+
+ Level should contain the actors current level.
+ If Level is None it is filled with the actors current level."""
+
+ #storing levels as an array makes them easier to deal with
+ if not Level:
+ Levels = [GemRB.GetPlayerStat (pc, IE_LEVEL)-1, \
+ GemRB.GetPlayerStat (pc, IE_LEVEL2)-1, \
+ GemRB.GetPlayerStat (pc, IE_LEVEL3)-1]
+ else:
+ Levels = []
+ for level in Level:
+ Levels.append (level-1)
+
+ #get some basic values
+ Class = [GemRB.GetPlayerStat (pc, IE_CLASS)]
+ ThacoTable = GemRB.LoadTable ("THAC0")
+
+ #adjust the class for multi/dual chars
+ Multi = GUICommon.IsMultiClassed (pc, 1)
+ Dual = GUICommon.IsDualClassed (pc, 1)
+ NumClasses = 1
+ if Multi[0]>1: #get each of the multi-classes
+ NumClasses = Multi[0]
+ Class = [Multi[1], Multi[2], Multi[3]]
+ elif Dual[0]: #only worry about the newer class
+ Class = [CommonTables.Classes.GetValue (Dual[2], 5)]
+ #assume Level is correct if passed
+ if GUICommon.IsDualSwap(pc) and not Level:
+ Levels = [Levels[1], Levels[0], Levels[2]]
+ if NumClasses>len(Levels):
+ return
+
+ #make sure to limit the levels to the table allowable
+ MaxLevel = ThacoTable.GetColumnCount ()-1
+ for i in range (len(Levels)):
+ if Levels[i] > MaxLevel:
+ Levels[i] = MaxLevel
+
+ CurrentThaco = GemRB.GetPlayerStat (pc, IE_TOHIT, 1)
+ NewThaco = 0
+ for i in range (NumClasses):
+ #loop through each class and update the save value if we have
+ #a better thac0
+ ClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, Class[i]))
+ TmpThaco = ThacoTable.GetValue (ClassName, str(Levels[i]+1))
+ if TmpThaco < CurrentThaco:
+ NewThaco = 1
+ CurrentThaco = TmpThaco
+
+ #only update if we have a better thac0
+ if NewThaco:
+ GemRB.SetPlayerStat (pc, IE_TOHIT, CurrentThaco)
+ return
+
+def SetupLore (pc, LevelDiff=None):
+ """Updates an actors lore based upon level.
+
+ Level should contain the actors current level.
+ LevelDiff should contain the change in levels.
+ Level and LevelDiff must be of the same length.
+ If either are None, they are filled with the actors current level."""
+
+ #storing levels as an array makes them easier to deal with
+ if not LevelDiff:
+ LevelDiffs = [GemRB.GetPlayerStat (pc, IE_LEVEL), \
+ GemRB.GetPlayerStat (pc, IE_LEVEL2), \
+ GemRB.GetPlayerStat (pc, IE_LEVEL3)]
+ else:
+ LevelDiffs = []
+ for diff in LevelDiff:
+ LevelDiffs.append (diff)
+
+ #get some basic values
+ Class = [GemRB.GetPlayerStat (pc, IE_CLASS)]
+ LoreTable = GemRB.LoadTable ("lore")
+
+ #adjust the class for multi/dual chars
+ Multi = GUICommon.IsMultiClassed (pc, 1)
+ Dual = GUICommon.IsDualClassed (pc, 1)
+ NumClasses = 1
+ if Multi[0]>1: #get each of the multi-classes
+ NumClasses = Multi[0]
+ Class = [Multi[1], Multi[2], Multi[3]]
+ elif Dual[0]: #only worry about the newer class
+ Class = [CommonTables.Classes.GetValue (Dual[2], 5)]
+ #if LevelDiff is passed, we assume it is correct
+ if GUICommon.IsDualSwap(pc) and not LevelDiff:
+ LevelDiffs = [LevelDiffs[1], LevelDiffs[0], LevelDiffs[2]]
+ if NumClasses>len(LevelDiffs):
+ return
+
+ #loop through each class and update the lore value if we have
+ CurrentLore = GemRB.GetPlayerStat (pc, IE_LORE, 1)
+ for i in range (NumClasses):
+ #correct unlisted progressions
+ ClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, Class[i]) )
+ if ClassName == "SORCERER":
+ ClassName = "MAGE"
+ elif ClassName == "MONK": #monks have a rate of 1, so this is arbitrary
+ ClassName = "CLERIC"
+
+ #add the lore from this class to the total lore
+ TmpLore = LevelDiffs[i] * LoreTable.GetValue (ClassName, "RATE", 1)
+ if TmpLore:
+ CurrentLore += TmpLore
+
+ #update our lore value
+ GemRB.SetPlayerStat (pc, IE_LORE, CurrentLore)
+ return
+
+def SetupHP (pc, Level=None, LevelDiff=None):
+ """Updates an actors hp based upon level.
+
+ Level should contain the actors current level.
+ LevelDiff should contain the change in levels.
+ Level and LevelDiff must be of the same length.
+ If either are None, they are filled with the actors current level."""
+
+ #storing levels as an array makes them easier to deal with
+ if not Level:
+ Levels = [GemRB.GetPlayerStat (pc, IE_LEVEL), \
+ GemRB.GetPlayerStat (pc, IE_LEVEL2), \
+ GemRB.GetPlayerStat (pc, IE_LEVEL3)]
+ else:
+ Levels = []
+ for level in Level:
+ Levels.append (level)
+ if not LevelDiff:
+ LevelDiffs = [GemRB.GetPlayerStat (pc, IE_LEVEL), \
+ GemRB.GetPlayerStat (pc, IE_LEVEL2), \
+ GemRB.GetPlayerStat (pc, IE_LEVEL3)]
+ else:
+ LevelDiffs = []
+ for diff in LevelDiff:
+ LevelDiffs.append (diff)
+ if len (Levels) != len (LevelDiffs):
+ return
+
+ #get some basic values
+ Class = [GemRB.GetPlayerStat (pc, IE_CLASS)]
+
+ #adjust the class for multi/dual chars
+ Multi = GUICommon.IsMultiClassed (pc, 1)
+ Dual = GUICommon.IsDualClassed (pc, 1)
+ NumClasses = 1
+ if Multi[0]>1: #get each of the multi-classes
+ NumClasses = Multi[0]
+ Class = [Multi[1], Multi[2], Multi[3]]
+ elif Dual[0]: #only worry about the newer class
+ #we only get the hp bonus if the old class is reactivated
+ if (Levels[0]<=Levels[1]):
+ return
+ Class = [CommonTables.Classes.GetValue (Dual[2], 5)]
+ #if Level and LevelDiff are passed, we assume it is correct
+ if GUICommon.IsDualSwap(pc) and not Level and not LevelDiff:
+ LevelDiffs = [LevelDiffs[1], LevelDiffs[0], LevelDiffs[2]]
+ if NumClasses>len(Levels):
+ return
+
+ #get the correct hp for barbarians
+ Kit = GUICommon.GetKitIndex (pc)
+ ClassName = None
+ if Kit and not Dual[0] and Multi[0]<2:
+ KitName = CommonTables.KitList.GetValue (Kit, 0, 0)
+ if CommonTables.Classes.GetRowIndex (KitName) >= 0:
+ ClassName = KitName
+
+ #loop through each class and update the hp
+ OldHP = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS, 1)
+ CurrentHP = 0
+ Divisor = float (NumClasses)
+ for i in range (NumClasses):
+ #check this classes hp table for any gain
+ if not ClassName or NumClasses > 1:
+ ClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, Class[i]) )
+ HPTable = CommonTables.Classes.GetValue (ClassName, "HP")
+ HPTable = GemRB.LoadTable (HPTable)
+
+ #make sure we are within table ranges
+ MaxLevel = HPTable.GetRowCount()-1
+ LowLevel = Levels[i]-LevelDiffs[i]
+ HiLevel = Levels[i]
+ if LowLevel >= HiLevel:
+ continue
+ if LowLevel < 0:
+ LowLevel = 0
+ elif LowLevel > MaxLevel:
+ LowLevel = MaxLevel
+ if HiLevel < 0:
+ HiLevel = 0
+ elif HiLevel > MaxLevel:
+ HiLevel = MaxLevel
+
+ #add all the hp for the given level
+ #we use ceil to ensure each class gets hp
+ for level in range(LowLevel, HiLevel):
+ rolls = HPTable.GetValue (level, 1)
+ bonus = HPTable.GetValue (level, 2)
+
+ # we only do a roll if core diff or higher, or uncheck max
+ if rolls:
+ if GemRB.GetVar ("Difficulty Level") >= 3 and not GemRB.GetVar ("Maximum HP"):
+ CurrentHP += int (GemRB.Roll (rolls, HPTable.GetValue (level, 0), bonus) / Divisor + 0.5)
+ else:
+ CurrentHP += int ((rolls * HPTable.GetValue (level, 0) + bonus) / Divisor + 0.5)
+ else:
+ CurrentHP += int (bonus / Divisor + 0.5)
+ CurrentHP = int (CurrentHP)
+
+ #update our hp values
+ GemRB.SetPlayerStat (pc, IE_MAXHITPOINTS, CurrentHP+OldHP)
+ GemRB.SetPlayerStat (pc, IE_HITPOINTS, GemRB.GetPlayerStat (pc, IE_HITPOINTS, 1)+CurrentHP)
+ return
diff --git a/gemrb/GUIScripts/LUProfsSelection.py b/gemrb/GUIScripts/LUProfsSelection.py
new file mode 100644
index 0000000..55be6e4
--- /dev/null
+++ b/gemrb/GUIScripts/LUProfsSelection.py
@@ -0,0 +1,477 @@
+# -*-python-*-
+# 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.
+#
+# LUProfsSelection.py - Modular selection of proficiencies
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import GUICommon
+import CommonTables
+
+#the different types possible
+LUPROFS_TYPE_LEVELUP = 1
+LUPROFS_TYPE_DUALCLASS = 2
+LUPROFS_TYPE_CHARGEN = 4
+
+#refs to the script calling this
+ProfsWindow = 0
+ProfsCallback = 0
+
+#all our offsets for the various parts of the window
+ProfsOffsetSum = 0
+ProfsOffsetButton1 = 0
+ProfsOffsetLabel = 0
+ProfsOffsetStar = 0
+ProfsOffsetPress = 0
+Profs2ndOffsetButton1 = Profs2ndOffsetStar = Profs2ndOffsetLabel = -1
+ClassNameSave = 0
+OddIDs = 0
+
+#internal variables
+ProfsPointsLeft = 0
+ProfsNumButtons = 0
+ProfsTopIndex = 0
+ProfsTextArea = 0
+ProfsColumn = 0
+ProfsTable = 0
+ProfCount = 0
+
+# the table offset is used in bg2, since in the end they made it use different
+# profs than in bg1, but left the table entries intact
+ProfsTableOffset = 0
+ProfsScrollBar = None
+
+# iwd1/bg1 table that holds the allowed proficiency <-> class mapping
+ClassWeaponsTable = None
+
+ProfsType = None
+
+def SetupProfsWindow (pc, type, window, callback, level1=[0,0,0], level2=[1,1,1], classid=0, scroll=True, profTableOffset=8):
+ """Opens the proficiency selection window.
+
+ type specifies the type of selection we are doing; choices are above.
+ window specifies the window to be updated.
+ callback specifies the function to call on changes.
+ classid is sent only during dualclassing to specify the new class."""
+
+ global ProfsOffsetSum, ProfsOffsetButton1, ProfsOffsetLabel, ProfsOffsetStar, OddIDs
+ global ProfsOffsetPress, ProfsPointsLeft, ProfsNumButtons, ProfsTopIndex
+ global ProfsScrollBar, ProfsTableOffset, ProfsType
+ global ProfsWindow, ProfsCallback, ProfsTextArea, ProfsColumn, ProfsTable, ProfCount
+ global Profs2ndOffsetButton1, Profs2ndOffsetStar, Profs2ndOffsetLabel, ClassNameSave, ClassWeaponsTable
+
+ # make sure we're within ranges
+ GemRB.SetVar ("ProfsPointsLeft", 0)
+ if not window or not callback or len(level1)!=len(level2):
+ return
+
+ # save the values we'll point to
+ ProfsWindow = window
+ ProfsCallback = callback
+ ProfsTableOffset = profTableOffset
+ ProfsType = type
+
+ if type == LUPROFS_TYPE_CHARGEN and GUICommon.GameIsBG2(): #chargen
+ ProfsOffsetSum = 9
+ ProfsOffsetButton1 = 11
+ ProfsOffsetStar = 27
+ ProfsOffsetLabel = 1
+ ProfsOffsetPress = 69
+ ProfsNumButtons = 8
+ ProfsTextArea = ProfsWindow.GetControl (68)
+ ProfsTextArea.SetText (9588)
+ if (scroll):
+ ProfsScrollBar = ProfsWindow.GetControl (78)
+ elif type == LUPROFS_TYPE_CHARGEN and GUICommon.GameIsBG1(): #chargen
+ ProfsOffsetSum = 9
+ ProfsOffsetButton1 = 11
+ ProfsOffsetStar = 27
+ ProfsOffsetLabel = 1
+ ProfsOffsetPress = 69
+ ProfsNumButtons = 8
+ ProfsTextArea = ProfsWindow.GetControl (68)
+ ProfsTextArea.SetText (9588)
+ if (scroll):
+ ProfsScrollBar = ProfsWindow.GetControl (78)
+ elif type == LUPROFS_TYPE_LEVELUP and GUICommon.GameIsBG2(): #levelup
+ ProfsOffsetSum = 36
+ ProfsOffsetButton1 = 1
+ ProfsOffsetStar = 48
+ ProfsOffsetLabel = 24
+ ProfsOffsetPress = 112
+ ProfsNumButtons = 7
+ ProfsTextArea = ProfsWindow.GetControl (110)
+ ProfsScrollBar = ProfsWindow.GetControl (108)
+ elif type == LUPROFS_TYPE_LEVELUP and GUICommon.GameIsBG1(): #levelup
+ ProfsOffsetSum = 36
+ ProfsOffsetButton1 = 1
+ ProfsOffsetStar = 48
+ ProfsOffsetLabel = 24
+ ProfsOffsetPress = 17
+ ProfsNumButtons = 8
+ ProfsTextArea = ProfsWindow.GetControl (42)
+ if (scroll):
+ ProfsScrollBar = ProfsWindow.GetControl (108)
+ elif type == LUPROFS_TYPE_LEVELUP and GUICommon.GameIsIWD1(): #levelup
+ ProfsOffsetSum = 36
+ ProfsOffsetButton1 = 1
+ ProfsOffsetStar = 48
+ ProfsOffsetLabel = 24
+ ProfsOffsetPress = -1
+ ProfsNumButtons = 15 # 8+7, the 7 are done with the following vars
+ Profs2ndOffsetButton1 = 150
+ Profs2ndOffsetStar = 115
+ Profs2ndOffsetLabel = 108
+ ProfsTextArea = ProfsWindow.GetControl (42)
+ if (scroll):
+ ProfsScrollBar = ProfsWindow.GetControl (108)
+ OddIDs = 0
+ elif type == LUPROFS_TYPE_DUALCLASS and GUICommon.GameIsIWD1(): #dualclass
+ ProfsOffsetSum = 40
+ ProfsOffsetButton1 = 50
+ ProfsOffsetStar = 0
+ ProfsOffsetLabel = 41
+ ProfsOffsetPress = -1 #66
+ ProfsNumButtons = 15
+ Profs2ndOffsetButton1 = 78
+ Profs2ndOffsetStar = 92
+ Profs2ndOffsetLabel = 126
+ ProfsTextArea = ProfsWindow.GetControl (74)
+ ProfsTextArea.SetText (9588)
+ if (scroll):
+ ProfsScrollBar = ProfsWindow.GetControl (None)
+ OddIDs = 1
+ elif type == LUPROFS_TYPE_DUALCLASS and GUICommon.GameIsBG1(): #dualclass
+ ProfsOffsetSum = 40
+ ProfsOffsetButton1 = 50
+ ProfsOffsetStar = 0
+ ProfsOffsetLabel = 41
+ ProfsOffsetPress = -1 #FIXME
+ ProfsNumButtons = 8
+ ProfsTextArea = ProfsWindow.GetControl (74)
+ ProfsTextArea.SetText (9588)
+ if (scroll):
+ ProfsScrollBar = ProfsWindow.GetControl (None)
+ elif type == LUPROFS_TYPE_DUALCLASS: #dualclass
+ ProfsOffsetSum = 40
+ ProfsOffsetButton1 = 50
+ ProfsOffsetStar = 0
+ ProfsOffsetLabel = 41
+ ProfsOffsetPress = 66
+ ProfsNumButtons = 8
+ ProfsTextArea = ProfsWindow.GetControl (74)
+ ProfsTextArea.SetText (9588)
+ if (scroll):
+ ProfsScrollBar = ProfsWindow.GetControl (78)
+ else: #unknown
+ return
+
+ #nullify internal variables
+ GemRB.SetVar ("ProfsTopIndex", 0)
+ ProfsPointsLeft = 0
+ ProfsTopIndex = 0
+
+ ProfsTable = GemRB.LoadTable ("profs")
+ if GUICommon.GameIsIWD1() or GUICommon.GameIsBG1():
+ ClassWeaponsTable = GemRB.LoadTable ("clasweap")
+ else:
+ ClassWeaponsTable = None
+
+ #get the class name
+ IsDual = GUICommon.IsDualClassed (pc, 1)
+ if classid: #for dual classes when we can't get the class dualing to
+ Class = classid
+ elif IsDual[0]:
+ Class = CommonTables.Classes.GetValue (IsDual[2], 5)
+ else:
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ ClassName = CommonTables.Classes.FindValue (5, Class)
+ ClassName = CommonTables.Classes.GetRowName (ClassName)
+ if ClassName == "SORCERER":
+ ClassName = "MAGE"
+ ClassNameSave = ClassName
+
+ #find the class with the greatest prof potential
+ FastestProf = 0
+ ProfsRate = 100
+ IsMulti = GUICommon.IsMultiClassed (pc, 1)
+ if IsMulti[0] > 1:
+ for i in range (1, IsMulti[0]+1):
+ TmpRate = ProfsTable.GetValue (IsMulti[i]-1, 1)
+ if TmpRate < ProfsRate:
+ ProfsRate = TmpRate
+ FastestProf = i-1
+ else:
+ ProfsRate = ProfsTable.GetValue (Class-1, 1)
+
+ #figure out how many prof points we have
+ if sum (level1) == 0: #character is being generated (either chargen or dual)
+ ProfsPointsLeft = ProfsTable.GetValue (ClassName, "FIRST_LEVEL")
+
+ #we need these 2 number to floor before subtracting
+ ProfsPointsLeft += level2[FastestProf]/ProfsRate - level1[FastestProf]/ProfsRate
+
+ #setup prof vars for passing between functions
+ ProfsTable = GemRB.LoadTable ("weapprof")
+
+ # if we have the classweapons table, use it
+ if ClassWeaponsTable:
+ ProfsColumn = ClassWeaponsTable.GetRowIndex (ClassName)
+ else:
+ Kit = GUICommon.GetKitIndex (pc)
+ if Kit and type != LUPROFS_TYPE_DUALCLASS and IsMulti[0]<2 and not IsDual[0]:
+ #if we do kit with dualclass, we'll get the old kit
+ #also don't want to worry about kitted multis
+ ProfsColumn = CommonTables.KitList.GetValue (Kit, 5)
+ else:
+ ProfsColumn = ProfsTable.GetColumnIndex (ClassName)
+
+ #setup some basic counts
+ RowCount = ProfsTable.GetRowCount () - ProfsTableOffset + 1
+ ProfCount = RowCount-ProfsNumButtons #decrease it with the number of controls
+
+ ProfsAssignable = 0
+ TwoWeapIndex = ProfsTable.GetRowIndex ("2WEAPON")
+ for i in range(RowCount):
+ ProfName = ProfsTable.GetValue (i+ProfsTableOffset, 1)
+ #decrease it with the number of invalid proficiencies
+ if ProfName > 0x1000000 or ProfName < 0:
+ ProfCount -= 1
+
+ #we only need the low 3 bits for profeciencies on levelup; otherwise
+ #we just set them all to 0
+ currentprof = 0
+ if type == LUPROFS_TYPE_LEVELUP:
+ stat = ProfsTable.GetValue (i+ProfsTableOffset, 0)
+ if GUICommon.GameIsBG1():
+ stat = stat + IE_PROFICIENCYBASTARDSWORD
+ currentprof = GemRB.GetPlayerStat (pc, stat)&0x07
+ else:
+ #rangers always get 2 points in 2 weapons style
+ if (i+ProfsTableOffset) == TwoWeapIndex and "RANGER" in ClassName.split("_"):
+ currentprof = 2
+ GemRB.SetVar ("Prof "+str(i), currentprof)
+ GemRB.SetVar ("ProfBase "+str(i), currentprof)
+
+ #see if we can assign to this prof
+ if ClassWeaponsTable:
+ maxprof = ClassWeaponsTable.GetValue (ProfsColumn, i) # this table has profs as rows
+ else:
+ maxprof = ProfsTable.GetValue(i+ProfsTableOffset, ProfsColumn)
+ if maxprof > currentprof:
+ ProfsAssignable += maxprof-currentprof
+
+ #correct the profs left if we can't assign that much
+ if ProfsPointsLeft > ProfsAssignable:
+ ProfsPointsLeft = ProfsAssignable
+ GemRB.SetVar ("ProfsPointsLeft", ProfsPointsLeft)
+
+ # setup the +/- and info controls
+ for i in range (ProfsNumButtons):
+ if ProfsOffsetPress != -1:
+ Button=ProfsWindow.GetControl(i+ProfsOffsetPress)
+ Button.SetVarAssoc("Prof", i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ProfsJustPress)
+
+ cid = i*2+ProfsOffsetButton1
+ if Profs2ndOffsetButton1 != -1 and i > 7:
+ cid = (i-8)*2+Profs2ndOffsetButton1
+
+ Button=ProfsWindow.GetControl(cid)
+ Button.SetVarAssoc("Prof", i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ProfsLeftPress)
+
+ Button=ProfsWindow.GetControl(cid+1)
+ Button.SetVarAssoc("Prof", i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ProfsRightPress)
+
+ if(ProfsScrollBar):
+ # proficiencies scrollbar
+ ProfsScrollBar.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, ProfsScrollBarPress)
+ ProfsScrollBar.SetDefaultScrollBar ()
+ ProfsScrollBar.SetVarAssoc ("ProfsTopIndex", ProfCount)
+ ProfsRedraw (1)
+ return
+
+def ProfsRedraw (first=0):
+ """Redraws the proficiencies part of the window.
+
+ If first is true, it skips ahead to the first assignable proficiency."""
+
+ global ProfsTopIndex, ProfsScrollBar
+
+ ProfSumLabel = ProfsWindow.GetControl(0x10000000+ProfsOffsetSum)
+ ProfSumLabel.SetText(str(ProfsPointsLeft))
+ SkipProfs = []
+
+ for i in range(ProfsNumButtons):
+ Pos=ProfsTopIndex+i
+ ProfName = ProfsTable.GetValue(Pos+ProfsTableOffset, 1) #we add the bg1 skill count offset
+ if ClassWeaponsTable: # iwd
+ MaxProf = ClassWeaponsTable.GetValue (ClassNameSave, ProfsTable.GetRowName(Pos))
+ else:
+ MaxProf = ProfsTable.GetValue(Pos+ProfsTableOffset, ProfsColumn)
+
+ cid = i*2+ProfsOffsetButton1
+ if Profs2ndOffsetButton1 != -1 and i > 7:
+ cid = (i-8)*2+Profs2ndOffsetButton1
+ Button1=ProfsWindow.GetControl (cid)
+ Button2=ProfsWindow.GetControl (cid+1)
+ if MaxProf == 0:
+ Button1.SetState(IE_GUI_BUTTON_DISABLED)
+ Button2.SetState(IE_GUI_BUTTON_DISABLED)
+ Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+ Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+ if GUICommon.GameIsBG2() and (i==0 or ((i-1) in SkipProfs)):
+ SkipProfs.append (i)
+ else:
+ Button1.SetState(IE_GUI_BUTTON_ENABLED)
+ Button2.SetState(IE_GUI_BUTTON_ENABLED)
+ Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND)
+ Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND)
+
+ cid = 0x10000000 + ProfsOffsetLabel + i
+ if Profs2ndOffsetLabel != -1 and i > 7:
+ if OddIDs:
+ cid = 0x10000000 + Profs2ndOffsetLabel + 2*(i - 8)
+ else:
+ cid = 0x10000000 + Profs2ndOffsetLabel + i - 8
+
+ Label=ProfsWindow.GetControl (cid)
+ Label.SetText(ProfName)
+
+ ActPoint = GemRB.GetVar("Prof "+str(Pos) )
+ for j in range(5): #5 is maximum distributable
+ cid = i*5 + j + ProfsOffsetStar
+ if Profs2ndOffsetStar != -1 and i > 7:
+ cid = (i-8)*5 + j + Profs2ndOffsetStar
+ Star=ProfsWindow.GetControl (cid)
+ Star.SetSprites("GUIPFC", 0, 0, 0, 0, 0)
+ if ActPoint > j:
+ Star.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND)
+ else:
+ Star.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+
+ if first and len (SkipProfs):
+ ProfsTopIndex += SkipProfs[-1]+1
+ GemRB.SetVar ("ProfsTopIndex", ProfsTopIndex)
+ if (ProfsScrollBar):
+ ProfsScrollBar.SetVarAssoc ("ProfsTopIndex", ProfCount)
+ ProfsRedraw ()
+ return
+
+def ProfsScrollBarPress():
+ """Scrolls the window by reassigning ProfsTopIndex."""
+
+ global ProfsTopIndex
+
+ ProfsTopIndex = GemRB.GetVar ("ProfsTopIndex")
+ ProfsRedraw ()
+ return
+
+def ProfsJustPress():
+ """Updates the text area with a description of the proficiency."""
+ Pos = GemRB.GetVar ("Prof")+ProfsTopIndex
+ ProfsTextArea.SetText (ProfsTable.GetValue(Pos+ProfsTableOffset, 2) )
+ return
+
+def ProfsRightPress():
+ """Decrease the current proficiency by one."""
+
+ global ProfsPointsLeft
+
+ Pos = GemRB.GetVar("Prof")+ProfsTopIndex
+ ProfsTextArea.SetText(ProfsTable.GetValue(Pos+ProfsTableOffset, 2) )
+ ActPoint = GemRB.GetVar("Prof "+str(Pos) )
+ MinPoint = GemRB.GetVar ("ProfBase "+str(Pos) )
+ if ActPoint <= 0 or ActPoint <= MinPoint:
+ return
+ GemRB.SetVar("Prof "+str(Pos),ActPoint-1)
+ ProfsPointsLeft += 1
+ GemRB.SetVar ("ProfsPointsLeft", ProfsPointsLeft)
+ ProfsRedraw ()
+ ProfsCallback ()
+ return
+
+def ProfsLeftPress():
+ """Increases the current proficiency by one."""
+
+ global ProfsPointsLeft
+
+ Pos = GemRB.GetVar("Prof")+ProfsTopIndex
+ ProfsTextArea.SetText(ProfsTable.GetValue(Pos+ProfsTableOffset, 2) )
+ if ProfsPointsLeft == 0:
+ return
+ if GUICommon.GameIsIWD1() or GUICommon.GameIsBG1():
+ ProfMaxTable = GemRB.LoadTable ("profsmax")
+ MaxProf = ProfMaxTable.GetValue(ClassNameSave, "OTHER_LEVELS")
+ else:
+ MaxProf = ProfsTable.GetValue(Pos+ProfsTableOffset, ProfsColumn)
+ if MaxProf>5:
+ MaxProf = 5
+ # FIXME: use profsmax.2da (in all games? could be problematic for weapon styles)
+ #if (MaxProf>2) and GUICommon.GameIsBG1():
+ # MaxProf = 2
+
+ ActPoint = GemRB.GetVar("Prof "+str(Pos) )
+ if ActPoint >= MaxProf:
+ return
+ GemRB.SetVar("Prof "+str(Pos),ActPoint+1)
+ ProfsPointsLeft -= 1
+ GemRB.SetVar ("ProfsPointsLeft", ProfsPointsLeft)
+ ProfsRedraw ()
+ ProfsCallback ()
+ return
+
+def ProfsSave (pc, type=LUPROFS_TYPE_LEVELUP):
+ """Updates the actor with the new proficiencies."""
+
+ ProfCount = ProfsTable.GetRowCount () - ProfsTableOffset
+ for i in range(ProfCount): # skip bg1 weapprof.2da proficiencies
+ ProfID = ProfsTable.GetValue (i+ProfsTableOffset, 0)
+ if GUICommon.GameIsBG1():
+ ProfID = ProfID + IE_PROFICIENCYBASTARDSWORD
+ SaveProf = GemRB.GetVar ("Prof "+str(i))
+
+ if type == LUPROFS_TYPE_CHARGEN and GUICommon.GameIsBG2():
+ GemRB.DispelEffect (pc, "Proficiency", ProfID)
+ else:
+ if type != LUPROFS_TYPE_DUALCLASS:
+ OldProf = GemRB.GetPlayerStat (pc, ProfID) & 0x38
+ SaveProf = OldProf | SaveProf
+ else: # gotta move the old prof to the back for dual class
+ OldProf = GemRB.GetPlayerStat (pc, ProfID) & 0x07
+ SaveProf = (OldProf << 3) | SaveProf
+
+ GemRB.SetPlayerStat (pc, ProfID, SaveProf)
+ if GUICommon.GameIsBG2() and (type == LUPROFS_TYPE_LEVELUP or type == LUPROFS_TYPE_CHARGEN):
+ if SaveProf:
+ GemRB.ApplyEffect (pc, "Proficiency", SaveProf, ProfID)
+ return
+
+def ProfsNullify ():
+ """Resets all of the internal variables to 0."""
+
+ global ProfsTable
+ if not ProfsTable:
+ ProfsTable = GemRB.LoadTable ("weapprof")
+ for i in range (ProfsTable.GetRowCount()-ProfsTableOffset+1): #skip bg1 profs
+ GemRB.SetVar ("Prof "+str(i), 0)
+ GemRB.SetVar ("ProfBase "+str(i), 0)
+ return
diff --git a/gemrb/GUIScripts/LUSkillsSelection.py b/gemrb/GUIScripts/LUSkillsSelection.py
new file mode 100644
index 0000000..2e5fdde
--- /dev/null
+++ b/gemrb/GUIScripts/LUSkillsSelection.py
@@ -0,0 +1,408 @@
+# -*-python-*-
+# 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.
+#
+# LUSkillSelection.py - selection of thief skills
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import GUICommon
+import CommonTables
+
+#constants
+LUSKILLS_TYPE_LEVELUP = 1
+LUSKILLS_TYPE_CHARGEN = 2
+LUSKILLS_TYPE_DUALCLASS = 4
+LUSKILLS_MAX = 250
+
+#refs to the script calling this
+SkillsWindow = 0
+SkillsCallback = 0
+
+#offsets to the various parts of all 3 windows
+SkillsOffsetPress = 0
+SkillsOffsetButton1 = 0
+SkillsOffsetName = 0
+SkillsOffsetPoints = 0
+SkillsOffsetSum = 0
+SkillsNumButtons = 0
+
+#internal variables
+SkillsIndices = []
+SkillPointsLeft = 0
+SkillsTopIndex = 0
+SkillsTable = 0
+SkillsOldPos = 0
+SkillsClickCount = 0
+SkillsOldDirection = 0
+SkillsTextArea = 0
+SkillsKitName = 0
+SkillsAssignable = 0
+
+#WARNING: This WILL NOT show the window, only access it. To see the return, call GemRB.GetVar ("SkillPointsLeft").
+# If nothing can be assigned, it will return 0 prior to accessing any of the window methods.
+def SetupSkillsWindow (pc, type, window, callback, level1=[0,0,0], level2=[1,1,1], classid=0, scroll=True):
+ global SkillsWindow, SkillsCallback, SkillsOffsetPress, SkillsOffsetButton1, SkillsOffsetName
+ global SkillsOffsetPoints, SkillsOffsetSum, SkillsIndices, SkillPointsLeft, SkillsTopIndex
+ global SkillsTable, SkillsOldPos, SkillsClickCount, SkillsOldDirection, SkillsNumButtons
+ global SkillsTextArea, SkillsKitName, SkillsAssignable
+
+ #reset some basic values
+ SkillsWindow = window
+ SkillsCallback = callback
+ SkillsOldPos = 0
+ SkillsClickCount = 0
+ SkillsOldDirection = 0
+ SkillsAssignable = 0
+ SkillsTable = GemRB.LoadTable ("skills")
+ SkillPointsLeft = 0
+ GemRB.SetVar ("SkillPointsLeft", 0)
+ SkillsNullify ()
+
+ #make sure we're within ranges
+ if not window or not callback or len(level1)!=len(level2):
+ return
+
+ #setup the offsets
+ if type == LUSKILLS_TYPE_LEVELUP and GUICommon.GameIsBG2():
+ SkillsOffsetPress = 120
+ SkillsOffsetButton1 = 17
+ SkillsOffsetSum = 37
+ SkillsOffsetName = 32
+ SkillsOffsetPoints = 43
+ SkillsNumButtons = 4
+ SkillsTextArea = SkillsWindow.GetControl (110)
+ ScrollBar = SkillsWindow.GetControl (109)
+ elif type == LUSKILLS_TYPE_LEVELUP:
+ SkillsOffsetPress = -1
+ SkillsOffsetButton1 = 17
+ SkillsOffsetSum = 37
+ SkillsOffsetName = 32
+ SkillsOffsetPoints = 43
+ SkillsNumButtons = 4
+ SkillsTextArea = SkillsWindow.GetControl (42)
+ if (scroll):
+ ScrollBar = SkillsWindow.GetControl (109)
+ elif type == LUSKILLS_TYPE_DUALCLASS:
+ SkillsOffsetPress = 5
+ SkillsOffsetButton1 = 14
+ SkillsOffsetSum = 8
+ SkillsOffsetName = 0
+ SkillsOffsetPoints = 9
+ SkillsNumButtons = 4
+ SkillsTextArea = SkillsWindow.GetControl (22)
+ SkillsTextArea.SetText(17248)
+ if (scroll):
+ ScrollBar = SkillsWindow.GetControl (26)
+ ScrollBar.SetDefaultScrollBar ()
+ elif type == LUSKILLS_TYPE_CHARGEN:
+ SkillsOffsetPress = 21
+ SkillsOffsetButton1 = 11
+ SkillsOffsetSum = 5
+ SkillsOffsetName = 6
+ SkillsOffsetPoints = 1
+ SkillsNumButtons = 4
+ SkillsTextArea = SkillsWindow.GetControl (19)
+ SkillsTextArea.SetText(17248)
+ if (scroll):
+ ScrollBar = SkillsWindow.GetControl (26)
+ ScrollBar.SetDefaultScrollBar ()
+ else:
+ return
+
+ #get our class id and name
+ IsDual = GUICommon.IsDualClassed (pc, 1)
+ IsMulti = GUICommon.IsMultiClassed (pc, 1)
+ if classid: #used when dual-classing
+ Class = classid
+ elif IsDual[0]: #only care about the current class
+ Class = CommonTables.Classes.GetValue (IsDual[2], 5)
+ else:
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ ClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, Class) )
+
+ #get the number of classes
+ if IsMulti[0]>1:
+ NumClasses = IsMulti[0]
+ else:
+ NumClasses = 1
+ if NumClasses > len (level2):
+ return
+
+ #figure out the kitname if we need it
+ #protect against kitted multiclasses
+ Kit = GUICommon.GetKitIndex (pc)
+ if not Kit or type == LUSKILLS_TYPE_DUALCLASS or IsDual[0] or IsMulti[0]>1:
+ SkillsKitName = ClassName
+ else:
+ SkillsKitName = CommonTables.KitList.GetValue (Kit, 0, 0)
+
+ #figure out the correct skills table
+ SkillIndex = -1
+ for i in range (NumClasses):
+ TmpClass = Class
+ if NumClasses > 1:
+ TmpClass = IsMulti[i+1]
+ if (CommonTables.ClassSkills.GetValue (TmpClass, 5, 0) != "*"):
+ SkillIndex = i
+ break
+
+ #see if we got a thief (or monk)
+ SkillsIndices = []
+ if SkillIndex >= 0:
+ #SkillsKitName should be fine as all multis are in classes.2da
+ #also allows for thief kits
+ SkillsAssignable = 1
+ for i in range(SkillsTable.GetRowCount()-2):
+ SkillName = SkillsTable.GetRowName (i+2)
+ if SkillsTable.GetValue (SkillName, SkillsKitName) != -1:
+ SkillsIndices.append(i)
+
+ LevelDiff = []
+ for i in range (NumClasses):
+ LevelDiff.append (level2[i]-level1[i])
+ if level1[SkillIndex] == 0:
+ SkillPointsLeft = SkillsTable.GetValue ("FIRST_LEVEL", SkillsKitName, 1)
+ LevelDiff[SkillIndex] -= 1
+ SkillPointsLeft += LevelDiff[SkillIndex] * SkillsTable.GetValue("RATE", SkillsKitName, 1)
+ TotalSkillsAssignable = 0
+
+ if SkillPointsLeft < 0:
+ #really don't have an entry
+ SkillPointsLeft = 0
+ else:
+ #get racial values for dual-classing
+ SkillRacTable = GemRB.LoadTable ("SKILLRAC")
+ Race = CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (pc, IE_RACE))
+ Race = CommonTables.Races.GetRowName (Race)
+
+ #get the skill values
+ for i in range(SkillsTable.GetRowCount()-2):
+ SkillName = SkillsTable.GetRowName (i+2)
+ SkillID = SkillsTable.GetValue (SkillName, "ID")
+ if type != LUSKILLS_TYPE_LEVELUP: #give racial bonuses to starting classes
+ SkillValue = SkillRacTable.GetValue (Race, SkillName)
+ else:
+ SkillValue = GemRB.GetPlayerStat (pc, SkillID, 1)
+ GemRB.SetVar("Skill "+str(i), SkillValue)
+ GemRB.SetVar("SkillBase "+str(i), SkillValue)
+ TotalSkillsAssignable += LUSKILLS_MAX-SkillValue
+
+ #protect against having more skills than we can assign
+ if SkillPointsLeft > TotalSkillsAssignable:
+ SkillPointsLeft = TotalSkillsAssignable
+ GemRB.SetVar ("SkillPointsLeft", SkillPointsLeft)
+ else:
+ #get ranger and bard skills
+ SpecialSkillsMap = []
+ for i in range(NumClasses):
+ if IsMulti[0]>1:
+ classname = IsMulti[i+1]
+ else:
+ classname = Class
+ classname = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, classname))
+ for table in "RANGERSKILL", "BARDSKILL":
+ SpecialSkillsTable = CommonTables.ClassSkills.GetValue (classname, table)
+ if SpecialSkillsTable != "*":
+ SpecialSkillsMap.append((SpecialSkillsTable, i))
+ break
+ for skills in SpecialSkillsMap:
+ SpecialSkillsTable = GemRB.LoadTable (skills[0])
+ for skill in range(SpecialSkillsTable.GetColumnCount ()):
+ skillname = SpecialSkillsTable.GetColumnName (skill)
+ value = SpecialSkillsTable.GetValue (str(level2[skills[1]]), skillname)
+ skillindex = SkillsTable.GetRowIndex (skillname) - 2
+ GemRB.SetVar ("Skill " + str(skillindex), value)
+ SkillsIndices.append(skillindex)
+
+ #we didn't find anything, so don't continue (will show as a return of 0)
+ #or don't display if we aren't leveling and have a bard/ranger
+ if not len (SkillsIndices) or (not SkillPointsLeft and type != LUSKILLS_TYPE_LEVELUP):
+ SkillSumLabel = SkillsWindow.GetControl(0x10000000+SkillsOffsetSum)
+ SkillSumLabel.SetText("")
+ return
+
+ #skills scrollbar
+ GemRB.SetVar ("SkillsTopIndex", 0)
+ if len(SkillsIndices) > SkillsNumButtons:
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, SkillScrollBarPress)
+ #decrease it with the number of controls on screen (list size) and two unrelated rows
+ ScrollBar.SetVarAssoc ("SkillsTopIndex", SkillsTable.GetRowCount()-SkillsNumButtons-1)
+ else:
+ if len(SkillsIndices) and SkillsAssignable:
+ #autoscroll to the first valid skill; luckily all three monk ones are adjacent
+ GemRB.SetVar ("SkillsTopIndex", SkillsIndices[0])
+
+ #setup all the visible buttons
+ for i in range(len(SkillsIndices)):
+ if i == SkillsNumButtons:
+ break
+ if SkillsOffsetPress != -1:
+ Button = SkillsWindow.GetControl(i+SkillsOffsetPress)
+ Button.SetVarAssoc("Skill",SkillsIndices[i])
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, SkillJustPress)
+
+ Button = SkillsWindow.GetControl(i*2+SkillsOffsetButton1)
+ Button.SetVarAssoc("Skill",SkillsIndices[i])
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, SkillLeftPress)
+
+ Button = SkillsWindow.GetControl(i*2+SkillsOffsetButton1+1)
+ Button.SetVarAssoc("Skill",SkillsIndices[i])
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, SkillRightPress)
+
+ SkillsRedraw ()
+ return
+
+def SkillsRedraw (direction=0):
+ global SkillsOldDirection, SkillsClickCount
+
+ #update how many skill points are left and call the callback function
+ SkillSumLabel = SkillsWindow.GetControl(0x10000000+SkillsOffsetSum)
+ SkillSumLabel.SetText(str(SkillPointsLeft))
+
+ for i in range (SkillsNumButtons):
+ if len(SkillsIndices) <= i:
+ SkillsHide (i)
+ continue
+
+ #show the current skills name
+ Pos = SkillsIndices[SkillsTopIndex+i]
+ SkillName = SkillsTable.GetValue (SkillsTable.GetRowName (Pos+2), "CAP_REF")
+ Label = SkillsWindow.GetControl (0x10000000+SkillsOffsetName+i)
+ Label.SetText (SkillName)
+
+ #enable/disable the button if we can(not) get the skills
+ SkillName = SkillsTable.GetRowName (Pos+2)
+ Ok = SkillsTable.GetValue (SkillName, SkillsKitName) and SkillsAssignable
+ Button1 = SkillsWindow.GetControl(i*2+SkillsOffsetButton1)
+ Button2 = SkillsWindow.GetControl(i*2+SkillsOffsetButton1+1)
+ if not Ok:
+ Button1.SetState(IE_GUI_BUTTON_DISABLED)
+ Button2.SetState(IE_GUI_BUTTON_DISABLED)
+ Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+ Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+ else:
+ Button1.SetState(IE_GUI_BUTTON_ENABLED)
+ Button2.SetState(IE_GUI_BUTTON_ENABLED)
+ Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND)
+ Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_NAND)
+
+ #show how many points are allocated to this skill
+ Label = SkillsWindow.GetControl(0x10000000+SkillsOffsetPoints+i)
+ ActPoint = GemRB.GetVar("Skill "+str(Pos) )
+ Label.SetText(str(ActPoint))
+
+ #setup doublespeed
+ if SkillsOldDirection == direction:
+ SkillsClickCount = SkillsClickCount + 1
+ if SkillsClickCount>10:
+ GemRB.SetRepeatClickFlags(GEM_RK_QUADRUPLESPEED, OP_OR)
+ return
+
+ SkillsOldDirection = direction
+ SkillsClickCount = 0
+ GemRB.SetRepeatClickFlags(GEM_RK_QUADRUPLESPEED, OP_NAND)
+ return
+
+def SkillJustPress():
+ Pos = GemRB.GetVar("Skill")+SkillsTopIndex
+ SkillsTextArea.SetText (SkillsTable.GetValue (SkillsTable.GetRowName (Pos+2), "DESC_REF"))
+ return
+
+def SkillRightPress():
+ global SkillPointsLeft, SkillsClickCount, SkillsOldPos
+
+ Pos = GemRB.GetVar("Skill")+SkillsTopIndex
+ SkillsTextArea.SetText (SkillsTable.GetValue (SkillsTable.GetRowName (Pos+2), "DESC_REF"))
+ ActPoint = GemRB.GetVar("Skill "+str(Pos) )
+ BasePoint = GemRB.GetVar("SkillBase "+str(Pos) )
+ if ActPoint <= 0 or ActPoint <= BasePoint:
+ return
+ GemRB.SetVar("Skill "+str(Pos),ActPoint-1)
+ SkillPointsLeft = SkillPointsLeft + 1
+ if SkillsOldPos != Pos:
+ SkillsOldPos = Pos
+ SkillsClickCount = 0
+
+ GemRB.SetVar ("SkillPointsLeft", SkillPointsLeft)
+ SkillsRedraw(2)
+ SkillsCallback ()
+ return
+
+def SkillLeftPress():
+ global SkillPointsLeft, SkillsClickCount, SkillsOldPos
+
+ Pos = GemRB.GetVar("Skill")+SkillsTopIndex
+ SkillsTextArea.SetText (SkillsTable.GetValue (SkillsTable.GetRowName (Pos+2), "DESC_REF"))
+ if SkillPointsLeft == 0:
+ return
+ ActPoint = GemRB.GetVar("Skill "+str(Pos) )
+ if ActPoint >= LUSKILLS_MAX:
+ return
+ GemRB.SetVar("Skill "+str(Pos), ActPoint+1)
+ SkillPointsLeft = SkillPointsLeft - 1
+ if SkillsOldPos != Pos:
+ SkillsOldPos = Pos
+ SkillsClickCount = 0
+
+ GemRB.SetVar ("SkillPointsLeft", SkillPointsLeft)
+ SkillsRedraw(1)
+ SkillsCallback ()
+ return
+
+def SkillScrollBarPress():
+ global SkillsTopIndex
+
+ SkillsTopIndex = GemRB.GetVar("SkillsTopIndex")
+ SkillsRedraw ()
+ return
+
+# saves all the skills
+# TODO: change the layout of the iwd/how skills table to match the rest
+def SkillsSave (pc):
+ global SkillsTable
+ if not SkillsTable:
+ SkillsTable = GemRB.LoadTable ("skills")
+
+ for i in range(SkillsTable.GetRowCount() - 2):
+ SkillName = SkillsTable.GetRowName (i+2)
+ SkillID = SkillsTable.GetValue (SkillName, "ID")
+ SkillValue = GemRB.GetVar ("Skill "+str(i))
+ if SkillValue >= 0:
+ GemRB.SetPlayerStat (pc, SkillID, SkillValue)
+
+def SkillsNullify ():
+ global SkillsTable
+ if not SkillsTable:
+ SkillsTable = GemRB.LoadTable ("skills")
+
+ for i in range(SkillsTable.GetRowCount()-2):
+ GemRB.SetVar ("Skill "+str(i), 0)
+ GemRB.SetVar ("SkillBase "+str(i), 0)
+
+def SkillsHide (i):
+ Label = SkillsWindow.GetControl (0x10000000+SkillsOffsetName+i)
+ Label.SetText ("")
+ Button1 = SkillsWindow.GetControl(i*2+SkillsOffsetButton1)
+ Button1.SetState(IE_GUI_BUTTON_DISABLED)
+ Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+ Button2 = SkillsWindow.GetControl(i*2+SkillsOffsetButton1+1)
+ Button2.SetState(IE_GUI_BUTTON_DISABLED)
+ Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+ Label = SkillsWindow.GetControl(0x10000000+SkillsOffsetPoints+i)
+ Label.SetText("")
diff --git a/gemrb/GUIScripts/LUSpellSelection.py b/gemrb/GUIScripts/LUSpellSelection.py
new file mode 100644
index 0000000..2ed1790
--- /dev/null
+++ b/gemrb/GUIScripts/LUSpellSelection.py
@@ -0,0 +1,532 @@
+# -*-python-*-
+# 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.
+#
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+from ie_restype import RES_BAM
+import GUICommon
+
+# storage variables
+pc = 0
+chargen = 0
+KitMask = 0
+
+# basic spell selection
+SpellsWindow = 0 # << spell selection window
+SpellKnownTable = 0 # << known spells per level (table)
+DoneButton = 0 # << done/next button
+SpellsTextArea = 0 # << spell description area
+SpellsSelectPointsLeft = [0]*9 # << spell selections left per level
+Spells = [0]*9 # << spells learnable per level
+SpellTopIndex = 0 # << scroll bar index
+SpellBook = [] # << array containing all the spell indexes to learn
+SpellLevel = 0 # << current level of spells
+SpellStart = 0 # << starting id of the spell list
+SpellPointsLeftLabel = 0 # << label indicating the number of points left
+EnhanceGUI = 0 # << scrollbars and extra spell slot for sorcs on LU
+
+# chargen only
+SpellsPickButton = 0 # << button to select random spells
+SpellsCancelButton = 0 # << cancel chargen
+
+
+def OpenSpellsWindow (actor, table, level, diff, kit=0, gen=0, recommend=True):
+ """Opens the spells selection window.
+
+ table should refer to the name of the classes MXSPLxxx.2da.
+ level contains the current level of the actor.
+ diff contains the difference from the old level.
+ kit should always be GetKitIndex except when dualclassing.
+ gen is true if this is for character generation.
+ recommend is used in bg2 for spell recommendation / autopick."""
+
+ global SpellsWindow, DoneButton, SpellsSelectPointsLeft, Spells, chargen, SpellPointsLeftLabel
+ global SpellsTextArea, SpellsKnownTable, SpellTopIndex, SpellBook, SpellLevel, pc, SpellStart
+ global KitMask, EnhanceGUI
+
+ #enhance GUI?
+ if (GemRB.GetVar("GUIEnhancements")):
+ EnhanceGUI = 1
+
+ # save our pc
+ pc = actor
+ chargen = gen
+
+ # this ensures compatibility with chargen, sorc, and dual-classing
+ if kit == 0:
+ KitMask = 0x4000
+ else: # need to implement this if converted to CharGen
+ KitMask = kit
+
+ # make sure there is an entry at the given level (bard)
+ SpellsKnownTable = GemRB.LoadTable (table)
+ if not SpellsKnownTable.GetValue (str(level), str(1), 1):
+ if chargen:
+ if GUICommon.GameIsBG2():
+ # HACK
+ GemRB.SetNextScript("GUICG6")
+ elif GUICommon.GameIsBG1():
+ # HACK
+ from CharGenCommon import next
+ next()
+ return
+
+ # load our window
+ if chargen:
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ SpellsWindow = GemRB.LoadWindow (7)
+ if not recommend:
+ GUICommon.CloseOtherWindow (SpellsWindow.Unload)
+ DoneButton = SpellsWindow.GetControl (0)
+ SpellsTextArea = SpellsWindow.GetControl (27)
+ SpellPointsLeftLabel = SpellsWindow.GetControl (0x1000001b)
+ if (EnhanceGUI):
+ SpellsWindow.CreateScrollBar (1000, 325,42, 16,252)
+ HideUnhideScrollBar(1)
+ SpellStart = 2
+
+ # cancel button only applicable for chargen
+ SpellsCancelButton = SpellsWindow.GetControl(29)
+ SpellsCancelButton.SetState(IE_GUI_BUTTON_ENABLED)
+ SpellsCancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SpellsCancelPress)
+ SpellsCancelButton.SetText(13727)
+ SpellsCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ if (recommend):
+ # recommended spell picks
+ SpellsPickButton = SpellsWindow.GetControl(30)
+ SpellsPickButton.SetState(IE_GUI_BUTTON_ENABLED)
+ SpellsPickButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SpellsPickPress)
+ SpellsPickButton.SetText(34210)
+ else:
+ SpellsWindow = GemRB.LoadWindow (8)
+ DoneButton = SpellsWindow.GetControl (28)
+ SpellsTextArea = SpellsWindow.GetControl(26)
+ SpellPointsLeftLabel = SpellsWindow.GetControl (0x10000018)
+ if(EnhanceGUI):
+ SpellsWindow.CreateScrollBar (1000, 290,142, 16,252)
+ HideUnhideScrollBar(1)
+ #25th spell button for sorcerers
+ SpellsWindow.CreateButton (24, 231, 345, 42, 42)
+ SpellStart = 0
+
+ # setup our variables
+ GemRB.SetVar ("SpellTopIndex", 0)
+
+ # the done button also doubles as a next button
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SpellsDonePress)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ AlreadyShown = 0
+ for i in range (9):
+ # make sure we always have a value to minus (bards)
+ SecondPoints = SpellsKnownTable.GetValue (str(level-diff), str(i+1), 1)
+ if not SecondPoints:
+ SecondPoints = 0
+
+ # make sure we get more spells of each class before continuing
+ SpellsSelectPointsLeft[i] = SpellsKnownTable.GetValue (str(level), str(i+1), 1) - SecondPoints
+ if SpellsSelectPointsLeft[i] <= 0:
+ continue
+ elif chargen and KitMask != 0x4000:
+ # specialists get an extra spell per level
+ SpellsSelectPointsLeft[i] += 1
+
+ # chargen character seem to get more spells per level (this is kinda dirty hack)
+ # except sorcerers
+ if chargen and GemRB.GetPlayerStat (pc, IE_CLASS) != 19:
+ SpellsSelectPointsLeft[i] += 1
+
+ # get all the spells of the given level
+ Spells[i] = GUICommon.GetMageSpells (KitMask, GemRB.GetPlayerStat (pc, IE_ALIGNMENT), i+1)
+
+ # dump all the spells we already know
+ NumDeleted = 0
+ for j in range (len (Spells[i])):
+ CurrentIndex = j - NumDeleted # this ensure we don't go out of range
+ if GUICommon.HasSpell (pc, IE_SPELL_TYPE_WIZARD, i, Spells[i][CurrentIndex][0]) >= 0:
+ del Spells[i][CurrentIndex]
+ NumDeleted += 1
+
+ # display these spells if it's the first non-zero level
+ if AlreadyShown == 0:
+ # save the level and spellbook data
+ SpellLevel = i
+ SpellBook = [0]*len(Spells[i])
+
+ if(EnhanceGUI):
+ # setup the scrollbar
+ ScrollBar = SpellsWindow.GetControl (1000)
+ #FIXME: use other resources instead, this one is bg2-only
+ if GemRB.HasResource ("GUISCRCW", RES_BAM):
+ ScrollBar.SetSprites ("GUISCRCW", 0, 0,1,2,3,5,4)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ShowSpells)
+ ScrollBar.SetDefaultScrollBar ()
+
+ # only scroll if we have more than 24 spells or 25 if extra 25th spell slot is available in sorcs LevelUp
+ if len (Spells[i]) > ( 24 + ExtraSpellButtons() ):
+ HideUnhideScrollBar(0)
+ if chargen:
+ ScrollBar.SetVarAssoc ("SpellTopIndex", GUICommon.ceildiv ( ( len (Spells[i])-24 ) , 6 ) + 1 )
+ else: #there are five rows of 5 spells in level up of sorcs
+ ScrollBar.SetVarAssoc ("SpellTopIndex", GUICommon.ceildiv ( ( len (Spells[i])-25 ) , 5 ) + 1 )
+ else:
+ ScrollBar.SetVarAssoc ("SpellTopIndex", 0)
+ HideUnhideScrollBar(1)
+
+ # show our spells
+ ShowSpells ()
+ AlreadyShown = 1
+
+ # show the selection window
+ if chargen:
+ if recommend:
+ SpellsWindow.SetVisible (WINDOW_VISIBLE)
+ else:
+ SpellsWindow.ShowModal (MODAL_SHADOW_NONE)
+ else:
+ SpellsWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+ return
+
+def SpellsDonePress ():
+ """Move to the next assignable level.
+
+ If there is not another assignable level, then save all the new spells and
+ close the window."""
+
+ global SpellBook, SpellLevel, SpellsWindow
+
+ # save all the spells
+ for i in range (len (Spells[SpellLevel])):
+ if SpellBook[i]: # we need to learn this spell
+ GemRB.LearnSpell (pc, Spells[SpellLevel][i][0])
+
+ # check to see if we need to update again
+ for i in range (SpellLevel+1, 9):
+ if SpellsSelectPointsLeft[i] > 0:
+ # reset the variables
+ GemRB.SetVar ("SpellTopIndex", 0)
+ SpellLevel = i
+ SpellBook = [0]*len(Spells[i])
+
+ if (EnhanceGUI):
+ # setup the scrollbar
+ ScrollBar = SpellsWindow.GetControl (1000)
+ if len (Spells[i]) > ( 24 + ExtraSpellButtons() ):
+ HideUnhideScrollBar(0)
+ if chargen:
+ ScrollBar.SetVarAssoc ("SpellTopIndex", GUICommon.ceildiv ( ( len (Spells[i])-24 ) , 6 ) + 1 )
+ else:
+ ScrollBar.SetVarAssoc ("SpellTopIndex", GUICommon.ceildiv ( ( len (Spells[i])-25 ) , 5 ) + 1 )
+ else:
+ ScrollBar.SetVarAssoc ("SpellTopIndex", 0)
+ HideUnhideScrollBar(1)
+
+ # show the spells and set the done button to off
+ ShowSpells ()
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+ # close our window and update our records
+ if SpellsWindow and (not chargen or GUICommon.GameIsBG2()):
+ SpellsWindow.Unload ()
+ SpellsWindow = None
+
+ # move to the next script if this is chargen
+ if chargen:
+ if GUICommon.GameIsBG2():
+ # HACK
+ GemRB.SetNextScript("GUICG6")
+ elif GUICommon.GameIsBG1():
+ # HACK
+ from CharGenCommon import next
+ next()
+
+ return
+
+def ShowSpells ():
+ """Shows the viewable 24 spells."""
+
+ j = RowIndex()
+
+ # we have a grid of 24 spells
+ for i in range (24 + ExtraSpellButtons()):
+ # ensure we can learn this many spells
+ SpellButton = SpellsWindow.GetControl (i+SpellStart)
+ if i + j >= len (Spells[SpellLevel]):
+ SpellButton.SetState (IE_GUI_BUTTON_DISABLED)
+ SpellButton.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ continue
+ else:
+ SpellButton.SetState (IE_GUI_BUTTON_ENABLED)
+ SpellButton.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+
+ # fill in the button with the spell data
+ Spell = GemRB.GetSpell (Spells[SpellLevel][i+j][0], 1)
+ SpellButton.SetTooltip(Spell['SpellName'])
+ SpellButton.SetVarAssoc("ButtonPressed", i)
+ SpellButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SpellsSelectPress)
+ if GUICommon.GameIsBG2():
+ SpellButton.SetSprites("GUIBTBUT",0, 0,1,2,3)
+ else:
+ SpellButton.SetSprites("GUIBTBUT",0, 0,1,24,25)
+
+ SpellButton.SetSpellIcon(Spells[SpellLevel][i+j][0], 1)
+ SpellButton.SetFlags(IE_GUI_BUTTON_PICTURE, OP_OR)
+
+ # don't allow the selection of an un-learnable spell
+ if Spells[SpellLevel][i+j][1] == 0:
+ SpellButton.SetState(IE_GUI_BUTTON_LOCKED)
+ # shade red
+ SpellButton.SetBorder (0, 0,0, 0,0, 200,0,0,100, 1,1)
+ elif Spells[SpellLevel][i+j][1] == 1: # learnable
+ SpellButton.SetState (IE_GUI_BUTTON_ENABLED)
+ # unset any borders on this button or an un-learnable from last level
+ # will still shade red even though it is clickable
+ SpellButton.SetBorder (0, 0,0, 0,0, 0,0,0,0, 0,0)
+ else: # specialist (shouldn't get here)
+ # use the green border state for matching specialist spells
+ SpellButton.SetBorder (0, 0,0, 0,0, 0,0,0,0, 0,0)
+ SpellButton.SetState (IE_GUI_BUTTON_THIRD)
+
+ # show which spells are selected
+ ShowSelectedSpells ()
+
+ GemRB.SetToken("number", str(SpellsSelectPointsLeft[SpellLevel]))
+ SpellsTextArea.SetText(17250)
+
+ return
+
+def SpellsSelectPress ():
+ """Toggles the selection of the given spell."""
+
+ global SpellsSelectPointsLeft, Spells, SpellBook
+
+ # get our variables
+ j = RowIndex()
+ i = GemRB.GetVar ("ButtonPressed") + j
+
+ # get the spell that's been pushed
+ Spell = GemRB.GetSpell (Spells[SpellLevel][i][0], 1)
+ SpellsTextArea.SetText (Spell["SpellDesc"])
+
+ # make sure we can learn the spell
+ if Spells[SpellLevel][i][1]:
+ if SpellBook[i]: # already picked -- unselecting
+ SpellsSelectPointsLeft[SpellLevel] = SpellsSelectPointsLeft[SpellLevel] + 1
+ SpellBook[i] = 0
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else: # selecting
+ # we don't have any picks left
+ if SpellsSelectPointsLeft[SpellLevel] == 0:
+ MarkButton (i, 0)
+ return
+
+ # if we have a specialist, we must make sure they pick at least
+ # one spell of their school per level
+ if SpellsSelectPointsLeft[SpellLevel] == 1 and not HasSpecialistSpell () \
+ and Spells[SpellLevel][i][1] != 2:
+ SpellsTextArea.SetText (33381)
+ MarkButton (i, 0)
+ return
+
+ # select the spell and change the done state if need be
+ SpellsSelectPointsLeft[SpellLevel] = SpellsSelectPointsLeft[SpellLevel] - 1
+ SpellBook[i] = 1
+ if SpellsSelectPointsLeft[SpellLevel] == 0:
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # show selected spells
+ ShowSelectedSpells ()
+
+ return
+
+def MarkButton (i, select):
+ """Shows enabled, disabled, or highlighted button.
+
+ If selected is true, the button is highlighted.
+ Be sure i is sent with +SpellTopIndex!"""
+
+ j = RowIndex()
+
+ if select:
+ type = IE_GUI_BUTTON_SELECTED
+ else:
+ if Spells[SpellLevel][i][1] == 1:
+ type = IE_GUI_BUTTON_ENABLED
+ elif Spells[SpellLevel][i][1] == 2:
+ # specialist spell
+ type = IE_GUI_BUTTON_THIRD
+ else: # can't learn
+ type = IE_GUI_BUTTON_LOCKED
+
+ # we have to use the index on the actual grid
+ SpellButton = SpellsWindow.GetControl(i+SpellStart-j)
+ SpellButton.SetState(type)
+ return
+
+def ShowSelectedSpells ():
+ """Highlights all selected spells."""
+
+ k = RowIndex()
+
+ # mark all of the spells picked thus far
+ for j in range (24 + ExtraSpellButtons()):
+ if j + k >= len (SpellBook): # make sure we don't call unavailable indexes
+ break
+ if SpellBook[j+k]: # selected
+ MarkButton (j+k, 1)
+ else: # not selected
+ MarkButton (j+k, 0)
+
+ # show how many spell picks are left
+ SpellPointsLeftLabel.SetText (str (SpellsSelectPointsLeft[SpellLevel]))
+ return
+
+def SpellsCancelPress ():
+ """Removes all known spells and close the window.
+
+ This is only callable within character generation."""
+
+ # remove all learned spells
+ GUICommon.RemoveKnownSpells (pc, IE_SPELL_TYPE_WIZARD, 1, 9, 1)
+
+ if GUICommon.GameIsBG2():
+ # unload teh window and go back
+ if SpellsWindow:
+ SpellsWindow.Unload()
+ GemRB.SetNextScript("CharGen6") #haterace
+ elif GUICommon.GameIsBG1():
+ import CharGenCommon
+ CharGenCommon.BackPress()
+ else:
+ print "Uh-oh in SpellsCancelPress in", GemRB.GameType
+ return
+
+def SpellsPickPress ():
+ """Auto-picks spells for the current level based on splautop.2da.
+
+ Only used in character generation.
+ Perhaps implement for sorcerers, if possible."""
+
+ global SpellBook, SpellsSelectPointsLeft
+
+ # load up our table
+ AutoTable = GemRB.LoadTable ("splautop")
+
+ for i in range (AutoTable.GetRowCount ()):
+ if SpellsSelectPointsLeft[SpellLevel] == 0:
+ break
+
+ CurrentSpell = AutoTable.GetValue (i, SpellLevel, 0)
+ for j in range (len (Spells[SpellLevel])):
+ # we can learn the spell, and it's not in our book
+ if Spells[SpellLevel][j][0].upper() == CurrentSpell.upper() \
+ and Spells[SpellLevel][j][1] and not SpellBook[j]:
+ # make sure we learn at least 1 specialist spell
+ if SpellsSelectPointsLeft[SpellLevel] == 1 and not HasSpecialistSpell () \
+ and Spells[SpellLevel][j][1] != 2:
+ SpellsTextArea.SetText (33381)
+ break
+
+ # save our spell and decrement the points left
+ SpellBook[j] = 1
+ SpellsSelectPointsLeft[SpellLevel] -= 1
+ break
+
+ # show the spells and update the counts
+ ShowSelectedSpells ()
+
+ # if we don't have any points left, we can enable the done button
+ if not SpellsSelectPointsLeft[SpellLevel]:
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ return
+
+def HasSpecialistSpell ():
+ """Determines if specialist requirements have been met.
+
+ Always returns true if the mage is not a specialist.
+ Returns true only if the specialists knows at least one spell from thier
+ school."""
+
+ # always return true for non-kitted classed
+ if KitMask == 0x4000:
+ return 1
+
+ # return true if we've memorized a school spell of this level
+ for i in range (len (Spells[SpellLevel])):
+ if Spells[SpellLevel][i][1] == 2 and SpellBook[i]:
+ return 1
+
+ # return true if there are no specialist spells of this level
+ SpecialistSpellCount = 0
+ for i in range (len (Spells[SpellLevel])):
+ if Spells[SpellLevel][i][1] == 2:
+ SpecialistSpellCount = 1
+ break
+ if SpecialistSpellCount == 0:
+ return 1
+
+ # no luck
+ return 0
+
+def RowIndex ():
+ """Determines which factor to use in scrolling of spells
+
+ It depends on if it is character generation where you have
+ 4 rows of 6 spells (24), or it is sorcs level up window where there
+ is 4 rows of 5 spells and 5th row of 4 spell, but you may also use 25th slot there
+ and it is 5 rows of 5 with 25 spells seen at once. """
+
+ SpellTopIndex = GemRB.GetVar ("SpellTopIndex")
+ if chargen:
+ return ( SpellTopIndex + 1 ) * 6 - 6
+ elif EnhanceGUI:
+ return ( SpellTopIndex + 1 ) * 5 - 5
+ else:
+ return SpellTopIndex
+
+def ExtraSpellButtons ():
+ """Determines if extra spell slots are available. """
+
+ if EnhanceGUI and (not chargen):
+ return 1
+ else:
+ return 0
+
+def HideUnhideScrollBar (hide = 0):
+ ScrollBar = SpellsWindow.GetControl (1000)
+
+ if hide == 1:
+ scrollx = -1
+ scrolly = -1
+ else:
+ if chargen:
+ scrollx = 325
+ scrolly = 42
+ else:
+ scrollx = 290
+ scrolly = 142
+
+ ScrollBar.SetPos (scrollx, scrolly)
diff --git a/gemrb/GUIScripts/LevelUp.py b/gemrb/GUIScripts/LevelUp.py
new file mode 100644
index 0000000..4c0ffd0
--- /dev/null
+++ b/gemrb/GUIScripts/LevelUp.py
@@ -0,0 +1,749 @@
+# -*-python-*-
+# 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.
+#
+
+# LevelUp.py - scripts to control the level up functionality and windows
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+from ie_restype import RES_2DA
+import GUICommon
+import CommonTables
+import LUSpellSelection
+import LUCommon
+if GUICommon.HasTOB():
+ import LUHLASelection
+import LUProfsSelection
+import LUSkillsSelection
+import Actor
+
+LevelUpWindow = None
+DoneButton = 0
+TextAreaControl = 0
+InfoCounter = 1
+NewProfPoints = 0
+NewSkillPoints = 0
+LevelDiff = 0
+Level = 0
+Classes = 0
+NumClasses = 0
+DualSwap = 0
+KitName = 0
+IsDual = 0
+IsMulti = 0
+pc = 0
+ClassName = 0
+actor = 0
+
+# old values (so we don't add too much)
+OldHPMax = 0 # << old maximum hitpoints
+OldSaves = [0]*5 # << old saves
+OldThaco = 0 # << old thac0 value
+OldLore = 0 # << old lore value
+OldDSpells = [0]*7 # << old divine spells per level
+OldWSpells = [0]*9 # << old wizard spells per level
+NewDSpells = [0]*7 # << new divine spells per level
+NewWSpells = [0]*9 # << new wizard spells per level
+DeltaDSpells = 0 # << total new divine spells
+DeltaWSpells = 0 # << total new wizard spells
+
+def OpenLevelUpWindow():
+ """Sets up the level up window."""
+ import GUIREC
+
+ global LevelUpWindow, TextAreaControl, NewProfPoints, actor
+ global DoneButton
+ global NewSkillPoints, KitName, LevelDiff
+ global Level, Classes, NumClasses, DualSwap, IsMulti
+ global OldHPMax, OldSaves, OldLore, OldThaco, DeltaDSpells, DeltaWSpells
+ global NewDSpells, NewWSpells, OldDSpells, OldWSpells, pc, HLACount, ClassName, IsDual
+
+ LevelUpWindow = GemRB.LoadWindow (3)
+
+ if GUICommon.GameIsBG2():
+ InfoButton = LevelUpWindow.GetControl (125)
+ InfoButton.SetText (13707)
+ InfoButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LevelUpInfoPress)
+
+ DoneButton = LevelUpWindow.GetControl (0)
+ DoneButton.SetText (11962)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LevelUpDonePress)
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # hide "Character Generation"
+ Label = LevelUpWindow.CreateLabel (0x1000007e, 0,0,0,0,"NUMBER","",1)
+
+ # name
+ pc = GemRB.GameGetSelectedPCSingle ()
+ actor = Actor.Actor(pc)
+ Label = LevelUpWindow.GetControl (0x10000000+90)
+ Label.SetText (GemRB.GetPlayerName (pc))
+
+ if GUICommon.GameIsBG1() or GUICommon.GameIsIWD1():
+ # armorclass
+ Label = LevelUpWindow.GetControl (0x10000057)
+ ac = GemRB.GetPlayerStat (pc, IE_ARMORCLASS)
+ #This is a temporary solution, the core engine should set the stat correctly!
+ ac += GemRB.GetAbilityBonus (IE_DEX, 2, GemRB.GetPlayerStat (pc, IE_DEX) )
+ Label.SetText (str (ac))
+ Label.SetTooltip (17183)
+
+ # hp now
+ Label = LevelUpWindow.GetControl (0x10000058)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_HITPOINTS)))
+ Label.SetTooltip (17184)
+
+ # hp max
+ Label = LevelUpWindow.GetControl (0x10000059)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)))
+ Label.SetTooltip (17378)
+
+ # some current values
+ OldHPMax = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS, 1)
+ OldThaco = GemRB.GetPlayerStat (pc, IE_TOHIT, 1)
+ OldLore = GemRB.GetPlayerStat (pc, IE_LORE, 1)
+ for i in range (5):
+ OldSaves[i] = GemRB.GetPlayerStat (pc, IE_SAVEVSDEATH+i, 1)
+
+ # class
+ Label = LevelUpWindow.GetControl (0x10000000+106)
+ Label.SetText (GUICommon.GetActorClassTitle (pc))
+ print "Title:",GUICommon.GetActorClassTitle (pc),"\tActor Title:",actor.ClassTitle()
+
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ print "Class:",Class,"\tActor Class:",actor.classid
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ SkillTable = GemRB.LoadTable("skills")
+
+ # kit
+ ClassName = CommonTables.Classes.GetRowName(ClassIndex)
+ Kit = GUICommon.GetKitIndex (pc)
+ print "Kit:", Kit, "\tActor Kit:",actor.KitIndex()
+ print "ClassName:",ClassName,"\tActor ClassNames:",actor.ClassNames()
+
+ # need this for checking gnomes
+ RaceName = GemRB.GetPlayerStat (pc, IE_RACE, 1)
+ RaceName = CommonTables.Races.FindValue (3, RaceName)
+ RaceName = CommonTables.Races.GetRowName (RaceName)
+
+ # figure our our proficiency table and index
+ if Kit == 0:
+ KitName = ClassName
+ else:
+ #rowname is just a number, the kitname is the first data column
+ KitName = CommonTables.KitList.GetValue(Kit, 0)
+
+ # our multiclass variables
+ IsMulti = GUICommon.IsMultiClassed (pc, 1)
+ Classes = [IsMulti[1], IsMulti[2], IsMulti[3]]
+ NumClasses = IsMulti[0] # 2 or 3 if IsMulti; 0 otherwise
+ IsMulti = NumClasses > 1
+ IsDual = 0
+ DualSwap = 0
+
+ # not multi, check dual
+ if not IsMulti:
+ # check if we're dual classed
+ IsDual = GUICommon.IsDualClassed (pc, 1)
+ Classes = [IsDual[2], IsDual[1]] # make sure the new class is first
+
+ # either dual or single only care about 1 class
+ NumClasses = 1
+
+ # not dual, must be single
+ if IsDual[0] == 0:
+ Classes = [Class]
+ else: # make sure Classes[1] is a class, not a kit
+ if IsDual[0] == 1: # kit
+ Classes[1] = CommonTables.KitList.GetValue (IsDual[1], 7)
+ else: # class
+ Classes[1] = CommonTables.Classes.GetValue (Classes[1], 5)
+
+ # store a boolean for IsDual
+ IsDual = IsDual[0] > 0
+
+ print "NumClasses:",NumClasses,"\tActor NumClasses:",actor.NumClasses()
+
+ Level = [0]*3
+ LevelDiff = [0]*3
+
+ # reorganize the leves if we're dc so the one we want to use is in Level[0]
+ # and the old one is in Level[1] (used to regain old class abilities)
+ if IsDual:
+ # convert the classes from indicies to class id's
+ DualSwap = GUICommon.IsDualSwap (pc)
+ ClassName = CommonTables.Classes.GetRowName (Classes[0])
+ KitName = ClassName # for simplicity throughout the code
+ Classes[0] = CommonTables.Classes.GetValue (Classes[0], 5)
+ # Class[1] is taken care of above
+
+ # we need the old level as well
+ if DualSwap:
+ Level[1] = GemRB.GetPlayerStat (pc, IE_LEVEL)
+ else:
+ Level[1] = GemRB.GetPlayerStat (pc, IE_LEVEL2)
+
+ print "Classes:",Classes,"\tActor Classes:",actor.Classes()
+ print "IsDual:",IsDual>0,"\tActor IsDual",actor.isdual
+
+ hp = 0
+ HaveCleric = 0
+ # clear some globals, since we may get called multiple times with different classes
+ DeltaWSpells = 0
+ DeltaDSpells = 0
+ OldDSpells = [0]*7
+ OldWSpells = [0]*9
+ NewDSpells = [0]*7
+ NewWSpells = [0]*9
+
+ # get a bunch of different things each level
+ for i in range(NumClasses):
+# print "Class:",Classes[i]
+ # we don't care about the current level, but about the to-be-achieved one
+ # get the next level
+ Level[i] = LUCommon.GetNextLevelFromExp (GemRB.GetPlayerStat (pc, IE_XP)/NumClasses, Classes[i])
+ TmpIndex = CommonTables.Classes.FindValue (5, Classes[i])
+ TmpName = CommonTables.Classes.GetRowName (TmpIndex)
+
+# print "Name:",TmpName
+
+ # find the level diff for each classes (3 max, obviously)
+ if i == 0:
+ if DualSwap:
+ LevelDiff[i] = Level[i] - GemRB.GetPlayerStat (pc, IE_LEVEL2)
+ else:
+ LevelDiff[i] = Level[i] - GemRB.GetPlayerStat (pc, IE_LEVEL)
+ elif i == 1:
+ LevelDiff[i] = Level[i] - GemRB.GetPlayerStat (pc, IE_LEVEL2)
+ elif i == 2:
+ LevelDiff[i] = Level[i] - GemRB.GetPlayerStat (pc, IE_LEVEL3)
+
+# print "Level (",i,"):",Level[i]
+# print "Level Diff (",i,"):",LevelDiff[i]
+
+ # save our current and next spell amounts
+ StartLevel = Level[i] - LevelDiff[i]
+ DruidTable = CommonTables.ClassSkills.GetValue (Classes[i], 0, 0)
+ ClericTable = CommonTables.ClassSkills.GetValue (Classes[i], 1, 0)
+ MageTable = CommonTables.ClassSkills.GetValue (Classes[i], 2, 0)
+
+ # see if we have mage spells
+ if MageTable != "*":
+ # we get 1 extra spell per level if we're a specialist
+ Specialist = 0
+ if CommonTables.KitList.GetValue (Kit, 7) == 1: # see if we're a kitted mage
+ Specialist = 1
+ MageTable = GemRB.LoadTable (MageTable)
+ # loop through each spell level and save the amount possible to cast (current)
+ for j in range (MageTable.GetColumnCount ()):
+ NewWSpells[j] = MageTable.GetValue (str(Level[i]), str(j+1), 1)
+ OldWSpells[j] = MageTable.GetValue (str(StartLevel), str(j+1), 1)
+ if NewWSpells[j] > 0: # don't want specialist to get 1 in levels they should have 0
+ NewWSpells[j] += Specialist
+ if OldWSpells[j] > 0:
+ OldWSpells[j] += Specialist
+ DeltaWSpells = sum(NewWSpells)-sum(OldWSpells)
+ elif ClericTable != "*":
+ # check for cleric spells
+ if not GemRB.HasResource(ClericTable, RES_2DA, 1):
+ ClericTable = "MXSPLPRS" # iwd1 doesn't have a DRUIDSPELL column in the table
+ ClericTable = GemRB.LoadTable (ClericTable)
+ HaveCleric = 1
+ # same as above
+ for j in range (ClericTable.GetColumnCount ()):
+ NewDSpells[j] = ClericTable.GetValue (str(Level[i]), str(j+1), 1)
+ OldDSpells[j] = ClericTable.GetValue (str(StartLevel), str(j+1), 1)
+ DeltaDSpells = sum(NewDSpells)-sum(OldDSpells)
+ elif DruidTable != "*":
+ # clerics have precedence in multis (ranger/cleric)
+ if HaveCleric == 0:
+ #use MXSPLPRS if we can't find the resource (SoA fix)
+ if not GemRB.HasResource (DruidTable, RES_2DA):
+ DruidTable = "MXSPLPRS"
+
+ # check druid spells
+ DruidTable = GemRB.LoadTable (DruidTable)
+ # same as above
+ for j in range (DruidTable.GetColumnCount ()):
+ NewDSpells[j] = DruidTable.GetValue (str(Level[i]), str(j+1), 1)
+ OldDSpells[j] = DruidTable.GetValue (str(StartLevel), str(j+1), 1)
+ DeltaDSpells = sum(NewDSpells)-sum(OldDSpells)
+
+ # setup class bonuses for this class
+ if IsMulti or IsDual or Kit == 0:
+ ABTable = CommonTables.ClassSkills.GetValue (TmpName, "ABILITIES")
+ else: # single-classed with a kit
+ ABTable = CommonTables.KitList.GetValue (str(Kit), "ABILITIES")
+
+ # add the abilites if we have a table to ref
+ if ABTable != "*" and GemRB.HasResource (ABTable, RES_2DA, 1):
+ GUICommon.AddClassAbilities (pc, ABTable, Level[i], LevelDiff[i])
+
+ print "Actor CurrentLevels:",actor.Levels()
+ print "Levels:",Level,"Actor NextLevels:",actor.NextLevels()
+ print "LevelDiffs:",LevelDiff,"Actor LevelDiffs:",actor.LevelDiffs()
+
+ #update our saves, thaco, hp and lore
+ LUCommon.SetupSavingThrows (pc, Level)
+ LUCommon.SetupThaco (pc, Level)
+ LUCommon.SetupLore (pc, LevelDiff)
+ LUCommon.SetupHP (pc, Level, LevelDiff)
+
+ # use total levels for HLAs
+ HLACount = 0
+ if GUICommon.HasTOB(): # make sure SoA doesn't try to get it
+ HLATable = GemRB.LoadTable("lunumab")
+ # we need to check each level against a multi value (this is kinda screwy)
+ if actor.multiclass:
+ print "Actor HLA Names:",["MULTI"+str(actor.NumClasses())+name \
+ for name in actor.ClassNames()]
+ else:
+ print "Actor HLA Names:",actor.ClassNames()
+
+ for i in range (NumClasses):
+ if IsMulti:
+ # get the row name for lookup ex. MULTI2FIGHTER, MULTI3THIEF
+ MultiName = CommonTables.Classes.FindValue (5, Classes[i])
+ MultiName = CommonTables.Classes.GetRowName (MultiName)
+ MultiName = "MULTI" + str(NumClasses) + MultiName
+ else:
+ MultiName = ClassName
+
+ # if we can't learn for this class, we can't learn at all
+ FirstLevel = HLATable.GetValue (MultiName, "FIRST_LEVEL", 1)
+ if Level[i] < FirstLevel:
+ HLACount = 0
+ break
+
+ if (Level[i] - LevelDiff[i]) < FirstLevel:
+ # count only from FirstLevel up
+ HLACount += (Level[i] - FirstLevel + 1)
+ else:
+ HLACount += LevelDiff[i]
+
+ # set values required by the hla level up code
+ HLACount = HLACount / HLATable.GetValue (ClassName, "RATE", 1)
+ GemRB.SetVar ("HLACount", HLACount)
+ if GUICommon.GameIsBG2():
+ HLAButton = LevelUpWindow.GetControl (126)
+ if HLACount:
+ HLAButton.SetText (4954)
+ HLAButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LevelUpHLAPress)
+ else:
+ HLAButton.SetFlags (IE_GUI_BUTTON_DISABLED, OP_OR)
+
+ # setup our profs
+ Level1 = []
+ for i in range (len (Level)):
+ Level1.append (Level[i]-LevelDiff[i])
+ if GUICommon.GameIsBG2():
+ LUProfsSelection.SetupProfsWindow (pc, LUProfsSelection.LUPROFS_TYPE_LEVELUP, LevelUpWindow, RedrawSkills, Level1, Level)
+ else:
+ LUProfsSelection.SetupProfsWindow (pc, LUProfsSelection.LUPROFS_TYPE_LEVELUP, LevelUpWindow, RedrawSkills, Level1, Level, 0, False, 0)
+ NewProfPoints = GemRB.GetVar ("ProfsPointsLeft")
+
+ #we autohide the skills and let SetupSkillsWindow show them if needbe
+ for i in range (4):
+ HideSkills (i)
+ if GUICommon.GameIsBG2():
+ LUSkillsSelection.SetupSkillsWindow (pc, LUSkillsSelection.LUSKILLS_TYPE_LEVELUP, LevelUpWindow, RedrawSkills, Level1, Level)
+ else:
+ LUSkillsSelection.SetupSkillsWindow (pc, LUSkillsSelection.LUSKILLS_TYPE_LEVELUP, LevelUpWindow, RedrawSkills, Level1, Level, 0, False)
+ NewSkillPoints = GemRB.GetVar ("SkillPointsLeft")
+
+ if GUICommon.GameIsBG2():
+ TextAreaControl = LevelUpWindow.GetControl(110)
+ TextAreaControl.SetText(GetLevelUpNews())
+ else:
+ TextAreaControl = LevelUpWindow.GetControl(42)
+ TextAreaControl.SetText(GUIREC.GetStatOverview(pc, LevelDiff))
+
+ RedrawSkills()
+ GemRB.SetRepeatClickFlags (GEM_RK_DISABLE, OP_NAND)
+ LevelUpWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+ # if we have a sorcerer who can learn spells, we need to do spell selection
+ if (Classes[0] == 19) and (DeltaWSpells > 0): # open our sorc spell selection window
+ LUSpellSelection.OpenSpellsWindow (pc, "SPLSRCKN", Level[0], LevelDiff[0])
+
+def HideSkills(i):
+ """Hides the given skill label from view."""
+ global LevelUpWindow
+
+ Label = LevelUpWindow.GetControl (0x10000000+32+i)
+ Label.SetText ("")
+ Button1 = LevelUpWindow.GetControl(i*2+17)
+ Button1.SetState(IE_GUI_BUTTON_DISABLED)
+ Button1.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+ Button2 = LevelUpWindow.GetControl(i*2+18)
+ Button2.SetState(IE_GUI_BUTTON_DISABLED)
+ Button2.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+ Label = LevelUpWindow.GetControl(0x10000000+43+i)
+ Label.SetText("")
+
+def RedrawSkills():
+ """Redraws the entire window.
+
+ Called whenever a state changes, such as a proficiency or skill being
+ added or taken away."""
+
+ global DoneButton, LevelUpWindow, HLACount
+
+ # we need to disable the HLA button if we don't have any HLAs left
+ HLACount = GemRB.GetVar ("HLACount")
+ if GUICommon.GameIsBG2() and HLACount == 0:
+ # turn the HLA button off
+ HLAButton = LevelUpWindow.GetControl (126)
+ HLAButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ # enable the done button if they've allocated all points
+ # sorcerer spell selection (if applicable) comes after hitting the done button
+ ProfPointsLeft = GemRB.GetVar ("ProfsPointsLeft")
+ SkillPointsLeft = GemRB.GetVar ("SkillPointsLeft")
+ if ProfPointsLeft == 0 and SkillPointsLeft == 0 and HLACount == 0:
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def GetLevelUpNews():
+ """Returns a string containing improvements gain on level up.
+
+ These include: HP, spells per level, and lore, among others."""
+
+ News = GemRB.GetString (5259) + '\n\n'
+
+ # display if our class has been reactivated
+ if IsDual:
+ if (Level[0] - LevelDiff[0]) <= Level[1] and Level[0] > Level[1]:
+ News = GemRB.GetString (5261) + '\n\n'
+
+ # 5271 - Additional weapon proficiencies
+ if NewProfPoints > 0:
+ News += GemRB.GetString (5271) + ": " + str(NewProfPoints) + '\n\n'
+
+ # temps to compare all our new saves against (we get the best of our saving throws)
+ LOHGain = 0
+ BackstabMult = 0
+
+ for i in range(NumClasses):
+ # get the class name
+ TmpClassName = CommonTables.Classes.FindValue (5, Classes[i])
+ TmpClassName = CommonTables.Classes.GetRowName (TmpClassName)
+
+ # backstab
+ # NOTE: Stalkers and assassins should get the correct mods at the correct levels based
+ # on AP_SPCL332 in their respective CLAB files.
+ # APND: Stalkers appear to get the correct mod at the correct levels; however, because they start
+ # at backstab multi x1, they are x1 too many at their respective levels.
+ if Classes[i] == 4 and GemRB.GetPlayerStat (pc, IE_BACKSTABDAMAGEMULTIPLIER, 1) > 1: # we have a thief who can backstab (2 at level 1)
+ # save the backstab multiplier if we have a thief
+ BackstabTable = GemRB.LoadTable ("BACKSTAB")
+ BackstabMult = BackstabTable.GetValue (0, Level[i])
+
+ # lay on hands
+ if (CommonTables.ClassSkills.GetValue (Classes[i], 6) != "*"):
+ # inquisitors and undead hunters don't get lay on hands out the chute, whereas cavaliers
+ # and unkitted paladins do; therefore, we check for the existence of lay on hands to ensure
+ # the character should get the new value; LoH is defined in GA_SPCL211 if anyone wants to
+ # make a pally kit with LoH
+ if (GUICommon.HasSpell (pc, IE_SPELL_TYPE_INNATE, 0, "SPCL211") >= 0):
+ LOHTable = GemRB.LoadTable ("layhands")
+ LOHGain = LOHTable.GetValue (0, Level[i]) - LOHTable.GetValue (0, Level[i]-LevelDiff[i])
+
+ # saving throws
+ # 5277 death
+ # 5278 wand
+ # 5279 polymorph
+ # 5282 breath
+ # 5292 spell
+ # include in news if the save is updated
+ Changed = 0
+ for i in range (5):
+ CurrentSave = GemRB.GetPlayerStat (pc, IE_SAVEVSDEATH+i, 1)
+ SaveString = 5277+i
+ if i == 3:
+ SaveString = 5282
+ elif i == 4:
+ SaveString = 5292
+
+ if CurrentSave < OldSaves[i]:
+ News += GemRB.GetString (SaveString) + ": " + str(OldSaves[i]-CurrentSave) + '\n'
+ Changed = 1
+ if Changed:
+ News += '\n'
+
+ # 5305 - THAC0 Reduced by
+ # only output if there is a change in thaco
+ NewThaco = GemRB.GetPlayerStat (pc, IE_TOHIT, 1)
+ if (NewThaco < OldThaco):
+ News += GemRB.GetString (5305) + ": " + str(OldThaco-NewThaco) + '\n\n'
+
+ # new spell slots
+ # 5373 - Additional Priest Spells
+ # 5374 - Additional Mage Spells
+ # 61269 - Level <LEVEL> Spells
+ if DeltaDSpells > 0: # new divine spells
+ News += GemRB.GetString (5373) + '\n'
+ for i in range (len (NewDSpells)):
+ # only display classes with new spells
+ if (NewDSpells[i]-OldDSpells[i]) == 0:
+ continue
+ GemRB.SetToken("level", str(i+1))
+ News += GemRB.GetString(61269)+": " + str(NewDSpells[i]-OldDSpells[i]) + '\n'
+ News += '\n'
+ if DeltaWSpells > 0: # new wizard spells
+ News += GemRB.GetString (5374) + '\n'
+ for i in range (len (NewWSpells)):
+ # only display classes with new spells
+ if (NewWSpells[i]-OldWSpells[i]) == 0:
+ continue
+ GemRB.SetToken("level", str(i+1))
+ News += GemRB.GetString(61269)+": " + str(NewWSpells[i]-OldWSpells[i]) + '\n'
+ News += '\n'
+
+ # 5375 - Backstab Multiplier Increased by
+ # this auto-updates... we just need to inform of the update
+ BSGain = BackstabMult - GemRB.GetPlayerStat (pc, IE_BACKSTABDAMAGEMULTIPLIER, 1)
+ if (BSGain > 0):
+ News += GemRB.GetString (5375) + ": " + str(BSGain) + '\n\n'
+
+ # 5376 - Lay on Hands increased
+ if LOHGain > 0:
+ News += GemRB.GetString (5376) + ": " + str(LOHGain) + '\n\n'
+
+ # 5293 - HP increased by
+ if (OldHPMax != GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS, 1)):
+ News += GemRB.GetString (5293) + ": " + str(GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS, 1) - OldHPMax) + '\n'
+
+ # 5377 - Lore Increased by
+ # add the lore gain if we haven't done so already
+ NewLore = GemRB.GetPlayerStat (pc, IE_LORE, 1)
+ if NewLore > OldLore:
+ News += GemRB.GetString (5377) + ": " + str(NewLore-OldLore) + '\n\n'
+
+ # 5378 - Additional Skill Points
+ # ranger and bard skill(point) gain is not mentioned here in the original
+ if NewSkillPoints > 0:
+ News += GemRB.GetString (5378) + ": " + str(NewSkillPoints) + '\n'
+
+ return News
+
+def LevelUpInfoPress():
+ """Displays new abilites gained on level up.
+
+ Alternates between overall and modified stats."""
+ import GUIREC
+ global LevelUpWindow, TextAreaControl, InfoCounter, LevelDiff
+
+ if InfoCounter % 2:
+ # call GetStatOverview with the new levels, so the future overview is shown
+ # TODO: show only xp, levels, thac0, #att, lore, reputation, backstab, saving throws
+ TextAreaControl.SetText(GUIREC.GetStatOverview(pc, LevelDiff))
+ else:
+ TextAreaControl.SetText(GetLevelUpNews())
+ InfoCounter += 1
+ return
+
+# save the results
+def LevelUpDonePress():
+ """Updates the PC with the new choices.
+
+ Closes the window when finished."""
+ import GUICommonWindows
+ import GUIREC
+ global SkillTable
+
+ # proficiencies
+ LUProfsSelection.ProfsSave (pc)
+
+ # skills
+ LUSkillsSelection.SkillsSave (pc)
+
+ # level
+ if DualSwap: # swap the IE_LEVELs around if a backward dual
+ GemRB.SetPlayerStat (pc, IE_LEVEL2, Level[0])
+ GemRB.SetPlayerStat (pc, IE_LEVEL, Level[1])
+ else:
+ GemRB.SetPlayerStat (pc, IE_LEVEL, Level[0])
+ GemRB.SetPlayerStat (pc, IE_LEVEL2, Level[1])
+ GemRB.SetPlayerStat (pc, IE_LEVEL3, Level[2])
+
+ print "Levels:",Level[0],"/",Level[1],"/",Level[2]
+
+ # save our number of memorizable spells per level
+ if DeltaWSpells > 0:
+ # loop through each wizard spell level
+ for i in range(len(NewWSpells)):
+ if NewWSpells[i] > 0: # we have new spells, so update
+ GemRB.SetMemorizableSpellsCount(pc, NewWSpells[i], IE_SPELL_TYPE_WIZARD, i)
+
+ # save our number of memorizable priest spells
+ if DeltaDSpells > 0: # druids and clerics count
+ for i in range (len(NewDSpells)):
+ # get each update
+ if NewDSpells[i] > 0:
+ GemRB.SetMemorizableSpellsCount (pc, NewDSpells[i], IE_SPELL_TYPE_PRIEST, i)
+
+ # learn all the spells we're given, but don't have, up to our given casting level
+ # bonus spells don't count in determining if we can use this level
+ if GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, i, 0) > 0: # we can memorize spells of this level
+ for j in range(NumClasses): # loop through each class
+ IsDruid = CommonTables.ClassSkills.GetValue (Classes[j], 0, 0)
+ IsCleric = CommonTables.ClassSkills.GetValue (Classes[j], 1, 0)
+ if IsCleric == "*" and IsDruid == "*": # no divine spells (so mage/cleric multis don't screw up)
+ continue
+ elif IsCleric == "*": # druid spells
+ ClassFlag = 0x8000
+ else: # cleric spells
+ ClassFlag = 0x4000
+
+ Learnable = GUICommon.GetLearnablePriestSpells(ClassFlag, GemRB.GetPlayerStat (pc, IE_ALIGNMENT), i+1)
+ for k in range(len(Learnable)): # loop through all the learnable spells
+ if GUICommon.HasSpell (pc, IE_SPELL_TYPE_PRIEST, i, Learnable[k]) < 0: # only write it if we don't yet know it
+ GemRB.LearnSpell(pc, Learnable[k])
+
+ # hlas
+ # level, xp and other stuff by the core?
+
+ # 5261 - Regained abilities from inactive class
+ if IsDual: # we're dual classed
+ print "activation?"
+ if (Level[0] - LevelDiff[0]) <= Level[1] and Level[0] > Level[1]: # our new classes now surpasses our old class
+ print "reactivating base class"
+ ReactivateBaseClass ()
+
+ if LevelUpWindow:
+ LevelUpWindow.Unload()
+ GUICommonWindows.UpdatePortraitWindow ()
+ GUIREC.UpdateRecordsWindow()
+
+ GemRB.SetRepeatClickFlags (GEM_RK_DISABLE, OP_OR)
+ return
+
+def LevelUpHLAPress ():
+ """Opens the HLA selection window."""
+
+ # we can turn the button off and temporarily set HLACount to 0
+ # because there is no cancel button on the HLA window; therefore,
+ # it's guaranteed to come back as 0
+ TmpCount = GemRB.GetVar ("HLACount")
+ GemRB.SetVar ("HLACount", 0)
+ RedrawSkills ()
+ GemRB.SetVar ("HLACount", TmpCount)
+
+ LUHLASelection.OpenHLAWindow (pc, NumClasses, Classes, Level)
+ return
+
+def ReactivateBaseClass ():
+ """Regains all abilities of the base dual-class.
+
+ Proficiencies, THACO, saves, spells, and innate abilites,
+ most noteably."""
+
+ ClassIndex = CommonTables.Classes.FindValue (5, Classes[1])
+ ClassName = CommonTables.Classes.GetRowName (ClassIndex)
+ KitIndex = GUICommon.GetKitIndex (pc)
+
+ # reactivate all our proficiencies
+ TmpTable = GemRB.LoadTable ("weapprof")
+ ProfsTableOffset = 0
+ if GUICommon.GameIsBG2 ():
+ ProfsTableOffset = 8 # skip bg1 weapprof.2da proficiencies
+ ProfCount = TmpTable.GetRowCount () - ProfsTableOffset
+ for i in range(ProfCount):
+ ProfID = TmpTable.GetValue (i+ProfsTableOffset, 0)
+ if GUICommon.GameIsBG1():
+ ProfID = ProfID + IE_PROFICIENCYBASTARDSWORD
+ Value = GemRB.GetPlayerStat (pc, ProfID)
+ OldProf = (Value & 0x38) >> 3
+ NewProf = Value & 0x07
+ if OldProf > NewProf:
+ Value = (OldProf << 3) | OldProf
+ print "Value:",Value
+ if GUICommon.GameIsBG2():
+ GemRB.ApplyEffect (pc, "Proficiency", Value, ProfID )
+ else:
+ GemRB.SetPlayerStat (pc, ProfID, Value)
+
+ # see if this thac0 is lower than our current thac0
+ ThacoTable = GemRB.LoadTable ("THAC0")
+ TmpThaco = ThacoTable.GetValue(Classes[1]-1, Level[1]-1, 1)
+ if TmpThaco < GemRB.GetPlayerStat (pc, IE_TOHIT, 1):
+ GemRB.SetPlayerStat (pc, IE_TOHIT, TmpThaco)
+
+ # see if all our saves are lower than our current saves
+ SavesTable = CommonTables.Classes.GetValue (ClassIndex, 3, 0)
+ SavesTable = GemRB.LoadTable (SavesTable)
+ for i in range (5):
+ # see if this save is lower than our old save
+ TmpSave = SavesTable.GetValue (i, Level[1]-1)
+ if TmpSave < GemRB.GetPlayerStat (pc, IE_SAVEVSDEATH+i, 1):
+ GemRB.SetPlayerStat (pc, IE_SAVEVSDEATH+i, TmpSave)
+
+ # see if we're a caster
+ SpellTables = [CommonTables.ClassSkills.GetValue (Classes[1], 0, 0), CommonTables.ClassSkills.GetValue (Classes[1], 1, 0), CommonTables.ClassSkills.GetValue (Classes[1], 2, 0)]
+ if SpellTables[2] != "*": # casts mage spells
+ # set up our memorizations
+ SpellTable = GemRB.LoadTable (SpellTables[2])
+ for i in range (9):
+ # if we can cast more spells at this level (should be always), then update
+ NumSpells = SpellTable.GetValue (Level[1]-1, i)
+ if NumSpells > GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_WIZARD, i, 1):
+ GemRB.SetMemorizableSpellsCount (pc, NumSpells, IE_SPELL_TYPE_WIZARD, i)
+ elif SpellTables[1] != "*" or SpellTables[0] != "*": # casts priest spells
+ # get the correct table and mask
+ if SpellTables[1] != "*": # clerical spells
+ SpellTable = GemRB.LoadTable (SpellTables[1])
+ ClassMask = 0x4000
+ else: # druidic spells
+ if not GameRB.HasResource(SpellTables[0], RES_2DA):
+ SpellTables[0] = "MXSPLPRS"
+ SpellTable = GemRB.LoadTable (SpellTables[0])
+ ClassMask = 0x8000
+
+ # loop through each spell level
+ for i in range (7):
+ # update if we can cast more spells at this level
+ NumSpells = SpellTable.GetValue (str(Level[1]), str(i+1), 1)
+ if not NumSpells:
+ continue
+ if NumSpells > GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, i, 1):
+ GemRB.SetMemorizableSpellsCount (pc, NumSpells, IE_SPELL_TYPE_PRIEST, i)
+
+ # also re-learn the spells if we have to
+ # WARNING: this fixes the error whereby rangers dualed to clerics still got all druid spells
+ # they will now only get druid spells up to the level they could cast
+ # this should probably be noted somewhere (ranger/cleric multis still function the same,
+ # but that could be remedied if desired)
+ Learnable = GUICommon.GetLearnablePriestSpells(ClassMask, GemRB.GetPlayerStat (pc, IE_ALIGNMENT), i+1)
+ for k in range (len (Learnable)): # loop through all the learnable spells
+ if GUICommon.HasSpell (pc, IE_SPELL_TYPE_PRIEST, i, Learnable[k]) < 0: # only write it if we don't yet know it
+ GemRB.LearnSpell(pc, Learnable[k])
+
+ # setup class bonuses for this class
+ if KitIndex == 0: # no kit
+ ABTable = CommonTables.ClassSkills.GetValue (ClassName, "ABILITIES")
+ else: # kit
+ ABTable = CommonTables.KitList.GetValue (KitIndex, 4, 0)
+ print "ABTable:",ABTable
+
+ # add the abilites if we have a table to ref
+ if ABTable != "*" and GemRB.HasResource (ABTable, RES_2DA, 1):
+ GUICommon.AddClassAbilities (pc, ABTable, Level[1], Level[1]) # relearn class abilites
diff --git a/gemrb/GUIScripts/Makefile.am b/gemrb/GUIScripts/Makefile.am
new file mode 100644
index 0000000..4317dc2
--- /dev/null
+++ b/gemrb/GUIScripts/Makefile.am
@@ -0,0 +1,7 @@
+SUBDIRS = bg1 bg2 iwd iwd2 pst
+
+guiscript_DATA = *.py
+guiscriptdir = $(moddir)/GUIScripts/
+EXTRA_DIST = *.py CMakeLists.txt */CMakeLists.txt
+MOSTLYCLEANFILES = *.pyc
+
diff --git a/gemrb/GUIScripts/MetaClasses.py b/gemrb/GUIScripts/MetaClasses.py
new file mode 100644
index 0000000..8df0e8d
--- /dev/null
+++ b/gemrb/GUIScripts/MetaClasses.py
@@ -0,0 +1,79 @@
+#-*-python-*-
+#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.
+
+
+# The metaclasses below are used to define objects that call
+# functions in the GemRB module.
+#
+# Example:
+# class GTable:
+# __metaclass__ = metaIDWrapper
+# methods = {
+# 'GetValue': GemRB.GetTableValue,
+# }
+#
+# x = GTable(5)
+#
+# Calling
+# x.GetValue("Row", "Col")
+# will then execute
+# GemRB.GetTableValue(5, "Row", "Col")
+
+def make_caller_lambda_ID(M):
+ return lambda self, *args: M(self.ID, *args)
+class metaIDWrapper(type):
+ def __new__(cls, classname, bases, classdict):
+ def __init__(self, ID):
+ self.ID = ID
+ newdict = { '__slots__':['ID'], '__init__':__init__, }
+ if len(bases) == 1:
+ def __subinit__(self, ID):
+ bases[0].__init__(self, ID)
+ newdict['__init__'] = __subinit__
+ newdict['__slots__'] = []
+ methods = classdict['methods']
+ for key in methods:
+ newdict[key] = make_caller_lambda_ID(methods[key])
+ for key in classdict:
+ if key != 'methods':
+ newdict[key] = classdict[key]
+ return type.__new__(cls, classname, bases, newdict)
+
+
+# metaControl has two extra arguments: WinID and ID
+def make_caller_lambda_Control(M):
+ return lambda self, *args: M(self.WinID, self.ID, *args)
+class metaControl(type):
+ def __new__(cls, classname, bases, classdict):
+ def __init__(self, WinID, ID):
+ self.WinID = WinID
+ self.ID = ID
+ newdict = { '__slots__':['WinID', 'ID'], '__init__':__init__, }
+ if len(bases) == 1:
+ def __subinit__(self, WinID, ID):
+ bases[0].__init__(self, WinID, ID)
+ newdict['__init__'] = __subinit__
+ newdict['__slots__'] = []
+ methods = classdict['methods']
+ for key in methods:
+ newdict[key] = make_caller_lambda_Control(methods[key])
+ for key in classdict:
+ if key != 'methods':
+ newdict[key] = classdict[key]
+ return type.__new__(cls, classname, bases, newdict)
+
diff --git a/gemrb/GUIScripts/TextScreen.py b/gemrb/GUIScripts/TextScreen.py
new file mode 100644
index 0000000..bb0ee76
--- /dev/null
+++ b/gemrb/GUIScripts/TextScreen.py
@@ -0,0 +1,156 @@
+# -*-python-*-
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2010 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.
+#
+#################### Interchapter text screen functions #################
+
+import GemRB
+from GUIDefines import *
+import GUICommon
+
+TextScreen = None
+TextArea = None
+Row = 1
+Position = 1
+Chapter = 0
+TableName = None
+BGTICKS = 300 # TODO: verify for suitability
+Ticks = IWDTICKS = 200 # TODO: verify for suitability in iwd1
+
+def StartTextScreen ():
+ global TextScreen, TextArea, Chapter, TableName, Row, Ticks
+
+ if GUICommon.GameIsIWD2():
+ GemRB.LoadWindowPack ("GUICHAP", 800, 600)
+ else:
+ GemRB.LoadWindowPack ("GUICHAP", 640, 480)
+ if GUICommon.GameIsBG1() or GUICommon.GameIsBG2():
+ Ticks = BGTICKS
+
+ LoadPic = TableName = GemRB.GetGameString (STR_LOADMOS)
+ #if there is no preset loadpic, try to determine it from the chapter
+ #fixme: we always assume there isn't for non-bg2
+ if GUICommon.GameIsBG2():
+ if TableName == "":
+ Chapter = GemRB.GetGameVar ("CHAPTER") & 0x7fffffff
+ TableName = "CHPTXT"+str(Chapter)
+ ID = 62
+ else:
+ ID = (GemRB.GetGameVar("CHAPTER") + 1) & 0x7fffffff
+ Chapter = ID + 1
+
+ #iwd2/bg2 has no separate music
+ if GUICommon.GameIsIWD1():
+ if LoadPic == "":
+ GemRB.LoadMusicPL ("chap0.mus")
+ else:
+ GemRB.LoadMusicPL ("chap1.mus")
+ TableName = "chapters"
+ elif GUICommon.GameIsIWD2():
+ GemRB.HardEndPL ()
+ TableName = "chapters"
+ elif GUICommon.GameIsBG1() and TableName[:6] == "chptxt":
+ GemRB.LoadMusicPL ("chapter.mus")
+
+ TextScreen = GemRB.LoadWindow (ID)
+ TextScreen.SetFrame ()
+
+ TextArea = TextScreen.GetControl (2)
+ TextArea.SetFlags (IE_GUI_TEXTAREA_SMOOTHSCROLL)
+ TextArea.SetEvent (IE_GUI_TEXTAREA_OUT_OF_TEXT, FeedScroll)
+
+ #caption
+ Table = GemRB.LoadTable (TableName)
+ if GUICommon.GameIsBG1():
+ #these suckers couldn't use a fix row
+ Row = Table.GetRowIndex("DEFAULT")
+ Value = Table.GetValue (Row, 0)
+ elif GUICommon.GameIsBG2():
+ LoadPic = Table.GetValue (-1, -1)
+ if LoadPic != "":
+ TextScreen.SetPicture (LoadPic)
+ # TODO: verify
+ Value = 0
+ else:
+ Value = Table.GetValue (Chapter, 0)
+ if Value:
+ Label=TextScreen.GetControl (0x10000000)
+ Label.SetText (Value)
+
+ #done
+ Button=TextScreen.GetControl (0)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, EndTextScreen)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT|IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ #replay
+ Button=TextScreen.GetControl (3)
+ Button.SetText (16510)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ReplayTextScreen)
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE) #removing the gamecontrol screen
+ TextScreen.SetVisible (WINDOW_VISIBLE)
+
+ TextArea.Rewind (Ticks)
+ GemRB.DisplayString (17556, 0xff0000) #Paused for chapter text
+ GemRB.GamePause (1, 1)
+ return
+
+def FeedScroll ():
+ global TextArea, Position
+
+ Table = GemRB.LoadTable (TableName)
+ if GUICommon.GameIsBG2():
+ #this is a rather primitive selection but works for the games
+ Value = Table.GetValue (0, 1)
+ if Value == "REPUTATION":
+ line = 2
+ else:
+ line = 1
+ Value = Table.GetValue (line, 1)
+ elif GUICommon.GameIsBG1():
+ Value = Table.GetValue (Row, Position)
+ if Value == 'NONE':
+ Position = 1
+ else:
+ Position = Position + 1
+ else:
+ Value = Table.GetValue (Chapter, 1)
+
+ TextArea.Append (Value, -1, 6)
+ return
+
+def EndTextScreen ():
+ global TextScreen
+
+ TextScreen.SetVisible (WINDOW_INVISIBLE)
+ if TextScreen:
+ TextScreen.Unload ()
+ GemRB.PlaySound(None, 0, 0, 4)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE) # enable the gamecontrol screen
+ GemRB.UnhideGUI ()
+ GemRB.GamePause (0, 1)
+ return
+
+def ReplayTextScreen ():
+ global TextArea, Position
+
+ Position = 1
+ TextArea.SetEvent (IE_GUI_TEXTAREA_OUT_OF_TEXT, FeedScroll)
+ TextArea.Rewind (Ticks)
+ return
diff --git a/gemrb/GUIScripts/bg1/Autodetect.py b/gemrb/GUIScripts/bg1/Autodetect.py
new file mode 100644
index 0000000..2a7477b
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/Autodetect.py
@@ -0,0 +1,34 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2010 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.
+#
+
+import GemRB
+from ie_restype import *
+from AutodetectCommon import CheckFiles
+
+files = (
+ ("START", "CHU", RES_CHU),
+ ("STARTPOS", "2DA", RES_2DA),
+ ("STARTARE", "2DA", RES_2DA),
+)
+
+
+if CheckFiles(files):
+ GemRB.AddGameTypeHint ("bg1", 80)
+
diff --git a/gemrb/GUIScripts/bg1/CMakeLists.txt b/gemrb/GUIScripts/bg1/CMakeLists.txt
new file mode 100644
index 0000000..965e7a6
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/CMakeLists.txt
@@ -0,0 +1,3 @@
+FILE( GLOB FILES_TO_INSTALL *.py )
+
+INSTALL( FILES ${FILES_TO_INSTALL} DESTINATION ${DATA_DIR}/GUIScripts/bg1 )
diff --git a/gemrb/GUIScripts/bg1/CharGen.py b/gemrb/GUIScripts/bg1/CharGen.py
new file mode 100644
index 0000000..c9150d9
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/CharGen.py
@@ -0,0 +1,66 @@
+# 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.
+#
+#
+#character generation (GUICG 0)
+from GUIDefines import *
+
+import CharGenCommon
+import CharGenGui
+
+def init():
+ print("init")
+ #(name,control,text)
+ #(name,script | setFn,commentFn,unsetFn,gaurd)
+ stages = [
+ ("gender" , 0 , 11956 ),
+ ("Show name, create player" , None ,CharGenGui.getName, CharGenGui.unsetPlayer, CharGenGui.setPlayer ),
+ ("setGender" , "GUICG1" , CharGenGui.getGender ,CharGenGui.unsetGender , None ), #BG2: same
+ ("setPortrait" , "GUICG12" , None ,CharGenGui.unsetPortrait , None ), #BG2: different setPicture, PortraitName and extra unused control (TextAreaControl)
+ ("race" , 1 ,11957 ),
+ ("setRace" , "GUICG8" , CharGenGui.getRace ,CharGenGui.unsetRace , None ), #BG2: different resources
+ ("class" , 2 ,11959 ),
+ ("setClass" , "GUICG2" , CharGenGui.getClass ,CharGenGui.unsetClass , None ), #BG2: different resources,other setLogic, has levels/XP
+ ("setMultiClass", "GUICG10" , None ,CharGenGui.unsetClass , CharGenGui.guardMultiClass), #BG2: not same?
+ ("setSpecialist", "GUICG22" , None ,CharGenGui.unsetClass , CharGenGui.guardSpecialist), #BG2: not same
+ ("alignment" , 3 ,11958 ),
+ ("setAlignment" , "GUICG3" , CharGenGui.getAlignment ,CharGenGui.unsetAlignment , None ), #bg2: BG1 doesn't take kits into account
+ ("abilities" , 4 ,11960 ),
+ ("setAbilities" , "GUICG4" , CharGenGui.getAbilities ,CharGenGui.unsetAbilities , None ), #bg2: BG2 has extra strength, overpress (functionality + resources), kits
+ ("skill" , 5 ,17372 ),
+ ("divine spells" , None , CharGenGui.getDivineSpells,CharGenGui.unsetDivineSpells,CharGenGui.setDivineSpells ),
+ ("hate race" , "GUICG15" , CharGenGui.getHatedRace ,CharGenGui.unsetHateRace , CharGenGui.guardHateRace), #BG2: other listsize, button offset, BG2 has TopIndex
+ ("mage spells" , "GUICG7" , CharGenGui.getMageSpells,CharGenGui.unsetMageSpells , CharGenGui.guardMageSpells), #also sets priest in CharGen6, we do it at the end, other animations for buttons
+ ("setSkill" , "GUICG6" , CharGenGui.getSkills ,CharGenGui.unsetSkill , CharGenGui.guardSkills ),
+ ("proficiencies", "GUICG9" , CharGenGui.getProfi ,CharGenGui.unsetProfi , None ),
+ ("appearance" , 6 ,11961 ),
+ ("colors" , "GUICG13" , None ,CharGenGui.unsetColors , None ), #bg2: other cycleids (SetBAM)
+ ("sounds" , "GUICG19" , None ,CharGenGui.unsetSounds , None ), #BG2: other: first male sound, logic
+ ("name" , 7 ,11963 ),
+ ("setName" ,"GUICG5" , None ,CharGenGui.unsetName , None ), #BG2: same
+ ("accept" , 8 ,11962 ),
+ ("finish" , CharGenGui.setAccept , None , None , None )]
+
+ CharGenCommon.CharGenMaster = CharGenCommon.CharGen(stages,16575,CharGenGui.Imprt)
+
+ return
+
+def OnLoad():
+ if(CharGenCommon.CharGenMaster == None):
+ init()
+ CharGenCommon.CharGenMaster.displayOverview()
+ return
diff --git a/gemrb/GUIScripts/bg1/CharGenCommon.py b/gemrb/GUIScripts/bg1/CharGenCommon.py
new file mode 100644
index 0000000..9c6aeed
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/CharGenCommon.py
@@ -0,0 +1,283 @@
+# 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.
+#
+#
+# common character generation display code
+import GemRB
+import GUICommon
+#import GUIClasses
+from ie_stats import *
+from GUIDefines import *
+
+class CharGen:
+ def __init__(self,stages,startText,importFn,resetButton=False):
+ """Sets up a character generation system.
+ slot: index of slot for which to generate
+ stages: List stages
+ startText: text to set info field in stage 0
+ importFn: function ran when import is pressed
+ resetButton: whether or not to show a reset button
+
+ where each stage is defined by either an intermediate screen:
+ name: name
+ control: id of the controle
+ text: id of button text or string to use
+ or a request stage
+ name: name
+ script | setFn: script name to proceed or function to request and set data for this stage
+ commentFn(area): function to append information to the text area
+ unsetFn: function to remove data for this stage
+ guard return wether or not to activate this stage
+ """
+ self.stages = stages
+ self.imp = importFn
+ self.showReset = resetButton
+ self.step = 0
+ self.window = None
+ self.startText=startText
+
+ def displayOverview(self):
+ """
+ Sets up the primary character generation window.
+ show all comments of previous stages
+ """
+ if(self.window):
+ CharGenWindow = self.window
+ else:
+ GemRB.LoadWindowPack ("GUICG", 640, 480)
+ CharGenWindow = GemRB.LoadWindow (0)
+ CharGenWindow.SetFrame ()
+
+ step = self.step
+
+ #set portrait
+ PortraitButton = CharGenWindow.GetControl (12)
+ PortraitButton.SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitName = GemRB.GetToken ("LargePortrait")
+ PortraitButton.SetPicture (PortraitName, "NOPORTMD")
+ PortraitButton.SetState(IE_GUI_BUTTON_LOCKED)
+
+ #set stage buttons
+ i = 0
+ for stage in self.stages:
+ if(len(stage) == 3): #short stage
+ (name,control,text) = stage
+ button = CharGenWindow.GetControl(control)
+ button.SetText(text);
+ if i == step:
+ button.SetState(IE_GUI_BUTTON_ENABLED)
+ button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, NextPress)
+ else:
+ button.SetState(IE_GUI_BUTTON_DISABLED)
+ i = i + 1
+
+ #set back button
+ BackButton = CharGenWindow.GetControl (11)
+ #BackButton.SetText (15416)
+ if(self.step != 0):
+ BackButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ BackButton.SetState(IE_GUI_BUTTON_DISABLED)
+ BackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BackPress)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ AcceptButton = CharGenWindow.GetControl (8)
+ playmode = GemRB.GetVar ("PlayMode")
+ if playmode>=0:
+ AcceptButton.SetText (11962)
+ else:
+ AcceptButton.SetText (13956)
+
+ #set scrollbar
+ ScrollBar = CharGenWindow.GetControl (10)
+ ScrollBar.SetDefaultScrollBar()
+
+ #set import
+ ImportButton = CharGenWindow.GetControl (13)
+ ImportButton.SetText (13955)
+ ImportButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ImportButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ImportPress)
+
+ #set cancel and start over
+ CancelButton = CharGenWindow.GetControl (15)
+ if step == 0 or not self.showReset:
+ CancelButton.SetText (13727) # Cancel
+ else:
+ CancelButton.SetText (8159) # Start over
+ CancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+
+ #set and fill overview
+ TextAreaControl = CharGenWindow.GetControl (9)
+ if(self.step == 0):
+ TextAreaControl.SetText(self.startText)
+ else:
+ TextAreaControl.SetText("")
+ for part in range(step):
+ if(len(self.stages[part]) == 5):
+ (name,setFn,commentFn,unsetFn,guard) = self.stages[part]
+ if(commentFn != None):
+ commentFn(TextAreaControl)
+
+ #show
+ CharGenWindow.SetVisible(WINDOW_VISIBLE)
+ CharGenWindow.Invalidate()
+ self.window = CharGenWindow
+
+ def unset(self,stage):
+ #print "unset" , stage
+ if(len(self.stages[stage]) == 5):
+ (name,setFn,commentFn,unsetFn,guard) = self.stages[stage]
+ if(unsetFn != None):
+ unsetFn()
+ #set next script to for step, return false if the current step should be skipped
+ def setScript(self):
+ #print 'set',self.step
+ if(len(self.stages[self.step]) == 5): #long record: script of function
+ (name,setFn,commentFn,unsetFn,guardFn) = self.stages[self.step]
+ if(guardFn and not guardFn()):
+ return False
+ if(hasattr(setFn, "__call__")):
+ setFn()
+ else:
+ GemRB.SetNextScript(setFn)
+ else: #short record: overview
+ GemRB.SetNextScript ("CharGen")
+ return True
+
+ def cancel(self):
+ """Revert back to the first step; unset all actions."""
+ #if self.window:
+ # self.window.Unload()
+ #reset
+ for i in range(self.step,-1,-1):
+ self.unset(i)
+
+ # if required back to main screen, otherwise reloop
+ if self.step == 0 or not self.showReset:
+ self.window.Unload()
+ self.window = None
+ GemRB.SetNextScript ("Start")
+ else:
+ GemRB.SetNextScript ("CharGen")
+
+ self.step = 0
+
+
+ def imprt(self):
+ """Opens the character import window."""
+ self.imp()
+
+ def back(self):
+ """Moves to the previous step. Unsets last"""
+ GUICommon.CloseOtherWindow(None)
+ self.unset(self.step)
+ if len(self.stages[self.step]) == 3:
+ #short, return to other short
+ self.step = self.step - 1
+ self.unset(self.step)
+ while len(self.stages[self.step]) != 3:
+ self.step = self.step - 1
+ self.unset(self.step)
+ else:
+ self.step = self.step - 1
+ self.unset(self.step)
+
+ while(not self.setScript()):
+ self.step = self.step - 1
+ self.unset(self.step)
+
+
+ def next(self):
+ """Calls the next setter."""
+ GUICommon.CloseOtherWindow(None)
+ self.step = self.step + 1
+ while(not self.setScript()):
+ self.step = self.step + 1
+
+ def close(self):
+ if(self.window):
+ self.window.Unload()
+
+ def jumpTo(self,to):
+ if type(to) == str:
+ done = False
+ for i in range(len(self.stages)):
+ if(to == self.stages[i][0]):
+ self.step = i
+ done = True
+ if(not done):
+ raise ValueError("stage name not found: "+str(to))
+ elif type(to) == int:
+ if(to<0):
+ to = len(self.stages)+to
+ if(to<0 or to>=len(self.stages)):
+ raise ValueError("stage index not found: "+str(to))
+ self.step = to
+
+ return
+ else:
+ raise ValueError("bad arg type: "+str(type(to)) + " " + str(to))
+ GUICommon.CloseOtherWindow(None)
+ while(not self.setScript()):
+ self.step = self.step + 1
+
+CharGenMaster = None
+
+def CancelPress():
+ """Revert back to the first step; if there, free the actor."""
+ global CharGenMaster
+ CharGenMaster.cancel()
+
+def ImportPress():
+ """Opens the character import window."""
+ global CharGenMaster
+ CharGenMaster.imprt()
+
+def BackPress():
+ """Moves to the previous step."""
+ global CharGenMaster
+ print "back"
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ CharGenMaster.back()
+
+def NextPress():
+ """Moves to the next step."""
+ global CharGenMaster
+ CharGenMaster.next()
+
+def back():
+ """Moves to the previous step."""
+ global CharGenMaster
+ CharGenMaster.back()
+
+def next():
+ """Moves to the next step."""
+ global CharGenMaster
+ CharGenMaster.next()
+
+def close():
+ """Terminates CharGen."""
+ global CharGenMaster
+ CharGenMaster.close()
+ CharGenMaster = None
+
+def jumpTo(stage):
+ global CharGenMaster
+ CharGenMaster.jumpTo(stage)
+
diff --git a/gemrb/GUIScripts/bg1/CharGenGui.py b/gemrb/GUIScripts/bg1/CharGenGui.py
new file mode 100644
index 0000000..17b92cd
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/CharGenGui.py
@@ -0,0 +1,393 @@
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+import CommonTables
+import LUCommon
+import LUProfsSelection
+
+def Imprt():
+ GemRB.SetToken("NextScript","CharGen")
+ GemRB.SetNextScript("ImportFile") #import
+ return
+
+def setPlayer():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.CreatePlayer ("charbase", MyChar | 0x8000 )
+ return False
+
+def unsetPlayer():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.CreatePlayer ("", MyChar | 0x8000 )
+ return False
+
+def unsetGender():
+ #print "unset Gender"
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_SEX, 0)
+
+def unsetPortrait():
+ #print "unset Portrait"
+ GemRB.SetToken("SmallPortrait","")
+ GemRB.SetToken("LargePortrait","")
+
+def getGender(area):
+ MyChar = GemRB.GetVar ("Slot")
+ area.SetText(12135)
+ area.Append (": ")
+ if GemRB.GetPlayerStat(MyChar,IE_SEX) == 1:
+ return area.Append(1050)
+ else:
+ return area.Append(1051)
+
+#race
+def unsetRace():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_RACE, 0)
+
+def getRace(area):
+ MyChar = GemRB.GetVar ("Slot")
+ RaceID = GemRB.GetPlayerStat (MyChar, IE_RACE)
+ RaceIndex = CommonTables.Races.FindValue(3,RaceID)
+ RaceCap = CommonTables.Races.GetValue(RaceIndex,2)
+ area.Append(1048,-1) # new line
+ area.Append(": ")
+ area.Append(RaceCap)
+
+#class
+def unsetClass():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, 0)
+ GemRB.SetPlayerStat (MyChar, IE_KIT, 0)
+ GemRB.SetVar ("MAGESCHOOL", 0)
+
+def getClass(area):
+ MyChar = GemRB.GetVar ("Slot")
+ ClassTitle = GUICommon.GetActorClassTitle(MyChar)
+
+ area.Append(12136, -1)
+ area.Append(": ")
+ area.Append(ClassTitle)
+
+def guardSpecialist():
+ return GemRB.GetVar("Specialist") == 1
+
+def guardMultiClass():
+ return GemRB.GetVar("Multi Class") == 1
+
+
+#Alignment
+def unsetAlignment():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_ALIGNMENT,0)
+
+def getAlignment(area):
+ AlignmentTable = GemRB.LoadTable("aligns")
+
+ MyChar = GemRB.GetVar ("Slot")
+ AllignID = GemRB.GetPlayerStat (MyChar, IE_ALIGNMENT)
+
+ area.Append(1049, -1)
+ area.Append(": ")
+ AllignIndex = AlignmentTable.FindValue(3,AllignID)
+ AllignCap = AlignmentTable.GetValue(AllignIndex,2)
+ area.Append(AllignCap)
+ area.Append("\n")
+
+#Abilties
+def unsetAbilities():
+ MyChar = GemRB.GetVar ("Slot")
+ AbilityTable = GemRB.LoadTable ("ability")
+ AbilityCount = AbilityTable.GetRowCount ()
+
+ # set all our abilites to zero
+ GemRB.SetPlayerStat (MyChar, IE_STREXTRA, 0)
+ for i in range(AbilityCount):
+ StatID = AbilityTable.GetValue (i, 3)
+ GemRB.SetPlayerStat (MyChar, StatID, 0)
+
+def getAbilities(area):
+ MyChar = GemRB.GetVar ("Slot")
+ AbilityTable = GemRB.LoadTable ("ability")
+ AbilityCount = AbilityTable.GetRowCount ()
+ for i in range(AbilityCount):
+ v = AbilityTable.GetValue(i,2)
+ id = AbilityTable.GetValue(i,3)
+ area.Append(v, -1)
+ area.Append(": "+str(GemRB.GetPlayerStat(MyChar,id)))
+ area.Append("\n")
+ area.Append("\n")
+
+#Skill
+def unsetHateRace():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat(MyChar, IE_HATEDRACE, 0 )
+
+def guardHateRace():
+ MyChar = GemRB.GetVar ("Slot")
+ Class = GemRB.GetPlayerStat(MyChar,IE_CLASS)
+ ClassName = CommonTables.ClassSkills.GetRowName(Class)
+ TableName = CommonTables.ClassSkills.GetValue(ClassName, "HATERACE")
+ return TableName != "*"
+
+def getHatedRace(TextAreaControl):
+ MyChar = GemRB.GetVar ("Slot")
+ Race = GemRB.GetPlayerStat(MyChar, IE_HATEDRACE)
+ if Race:
+ HateRaceTable = GemRB.LoadTable ("HATERACE")
+ Row = HateRaceTable.FindValue (1, Race)
+ info = GemRB.GetString (HateRaceTable.GetValue(Row, 0))
+ if info != "":
+ #TextAreaControl.Append("\n")
+ info = ": " + info + "\n"
+ TextAreaControl.Append(15982)
+ TextAreaControl.Append(info)
+
+def unsetMageSpells():
+ print("unsetMageSpells")
+ MyChar = GemRB.GetVar ("Slot")
+
+ GUICommon.RemoveKnownSpells (MyChar, IE_SPELL_TYPE_WIZARD, 1, 5, 1)
+
+def guardMageSpells():
+ MyChar = GemRB.GetVar ("Slot")
+ Class = GemRB.GetPlayerStat(MyChar,IE_CLASS)
+ TableName = CommonTables.ClassSkills.GetValue(Class, 2)
+ return TableName != "*"
+
+def getMageSpells(TextAreaControl):
+ MyChar = GemRB.GetVar ("Slot")
+ # arcane spells
+ info = ""
+ for level in range(0, 9):
+ for j in range(0, GemRB.GetKnownSpellsCount (MyChar, IE_SPELL_TYPE_WIZARD, level) ):
+ Spell = GemRB.GetKnownSpell (MyChar, IE_SPELL_TYPE_WIZARD, level, j)
+ Spell = GemRB.GetSpell (Spell['SpellResRef'], 1)['SpellName']
+ info += GemRB.GetString (Spell) + "\n"
+ if info != "":
+ info = "\n" + info + "\n"
+ TextAreaControl.Append (11027)
+ TextAreaControl.Append (info)
+
+def guardSkills():
+ SkillTable = GemRB.LoadTable("skills")
+ RowCount = SkillTable.GetRowCount()-2
+
+ MyChar = GemRB.GetVar ("Slot")
+ Kit = GUICommon.GetKitIndex(MyChar)
+
+ if Kit != 0: # luckily the first row is a dummy
+ KitName = CommonTables.KitList.GetValue(Kit, 0) #rowname is just a number
+ else:
+ ClassID = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue(5,ClassID)
+ KitName = CommonTables.Classes.GetRowName(ClassIndex)
+
+ for i in range(RowCount):
+ SkillName = SkillTable.GetRowName(i+2)
+ if SkillTable.GetValue(SkillName, KitName)==1:
+ return True
+
+ return False
+
+def unsetSkill():
+ import LUSkillsSelection
+ MyChar = GemRB.GetVar ("Slot")
+ LUSkillsSelection.SkillsNullify ()
+ LUSkillsSelection.SkillsSave (MyChar)
+
+def getSkills(TextAreaControl):
+ MyChar = GemRB.GetVar ("Slot")
+ # thieving and other skills
+ info = ""
+ SkillTable = GemRB.LoadTable ("skills")
+ ClassID = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, ClassID)
+ ClassName = CommonTables.Classes.GetRowName(Class)
+ RangerSkills = CommonTables.ClassSkills.GetValue (ClassName, "RANGERSKILL")
+ BardSkills = CommonTables.ClassSkills.GetValue (ClassName, "BARDSKILL")
+ KitName = GUICommon.GetKitIndex (MyChar)
+ if KitName == 0:
+ KitName = ClassName
+ else:
+ KitName = CommonTables.KitList.GetValue (KitName, 0)
+
+ if SkillTable.GetValue ("RATE", KitName) != -1 or BardSkills != "*" or RangerSkills != "*":
+ for skill in range(SkillTable.GetRowCount () - 2):
+ name = GemRB.GetString (SkillTable.GetValue (skill+2, 1))
+ id = SkillTable.GetValue (skill+2, 2)
+ available = SkillTable.GetValue (SkillTable.GetRowName (skill+2), KitName)
+ value = GemRB.GetPlayerStat(MyChar,id)
+ if value >= 0 and available != -1:
+ info += name + ": " + str(value) + "\n"
+
+ if info != "":
+ info = "\n" + info + "\n"
+ TextAreaControl.Append("\n")
+ TextAreaControl.Append (8442)
+ TextAreaControl.Append (info)
+
+def unsetProfi():
+ MyChar = GemRB.GetVar ("Slot")
+ LUProfsSelection.ProfsNullify ()
+ LUProfsSelection.ProfsSave(MyChar, LUProfsSelection.LUPROFS_TYPE_CHARGEN)
+
+def getProfi(TextAreaControl):
+ MyChar = GemRB.GetVar ("Slot")
+ # weapon proficiencies
+ TextAreaControl.Append ("\n")
+ TextAreaControl.Append (9466)
+ TextAreaControl.Append ("\n")
+ TmpTable=GemRB.LoadTable ("weapprof")
+ ProfCount = TmpTable.GetRowCount ()
+ #bg2 weapprof.2da contains the bg1 proficiencies too, skipping those
+ for i in range(ProfCount):
+ # 4294967296 overflows to -1 on some arches, so we use a smaller invalid strref
+ id = TmpTable.GetValue (i, 0)+IE_PROFICIENCYBASTARDSWORD
+ Weapon = GemRB.GetString (TmpTable.GetValue (i, 1))
+ Value = GemRB.GetPlayerStat (MyChar,id)
+ if Value:
+ pluses = " "
+ for plus in range(0, Value):
+ pluses += "+"
+ TextAreaControl.Append (Weapon + pluses + "\n")
+
+#Appearance
+def unsetColors():
+ MyChar = GemRB.GetVar ("Slot")
+ GUICommon.SetColorStat (MyChar, IE_HAIR_COLOR, 0 )
+ GUICommon.SetColorStat (MyChar, IE_SKIN_COLOR, 0 )
+ GUICommon.SetColorStat (MyChar, IE_MAJOR_COLOR, 0 )
+ GUICommon.SetColorStat (MyChar, IE_MINOR_COLOR, 0 )
+
+def unsetSounds():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerSound(MyChar,"")
+
+
+#name
+def unsetName():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerName (MyChar, "", 0)
+
+def getName(TextAreaControl):
+ MyChar = GemRB.GetVar ("Slot")
+ name = GemRB.GetPlayerName(MyChar)
+ if(name != ""):
+ TextAreaControl.Append(name + "\n")
+
+#divine spells
+def setDivineSpells():
+ MyChar = GemRB.GetVar ("Slot")
+
+ ClassID = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, ClassID)
+ ClassName = CommonTables.Classes.GetRowName(Class)
+
+ DruidTable = CommonTables.ClassSkills.GetValue (ClassName, "DRUIDSPELL")
+ ClericTable = CommonTables.ClassSkills.GetValue (ClassName, "CLERICSPELL")
+
+ print("CGG setDivineSpells: CP1",ClassID,Class,ClassName,DruidTable,ClericTable)
+
+ AllignID = GemRB.GetPlayerStat (MyChar, IE_ALIGNMENT)
+
+ if ClericTable != "*":
+ learnDivine(MyChar,0x4000,ClericTable,AllignID)
+ if DruidTable != "*":
+ learnDivine(MyChar,0x8000,DruidTable,AllignID)
+
+ return False
+
+def learnDivine(MyChar,ClassFlag,TableName,AllignID):
+ #print ("CGG setDivineSpells: CP2",MyChar,ClassFlag,TableName,AllignID )
+ GUICommon.SetupSpellLevels(MyChar, TableName, IE_SPELL_TYPE_PRIEST, 1)
+ Learnable = GUICommon.GetLearnablePriestSpells( ClassFlag, AllignID, 1)
+ for i in range(len(Learnable) ):
+ #print ("CGG setDivineSpells: CP3",Learnable[i])
+ if -1 == GUICommon.HasSpell(MyChar,IE_SPELL_TYPE_PRIEST,1,Learnable[i]):
+ #print ("CGG setDivineSpells: CP4",Learnable[i])
+ GemRB.LearnSpell (MyChar, Learnable[i], 0)
+
+def unsetDivineSpells():
+ print("unsetDivineSpells")
+ MyChar = GemRB.GetVar ("Slot")
+ GUICommon.RemoveKnownSpells (MyChar, IE_SPELL_TYPE_PRIEST, 1, 1, 1)
+
+def getDivineSpells(TextAreaControl):
+ MyChar = GemRB.GetVar ("Slot")
+ # divine spells
+ info = ""
+ for level in range(0, 7):
+ for j in range(0, GemRB.GetKnownSpellsCount (MyChar, IE_SPELL_TYPE_PRIEST, level) ):
+ Spell = GemRB.GetKnownSpell (MyChar, IE_SPELL_TYPE_PRIEST, level, j)
+ Spell = GemRB.GetSpell (Spell['SpellResRef'], 1)['SpellName']
+ info += GemRB.GetString (Spell) + "\n"
+ if info != "":
+ info = "\n" + info + "\n"
+ TextAreaControl.Append (11028)
+ TextAreaControl.Append (info)
+
+#finish
+def setAccept():
+ #set my character up
+ MyChar = GemRB.GetVar ("Slot")
+
+ ClassID = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, ClassID)
+ ClassName = CommonTables.Classes.GetRowName(Class)
+
+ #reputation
+ AllignID = GemRB.GetPlayerStat (MyChar, IE_ALIGNMENT)
+
+ TmpTable=GemRB.LoadTable ("repstart")
+ AlignmentTable = GemRB.LoadTable ("aligns")
+ t = TmpTable.GetValue (AllignID,0) * 10
+ GemRB.SetPlayerStat (MyChar, IE_REPUTATION, t)
+
+ #lore, thac0, hp, and saves
+ GemRB.SetPlayerStat (MyChar, IE_MAXHITPOINTS, 0)
+ GemRB.SetPlayerStat (MyChar, IE_HITPOINTS, 0)
+ LUCommon.SetupSavingThrows (MyChar)
+ LUCommon.SetupThaco (MyChar)
+ LUCommon.SetupLore (MyChar)
+ LUCommon.SetupHP (MyChar)
+
+ #slot 1 is the protagonist
+ if MyChar == 1:
+ GemRB.GameSetReputation( t )
+
+ #gold
+ TmpTable=GemRB.LoadTable ("strtgold")
+ t = GemRB.Roll (TmpTable.GetValue (ClassName,"ROLLS"),TmpTable.GetValue(ClassName,"SIDES"), TmpTable.GetValue (ClassName,"MODIFIER") )
+ GemRB.SetPlayerStat (MyChar, IE_GOLD, t*TmpTable.GetValue (ClassName,"MULTIPLIER") )
+
+ #set the base number of attacks; effects will add the proficiency bonus
+ GemRB.SetPlayerStat (MyChar, IE_NUMBEROFATTACKS, 2)
+
+ #colors
+ GUICommon.SetColorStat (MyChar, IE_METAL_COLOR, 0x1B )
+ GUICommon.SetColorStat (MyChar, IE_LEATHER_COLOR, 0x16 )
+ GUICommon.SetColorStat (MyChar, IE_ARMOR_COLOR, 0x17 )
+
+ #does all the rest
+ LargePortrait = GemRB.GetToken ("LargePortrait")
+ SmallPortrait = GemRB.GetToken ("SmallPortrait")
+ GemRB.FillPlayerInfo (MyChar, LargePortrait, SmallPortrait)
+ #10 is a weapon slot (see slottype.2da row 10)
+ GemRB.CreateItem (MyChar, "staf01", 10, 1, 0, 0)
+ GemRB.SetEquippedQuickSlot (MyChar, 0)
+ #LETS PLAY!!
+ playmode = GemRB.GetVar ("PlayMode")
+
+ GUICommon.CloseOtherWindow(None)
+
+ if playmode >=0:
+ CharGenCommon.close()
+ if GemRB.GetVar("GUIEnhancements"):
+ GemRB.SaveCharacter ( GemRB.GetVar ("Slot"), "gembak" )
+ GemRB.EnterGame()
+ else:
+ #show the export window
+ GemRB.SetToken("NextScript","CharGen")
+ GemRB.SetNextScript ("ExportFile")
diff --git a/gemrb/GUIScripts/bg1/ExportFile.py b/gemrb/GUIScripts/bg1/ExportFile.py
new file mode 100644
index 0000000..5f71964
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/ExportFile.py
@@ -0,0 +1,87 @@
+# 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.
+#
+#
+#character generation, export (GUICG21)
+import GemRB
+from GUIDefines import *
+import GUICommon
+import CharGenCommon
+
+#import from a character sheet
+ExportWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global ExportWindow, TextAreaControl
+
+ GemRB.LoadWindowPack ("GUICG", 640, 480)
+ ExportWindow = GemRB.LoadWindow (21)
+
+ GUICommon.CloseOtherWindow(ExportWindow.Unload)
+
+ TextAreaControl = ExportWindow.GetControl (4)
+ TextAreaControl.SetText (10962)
+
+ TextAreaControl = ExportWindow.GetControl (2)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ TextAreaControl.GetCharacters ()
+
+ FileNameEditBox = ExportWindow.GetControl (7)
+ FileNameEditBox.SetEvent (IE_GUI_EDIT_ON_CHANGE, FileNameChange)
+
+ DoneButton = ExportWindow.GetControl (0)
+ DoneButton.SetText (11973)
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = ExportWindow.GetControl (1)
+ CancelButton.SetText (13727)
+ #CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ TextAreaControl.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, SelectPress)
+ ExportWindow.ShowModal(MODAL_SHADOW_NONE)
+ FileNameEditBox.SetStatus(IE_GUI_CONTROL_FOCUSED)
+ return
+
+def SelectPress ():
+ FileNameEditBox = ExportWindow.GetControl (7)
+ FileName = TextAreaControl.QueryText ()
+ FileNameEditBox.SetText(FileName)
+ FileNameChange()
+ return
+
+def FileNameChange ():
+ DoneButton = ExportWindow.GetControl (0)
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def DonePress ():
+ FileNameEditBox = ExportWindow.GetControl (7)
+ FileName = FileNameEditBox.QueryText ()
+ Slot = GemRB.GetVar ("Slot")
+ GemRB.SaveCharacter (Slot, FileName)
+ GUICommon.CloseOtherWindow(None)
+ CharGenCommon.close()
+ GemRB.SetNextScript ("Start")
+ return
+
+def CancelPress ():
+ CharGenCommon.jumpTo("accept")
+ return
diff --git a/gemrb/GUIScripts/bg1/GUICG1.py b/gemrb/GUIScripts/bg1/GUICG1.py
new file mode 100644
index 0000000..b6951bf
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG1.py
@@ -0,0 +1,86 @@
+# 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.
+#
+#
+#character generation, gender (GUICG1)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+
+import CharGenCommon
+import GUICommon
+
+GenderWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+
+def OnLoad():
+ global GenderWindow, TextAreaControl, DoneButton
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(GenderWindow):
+ GenderWindow.Unload()
+ GenderWindow = None
+ return
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+
+ GenderWindow = GemRB.LoadWindow(1)
+
+ BackButton = GenderWindow.GetControl(6)
+ BackButton.SetText(15416)
+ DoneButton = GenderWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = GenderWindow.GetControl(5)
+ TextAreaControl.SetText(17236)
+
+ MaleButton = GenderWindow.GetControl(2)
+ MaleButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ FemaleButton = GenderWindow.GetControl(3)
+ FemaleButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ GemRB.SetVar("Gender",0)
+ MaleButton.SetVarAssoc("Gender",1)
+ FemaleButton.SetVarAssoc("Gender",2)
+ MaleButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClickedMale)
+ FemaleButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClickedFemale)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ GenderWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def ClickedMale():
+ TextAreaControl.SetText(13083)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def ClickedFemale():
+ TextAreaControl.SetText(13084)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def NextPress():
+ MyChar = GemRB.GetVar ("Slot")
+ #GemRB.CreatePlayer ("charbase", MyChar | 0x8000 )
+ Gender = GemRB.GetVar ("Gender")
+ GemRB.SetPlayerStat (MyChar, IE_SEX, Gender)
+ CharGenCommon.next()
+ return
diff --git a/gemrb/GUIScripts/bg1/GUICG10.py b/gemrb/GUIScripts/bg1/GUICG10.py
new file mode 100644
index 0000000..a156882
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG10.py
@@ -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.
+#
+#
+#character generation, multi-class (GUICG10)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import GUICommon
+import CommonTables
+
+import CharGenCommon
+
+ClassWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+
+def OnLoad():
+ global ClassWindow, TextAreaControl, DoneButton
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ ClassWindow = GemRB.LoadWindow(10)
+
+ GUICommon.CloseOtherWindow (ClassWindow.Unload)
+
+ ClassCount = CommonTables.Classes.GetRowCount()+1
+ RaceName = CommonTables.Races.GetRowName(GemRB.GetVar("Race")-1 )
+
+ j=0
+ for i in range(1,ClassCount):
+ if CommonTables.Classes.GetValue(i-1,4)==0:
+ continue
+ if j>11:
+ Button = ClassWindow.GetControl(j+7)
+ else:
+ Button = ClassWindow.GetControl(j+2)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ j = j + 1
+ j=0
+ for i in range(1,ClassCount):
+ ClassName = CommonTables.Classes.GetRowName(i-1)
+ Allowed = CommonTables.Classes.GetValue(ClassName, RaceName)
+ if CommonTables.Classes.GetValue(i-1,4)==0:
+ continue
+ if j>11:
+ Button = ClassWindow.GetControl(j+7)
+ else:
+ Button = ClassWindow.GetControl(j+2)
+
+ t = CommonTables.Classes.GetValue(i-1, 0)
+ Button.SetText(t )
+ j=j+1
+ if Allowed ==0:
+ continue
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClassPress)
+ Button.SetVarAssoc("Class", i) #multiclass, actually
+
+ BackButton = ClassWindow.GetControl(14)
+ BackButton.SetText(15416)
+ DoneButton = ClassWindow.GetControl(0)
+ DoneButton.SetText(11973)
+
+ TextAreaControl = ClassWindow.GetControl(12)
+ TextAreaControl.SetText(17244)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ ClassWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def ClassPress():
+ Class = GemRB.GetVar("Class")-1
+ TextAreaControl.SetText(CommonTables.Classes.GetValue(Class,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def NextPress():
+ #class
+ ClassIndex = GemRB.GetVar ("Class")-1
+ Class = CommonTables.Classes.GetValue (ClassIndex, 5)
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, Class)
+ CharGenCommon.next()
diff --git a/gemrb/GUIScripts/bg1/GUICG12.py b/gemrb/GUIScripts/bg1/GUICG12.py
new file mode 100644
index 0000000..a4bfb7a
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG12.py
@@ -0,0 +1,240 @@
+# 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.
+#
+#
+#character generation, portrait (GUICG12)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+
+
+AppearanceWindow = 0
+CustomWindow = 0
+PortraitButton = 0
+PortraitsTable = 0
+LastPortrait = 0
+Gender = 0
+
+def SetPicture ():
+ global PortraitsTable, LastPortrait
+
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"G"
+ PortraitButton.SetPicture (PortraitName)
+ return
+
+def OnLoad():
+ global AppearanceWindow, PortraitButton, PortraitsTable, LastPortrait
+ global Gender
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(AppearanceWindow):
+ AppearanceWindow.Unload()
+ return
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ AppearanceWindow = GemRB.LoadWindow (11)
+
+ #Load the Gender
+ MyChar = GemRB.GetVar ("Slot")
+ Gender = GemRB.GetPlayerStat (MyChar, IE_SEX)
+
+ #Load the Portraits Table
+ PortraitsTable = GemRB.LoadTable ("PICTURES")
+ PortraitsStart = PortraitsTable.FindValue (0, 2)
+ FemaleCount = PortraitsTable.GetRowCount () - PortraitsStart + 1
+ if Gender == 2:
+ LastPortrait = GemRB.Roll (1, FemaleCount, PortraitsStart-1)
+ else:
+ LastPortrait = GemRB.Roll (1, PortraitsTable.GetRowCount()-FemaleCount, 0)
+
+ PortraitButton = AppearanceWindow.GetControl (1)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ LeftButton = AppearanceWindow.GetControl (2)
+ RightButton = AppearanceWindow.GetControl (3)
+
+ BackButton = AppearanceWindow.GetControl (5)
+ BackButton.SetText (15416)
+
+ CustomButton = AppearanceWindow.GetControl (6)
+ CustomButton.SetText (17545)
+
+ DoneButton = AppearanceWindow.GetControl (0)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ RightButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RightPress)
+ LeftButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LeftPress)
+ BackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ CustomButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomPress)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, NextPress)
+
+ flag = False
+ while True:
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ SetPicture ()
+ break
+ LastPortrait = LastPortrait + 1
+ if LastPortrait >= PortraitsTable.GetRowCount ():
+ LastPortrait = 0
+ if flag:
+ SetPicture ()
+ break
+ flag = True
+
+ AppearanceWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def RightPress():
+ global LastPortrait
+ while True:
+ LastPortrait = LastPortrait + 1
+ if LastPortrait >= PortraitsTable.GetRowCount ():
+ LastPortrait = 0
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ SetPicture ()
+ return
+
+def LeftPress():
+ global LastPortrait
+ while True:
+ LastPortrait = LastPortrait - 1
+ if LastPortrait < 0:
+ LastPortrait = PortraitsTable.GetRowCount ()-1
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ SetPicture ()
+ return
+
+def CustomDone():
+ global AppearanceWindow
+
+ Window = CustomWindow
+ Portrait = PortraitList1.QueryText ()
+ GemRB.SetToken ("LargePortrait", Portrait)
+ Portrait = PortraitList2.QueryText ()
+ GemRB.SetToken ("SmallPortrait", Portrait)
+ if Window:
+ Window.Unload ()
+ CharGenCommon.next()
+ return
+
+def CustomAbort():
+ global CustomWindow
+
+ if CustomWindow:
+ CustomWindow.Unload ()
+ return
+
+def LargeCustomPortrait():
+ Window = CustomWindow
+
+ Portrait = PortraitList1.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row1") == RowCount1:
+ return
+
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (6)
+ if Portrait=="":
+ Portrait = "NOPORTLG"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList2.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (0)
+ Button.SetPicture (Portrait, "NOPORTLG")
+ return
+
+def SmallCustomPortrait():
+ Window = CustomWindow
+
+ Portrait = PortraitList2.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row2") == RowCount2:
+ return
+
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (6)
+ if Portrait=="":
+ Portrait = "NOPORTSM"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList1.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (1)
+ Button.SetPicture (Portrait, "NOPORTSM")
+ return
+
+def CustomPress():
+ global PortraitList1, PortraitList2
+ global RowCount1, RowCount2
+ global CustomWindow
+
+ CustomWindow = Window = GemRB.LoadWindow (18)
+ PortraitList1 = Window.GetControl (2)
+ RowCount1 = PortraitList1.GetPortraits (0)
+ PortraitList1.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, LargeCustomPortrait)
+ GemRB.SetVar ("Row1", RowCount1)
+ PortraitList1.SetVarAssoc ("Row1",RowCount1)
+
+ PortraitList2 = Window.GetControl (4)
+ RowCount2 = PortraitList2.GetPortraits (1)
+ PortraitList2.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, SmallCustomPortrait)
+ GemRB.SetVar ("Row2", RowCount2)
+ PortraitList2.SetVarAssoc ("Row2",RowCount2)
+
+ Button = Window.GetControl (6)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomDone)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Button = Window.GetControl (7)
+ Button.SetText (15416)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomAbort)
+
+ Button = Window.GetControl (0)
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"L"
+ Button.SetPicture (PortraitName, "NOPORTLG")
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Button = Window.GetControl (1)
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"S"
+ Button.SetPicture (PortraitName, "NOPORTSM")
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def NextPress():
+ PortraitTable = GemRB.LoadTable ("pictures")
+ PortraitName = PortraitTable.GetRowName (LastPortrait )
+ GemRB.SetToken ("SmallPortrait", PortraitName+"S")
+ GemRB.SetToken ("LargePortrait", PortraitName+"L")
+ CharGenCommon.next()
+ #GemRB.SetVar("Step",1)
+ #GemRB.SetNextScript("CharGen")
+ #GemRB.SetNextScript ("CharGen2") #Before race
+ #return
diff --git a/gemrb/GUIScripts/bg1/GUICG13.py b/gemrb/GUIScripts/bg1/GUICG13.py
new file mode 100644
index 0000000..40e687a
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG13.py
@@ -0,0 +1,207 @@
+# 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.
+#
+#
+#character generation, color (GUICG13)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+import BGCommon
+
+ColorTable = 0
+ColorWindow = 0
+ColorPicker = 0
+DoneButton = 0
+ColorIndex = 0
+PickedColor = 0
+HairButton = 0
+SkinButton = 0
+MajorButton = 0
+MinorButton = 0
+HairColor = 0
+SkinColor = 0
+MinorColor = 0
+MajorColor = 0
+PDollButton = 0
+
+def OnLoad():
+ global ColorWindow, DoneButton, PDollButton, ColorTable
+ global HairButton, SkinButton, MajorButton, MinorButton
+ global HairColor, SkinColor, MinorColor, MajorColor
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ ColorWindow=GemRB.LoadWindow(13)
+ GUICommon.CloseOtherWindow (ColorWindow.Unload)
+
+ ColorTable = GemRB.LoadTable("clowncol")
+ #set these colors to some default
+ PortraitTable = GemRB.LoadTable("pictures")
+ PortraitName = GemRB.GetToken("LargePortrait")
+ PortraitName = PortraitName[0:len(PortraitName)-1]
+ PortraitIndex = PortraitTable.GetRowIndex(PortraitName)
+ if PortraitIndex<0:
+ HairColor=PortraitTable.GetValue(0,1)
+ SkinColor=PortraitTable.GetValue(0,2)
+ MinorColor=PortraitTable.GetValue(0,3)
+ MajorColor=PortraitTable.GetValue(0,4)
+ else:
+ HairColor=PortraitTable.GetValue(PortraitIndex,1)
+ SkinColor=PortraitTable.GetValue(PortraitIndex,2)
+ MinorColor=PortraitTable.GetValue(PortraitIndex,3)
+ MajorColor=PortraitTable.GetValue(PortraitIndex,4)
+
+ PDollButton = ColorWindow.GetControl(1)
+ PDollButton.SetState (IE_GUI_BUTTON_LOCKED)
+ PDollButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+
+ HairButton = ColorWindow.GetControl(2)
+ HairButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ HairButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, HairPress)
+ HairButton.SetBAM("COLGRAD", 0, 0, HairColor)
+
+ SkinButton = ColorWindow.GetControl(3)
+ SkinButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ SkinButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SkinPress)
+ SkinButton.SetBAM("COLGRAD", 0, 0, SkinColor)
+
+ MajorButton = ColorWindow.GetControl(5)
+ MajorButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ MajorButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MajorPress)
+ MajorButton.SetBAM("COLGRAD", 0, 0, MinorColor)
+
+ MinorButton = ColorWindow.GetControl(4)
+ MinorButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ MinorButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MinorPress)
+ MinorButton.SetBAM("COLGRAD", 0, 0, MajorColor)
+
+ BackButton = ColorWindow.GetControl(13)
+ BackButton.SetText(15416)
+ DoneButton = ColorWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ ColorWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def DonePress():
+ global HairColor, SkinColor, MinorColor, MajorColor
+
+ if ColorPicker:
+ ColorPicker.Unload()
+
+ ColorWindow.ShowModal(MODAL_SHADOW_NONE)
+ PickedColor=ColorTable.GetValue(ColorIndex, GemRB.GetVar("Selected"))
+ if ColorIndex==0:
+ HairColor=PickedColor
+ HairButton.SetBAM("COLGRAD", 0, 0, HairColor)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ return
+ if ColorIndex==1:
+ SkinColor=PickedColor
+ SkinButton.SetBAM("COLGRAD", 0, 0, SkinColor)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ return
+ if ColorIndex==2:
+ MinorColor=PickedColor
+ MajorButton.SetBAM("COLGRAD", 0, 0, MinorColor)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ return
+
+ MajorColor=PickedColor
+ MinorButton.SetBAM("COLGRAD", 0, 0, MajorColor)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ return
+
+def GetColor():
+ global ColorPicker
+
+ ColorPicker=GemRB.LoadWindow(14)
+ GemRB.SetVar("Selected",-1)
+ for i in range(34):
+ Button = ColorPicker.GetControl(i)
+ Button.SetState(IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+
+ Selected = -1
+ for i in range(34):
+ MyColor = ColorTable.GetValue(ColorIndex, i)
+ if MyColor == "*":
+ Button = ColorPicker.GetControl(i)
+ Button.SetFlags(IE_GUI_BUTTON_PICTURE, OP_NAND)
+ continue
+ Button = ColorPicker.GetControl(i)
+ Button.SetBAM("COLGRAD", 2, 0, MyColor)
+ if PickedColor == MyColor:
+ GemRB.SetVar("Selected",i)
+ Selected = i
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetVarAssoc("Selected",i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+
+ ColorPicker.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def HairPress():
+ global ColorIndex, PickedColor
+
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 0
+ PickedColor = HairColor
+ GetColor()
+ return
+
+def SkinPress():
+ global ColorIndex, PickedColor
+
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 1
+ PickedColor = SkinColor
+ GetColor()
+ return
+
+def MajorPress():
+ global ColorIndex, PickedColor
+
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 2
+ PickedColor = MinorColor
+ GetColor()
+ return
+
+def MinorPress():
+ global ColorIndex, PickedColor
+
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 3
+ PickedColor = MajorColor
+ GetColor()
+ return
+
+
+def NextPress():
+ MyChar = GemRB.GetVar ("Slot")
+ GUICommon.SetColorStat (MyChar, IE_HAIR_COLOR, HairColor )
+ GUICommon.SetColorStat (MyChar, IE_SKIN_COLOR, SkinColor )
+ GUICommon.SetColorStat (MyChar, IE_MAJOR_COLOR, MajorColor)
+ GUICommon.SetColorStat (MyChar, IE_MINOR_COLOR, MinorColor )
+ CharGenCommon.next()
+ return
diff --git a/gemrb/GUIScripts/bg1/GUICG15.py b/gemrb/GUIScripts/bg1/GUICG15.py
new file mode 100644
index 0000000..cfcbe55
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG15.py
@@ -0,0 +1,113 @@
+# 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.
+#
+#
+#character generation, racial enemy (GUICG15)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+import CommonTables
+
+RaceWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+RacialEnemyTable = 0
+RaceCount = 0
+TopIndex = 0
+#the size of the selection list
+LISTSIZE = 6
+
+def DisplayRaces():
+ global TopIndex
+
+ TopIndex=GemRB.GetVar("TopIndex")
+ for i in range(LISTSIZE):
+ Button = RaceWindow.GetControl(i+2)
+ Val = RacialEnemyTable.GetValue(i+TopIndex,0)
+ if Val==0:
+ Button.SetText("")
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetText(Val)
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RacePress)
+ Button.SetVarAssoc("HatedRace",RacialEnemyTable.GetValue(i+TopIndex,1) )
+ return
+
+def OnLoad():
+ global RaceWindow, TextAreaControl, DoneButton
+ global RacialEnemyTable, RaceCount, TopIndex
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(RaceWindow):
+ RaceWindow.Unload()
+ RaceWindow = None
+ return
+
+ GemRB.SetVar ("HatedRace",0)
+
+ ClassRow = GemRB.GetVar("Class")-1
+ Class = CommonTables.Classes.GetValue(ClassRow, 5)
+ ClassName = CommonTables.ClassSkills.GetRowName(Class)
+ TableName = CommonTables.ClassSkills.GetValue(ClassName, "HATERACE")
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ RaceWindow = GemRB.LoadWindow(15)
+ RacialEnemyTable = GemRB.LoadTable(TableName)
+ RaceCount = RacialEnemyTable.GetRowCount()-LISTSIZE+1
+ if RaceCount<0:
+ RaceCount=0
+
+ for i in range(LISTSIZE):
+ Button = RaceWindow.GetControl(i+2)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ Button.SetSprites("GUIHRC",i,0,1,2,3)
+
+ BackButton = RaceWindow.GetControl(10)
+ BackButton.SetText(15416)
+ DoneButton = RaceWindow.GetControl(11)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ TextAreaControl = RaceWindow.GetControl(8)
+ TextAreaControl.SetText(17256)
+ TopIndex = 0
+ GemRB.SetVar("TopIndex",0)
+ ScrollBarControl = RaceWindow.GetControl(1)
+ ScrollBarControl.SetVarAssoc("TopIndex",RaceCount)
+ ScrollBarControl.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, DisplayRaces)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ RaceWindow.ShowModal(MODAL_SHADOW_NONE)
+ DisplayRaces()
+ return
+
+def RacePress():
+ Race = GemRB.GetVar("HatedRace")
+ Row = RacialEnemyTable.FindValue(1, Race)
+ TextAreaControl.SetText(RacialEnemyTable.GetValue(Row, 2) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def NextPress():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_HATEDRACE, GemRB.GetVar ("HatedRace") )
+ CharGenCommon.next()
diff --git a/gemrb/GUIScripts/bg1/GUICG19.py b/gemrb/GUIScripts/bg1/GUICG19.py
new file mode 100644
index 0000000..9cc2254
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG19.py
@@ -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.
+#
+#
+#character generation, sounds (GUICG19)
+import GemRB
+from GUIDefines import *
+from ie_restype import RES_WAV
+import CharGenCommon
+import GUICommon
+
+SoundSequence = [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m" ]
+VoiceList = 0
+CharSoundWindow = 0
+SoundIndex = 0
+
+def OnLoad():
+ global CharSoundWindow, VoiceList
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(CharSoundWindow):
+ CharSoundWindow.Unload()
+ CharSoundWindow = None
+ return
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ CharSoundWindow=GemRB.LoadWindow(19)
+
+ VoiceList = CharSoundWindow.GetControl (45)
+ VoiceList.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ if GemRB.GetVar ("Gender")==1:
+ GemRB.SetVar ("Selected", 3) #first male sound
+ else:
+ GemRB.SetVar ("Selected", 0)
+
+ VoiceList.SetVarAssoc ("Selected", 0)
+ RowCount=VoiceList.GetCharSounds()
+
+ PlayButton = CharSoundWindow.GetControl (47)
+ PlayButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ PlayButton.SetText (17318)
+
+ TextArea = CharSoundWindow.GetControl (50)
+ TextArea.SetText (11315)
+
+ BackButton = CharSoundWindow.GetControl(10)
+ BackButton.SetText(15416)
+ DoneButton = CharSoundWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ VoiceList.SetEvent(IE_GUI_TEXTAREA_ON_CHANGE, ChangeVoice)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ CharSoundWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def PlayPress():
+ global CharSoundWindow, SoundIndex, SoundSequence
+
+ CharSound = VoiceList.QueryText()
+ # SClassID.h -> IE_WAV_CLASS_ID = 0x00000004
+ while (not GemRB.HasResource (CharSound + SoundSequence[SoundIndex], RES_WAV)):
+ NextSound()
+ # play the sound like it was a speech, so any previous yells are quieted
+ GemRB.PlaySound (CharSound + SoundSequence[SoundIndex], 0, 0, 4)
+ NextSound()
+ return
+
+def NextSound():
+ global SoundIndex, SoundSequence
+ SoundIndex += 1
+ if SoundIndex >= len(SoundSequence):
+ SoundIndex = 0
+ return
+
+def ChangeVoice():
+ global SoundIndex
+ SoundIndex = 0
+ return
+
+def NextPress():
+ global CharSoundWindow
+ CharSound = VoiceList.QueryText ()
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerSound(MyChar,CharSound)
+ CharGenCommon.next()
+ return
diff --git a/gemrb/GUIScripts/bg1/GUICG2.py b/gemrb/GUIScripts/bg1/GUICG2.py
new file mode 100644
index 0000000..60429c4
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG2.py
@@ -0,0 +1,147 @@
+# 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.
+#
+#
+#character generation, class (GUICG2)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+import CommonTables
+
+
+ClassWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+
+def OnLoad():
+ global ClassWindow, TextAreaControl, DoneButton
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(ClassWindow):
+ ClassWindow.Unload()
+ ClassWindow = None
+ return
+
+ MyChar = GemRB.GetVar ("Slot")
+
+ GemRB.SetVar("Class",0)
+ GemRB.SetVar("Multi Class",0)
+ GemRB.SetVar("Specialist",0)
+ GemRB.SetVar("Class Kit",0)
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ ClassCount = CommonTables.Classes.GetRowCount()+1
+ ClassWindow = GemRB.LoadWindow(2)
+ RaceRow = CommonTables.Races.FindValue(3,GemRB.GetPlayerStat (MyChar, IE_RACE))
+ RaceName = CommonTables.Races.GetRowName(RaceRow)
+
+ #radiobutton groups must be set up before doing anything else to them
+ for i in range(1,ClassCount):
+ if CommonTables.Classes.GetValue(i-1,4):
+ continue
+
+ Button = ClassWindow.GetControl(i+1)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+
+ GemRB.SetVar("MAGESCHOOL",0)
+ HasMulti = 0
+ for i in range(1,ClassCount):
+ ClassName = CommonTables.Classes.GetRowName(i-1)
+ Allowed = CommonTables.Classes.GetValue(ClassName, RaceName)
+ if CommonTables.Classes.GetValue(i-1,4):
+ if Allowed!=0:
+ HasMulti = 1
+ continue
+
+ Button = ClassWindow.GetControl(i+1)
+
+ t = CommonTables.Classes.GetValue(i-1, 0)
+ Button.SetText(t )
+
+ if Allowed==2:
+ GemRB.SetVar("MAGESCHOOL",5) #illusionist
+ if Allowed!=1:
+ continue
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClassPress)
+ Button.SetVarAssoc("Class", i)
+
+ MultiClassButton = ClassWindow.GetControl(10)
+ MultiClassButton.SetText(11993)
+ if HasMulti == 0:
+ MultiClassButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ Allowed = CommonTables.Classes.GetValue ("MAGE", RaceName)
+ SpecialistButton = ClassWindow.GetControl(11)
+ SpecialistButton.SetText(11994)
+ if Allowed == 0:
+ SpecialistButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ BackButton = ClassWindow.GetControl(14)
+ BackButton.SetText(15416)
+ DoneButton = ClassWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = ClassWindow.GetControl(13)
+
+ Class = GemRB.GetVar("Class")-1
+ if Class<0:
+ TextAreaControl.SetText(17242)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ TextAreaControl.SetText(CommonTables.Classes.GetValue(Class,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+
+ MultiClassButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MultiClassPress)
+ SpecialistButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SpecialistPress)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ ClassWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def MultiClassPress():
+ GemRB.SetVar("Multi Class",1)
+ CharGenCommon.next()
+
+def SpecialistPress():
+ GemRB.SetVar("Specialist",1)
+
+ GemRB.SetVar("Class Kit", 0)
+ GemRB.SetVar("Class", 6)
+ CharGenCommon.next()
+
+def ClassPress():
+ Class = GemRB.GetVar("Class")-1
+ TextAreaControl.SetText(CommonTables.Classes.GetValue(Class,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def NextPress():
+ # find the class from the class table
+ ClassIndex = GemRB.GetVar ("Class") - 1
+ Class = CommonTables.Classes.GetValue (ClassIndex, 5)
+ #protect against barbarians
+ ClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, Class) )
+
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, Class)
+ CharGenCommon.next()
+
diff --git a/gemrb/GUIScripts/bg1/GUICG22.py b/gemrb/GUIScripts/bg1/GUICG22.py
new file mode 100644
index 0000000..3cffeff
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG22.py
@@ -0,0 +1,137 @@
+# 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.
+#
+#
+#character generation, class kit (GUICG22)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+import CommonTables
+
+
+KitWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+SchoolList = 0
+ClassID = 0
+
+def OnLoad():
+ global KitWindow, TextAreaControl, DoneButton
+ global SchoolList, ClassID
+
+ if GUICommon.CloseOtherWindow(OnLoad):
+ if(KitWindow):
+ KitWindow.Unload()
+ KitWindow = None
+ return
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ RaceName = CommonTables.Races.GetRowName(GemRB.GetVar("Race")-1 )
+ Class = GemRB.GetVar("Class")-1
+ ClassName = CommonTables.Classes.GetRowName(Class)
+ ClassID = CommonTables.Classes.GetValue(Class, 5)
+ KitTable = GemRB.LoadTable("kittable")
+ KitTableName = KitTable.GetValue(ClassName, RaceName)
+ KitTable = GemRB.LoadTable(KitTableName,1)
+
+ SchoolList = GemRB.LoadTable("magesch")
+
+ #there is only a specialist mage window for bg1
+ KitWindow = GemRB.LoadWindow(12)
+
+ for i in range(8):
+ Button = KitWindow.GetControl(i+2)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ if not KitTable:
+ RowCount = 1
+ else:
+ RowCount = KitTable.GetRowCount()
+
+ for i in range(RowCount):
+ Button = KitWindow.GetControl(i+2)
+ if not KitTable:
+ if ClassID == 1:
+ Kit=GemRB.GetVar("MAGESCHOOL")
+ KitName = SchoolList.GetValue(i, 0)
+ else:
+ Kit = 0
+ KitName = CommonTables.Classes.GetValue(GemRB.GetVar("Class")-1, 0)
+
+ else:
+ Kit = KitTable.GetValue(i,0)
+ if ClassID == 1:
+ if Kit:
+ Kit = Kit - 21
+ KitName = SchoolList.GetValue(Kit, 0)
+ else:
+ if Kit:
+ KitName = CommonTables.KitList.GetValue(Kit, 1)
+ else:
+ KitName = CommonTables.Classes.GetValue(GemRB.GetVar("Class")-1, 0)
+
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetText(KitName)
+ Button.SetVarAssoc("Class Kit",Kit)
+ if i==0:
+ GemRB.SetVar("Class Kit",Kit)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, KitPress)
+
+ BackButton = KitWindow.GetControl(12)
+ BackButton.SetText(15416)
+ DoneButton = KitWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = KitWindow.GetControl(11)
+ TextAreaControl.SetText(17247)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ #KitPress()
+ KitWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def KitPress():
+ Kit = GemRB.GetVar("Class Kit")
+ if Kit == 0:
+ KitName = CommonTables.Classes.GetValue(GemRB.GetVar("Class")-1, 1)
+ else:
+ if ClassID==1:
+ KitName = SchoolList.GetValue(Kit, 1)
+ else:
+ KitName = CommonTables.KitList.GetValue(Kit, 3)
+ TextAreaControl.SetText(KitName)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def NextPress():
+ #class
+ ClassIndex = GemRB.GetVar ("Class")-1
+ Class = CommonTables.Classes.GetValue (ClassIndex, 5)
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, Class)
+ KitIndex = GemRB.GetVar ("Class Kit")
+ if Class == 1:
+ GemRB.SetVar("MAGESCHOOL", KitIndex)
+ #the same as the unusable field
+ Kit = CommonTables.KitList.GetValue(KitIndex, 6)
+ GemRB.SetPlayerStat (MyChar, IE_KIT, Kit)
+ CharGenCommon.next()
diff --git a/gemrb/GUIScripts/bg1/GUICG3.py b/gemrb/GUIScripts/bg1/GUICG3.py
new file mode 100644
index 0000000..d15afd0
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG3.py
@@ -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.
+#
+#
+#character generation, alignment (GUICG3)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+import CommonTables
+
+
+AlignmentWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+AlignmentTable = 0
+
+def OnLoad():
+ global AlignmentWindow, TextAreaControl, DoneButton
+ global AlignmentTable
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(AlignmentWindow):
+ AlignmentWindow.Unload()
+ AlignmentWindow = None
+ return
+
+ MyChar = GemRB.GetVar ("Slot")
+
+ GemRB.SetVar("Alignment",-1)
+
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ ClassRow = CommonTables.Classes.FindValue(5,Class)
+ KitName = CommonTables.Classes.GetRowName(ClassRow)
+
+ AlignmentOk = GemRB.LoadTable("ALIGNMNT")
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ AlignmentTable = GemRB.LoadTable("aligns")
+ AlignmentWindow = GemRB.LoadWindow(3)
+
+ for i in range(9):
+ Button = AlignmentWindow.GetControl(i+2)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetText(AlignmentTable.GetValue(i,0) )
+
+ # This section enables or disables different alignment selections
+ # based on Class, and depends on the ALIGNMNT.2DA table
+ #
+ # For now, we just enable all buttons
+ for i in range(9):
+ Button = AlignmentWindow.GetControl(i+2)
+ if AlignmentOk.GetValue(KitName, AlignmentTable.GetValue(i, 4) ) != 0:
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, AlignmentPress)
+ Button.SetVarAssoc("Alignment", i)
+
+ BackButton = AlignmentWindow.GetControl(13)
+ BackButton.SetText(15416)
+ DoneButton = AlignmentWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+
+ TextAreaControl = AlignmentWindow.GetControl(11)
+ TextAreaControl.SetText(9602)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ AlignmentWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def AlignmentPress():
+ Alignment = GemRB.GetVar("Alignment")
+ TextAreaControl.SetText(AlignmentTable.GetValue(Alignment,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ GemRB.SetVar("Alignment",AlignmentTable.GetValue(Alignment,3) )
+ return
+
+def NextPress():
+ MyChar = GemRB.GetVar ("Slot")
+ Alignment = GemRB.GetVar ("Alignment")
+ GemRB.SetPlayerStat (MyChar, IE_ALIGNMENT, Alignment)
+ CharGenCommon.next()
diff --git a/gemrb/GUIScripts/bg1/GUICG4.py b/gemrb/GUIScripts/bg1/GUICG4.py
new file mode 100644
index 0000000..7ec64a1
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG4.py
@@ -0,0 +1,301 @@
+# 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.
+#
+#
+#character generation, ability (GUICG4)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+import CommonTables
+
+AbilityWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+AbilityTable = 0
+Abclasrq = 0
+Abclsmod = 0
+Abclasrq = 0
+Abracerq = 0
+PointsLeft = 0
+Minimum = 0
+Maximum = 0
+Add = 0
+KitIndex = 0
+HasStrExtra = 0
+MyChar = 0
+
+def CalcLimits(Abidx):
+ global Minimum, Maximum, Add
+
+ Race = CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (MyChar, IE_RACE) )
+ RaceName = CommonTables.Races.GetRowName(Race)
+
+ Minimum = 3
+ Maximum = 18
+
+ tmp = Abclasrq.GetValue(KitIndex, Abidx)
+ if tmp!=0 and tmp>Minimum:
+ Minimum = tmp
+
+ Race = Abracerq.GetRowIndex(RaceName)
+ tmp = Abracerq.GetValue(Race, Abidx*2)
+ if tmp!=0 and tmp>Minimum:
+ Minimum = tmp
+
+ tmp = Abracerq.GetValue(Race, Abidx*2+1)
+ if tmp!=0 and tmp>Maximum:
+ Maximum = tmp
+
+ Race = Abracead.GetRowIndex(RaceName)
+ Add = Abracead.GetValue(Race, Abidx)# + Abclsmod.GetValue(KitIndex, Abidx)
+ Maximum = Maximum + Add
+ Minimum = Minimum + Add
+ if Minimum<1:
+ Minimum=1
+ if Maximum>25:
+ Maximum=25
+
+ return
+
+def RollPress():
+ global Minimum, Maximum, Add, HasStrExtra, PointsLeft
+
+ GemRB.SetVar("Ability",0)
+ GemRB.SetVar("Ability -1",0)
+ PointsLeft = 0
+ SumLabel = AbilityWindow.GetControl(0x10000002)
+ SumLabel.SetText("0")
+ SumLabel.SetUseRGB(1)
+
+ if HasStrExtra:
+ e = GemRB.Roll(1,100,0)
+ else:
+ e = 0
+ GemRB.SetVar("StrExtra", e)
+ for i in range(6):
+ dice = 3
+ size = 5
+ CalcLimits(i)
+ v = GemRB.Roll(dice, size, Add+3)
+ if v<Minimum:
+ v = Minimum
+ if v>Maximum:
+ v = Maximum
+ GemRB.SetVar("Ability "+str(i), v )
+ Label = AbilityWindow.GetControl(0x10000003+i)
+ if i==0 and v==18 and HasStrExtra:
+ Label.SetText("18/"+str(e) )
+ else:
+ Label.SetText(str(v) )
+ Label.SetUseRGB(1)
+ return
+
+def OnLoad():
+ global AbilityWindow, TextAreaControl, DoneButton
+ global PointsLeft, HasStrExtra
+ global AbilityTable, Abclasrq, Abclsmod, Abracerq, Abracead
+ global KitIndex, Minimum, Maximum, MyChar
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(AbilityWindow):
+ AbilityWindow.Unload()
+ AbilityWindow = None
+ return
+
+ Abracead = GemRB.LoadTable("ABRACEAD")
+ #Abclsmod = GemRB.LoadTable("ABCLSMOD")
+ Abclasrq = GemRB.LoadTable("ABCLASRQ")
+ Abracerq = GemRB.LoadTable("ABRACERQ")
+
+ MyChar = GemRB.GetVar ("Slot")
+ Kit = GUICommon.GetKitIndex (MyChar)
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ if Kit == 0:
+ KitName = CommonTables.Classes.GetRowName(Class)
+ else:
+ #rowname is just a number, first value row what we need here
+ KitName = CommonTables.KitList.GetValue(Kit, 0)
+
+ #if the class uses the warrior table for saves, then it may have the extra strength
+ if CommonTables.Classes.GetValue(Class, 3)=="SAVEWAR":
+ HasStrExtra=1
+ else:
+ HasStrExtra=0
+
+ KitIndex = Abclasrq.GetRowIndex(KitName)
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ AbilityTable = GemRB.LoadTable("ability")
+ AbilityWindow = GemRB.LoadWindow(4)
+
+ RerollButton = AbilityWindow.GetControl(2)
+ RerollButton.SetText(11982)
+ StoreButton = AbilityWindow.GetControl(37)
+ StoreButton.SetText(17373)
+ RecallButton = AbilityWindow.GetControl(38)
+ RecallButton.SetText(17374)
+
+ BackButton = AbilityWindow.GetControl(36)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = AbilityWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+
+ RollPress()
+ StorePress()
+ for i in range(6):
+ Button = AbilityWindow.GetControl(i+30)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, JustPress)
+ Button.SetVarAssoc("Ability", i)
+
+ Button = AbilityWindow.GetControl(i*2+16)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, LeftPress)
+ Button.SetVarAssoc("Ability", i )
+
+ Button = AbilityWindow.GetControl(i*2+17)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RightPress)
+ Button.SetVarAssoc("Ability", i )
+
+ TextAreaControl = AbilityWindow.GetControl(29)
+ TextAreaControl.SetText(17247)
+
+ StoreButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, StorePress)
+ RecallButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, RecallPress)
+ RerollButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, RollPress)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ AbilityWindow.ShowModal(MODAL_SHADOW_NONE)
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+ return
+
+def RightPress():
+ global PointsLeft
+
+ Abidx = GemRB.GetVar("Ability")
+ Ability = GemRB.GetVar("Ability "+str(Abidx) )
+ CalcLimits(Abidx)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(Abidx, 1) )
+ if Ability<=Minimum:
+ return
+ GemRB.SetVar("Ability "+str(Abidx), Ability-1)
+ PointsLeft = PointsLeft + 1
+ GemRB.SetVar("Ability -1",PointsLeft)
+ SumLabel = AbilityWindow.GetControl(0x10000002)
+ SumLabel.SetText(str(PointsLeft) )
+ Label = AbilityWindow.GetControl(0x10000003+Abidx)
+ StrExtra = GemRB.GetVar("StrExtra")
+ if Abidx==0 and Ability==19 and StrExtra:
+ Label.SetText("18/"+str(StrExtra) )
+ else:
+ Label.SetText(str(Ability-1) )
+ return
+
+def JustPress():
+ Abidx = GemRB.GetVar("Ability")
+ Ability = GemRB.GetVar("Ability "+str(Abidx) )
+ CalcLimits(Abidx)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(Abidx, 1) )
+ return
+
+def LeftPress():
+ global PointsLeft
+
+ Abidx = GemRB.GetVar("Ability")
+ Ability = GemRB.GetVar("Ability "+str(Abidx) )
+ CalcLimits(Abidx)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(Abidx, 1) )
+ if PointsLeft == 0:
+ return
+ if Ability>=Maximum: #should be more elaborate
+ return
+ GemRB.SetVar("Ability "+str(Abidx), Ability+1)
+ PointsLeft = PointsLeft - 1
+ GemRB.SetVar("Ability -1",PointsLeft)
+ SumLabel = AbilityWindow.GetControl(0x10000002)
+ SumLabel.SetText(str(PointsLeft) )
+ Label = AbilityWindow.GetControl(0x10000003+Abidx)
+ StrExtra = GemRB.GetVar("StrExtra")
+ if Abidx==0 and Ability==17 and HasStrExtra==1:
+ Label.SetText("18/%02d"%(StrExtra) )
+ else:
+ Label.SetText(str(Ability+1) )
+ return
+
+def EmptyPress():
+ TextAreaControl = AbilityWindow.GetControl(29)
+ TextAreaControl.SetText(17247)
+ return
+
+def StorePress():
+ GemRB.SetVar("StoredStrExtra",GemRB.GetVar("StrExtra") )
+ for i in range(-1,6):
+ GemRB.SetVar("Stored "+str(i),GemRB.GetVar("Ability "+str(i) ) )
+ return
+
+def RecallPress():
+ global PointsLeft
+
+ e=GemRB.GetVar("StoredStrExtra")
+ GemRB.SetVar("StrExtra",e)
+ for i in range(-1,6):
+ v = GemRB.GetVar("Stored "+str(i) )
+ GemRB.SetVar("Ability "+str(i), v)
+ Label = AbilityWindow.GetControl(0x10000003+i)
+ if i==0 and v==18 and HasStrExtra==1:
+ Label.SetText("18/"+str(e) )
+ else:
+ Label.SetText(str(v) )
+
+ PointsLeft = GemRB.GetVar("Ability -1")
+ return
+
+def BackPress():
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ CharGenCommon.back()
+
+def NextPress():
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+
+ AbilityTable = GemRB.LoadTable ("ability")
+ AbilityCount = AbilityTable.GetRowCount ()
+
+ # print our diagnostic as we loop (so as not to duplicate)
+ for i in range (AbilityCount):
+ StatID = AbilityTable.GetValue (i, 3)
+ StatName = AbilityTable.GetRowName (i)
+ StatValue = GemRB.GetVar ("Ability "+str(i))
+ GemRB.SetPlayerStat (MyChar, StatID, StatValue)
+ print "\t",StatName,":\t", StatValue
+
+ GemRB.SetPlayerStat (MyChar, IE_STREXTRA, GemRB.GetVar ("StrExtra"))
+ print "\tSTREXTRA:\t",GemRB.GetVar ("StrExtra")
+
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+
+ CharGenCommon.next()
+
diff --git a/gemrb/GUIScripts/bg1/GUICG5.py b/gemrb/GUIScripts/bg1/GUICG5.py
new file mode 100644
index 0000000..ac6a696
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG5.py
@@ -0,0 +1,75 @@
+# 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.
+#
+#
+#character generation, name (GUICG5)
+import GemRB
+from GUIDefines import *
+
+import CharGenCommon
+import GUICommon
+
+
+NameWindow = 0
+NameField = 0
+DoneButton = 0
+
+def OnLoad():
+ global NameWindow, NameField, DoneButton
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(NameWindow):
+ NameWindow.Unload()
+ NameWindow = None
+ return
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ NameWindow = GemRB.LoadWindow(5)
+
+ BackButton = NameWindow.GetControl(3)
+ BackButton.SetText(15416)
+
+ DoneButton = NameWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ NameField = NameWindow.GetControl(2)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ NameField.SetEvent(IE_GUI_EDIT_ON_CHANGE, EditChange)
+ NameWindow.ShowModal(MODAL_SHADOW_NONE)
+ NameField.SetStatus(IE_GUI_CONTROL_FOCUSED)
+ return
+
+def NextPress():
+ Name = NameField.QueryText()
+ #check length?
+ #seems like a good idea to store it here for the time being
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerName (MyChar, Name, 0)
+ CharGenCommon.next()
+ return
+
+def EditChange():
+ Name = NameField.QueryText()
+ if len(Name)==0:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
diff --git a/gemrb/GUIScripts/bg1/GUICG6.py b/gemrb/GUIScripts/bg1/GUICG6.py
new file mode 100644
index 0000000..dcb501b
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG6.py
@@ -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.
+#
+#
+#character generation, skills (GUICG6)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+import LUSkillsSelection
+
+
+SkillWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+
+def RedrawSkills():
+ global TopIndex
+ if GemRB.GetVar ("SkillPointsLeft") == 0:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ return
+
+def OnLoad():
+ global SkillWindow, DoneButton
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(SkillWindow):
+ SkillWindow.Unload()
+ SkillWindow = None
+ return
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ SkillWindow = GemRB.LoadWindow(6)
+
+ MyChar = GemRB.GetVar ("Slot")
+
+ Levels = [GemRB.GetPlayerStat (MyChar, IE_LEVEL), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL2), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL3)]
+ LUSkillsSelection.SetupSkillsWindow (MyChar, LUSkillsSelection.LUSKILLS_TYPE_CHARGEN, SkillWindow, RedrawSkills, [0,0,0], Levels,0,False)
+
+ BackButton = SkillWindow.GetControl(25)
+ BackButton.SetText(15416)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+
+ DoneButton = SkillWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+
+ RedrawSkills()
+ SkillWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def NextPress():
+ MyChar = GemRB.GetVar ("Slot")
+ LUSkillsSelection.SkillsSave(MyChar)
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ CharGenCommon.next()
diff --git a/gemrb/GUIScripts/bg1/GUICG7.py b/gemrb/GUIScripts/bg1/GUICG7.py
new file mode 100644
index 0000000..a0559ab
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG7.py
@@ -0,0 +1,53 @@
+# -*-python-*-
+# 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.
+#
+# character generation, mage spells (GUICG7)
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import GUICommon
+import CommonTables
+import LUSpellSelection
+
+def OnLoad():
+ Slot = GemRB.GetVar ("Slot")
+ Class = GemRB.GetPlayerStat (Slot, IE_CLASS)
+ TableName = CommonTables.ClassSkills.GetValue(Class, 2)
+
+ # make sure we have a correct table
+ if Class == 19:
+ # sorcerer's need their known not max table
+ TableName = "SPLSRCKN"
+
+ # get our kit
+ KitValue = GemRB.GetPlayerStat (Slot, IE_KIT)
+
+ # open up the spell selection window
+ # remember, it is pc, table, level, diff, kit, chargen
+ IsMulti = GUICommon.IsMultiClassed (Slot, 1)
+ Level = GemRB.GetPlayerStat (Slot, IE_LEVEL)
+ if IsMulti[0]>1:
+ for i in range (1, IsMulti[0]):
+ if CommonTables.ClassSkills.GetValue (IsMulti[i], 2, 0) != "*":
+ Level = GemRB.GetPlayerStat (Slot, IE_LEVEL2+i-1)
+ break
+ GUICommon.SetupSpellLevels(Slot, TableName, IE_SPELL_TYPE_WIZARD, 1)
+ LUSpellSelection.OpenSpellsWindow (Slot, TableName, Level, Level, KitValue, 1,False)
+
+ return
diff --git a/gemrb/GUIScripts/bg1/GUICG8.py b/gemrb/GUIScripts/bg1/GUICG8.py
new file mode 100644
index 0000000..b34ca39
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG8.py
@@ -0,0 +1,84 @@
+# 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.
+#
+#
+#character generation, race (GUICG2)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import GUICommon
+import CommonTables
+
+
+RaceWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+
+def OnLoad():
+ global RaceWindow, TextAreaControl, DoneButton
+ global RaceTable
+
+ if GUICommon.CloseOtherWindow (OnLoad):
+ if(RaceWindow):
+ RaceWindow.Unload()
+ RaceWindow = None
+ return
+
+ GemRB.SetVar("Race",0)
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ RaceWindow = GemRB.LoadWindow(8)
+
+ RaceCount = CommonTables.Races.GetRowCount()
+
+ for i in range(2,RaceCount+2):
+ Button = RaceWindow.GetControl(i)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ for i in range(2, RaceCount+2):
+ Button = RaceWindow.GetControl(i)
+ Button.SetText(CommonTables.Races.GetValue(i-2,0) )
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RacePress)
+ Button.SetVarAssoc("Race",i-1)
+
+ BackButton = RaceWindow.GetControl(10)
+ BackButton.SetText(15416)
+ DoneButton = RaceWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ TextAreaControl = RaceWindow.GetControl(8)
+ TextAreaControl.SetText(17237)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+ RaceWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def RacePress():
+ Race = GemRB.GetVar("Race")-1
+ TextAreaControl.SetText(CommonTables.Races.GetValue(Race,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def NextPress():
+ Race = GemRB.GetVar ("Race") - 1
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_RACE, CommonTables.Races.GetValue (Race, 3))
+ CharGenCommon.next()
diff --git a/gemrb/GUIScripts/bg1/GUICG9.py b/gemrb/GUIScripts/bg1/GUICG9.py
new file mode 100644
index 0000000..01ef36a
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICG9.py
@@ -0,0 +1,69 @@
+# 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.
+#
+#
+#character generation, proficiencies (GUICG9)
+import GemRB
+import GUICommon
+from GUIDefines import *
+from ie_stats import *
+import CharGenCommon
+import LUProfsSelection
+
+SkillWindow = 0
+DoneButton = 0
+
+
+def RedrawSkills():
+ ProfsPointsLeft = GemRB.GetVar ("ProfsPointsLeft")
+ if not ProfsPointsLeft:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ return
+
+def OnLoad():
+ global SkillWindow, DoneButton
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ SkillWindow = GemRB.LoadWindow(9)
+
+ GUICommon.CloseOtherWindow(SkillWindow.Unload)
+
+ MyChar = GemRB.GetVar ("Slot")
+ Levels = [GemRB.GetPlayerStat (MyChar, IE_LEVEL), GemRB.GetPlayerStat (MyChar, IE_LEVEL2), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL3)]
+ LUProfsSelection.SetupProfsWindow (MyChar, LUProfsSelection.LUPROFS_TYPE_CHARGEN, SkillWindow, RedrawSkills, [0,0,0], Levels,scroll=False,profTableOffset=0)
+
+ BackButton = SkillWindow.GetControl(77)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharGenCommon.BackPress)
+
+ DoneButton = SkillWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ SkillWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def NextPress():
+ MyChar = GemRB.GetVar ("Slot")
+ LUProfsSelection.ProfsSave(MyChar, LUProfsSelection.LUPROFS_TYPE_CHARGEN)
+ CharGenCommon.next()
diff --git a/gemrb/GUIScripts/bg1/GUICommonWindows.py b/gemrb/GUIScripts/bg1/GUICommonWindows.py
new file mode 100644
index 0000000..8475bff
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUICommonWindows.py
@@ -0,0 +1,866 @@
+# -*-python-*-
+# 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.
+#
+
+# GUICommonWindows.py - functions to open common
+# windows in lower part of the screen
+###################################################
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+from ie_modal import *
+from ie_action import *
+import GUICommon
+import LUCommon
+import InventoryCommon
+
+# needed for all the Open*Window callbacks in the OptionsWindow
+import GUIJRNL
+import GUIMA
+import GUIMG
+import GUIINV
+import GUIOPT
+import GUIPR
+import GUIREC
+
+FRAME_PC_SELECTED = 0
+FRAME_PC_TARGET = 1
+
+PortraitWindow = None
+OptionsWindow = None
+ActionsWindow = None
+DraggedPortrait = None
+
+def SetupMenuWindowControls (Window, Gears, ReturnToGame):
+ """Sets up all of the basic control windows."""
+ global OptionsWindow
+
+ OptionsWindow = Window
+ # FIXME: add "(key)" to tooltips!
+
+ # Return to Game
+ Button = Window.GetControl (0)
+ Button.SetTooltip (16313)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ # enabled BAM isn't present in .chu, defining it here
+ Button.SetSprites ("GUILSOP", 0,16,17,28,16)
+ Button.SetVarAssoc ("SelectedWindow", 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ReturnToGame)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Map
+ Button = Window.GetControl (1)
+ Button.SetTooltip (16310)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetSprites ("GUILSOP", 0,0,1,20,0)
+ Button.SetVarAssoc ("SelectedWindow", 1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIMA.OpenMapWindow)
+
+ # Journal
+ Button = Window.GetControl (2)
+ Button.SetTooltip (16308)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetSprites ("GUILSOP", 0,4,5,22,4)
+ Button.SetVarAssoc ("SelectedWindow", 2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIJRNL.OpenJournalWindow)
+
+ # Inventory
+ Button = Window.GetControl (3)
+ Button.SetTooltip (16307)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetSprites ("GUILSOP", 0,2,3,21,2)
+ Button.SetVarAssoc ("SelectedWindow", 3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIINV.OpenInventoryWindow)
+
+ # Records
+ Button = Window.GetControl (4)
+ Button.SetTooltip (16306)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetSprites ("GUILSOP", 0,6,7,23,6)
+ Button.SetVarAssoc ("SelectedWindow", 4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIREC.OpenRecordsWindow)
+
+ # Mage
+ Button = Window.GetControl (5)
+ Button.SetTooltip (16309)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetSprites ("GUILSOP", 0,8,9,24,8)
+ Button.SetVarAssoc ("SelectedWindow", 5)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIMG.OpenMageWindow)
+
+ # Priest
+ Button = Window.GetControl (6)
+ Button.SetTooltip (14930)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetSprites ("GUILSOP", 0,10,11,25,10)
+ Button.SetVarAssoc ("SelectedWindow", 6)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIPR.OpenPriestWindow)
+
+ # Options
+ Button = Window.GetControl (7)
+ Button.SetTooltip (16311)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetSprites ("GUILSOP", 0,12,13,26,12)
+ Button.SetVarAssoc ("SelectedWindow", 7)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIOPT.OpenOptionsWindow)
+
+ # Party mgmt
+ Button = Window.GetControl (8)
+ Button.SetTooltip (16312)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None) #TODO: OpenPartyWindow
+
+ #gears
+ if Gears:
+ # Pendulum, gears, sun/moon dial (time)
+ # FIXME: display all animations: CPEN, CGEAR, CDIAL
+ Button = Window.GetControl (9)
+ Button.SetAnimation ("CGEAR")
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GUICommon.GearsClicked)
+ GUICommon.SetGamedaysAndHourToken()
+ Button.SetTooltip(16041)
+
+ MarkMenuButton (Window)
+
+ return
+
+def MarkMenuButton (WindowIndex):
+ Pressed = WindowIndex.GetControl( GemRB.GetVar ("SelectedWindow") )
+
+ for button in range (9):
+ Button = WindowIndex.GetControl (button)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ if Pressed:
+ Button = Pressed
+ else: # highlight return to game
+ Button = WindowIndex.GetControl (0)
+
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+
+def AIPress ():
+ Button = PortraitWindow.GetControl (6)
+ AI = GemRB.GetMessageWindowSize () & GS_PARTYAI
+
+ if AI:
+ GemRB.GameSetScreenFlags (GS_PARTYAI, OP_NAND)
+ Button.SetTooltip (15918)
+ else:
+ GemRB.GameSetScreenFlags (GS_PARTYAI, OP_OR)
+ Button.SetTooltip (15917)
+ return
+
+def EmptyControls ():
+ global ActionsWindow
+
+ GemRB.SetVar ("ActionLevel", 0)
+ Window = ActionsWindow
+ for i in range (12):
+ Button = Window.GetControl (i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetPicture ("")
+ Button.SetText ("")
+ Button.SetActionIcon (globals(), -1)
+ return
+
+def SelectFormationPreset ():
+ """Choose the default formation."""
+ GemRB.GameSetFormation (GemRB.GetVar ("Value"), GemRB.GetVar ("Formation") )
+ GroupControls ()
+ return
+
+def SetupFormation ():
+ """Opens the formation selection section."""
+ global ActionsWindow
+
+ Window = ActionsWindow
+ for i in range (12):
+ Button = Window.GetControl (i)
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetBAM ("FORM%x"%i,0,0,-1)
+ Button.SetVarAssoc ("Value", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectFormationPreset)
+ return
+
+def GroupControls ():
+ """Sections that control group actions."""
+
+ global ActionsWindow
+
+ GemRB.SetVar ("ActionLevel", 0)
+ Window = ActionsWindow
+ Button = Window.GetControl (0)
+ Button.SetActionIcon (globals(), 14)
+ Button = Window.GetControl (1)
+ Button.SetActionIcon (globals(), 7)
+ Button = Window.GetControl (2)
+ Button.SetActionIcon (globals(), 15)
+ Button = Window.GetControl (3)
+ Button.SetActionIcon (globals(), 21)
+ Button = Window.GetControl (4)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (5)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (6)
+ Button.SetActionIcon (globals(), -1)
+ GemRB.SetVar ("Formation", GemRB.GameGetFormation ())
+ for i in range (5):
+ Button = Window.GetControl (7+i)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ idx = GemRB.GameGetFormation (i)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetBAM ("FORM%x"%idx,0,0,-1)
+ Button.SetVarAssoc ("Formation", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectFormation)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, SetupFormation)
+ str = GemRB.GetString (4935)
+ Button.SetTooltip ("F%d - %s"%(8+i,str) )
+ return
+
+def OpenActionsWindowControls (Window):
+ global ActionsWindow
+
+ ActionsWindow = Window
+ # Gears (time) when options pane is down
+ #Button = Window.GetControl (62)
+ #Button.SetAnimation ("CGEAR")
+ #Button.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_ANIMATED, OP_SET)
+ #Button.SetState (IE_GUI_BUTTON_LOCKED)
+ UpdateActionsWindow ()
+ return
+
+def SelectItemAbility():
+ pc = GemRB.GameGetFirstSelectedActor ()
+ slot = GemRB.GetVar ("Slot")
+ ability = GemRB.GetVar ("Ability")
+ GemRB.SetupQuickSlot (pc, 0, slot, ability, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ return
+
+def SetupItemAbilities(pc, slot):
+ Window = ActionsWindow
+
+ slot_item = GemRB.GetSlotItem(pc, slot, 1)
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ Tips = item["Tooltips"]
+
+ for i in range (12):
+ Button = Window.GetControl (i)
+ Button.SetPicture ("")
+ if i<len(Tips):
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetItemIcon (slot_item['ItemResRef'], i+6)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectItemAbility)
+ Button.SetVarAssoc ("Ability", i)
+
+ Button.SetTooltip ("F%d - %s"%(i+1,GemRB.GetString(Tips[i])) )
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ return
+
+def UpdateActionsWindow ():
+ global ActionsWindow
+ global level, TopIndex
+
+ if ActionsWindow == -1:
+ return
+
+ if ActionsWindow == None:
+ return
+
+ Selected = GemRB.GetSelectedSize()
+
+ #setting up the disabled button overlay (using the second border slot)
+ for i in range (12):
+ Button = ActionsWindow.GetControl (i)
+ Button.SetBorder (0,6,6,4,4,0,254,0,255)
+ Button.SetBorder (1, 0, 0, 0, 0, 50,30,10,120, 0, 1)
+ Button.SetFont ("NUMBER")
+ Button.SetText ("")
+
+ if Selected == 0:
+ EmptyControls ()
+ return
+ if Selected > 1:
+ GroupControls ()
+ return
+
+ #we are sure there is only one actor selected
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ level = GemRB.GetVar ("ActionLevel")
+ TopIndex = GemRB.GetVar ("TopIndex")
+ if level == 0:
+ #this is based on class
+ ActionsWindow.SetupControls (globals(), pc, 0, 1)
+ elif level == 1:
+ ActionsWindow.SetupEquipmentIcons(globals(), pc, TopIndex, 0, 1)
+ elif level == 2: #spells
+ GemRB.SetVar ("Type", 3)
+ ActionsWindow.SetupSpellIcons(globals(), pc, 3, TopIndex, 0, 1)
+ elif level == 3: #innates
+ GemRB.SetVar ("Type", 4)
+ ActionsWindow.SetupSpellIcons(globals(), pc, 4, TopIndex, 0, 1)
+ elif level == 4: #quick weapon/item ability selection
+ SetupItemAbilities(pc, GemRB.GetVar("Slot") )
+ return
+
+def ActionQWeaponPressed (which):
+ """Selects the given quickslot weapon if possible."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+ qs = GemRB.GetEquippedQuickSlot (pc, 1, 1)
+
+ #38 is the magic slot
+ if ((qs==which) or (qs==38)) and GemRB.GameControlGetTargetMode() != TARGET_MODE_ATTACK:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK, GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
+ else:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_NONE)
+ GemRB.SetEquippedQuickSlot (pc, which, -1, 1)
+
+ ActionsWindow.SetupControls (globals(), pc, 0, 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionQWeapon1Pressed ():
+ ActionQWeaponPressed(0)
+
+def ActionQWeapon2Pressed ():
+ ActionQWeaponPressed(1)
+
+def ActionQWeapon3Pressed ():
+ ActionQWeaponPressed(2)
+
+def ActionQWeapon4Pressed ():
+ ActionQWeaponPressed(3)
+
+#no check needed because the button wouldn't be drawn if illegal
+def ActionLeftPressed ():
+ """Scrolls the actions window left.
+
+ Used primarily for spell selection."""
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ if TopIndex>10:
+ TopIndex -= 10
+ else:
+ TopIndex = 0
+ GemRB.SetVar ("TopIndex", TopIndex)
+ UpdateActionsWindow ()
+ return
+
+#no check needed because the button wouldn't be drawn if illegal
+def ActionRightPressed ():
+ """Scrolls the action window right.
+
+ Used primarily for spell selection."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Type = GemRB.GetVar ("Type")
+ #Type is a bitfield if there is no level given
+ #This is to make sure cleric/mages get all spells listed
+ Max = GemRB.GetMemorizedSpellsCount(pc, Type, -1, 1)
+ TopIndex += 10
+ if TopIndex > Max - 10:
+ if Max>10:
+ TopIndex = Max-10
+ else:
+ TopIndex = 0
+
+ GemRB.SetVar ("TopIndex", TopIndex)
+ UpdateActionsWindow ()
+ return
+
+def ActionBardSongPressed ():
+ """Toggles the battle song."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_BATTLESONG, 1)
+ GemRB.PlaySound ("act_01")
+ UpdateActionsWindow ()
+ return
+
+def ActionSearchPressed ():
+ """Toggles detect traps."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_DETECTTRAPS, 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionStealthPressed ():
+ """Toggles stealth."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_STEALTH, 1)
+ GemRB.PlaySound ("act_07")
+ UpdateActionsWindow ()
+ return
+
+def ActionTurnPressed ():
+ """Toggles turn undead."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_TURNUNDEAD, 1)
+ GemRB.PlaySound ("act_06")
+ UpdateActionsWindow ()
+ return
+
+def ActionUseItemPressed ():
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionCastPressed ():
+ """Opens the spell choice scrollbar."""
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 2)
+ UpdateActionsWindow ()
+ return
+
+def ActionQItemPressed (action):
+ """Uses the given quick item."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ #quick slot
+ GemRB.UseItem (pc, -2, action, -1, 1)
+ return
+
+def ActionQItem1Pressed ():
+ ActionQItemPressed (ACT_QSLOT1)
+ return
+
+def ActionQItem2Pressed ():
+ ActionQItemPressed (ACT_QSLOT2)
+ return
+
+def ActionQItem3Pressed ():
+ ActionQItemPressed (ACT_QSLOT3)
+ return
+
+def ActionQItem4Pressed ():
+ ActionQItemPressed (ACT_QSLOT4)
+ return
+
+def ActionQItem5Pressed ():
+ ActionQItemPressed (ACT_QSLOT5)
+ return
+
+def ActionQItemRightPressed (action):
+ """Selects the used ability of the quick item."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetVar ("Slot", action)
+ GemRB.SetVar ("ActionLevel", 4)
+ UpdateActionsWindow ()
+ return
+
+def ActionQItem1RightPressed ():
+ ActionQItemRightPressed (19)
+
+def ActionQItem2RightPressed ():
+ ActionQItemRightPressed (20)
+
+def ActionQItem3RightPressed ():
+ ActionQItemRightPressed (21)
+
+def ActionQItem4RightPressed ():
+ ActionQItemRightPressed (22)
+
+def ActionQItem5RightPressed ():
+ ActionQItemRightPressed (23)
+
+def ActionQWeapon1RightPressed ():
+ ActionQItemRightPressed (10)
+
+def ActionQWeapon2RightPressed ():
+ ActionQItemRightPressed (11)
+
+def ActionQWeapon3RightPressed ():
+ ActionQItemRightPressed (12)
+
+def ActionQWeapon4RightPressed ():
+ ActionQItemRightPressed (13)
+
+def ActionInnatePressed ():
+ """Opens the innate spell scrollbar."""
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 3)
+ UpdateActionsWindow ()
+ return
+
+def SpellPressed ():
+ """Prepares a spell to be cast."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
+ Spell = GemRB.GetVar ("Spell")
+ Type = GemRB.GetVar ("Type")
+ GemRB.SpellCast (pc, Type, Spell, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ UpdateActionsWindow ()
+ return
+
+def EquipmentPressed ():
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
+ Item = GemRB.GetVar ("Equipment")
+ #equipment index
+ GemRB.UseItem (pc, -1, Item, -1, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ UpdateActionsWindow ()
+ return
+
+SelectionChangeHandler = None
+
+def SetSelectionChangeHandler (handler):
+ """Updates the selection handler."""
+
+ global SelectionChangeHandler
+
+ # Switching from walking to non-walking environment:
+ # set the first selected PC in walking env as a selected
+ # in nonwalking env
+ #if (not SelectionChangeHandler) and handler:
+ if (not SelectionChangeHandler) and handler and (not GUICommon.NextWindowFn):
+ sel = GemRB.GameGetFirstSelectedPC ()
+ if not sel:
+ sel = 1
+ GemRB.GameSelectPCSingle (sel)
+
+ SelectionChangeHandler = handler
+
+ # redraw selection on change main selection | single selection
+ SelectionChanged ()
+ return
+
+def RunSelectionChangeHandler ():
+ if SelectionChangeHandler:
+ SelectionChangeHandler ()
+ return
+
+def OpenPortraitWindow (needcontrols):
+ global PortraitWindow
+
+ PortraitWindow = Window = GemRB.LoadWindow (1)
+
+ if needcontrols:
+ #Button=Window.GetControl (8)
+ #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MinimizePortraits)
+
+ # AI
+ Button = Window.GetControl (6)
+ GSFlags = GemRB.GetMessageWindowSize ()&GS_PARTYAI
+ Button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ #this control is crippled
+ Button.SetSprites ("GUIBTACT", 0, 46, 47, 48, 49)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, AIPress)
+ Button.SetVarAssoc ("", GSFlags)
+ if GSFlags:
+ Button.SetTooltip (15917)
+ else:
+ Button.SetTooltip (15918)
+
+ #Select All
+ Button = Window.GetControl (7)
+ Button.SetTooltip (10485)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectAllOnPress)
+ else:
+ #Rest
+ Button = Window.GetControl (6)
+ Button.SetTooltip (11942)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.RestPress)
+
+ for i in range (PARTY_SIZE):
+ Button = Window.GetControl (i)
+ Button.SetFont ("STATES2")
+ Button.SetVarAssoc ("PressedPortrait", i+1)
+
+ if (needcontrols):
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, GUIINV.OpenInventoryWindowClick)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, PortraitButtonOnPress)
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitButtonOnPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, PortraitButtonOnShiftPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDropItemToPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT, OnDropPortraitToPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG, PortraitButtonOnDrag)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, PortraitButtonOnMouseEnter)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, PortraitButtonOnMouseLeave)
+
+ Button.SetBorder (FRAME_PC_SELECTED, 1, 1, 2, 2, 0, 255, 0, 255)
+ Button.SetBorder (FRAME_PC_TARGET, 3, 3, 4, 4, 255, 255, 0, 255)
+
+ UpdatePortraitWindow ()
+ SelectionChanged ()
+ return Window
+
+def UpdatePortraitWindow ():
+ """Updates all of the portraits."""
+
+ Window = PortraitWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Inventory = GemRB.GetVar ("Inventory")
+
+ for portid in range (PARTY_SIZE):
+ Button = Window.GetControl (portid)
+ pic = GemRB.GetPlayerPortrait (portid+1, 1)
+ if Inventory and pc != portid+1:
+ pic = None
+
+ if pic and GemRB.GetPlayerStat(portid+1, IE_STATE_ID) & STATE_DEAD:
+ import GUISTORE
+ # dead pcs are hidden in all stores but temples
+ if GUISTORE.StoreWindow and not GUISTORE.StoreHealWindow:
+ pic = None
+
+ if not pic:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetText ("")
+ Button.SetTooltip ("")
+ continue
+
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE| IE_GUI_BUTTON_HORIZONTAL| \
+ IE_GUI_BUTTON_ALIGN_LEFT| IE_GUI_BUTTON_ALIGN_TOP| \
+ IE_GUI_BUTTON_DRAGGABLE|IE_GUI_BUTTON_MULTILINE, OP_SET)
+
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetPicture (pic, "NOPORTSM")
+ GUICommon.SetupDamageInfo (portid+1, Button)
+
+ #add effects on the portrait
+ effects = GemRB.GetPlayerStates (portid+1)
+ states = ""
+ for col in range(len(effects)):
+ states = effects[col:col+1] + states
+ if col % 3 == 2: states = "\n" + states
+ for x in range(3 - (len(effects)/3)):
+ states = "\n" + states
+ states = "\n" + states
+
+ # character - 1 == bam cycle
+ # blank space
+ flag = blank = chr(33)
+
+ # shopping icon
+ if pc==portid+1:
+ if GemRB.GetStore()!=None:
+ flag = chr(37)
+ # talk icon
+ if GemRB.GameGetSelectedPCSingle(1)==portid+1:
+ flag = chr(37)
+
+ if LUCommon.CanLevelUp (portid+1):
+ states = flag+blank+chr(255) + states
+ else:
+ states = flag+blank+blank + states
+ Button.SetText(states)
+ return
+
+def PortraitButtonOnDrag ():
+ global DraggedPortrait
+
+ #they start from 1
+ DraggedPortrait = GemRB.GetVar ("PressedPortrait")
+ GemRB.DragItem (DraggedPortrait, -1, "")
+ return
+
+def PortraitButtonOnPress ():
+ """Selects the portrait individually."""
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if GemRB.GameControlGetTargetMode() != TARGET_MODE_NONE:
+ GemRB.ActOnPC (i)
+ return
+
+ if (not SelectionChangeHandler):
+ if GemRB.GameIsPCSelected (i):
+ GemRB.GameControlSetScreenFlags (SF_CENTERONACTOR, OP_OR)
+ GemRB.GameSelectPC (i, True, SELECT_REPLACE)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def PortraitButtonOnShiftPress ():
+ """Handles selecting multiple portaits with shift."""
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if (not SelectionChangeHandler):
+ sel = GemRB.GameIsPCSelected (i)
+ sel = not sel
+ GemRB.GameSelectPC (i, sel)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def SelectionChanged ():
+ """Ran by the Game class when a PC selection is changed."""
+
+ global PortraitWindow
+
+ if not PortraitWindow:
+ return
+
+ GemRB.SetVar ("ActionLevel", 0)
+ if (not SelectionChangeHandler):
+ UpdateActionsWindow ()
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, GemRB.GameIsPCSelected (i + 1))
+ else:
+ sel = GemRB.GameGetSelectedPCSingle ()
+
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, i + 1 == sel)
+ import CommonWindow
+ CommonWindow.CloseContainerWindow()
+ return
+
+def PortraitButtonOnMouseEnter ():
+ global DraggedPortrait
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ GemRB.GameControlSetLastActor( i )
+ if GemRB.IsDraggingItem()==2:
+ if DraggedPortrait != None:
+ GemRB.SwapPCs (DraggedPortrait, i)
+ GemRB.SetVar ("PressedPortrait", DraggedPortrait)
+ DraggedPortrait = i
+ GemRB.SetTimedEvent (CheckDragging, 1)
+ else:
+ OnDropPortraitToPC()
+ return
+
+ if GemRB.IsDraggingItem ():
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_TARGET, 1)
+ return
+
+def OnDropPortraitToPC ():
+ GemRB.SetVar ("PressedPortrait",0)
+ GemRB.DragItem (0, -1, "")
+ DraggedPortrait = None
+ return
+
+def CheckDragging():
+ """Contains portrait dragging in case of mouse out-of-range."""
+
+ global DraggedPortrait
+
+ i = GemRB.GetVar ("PressedPortrait")
+ if not i:
+ GemRB.DragItem (0, -1, "")
+
+ if GemRB.IsDraggingItem()!=2:
+ DraggedPortrait = None
+ return
+
+def PortraitButtonOnMouseLeave ():
+ i = GemRB.GetVar ("PressedPortrait")
+ if not i:
+ return
+
+ Button = PortraitWindow.GetControl (i-1)
+ Button.EnableBorder (FRAME_PC_TARGET, 0)
+ GemRB.SetVar ("PressedPortrait", 0)
+ GemRB.SetTimedEvent (CheckDragging, 1)
+ return
+
+def ActionStopPressed ():
+ for i in GemRB.GetSelectedActors():
+ GemRB.ClearActions (i, 1)
+ return
+
+def ActionTalkPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_TALK,GA_NO_DEAD|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionAttackPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK,GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
+
+def ActionDefendPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_DEFEND,GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionThievingPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_PICK, GA_NO_DEAD|GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def OpenWaitForDiscWindow ():
+ global DiscWindow
+
+ if DiscWindow:
+ GemRB.HideGUI ()
+ if DiscWindow:
+ DiscWindow.Unload ()
+ GemRB.SetVar ("OtherWindow", -1)
+ # ...LoadWindowPack()
+ EnableAnimatedWindows ()
+ DiscWindow = None
+ GemRB.UnhideGUI ()
+ return
+
+ try:
+ GemRB.HideGUI ()
+ except:
+ pass
+
+ GemRB.LoadWindowPack ("GUIID")
+ DiscWindow = Window = GemRB.LoadWindow (0)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+ label = DiscWindow.GetControl (0)
+
+ disc_num = GemRB.GetVar ("WaitForDisc")
+ #disc_path = GemRB.GetVar ("WaitForDiscPath")
+ disc_path = 'XX:'
+
+ text = GemRB.GetString (31483) + " " + str (disc_num) + " " + GemRB.GetString (31569) + " " + disc_path + "\n" + GemRB.GetString (49152)
+ label.SetText (text)
+ DisableAnimatedWindows ()
+ # 31483 - Please place PS:T disc number
+ # 31568 - Please place the PS:T DVD
+ # 31569 - in drive
+ # 31570 - Wrong disc in drive
+ # 31571 - There is no disc in drive
+ # 31578 - No disc could be found in drive. Please place Disc 1 in drive.
+ # 49152 - To quit the game, press Alt-F4
+
+ try:
+ GemRB.UnhideGUI ()
+ except:
+ DiscWindow.SetVisible (WINDOW_VISIBLE)
+
+def CheckLevelUp(pc):
+ GemRB.SetVar ("CheckLevelUp"+str(pc), LUCommon.CanLevelUp (pc))
diff --git a/gemrb/GUIScripts/bg1/GUIINV.py b/gemrb/GUIScripts/bg1/GUIINV.py
new file mode 100644
index 0000000..2da0863
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIINV.py
@@ -0,0 +1,316 @@
+#-*-python-*-
+#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.
+#
+
+#GUIINV.py - scripts to control inventory windows from GUIINV winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import InventoryCommon
+from GUIDefines import *
+from ie_stats import *
+from ie_slots import *
+
+InventoryWindow = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+def OpenInventoryWindowClick ():
+ tmp = GemRB.GetVar ("PressedPortrait")
+ GemRB.GameSelectPC (tmp, True, SELECT_REPLACE)
+ OpenInventoryWindow ()
+ return
+
+def OpenInventoryWindow ():
+ """Opens the inventory window."""
+
+ import GUICommonWindows
+
+ global InventoryWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenInventoryWindow):
+ if GemRB.IsDraggingItem () == 1:
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #store the item in the inventory before window is closed
+ GemRB.DropDraggedItem (pc, -3)
+ #dropping on ground if cannot store in inventory
+ if GemRB.IsDraggingItem () == 1:
+ GemRB.DropDraggedItem (pc, -2)
+
+ if InventoryWindow:
+ InventoryWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ InventoryWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ GUICommonWindows.UpdatePortraitWindow ()
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIINV", 640, 480)
+ InventoryWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", InventoryWindow.ID)
+ GemRB.SetVar ("MessageLabel", Window.GetControl (0x1000003f).ID )
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ OptionsWindow.SetFrame ()
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenInventoryWindow)
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ #ground items scrollbar
+ ScrollBar = Window.GetControl (66)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RefreshInventoryWindow)
+
+ #Ground Item
+ for i in range (5):
+ Button = Window.GetControl (i+68)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, InventoryCommon.MouseEnterGround)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, InventoryCommon.MouseLeaveGround)
+ Button.SetVarAssoc ("GroundItemButton", i)
+ Button.SetSprites ("STONSLOT",0,0,2,4,3)
+ Button.SetFont ("NUMBER")
+ Button.SetBorder (0,0,0,0,0,128,128,255,64,0,1)
+ Button.SetBorder (1,2,2,2,2,32,32,255,0,0,0)
+ Button.SetBorder (2,0,0,0,0,255,128,128,64,0,1)
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_TOP | IE_GUI_BUTTON_PICTURE, OP_OR)
+
+ #major & minor clothing color
+ Button = Window.GetControl (62)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.MajorPress)
+ Button.SetTooltip (12007)
+
+ Button = Window.GetControl (63)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.MinorPress)
+ Button.SetTooltip (12008)
+
+ #portrait
+ Button = Window.GetControl (50)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnAutoEquip)
+
+ #encumbrance
+ Label = Window.CreateLabel (0x10000043, 14,375,60,20,"NUMBER","0:",IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_TOP)
+ Label = Window.CreateLabel (0x10000044, 8,445,60,20,"NUMBER","0:",IE_FONT_ALIGN_RIGHT|IE_FONT_ALIGN_TOP)
+
+ #armor class
+ Label = Window.GetControl (0x10000038)
+ Label.SetTooltip (17183)
+
+ #hp current
+ Label = Window.GetControl (0x10000039)
+ Label.SetTooltip (17184)
+
+ #hp max
+ Label = Window.GetControl (0x1000003a)
+ Label.SetTooltip (17378)
+
+ #info label, game paused, etc
+ Label = Window.GetControl (0x1000003f)
+ Label.SetText ("")
+
+ SlotCount = GemRB.GetSlotType (-1)["Count"]
+ for slot in range (SlotCount):
+ SlotType = GemRB.GetSlotType (slot+1)
+ if SlotType["ID"]:
+ Button = Window.GetControl (SlotType["ID"])
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, InventoryCommon.MouseEnterSlot)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, InventoryCommon.MouseLeaveSlot)
+ Button.SetVarAssoc ("ItemButton", slot+1)
+ #keeping 1 in the original place, because it is how
+ #the gui resource has it, but setting the other cycles
+ Button.SetSprites ("STONSLOT",0,0,1,2,3)
+ Button.SetFont ("NUMBER")
+ Button.SetBorder (0,0,0,0,0,128,128,255,64,0,1)
+ Button.SetBorder (1,2,2,2,2,32,32,255,0,0,0)
+ Button.SetBorder (2,0,0,0,0,255,128,128,64,0,1)
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_TOP | IE_GUI_BUTTON_PICTURE, OP_OR)
+
+ GemRB.SetVar ("TopIndex", 0)
+ GUICommonWindows.SetSelectionChangeHandler (UpdateInventoryWindow)
+ UpdateInventoryWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+
+ # force unpause the game
+ GemRB.GamePause(0, 0)
+ return
+
+#complete update
+def UpdateInventoryWindow ():
+ Window = InventoryWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Container = GemRB.GetContainer (pc, 1)
+ ScrollBar = Window.GetControl (66)
+ Count = Container['ItemCount']
+ if Count<1:
+ Count=1
+ ScrollBar.SetVarAssoc ("TopIndex", Count)
+ RefreshInventoryWindow ()
+ #populate inventory slot controls
+ SlotCount = GemRB.GetSlotType (-1)["Count"]
+ for i in range (SlotCount):
+ InventoryCommon.UpdateSlot (pc, i)
+ return
+
+#partial update without altering TopIndex
+def RefreshInventoryWindow ():
+ Window = InventoryWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ #name
+ Label = Window.GetControl (0x10000032)
+ Label.SetText (GemRB.GetPlayerName (pc, 0))
+
+ #portrait
+ Button = Window.GetControl (50)
+ Color1 = GemRB.GetPlayerStat (pc, IE_METAL_COLOR)
+ Color2 = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR)
+ Color3 = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR)
+ Color4 = GemRB.GetPlayerStat (pc, IE_SKIN_COLOR)
+ Color5 = GemRB.GetPlayerStat (pc, IE_LEATHER_COLOR)
+ Color6 = GemRB.GetPlayerStat (pc, IE_ARMOR_COLOR)
+ Color7 = GemRB.GetPlayerStat (pc, IE_HAIR_COLOR)
+ Button.SetPLT (GUICommon.GetActorPaperDoll (pc),
+ Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 0)
+
+ anim_id = GemRB.GetPlayerStat (pc, IE_ANIMATION_ID)
+ row = "0x%04X" %anim_id
+ size = CommonTables.Pdolls.GetValue (row, "SIZE")
+
+ #Weapon
+ slot_item = GemRB.GetSlotItem (pc, GemRB.GetEquippedQuickSlot (pc) )
+ if slot_item:
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ if (item['AnimationType'] != ''):
+ Button.SetPLT("WP" + size + item['AnimationType'] + "INV", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 1)
+
+ #Shield
+ slot_item = GemRB.GetSlotItem (pc, 3)
+ if slot_item:
+ itemname = slot_item["ItemResRef"]
+ item = GemRB.GetItem (itemname)
+ if (item['AnimationType'] != ''):
+ if (GemRB.CanUseItemType (SLOT_WEAPON, itemname)):
+ #off-hand weapon
+ Button.SetPLT("WP" + size + item['AnimationType'] + "OIN", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 2)
+ else:
+ #shield
+ Button.SetPLT("WP" + size + item['AnimationType'] + "INV", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 2)
+
+ #Helmet
+ slot_item = GemRB.GetSlotItem (pc, 1)
+ if slot_item:
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ if (item['AnimationType'] != ''):
+ Button.SetPLT("WP" + size + item['AnimationType'] + "INV", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 3)
+
+ #encumbrance
+ GUICommon.SetEncumbranceLabels ( Window, 0x10000043, 0x10000044, pc)
+
+ #armor class
+ ac = GemRB.GetPlayerStat (pc, IE_ARMORCLASS)
+ ac += GemRB.GetAbilityBonus (IE_DEX, 2, GemRB.GetPlayerStat (pc, IE_DEX) )
+ Label = Window.GetControl (0x10000038)
+ Label.SetText (str (ac))
+
+ #hp current
+ hp = GemRB.GetPlayerStat (pc, IE_HITPOINTS)
+ Label = Window.GetControl (0x10000039)
+ Label.SetText (str (hp))
+
+ #hp max
+ hpmax = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)
+ Label = Window.GetControl (0x1000003a)
+ Label.SetText (str (hpmax))
+
+ #party gold
+ Label = Window.GetControl (0x10000040)
+ Label.SetText (str (GemRB.GameGetPartyGold ()))
+
+ #class
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ Label = Window.GetControl (0x10000042)
+ Label.SetText (ClassTitle)
+
+ Button = Window.GetControl (62)
+ Color = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 0, 0, Color)
+
+ Button = Window.GetControl (63)
+ Color = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 0, 0, Color)
+
+ #update ground inventory slots
+ Container = GemRB.GetContainer (pc, 1)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ for i in range (5):
+ Button = Window.GetControl (i+68)
+ if GemRB.IsDraggingItem ()==1:
+ Button.SetState (IE_GUI_BUTTON_SECOND)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDragItemGround)
+ Slot = GemRB.GetContainerItem (pc, i+TopIndex)
+
+ if Slot == None:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.OnDragItemGround)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InventoryCommon.OpenGroundItemInfoWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None) #TODO: implement OpenGroundItemAmountWindow
+
+ GUICommon.UpdateInventorySlot (pc, Button, Slot, "ground")
+
+ #making window visible/shaded depending on the pc's state
+ held = GemRB.GetPlayerStat (pc, IE_HELD) + GemRB.GetPlayerStat (pc, IE_CASTERHOLD)
+ if held == 0 and GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD == 0:
+ Window.SetVisible (WINDOW_VISIBLE)
+ else:
+ Window.SetVisible (WINDOW_GRAYED)
+ return
+
+###################################################
+#End of file GUIINV.py
diff --git a/gemrb/GUIScripts/bg1/GUIJRNL.py b/gemrb/GUIScripts/bg1/GUIJRNL.py
new file mode 100644
index 0000000..dea6a10
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIJRNL.py
@@ -0,0 +1,153 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIJRNL.py - scripts to control journal/diary windows from GUIJRNL winpack
+
+###################################################
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+
+###################################################
+JournalWindow = None
+OldOptionsWindow = None
+OldPortraitWindow = None
+OverSlot = None
+Chapter = 0
+StartTime = 0
+StartYear = 0
+
+###################################################
+def OpenJournalWindow ():
+ global JournalWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+ global StartTime, StartYear, Chapter
+
+ if GUICommon.CloseOtherWindow (OpenJournalWindow):
+
+ if JournalWindow:
+ JournalWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ JournalWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ Table = GemRB.LoadTable("YEARS")
+ #StartTime is the time offset for ingame time, beginning from the startyear
+ StartTime = Table.GetValue("STARTTIME", "VALUE") / 4500
+ #StartYear is the year of the lowest ingame date to be printed
+ StartYear = Table.GetValue("STARTYEAR", "VALUE")
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIJRNL", 640, 480)
+ JournalWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", JournalWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenJournalWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ Button = Window.GetControl (3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, JournalPrevSectionPress)
+
+ Button = Window.GetControl (4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, JournalNextSectionPress)
+
+ Chapter = GemRB.GetGameVar("chapter")
+ GemRB.SetVar("TopIndex", 0)
+ GUICommonWindows.SetSelectionChangeHandler (UpdateJournalWindow)
+ UpdateJournalWindow ()
+
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+def UpdateJournalWindow ():
+ Window = JournalWindow
+
+ # Title
+ Title = Window.GetControl (5)
+ Title.SetText (16202 + Chapter)
+
+ # text area
+ Text = Window.GetControl (1)
+ Text.Clear ()
+
+ for i in range (GemRB.GetJournalSize (Chapter)):
+ je = GemRB.GetJournalEntry (Chapter, i)
+
+ if je == None:
+ continue
+
+ hours = je['GameTime'] / 4500
+ days = int(hours/24)
+ year = str (StartYear + int(days/365))
+ dayandmonth = StartTime + days%365
+ GemRB.SetToken("GAMEDAY", str(days) )
+ GemRB.SetToken("HOUR",str(hours%24 ) )
+ GemRB.SetVar("DAYANDMONTH",dayandmonth)
+ GemRB.SetToken("YEAR",year)
+ #Text.Append ("[color=FFFF00]"+GemRB.GetString(15980)+"[/color]", 3*i)
+ Text.Append (GemRB.GetString(15980), 3*i)
+ Text.Append (je['Text'], 3*i + 1)
+ Text.Append ("", 3*i + 2)
+
+
+###################################################
+def JournalPrevSectionPress ():
+ global Chapter
+
+ if Chapter > 0:
+ Chapter = Chapter - 1
+ UpdateJournalWindow ()
+
+
+###################################################
+def JournalNextSectionPress ():
+ global Chapter
+
+ #if GemRB.GetJournalSize (Chapter + 1) > 0:
+ if Chapter < GemRB.GetGameVar("chapter"):
+ Chapter = Chapter + 1
+ UpdateJournalWindow ()
+
+
+###################################################
+# End of file GUIJRNL.py
diff --git a/gemrb/GUIScripts/bg1/GUILOAD.py b/gemrb/GUIScripts/bg1/GUILOAD.py
new file mode 100644
index 0000000..316ec84
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUILOAD.py
@@ -0,0 +1,176 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUILOAD.py - Load window
+
+###################################################
+
+import GemRB
+import LoadScreen
+from GUIDefines import *
+
+LoadWindow = 0
+TextAreaControl = 0
+Games = ()
+ScrollBar = 0
+
+def OnLoad ():
+ global LoadWindow, TextAreaControl, Games, ScrollBar
+
+ GemRB.LoadWindowPack ("GUILOAD", 640, 480)
+ LoadWindow = GemRB.LoadWindow (0)
+ LoadWindow.SetFrame ()
+
+ CancelButton=LoadWindow.GetControl (34)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.SetVar ("LoadIdx",0)
+ for i in range (4):
+ Button = LoadWindow.GetControl (26+i)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ Button = LoadWindow.GetControl (30+i)
+ Button.SetText (13957)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ #area previews
+ Button = LoadWindow.GetControl (1+i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ #PC portraits
+ for j in range (PARTY_SIZE):
+ Button = LoadWindow.GetControl (40 + i*PARTY_SIZE + j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ ScrollBar=LoadWindow.GetControl (25)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games=GemRB.GetSaveGames ()
+ TopIndex = max (0, len(Games) - 4)
+ GemRB.SetVar ("TopIndex",TopIndex)
+ ScrollBar.SetVarAssoc ("TopIndex", TopIndex+1)
+ ScrollBarPress ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ScrollBarPress ():
+ #draw load game portraits
+ Pos = GemRB.GetVar ("TopIndex")
+ for i in range (4):
+ ActPos = Pos + i
+
+ Button1 = LoadWindow.GetControl (26+i)
+ Button2 = LoadWindow.GetControl (30+i)
+ if ActPos<len(Games):
+ Button1.SetState (IE_GUI_BUTTON_ENABLED)
+ Button2.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState (IE_GUI_BUTTON_DISABLED)
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetName()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl (0x10000008+i)
+ Label.SetText (Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetDate()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl (0x10000010+i)
+ Label.SetText (Slotname)
+
+ Button=LoadWindow.GetControl (1+i)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture ("")
+ for j in range (PARTY_SIZE):
+ Button=LoadWindow.GetControl (40 + i*PARTY_SIZE + j)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture ("")
+ return
+
+def LoadGamePress ():
+ if LoadWindow:
+ LoadWindow.Unload ()
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ LoadScreen.StartLoadScreen()
+ GemRB.LoadGame (Games[Pos]) #loads and enters savegame
+ GemRB.EnterGame ()
+ return
+
+def DeleteGameConfirm():
+ global Games
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Pos = TopIndex +GemRB.GetVar ("LoadIdx")
+ GemRB.DeleteSaveGame(Games[Pos])
+ if TopIndex>0:
+ GemRB.SetVar ("TopIndex",TopIndex-1)
+ del Games[Pos]
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress ()
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel ():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress ():
+ global ConfirmWindow
+
+ LoadWindow.SetVisible (WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow (1)
+ Text=ConfirmWindow.GetControl (0)
+ Text.SetText (15305)
+ DeleteButton=ConfirmWindow.GetControl (1)
+ DeleteButton.SetText (13957)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ CancelButton=ConfirmWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CancelPress ():
+ if LoadWindow:
+ LoadWindow.Unload ()
+ GemRB.SetNextScript ("Start")
+ return
diff --git a/gemrb/GUIScripts/bg1/GUIMA.py b/gemrb/GUIScripts/bg1/GUIMA.py
new file mode 100644
index 0000000..cd98f51
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIMA.py
@@ -0,0 +1,295 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMA.py - scripts to control map windows from the GUIMA and GUIWMAP winpacks
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+
+MapWindow = None
+WorldMapWindow = None
+WorldMapControl = None
+PortraitWindow = None
+OldPortraitWindow = None
+OptionsWindow = None
+OldOptionsWindow = None
+
+def RevealMap ():
+ global MapWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (ShowMap):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+
+ PosX = GemRB.GetVar ("MapControlX")
+ PosY = GemRB.GetVar ("MapControlY")
+
+ GemRB.RevealArea (PosX, PosY, 30, 1)
+ GemRB.GamePause (0,0)
+ return
+
+###################################################
+# for farsight effect
+###################################################
+def ShowMap ():
+ global MapWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (ShowMap):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMAP", 640, 480)
+ MapWindow = Window = GemRB.LoadWindow (2)
+ #this window type blocks the game normally, but map window doesn't
+ GemRB.SetVar ("OtherWindow", MapWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, ShowMap)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ OptionsWindow.SetFrame ()
+
+ # World Map
+ Button = Window.GetControl (1)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Map Control
+ Window.CreateMapControl (2, 0, 0, 0, 0)
+ Map = Window.GetControl (2)
+ Map.SetEvent (IE_GUI_MAP_ON_PRESS, RevealMap)
+ Window.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_GRAYED)
+ PortraitWindow.SetVisible (WINDOW_GRAYED)
+ OptionsWindow.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_FRONT)
+ Window.SetVisible (WINDOW_FRONT)
+ Map.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ GemRB.GamePause (0,0)
+ return
+
+###################################################
+def OpenMapWindow ():
+ global MapWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenMapWindow):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ #OldOptionsWindow.SetVisible (WINDOW_VISIBLE)
+ #OldPortraitWindow.SetVisible (WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMAP", 640, 480)
+ MapWindow = Window = GemRB.LoadWindow (2)
+ #this window type blocks the game normally, but map window doesn't
+ GemRB.SetVar ("OtherWindow", MapWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenMapWindow)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ OptionsWindow.SetFrame ()
+
+ # World Map
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenWorldMapWindowInside)
+
+ # Map Control
+ Window.CreateMapControl (2, 0, 0, 0, 0)
+ Map = Window.GetControl (2)
+ Map.SetEvent (IE_GUI_MAP_ON_DOUBLE_PRESS, LeftDoublePressMap)
+ #OldOptionsWindow.SetVisible (WINDOW_INVISIBLE)
+ #OldPortraitWindow.SetVisible (WINDOW_INVISIBLE)
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ Map.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def LeftDoublePressMap ():
+ OpenMapWindow()
+ return
+
+def OpenWorldMapWindowInside ():
+ global MapWindow
+
+ OpenMapWindow () #closes mapwindow
+ MapWindow = -1
+ print "MapWindow=",MapWindow
+ WorldMapWindowCommon (-1)
+ return
+
+def OpenWorldMapWindow ():
+ WorldMapWindowCommon (GemRB.GetVar ("Travel"))
+ return
+
+def MoveToNewArea ():
+ global WorldMapWindow, WorldMapControl
+
+ tmp = WorldMapControl.GetDestinationArea (1)
+ if tmp["Distance"]==-1:
+ print "Invalid target", tmp
+ return
+
+ CloseWorldMapWindow ()
+ GemRB.CreateMovement (tmp["Destination"], tmp["Entrance"], tmp["Direction"])
+ return
+
+def ChangeTooltip ():
+ global WorldMapWindow, WorldMapControl
+ global str
+
+ tmp = WorldMapControl.GetDestinationArea ()
+ if (tmp):
+ str = "%s: %d"%(GemRB.GetString(23084),tmp["Distance"])
+ else:
+ str=""
+
+ WorldMapControl.SetTooltip (str)
+ return
+
+def CloseWorldMapWindow ():
+ global WorldMapWindow, WorldMapControl
+
+ print "CloseWorldMapWindow found Mapwindow = ",MapWindow
+ if MapWindow:
+ # reopen map window
+ if WorldMapWindow:
+ WorldMapWindow.Unload ()
+ WorldMapWindow = None
+ WorldMapControl = None
+ OpenMapWindow ()
+ return
+
+ if WorldMapWindow:
+ WorldMapWindow.Unload ()
+ WorldMapWindow = None
+ WorldMapControl = None
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ return
+
+def WorldMapWindowCommon (Travel):
+ global WorldMapWindow, WorldMapControl
+
+ if WorldMapWindow:
+ CloseWorldMapWindow ()
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIWMAP", 640, 480)
+ WorldMapWindow = Window = GemRB.LoadWindow (0)
+ #saving the original portrait window
+ Window.SetFrame ()
+
+ Window.CreateWorldMapControl (4, 0, 62, 640, 418, Travel, "toolfont")
+ WorldMapControl = Window.GetControl (4)
+ WorldMapControl.SetTextColor (IE_GUI_WMAP_COLOR_BACKGROUND, 0xa4, 0x6a, 0x4c, 0x00)
+ WorldMapControl.SetAnimation ("WMDAG")
+ WorldMapControl.SetEvent (IE_GUI_WORLDMAP_ON_PRESS, MoveToNewArea)
+ WorldMapControl.SetEvent (IE_GUI_MOUSE_ENTER_WORLDMAP, ChangeTooltip)
+ #center on current area
+ WorldMapControl.AdjustScrolling (0, 0)
+
+ #north
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapN)
+
+ #south
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapS)
+
+ # Done
+ Button = Window.GetControl (0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseWorldMapWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MapN():
+ WorldMapControl.AdjustScrolling (0, -10)
+ return
+
+def MapS():
+ WorldMapControl.AdjustScrolling (0, 10)
+ return
+
+
+###################################################
+# End of file GUIMA.py
diff --git a/gemrb/GUIScripts/bg1/GUIMG.py b/gemrb/GUIScripts/bg1/GUIMG.py
new file mode 100644
index 0000000..ca0ab10
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIMG.py
@@ -0,0 +1,354 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMG.py - scripts to control mage spells windows from GUIMG winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+
+MageWindow = None
+MageSpellInfoWindow = None
+MageSpellLevel = 0
+MageSpellUnmemorizeWindow = None
+OldOptionsWindow = None
+PortraitWindow = None
+OldPortraitWindow = None
+
+
+def OpenMageWindow ():
+ global MageWindow, OptionsWindow
+ global OldOptionsWindow, PortraitWindow, OldPortraitWindow
+
+ if GUICommon.CloseOtherWindow (OpenMageWindow):
+ if MageWindow:
+ MageWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ MageWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.SetSelectionChangeHandler(None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+ GemRB.LoadWindowPack ("GUIMG", 640, 480)
+ MageWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", MageWindow.ID)
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenMageWindow)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ OptionsWindow.SetFrame ()
+
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MagePrevLevelPress)
+
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageNextLevelPress)
+
+## #unknown usage
+## Button = Window.GetControl (55)
+## Button.SetState (IE_GUI_BUTTON_LOCKED)
+## #Button.SetText (123)
+## #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, xxPress)
+
+## #setup level buttons
+## for i in range (9):
+## Button = Window.GetControl (56 + i)
+## Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RefreshMageLevel)
+## Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+## for i in range (9):
+## Button = Window.GetControl (56 + i)
+## Button.SetVarAssoc ("MageSpellLevel", i)
+
+ # Setup memorized spells buttons
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ Button.SetBorder (0,0,0,0,0,0,0,0,64,0,1)
+ Button.SetSprites ("SPELFRAM",0,0,0,0,0)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Setup book spells buttons
+ for i in range (20):
+ Button = Window.GetControl (27 + i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateMageWindow)
+ UpdateMageWindow ()
+
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+
+def UpdateMageWindow ():
+ global MageMemorizedSpellList, MageKnownSpellList
+
+ MageMemorizedSpellList = []
+ MageKnownSpellList = []
+
+ Window = MageWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+ type = IE_SPELL_TYPE_WIZARD
+ level = MageSpellLevel
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, type, level)
+
+ Label = Window.GetControl (0x10000032)
+ GemRB.SetToken ('LEVEL', str (level + 1))
+ Label.SetText (12137 )
+
+ Name = GemRB.GetPlayerName (pc, 0)
+ Label = Window.GetControl (0x10000035)
+ Label.SetText (Name)
+
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ if i < mem_cnt:
+ ms = GemRB.GetMemorizedSpell (pc, type, level, i)
+ Button.SetSpellIcon (ms['SpellResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if ms['Flags']:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellUnmemorizeWindow)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageUnmemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenMageSpellInfoWindow)
+ spell = GemRB.GetSpell (ms['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ MageMemorizedSpellList.append (ms['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", i)
+ Button.EnableBorder (0, ms['Flags'] == 0)
+ else:
+ if i < max_mem_cnt:
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+
+ known_cnt = GemRB.GetKnownSpellsCount (pc, type, level)
+ for i in range (20):
+ Button = Window.GetControl (27 + i)
+ if i < known_cnt:
+ ks = GemRB.GetKnownSpell (pc, type, level, i)
+ Button.SetSpellIcon (ks['SpellResRef'], 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageMemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenMageSpellInfoWindow)
+ spell = GemRB.GetSpell (ks['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ MageKnownSpellList.append (ks['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", 100 + i)
+
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+ if (CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS), 2)=="*") or \
+ GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MagePrevLevelPress ():
+ global MageSpellLevel
+
+ if MageSpellLevel > 0:
+ MageSpellLevel = MageSpellLevel - 1
+ UpdateMageWindow ()
+ return
+
+def MageNextLevelPress ():
+ global MageSpellLevel
+
+ if MageSpellLevel < 8:
+ MageSpellLevel = MageSpellLevel + 1
+ UpdateMageWindow ()
+ return
+
+def RefreshMageLevel ():
+ global MageSpellLevel
+
+ MageSpellLevel = GemRB.GetVar ("MageSpellLevel")
+ UpdateMageWindow ()
+ return
+
+def OpenMageSpellInfoWindow ():
+ global MageSpellInfoWindow
+
+ if MageSpellInfoWindow != None:
+ if MageSpellInfoWindow:
+ MageSpellInfoWindow.Unload ()
+ MageSpellInfoWindow = None
+ return
+
+ MageSpellInfoWindow = Window = GemRB.LoadWindow (3)
+
+ #back
+ Button = Window.GetControl (5)
+ Button.SetText (15416)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellInfoWindow)
+
+ #erase
+ #Button = Window.GetControl (6)
+ index = GemRB.GetVar ("SpellButton")
+ if index < 100:
+ ResRef = MageMemorizedSpellList[index]
+ else:
+ ResRef = MageKnownSpellList[index - 100]
+
+ spell = GemRB.GetSpell (ResRef)
+
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (spell['SpellName'])
+
+ Button = Window.GetControl (2)
+ Button.SetSpellIcon (ResRef, 1)
+
+ Text = Window.GetControl (3)
+ Text.SetText (spell['SpellDesc'])
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnMageMemorizeSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ if GemRB.MemorizeSpell (pc, type, level, index):
+ UpdateMageWindow ()
+ return
+
+def CloseMageSpellUnmemorizeWindow ():
+ global MageSpellUnmemorizeWindow
+
+ if MageSpellUnmemorizeWindow:
+ MageSpellUnmemorizeWindow.Unload ()
+ MageSpellUnmemorizeWindow = None
+ return
+
+#def OpenMageSpellRemoveWindow ():
+# global MageSpellUnmemorizeWindow
+#
+# MageSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+#
+# # "Are you sure you want to ....?"
+# TextArea = Window.GetControl (3)
+# TextArea.SetText (63745)
+#
+# # Remove
+# Button = Window.GetControl (0)
+# Button.SetText (17507)
+# Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageRemoveSpell)
+# Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+#
+# # Cancel
+# Button = Window.GetControl (1)
+# Button.SetText (13727)
+# Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseMageSpellUnmemorizeWindow)
+# Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+#
+# Window.ShowModal (MODAL_SHADOW_GRAY)
+# return
+
+def OpenMageSpellUnmemorizeWindow ():
+ global MageSpellUnmemorizeWindow
+
+ MageSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (11824)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageUnmemorizeSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseMageSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnMageUnmemorizeSpell ():
+ if MageSpellUnmemorizeWindow:
+ CloseMageSpellUnmemorizeWindow()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton")
+
+ if GemRB.UnmemorizeSpell (pc, type, level, index):
+ UpdateMageWindow ()
+ return
+
+#def OnMageRemoveSpell ():
+# CloseMageSpellUnmemorizeWindow()
+# OpenMageSpellInfoWindow()
+#
+# pc = GemRB.GameGetSelectedPCSingle ()
+# level = MageSpellLevel
+# type = IE_SPELL_TYPE_WIZARD
+#
+# index = GemRB.GetVar ("SpellButton")-100
+#
+# #remove spell from book
+# GemRB.RemoveSpell (pc, type, level, index)
+# UpdateMageWindow ()
+# return
+
+###################################################
+# End of file GUIMG.py
diff --git a/gemrb/GUIScripts/bg1/GUIMOVIE.py b/gemrb/GUIScripts/bg1/GUIMOVIE.py
new file mode 100644
index 0000000..4adbe87
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIMOVIE.py
@@ -0,0 +1,90 @@
+# 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.
+#
+#
+import GemRB
+from GUIDefines import *
+
+MovieWindow = 0
+TextAreaControl = 0
+MoviesTable = 0
+PlayButton = 0
+
+def OnLoad():
+ global MovieWindow, TextAreaControl, MoviesTable, PlayButton
+
+ GemRB.LoadWindowPack ("GUIMOVIE", 640, 480)
+ MovieWindow = GemRB.LoadWindow (0)
+ TextAreaControl = MovieWindow.GetControl (0)
+ PlayButton = MovieWindow.GetControl (2)
+ CreditsButton = MovieWindow.GetControl (3)
+ DoneButton = MovieWindow.GetControl (4)
+ MoviesTable = GemRB.LoadTable ("MOVIDESC")
+ for i in range( MoviesTable.GetRowCount () ):
+ t = MoviesTable.GetRowName (i)
+ if GemRB.GetVar (t)==1:
+ s = MoviesTable.GetValue (i, 0)
+ TextAreaControl.Append (s,-1)
+ TextAreaControl.SetVarAssoc ("MovieIndex",0)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE, OP_NAND)
+ TextAreaControl.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, MoviePress)
+ PlayButton.SetText (17318)
+ CreditsButton.SetText (15591)
+ DoneButton.SetText (11973)
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ PlayButton.SetStatus (IE_GUI_BUTTON_DISABLED)
+ CreditsButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CreditsPress)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePress)
+ MovieWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MoviePress():
+ PlayButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE,OP_SET) # show selection
+ return
+
+def PlayPress():
+ s = GemRB.GetVar ("MovieIndex")
+ for i in range( MoviesTable.GetRowCount () ):
+ t = MoviesTable.GetRowName (i)
+ if GemRB.GetVar (t)==1:
+ if s==0:
+ PlayButton.SetStatus (IE_GUI_BUTTON_DISABLED)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE,OP_NAND) # hide selection
+ s = MoviesTable.GetRowName (i)
+ GemRB.PlayMovie (s, 1)
+ MovieWindow.Invalidate ()
+ return
+ s = s - 1
+ return
+
+def CreditsPress():
+ if MovieWindow:
+ MovieWindow.Unload ()
+ GemRB.SetNextScript ("GUISONGS")
+ return
+
+#def CreditsPress():
+# GemRB.PlayMovie ("CREDITS",1)
+# MovieWindow.Invalidate ()
+# return
+
+def DonePress():
+ if MovieWindow:
+ MovieWindow.Unload ()
+ GemRB.SetNextScript ("Start")
+ return
diff --git a/gemrb/GUIScripts/bg1/GUIMP.py b/gemrb/GUIScripts/bg1/GUIMP.py
new file mode 100644
index 0000000..f0a7dc9
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIMP.py
@@ -0,0 +1,132 @@
+# 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.
+#
+#
+#Single Player Party Formation
+import GemRB
+from GUIDefines import *
+
+PartyFormationWindow = 0
+ExitWindow = 0
+
+def OnLoad():
+ global PartyFormationWindow
+ GemRB.LoadWindowPack("GUIMP", 640, 480)
+
+ PartyFormationWindow = GemRB.LoadWindow(0)
+
+ ExitButton = PartyFormationWindow.GetControl(30)
+ ExitButton.SetText(13906)
+ ExitButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitPress)
+ ExitButton.SetFlags(IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ModifyCharactersButton = PartyFormationWindow.GetControl(43)
+ ModifyCharactersButton.SetText(18816)
+ ModifyCharactersButton.SetState(IE_GUI_BUTTON_DISABLED)
+ #TODO: implement ModifyCharacters
+ #ModifyCharactersButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ModifyCharactersPress)
+
+ DoneButton = PartyFormationWindow.GetControl(28)
+ DoneButton.SetText(11973)
+ Portraits = 0
+
+ for i in range(18,24):
+ Label = PartyFormationWindow.GetControl(0x10000012+i)
+ #removing this label, it just disturbs us
+ Label.SetSize(0, 0)
+ Button = PartyFormationWindow.GetControl(i-12)
+ ResRef = GemRB.GetPlayerPortrait(i-17, 1)
+ if ResRef == "":
+ Button.SetFlags(IE_GUI_BUTTON_NORMAL,OP_SET)
+ else:
+ Button.SetPicture(ResRef)
+ Button.SetFlags(IE_GUI_BUTTON_PICTURE, OP_OR)
+ Portraits = Portraits+1
+
+ Button.SetVarAssoc("Slot",i-17)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GeneratePress)
+
+ Button = PartyFormationWindow.GetControl(i)
+ Button.SetVarAssoc("Slot",i-17)
+ if ResRef == "":
+ Button.SetText(10264)
+ else:
+ Button.SetText(GemRB.GetPlayerName(i-17,0) )
+
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GeneratePress)
+
+ if Portraits == 0:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, EnterGamePress)
+
+ PartyFormationWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ExitPress():
+ global PartyFormationWindow, ExitWindow
+ PartyFormationWindow.SetVisible(WINDOW_INVISIBLE)
+ ExitWindow = GemRB.LoadWindow(7)
+
+ TextArea = ExitWindow.GetControl(0)
+ TextArea.SetText(11329)
+
+ CancelButton = ExitWindow.GetControl(2)
+ CancelButton.SetText(13727)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitCancelPress)
+ CancelButton.SetFlags(IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton = ExitWindow.GetControl(1)
+ DoneButton.SetText(11973)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitDonePress)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ ExitWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ExitDonePress():
+ global PartyFormationWindow, ExitWindow
+ if ExitWindow:
+ ExitWindow.Unload()
+ if PartyFormationWindow:
+ PartyFormationWindow.Unload()
+ GemRB.SetNextScript("Start")
+ return
+
+def ExitCancelPress():
+ global PartyFormationWindow, ExitWindow
+ if ExitWindow:
+ ExitWindow.Unload()
+ PartyFormationWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def GeneratePress():
+ global PartyFormationWindow
+ slot = GemRB.GetVar("Slot")
+ ResRef = GemRB.GetPlayerPortrait(slot, 0)
+ if ResRef:
+ print "Already existing slot, we should drop it"
+ if PartyFormationWindow:
+ PartyFormationWindow.Unload()
+ GemRB.SetNextScript("CharGen")
+ return
+
+def EnterGamePress():
+ GemRB.EnterGame()
+ return
+
diff --git a/gemrb/GUIScripts/bg1/GUIOPT.py b/gemrb/GUIScripts/bg1/GUIOPT.py
new file mode 100644
index 0000000..59b7057
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIOPT.py
@@ -0,0 +1,733 @@
+# -*-python-*-
+# 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.
+#
+
+# GUIOPT.py - scripts to control options windows mostly from the GUIOPT winpack:
+# 0 - Main options window (peacock tail)
+# 1 - Video options window
+# 2 - msg win with 1 button
+# 3 - msg win with 2 buttons
+# 4 - msg win with 3 buttons
+# 5 - Audio options window
+# 6 - Gameplay options window
+# 8 - Feedback options window
+# 9 - Autopause options window
+
+###################################################
+import GemRB
+import GUICommon
+import GUICommonWindows
+import GUISAVE
+from GUIDefines import *
+
+###################################################
+OptionsWindow = None
+SubOptionsWindow = None
+SubSubOptionsWindow = None
+GameOptionsWindow = None
+OldOptionsWindow = None
+MoviesWindow = None
+KeysWindow = None
+HelpTextArea = None
+
+LoadMsgWindow = None
+QuitMsgWindow = None
+hideflag = None
+
+###################################################
+def CloseOptionsWindow ():
+ global GameOptionsWindow, OptionsWindow
+ global OldOptionsWindow
+
+ if GameOptionsWindow == None:
+ return
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+
+ GameOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ return
+
+###################################################
+def OpenOptionsWindow ():
+ """Open main options window"""
+ global GameOptionsWindow, OptionsWindow
+ global OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenOptionsWindow):
+ CloseOptionsWindow()
+ return
+
+ #hideflag = GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+ GUICommonWindows.SetSelectionChangeHandler (None)
+
+ GemRB.LoadWindowPack ("GUIOPT", 640, 480)
+ GameOptionsWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", GameOptionsWindow.ID)
+ if OldOptionsWindow == None:
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenOptionsWindow)
+ OptionsWindow.SetFrame ()
+
+ # Return to Game
+ Button = Window.GetControl (11)
+ Button.SetText (10308)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenOptionsWindow)
+
+ # Quit Game
+ Button = Window.GetControl (10)
+ Button.SetText (13731)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenQuitMsgWindow)
+
+ # Load Game
+ Button = Window.GetControl (5)
+ Button.SetText (13729)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenLoadMsgWindow)
+
+ # Save Game
+ Button = Window.GetControl (6)
+ Button.SetText (13730)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSaveMsgWindow)
+
+ # Video Options
+ Button = Window.GetControl (7)
+ Button.SetText (17162)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenVideoOptionsWindow)
+
+ # Audio Options
+ Button = Window.GetControl (8)
+ Button.SetText (17164)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenAudioOptionsWindow)
+
+ # Gameplay Options
+ Button = Window.GetControl (9)
+ Button.SetText (17165)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenGameplayOptionsWindow)
+
+ # game version, e.g. v1.1.0000
+ Label = Window.GetControl (0x1000000b)
+ Label.SetText (GEMRB_VERSION)
+
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ GUICommonWindows.PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+
+def CloseVideoOptionsWindow ():
+ global SubOptionsWindow
+
+ if SubOptionsWindow:
+ SubOptionsWindow.Unload()
+ SubOptionsWindow = None
+ return
+
+def OpenVideoOptionsWindow ():
+ """Open video options window"""
+ global SubOptionsWindow, HelpTextArea
+
+ if SubOptionsWindow:
+ if SubOptionsWindow:
+ SubOptionsWindow.Unload()
+ SubOptionsWindow = None
+
+ SubOptionsWindow = Window = GemRB.LoadWindow (6)
+
+ HelpTextArea = OptHelpText ('VideoOptions', Window, 33, 18038)
+
+ OptDone ('VideoOptions', Window, 21)
+ OptCancel ('VideoOptions', Window, 32)
+
+ OptSlider ('Brightness', Window, 3, 'Brightness Correction', 4)
+ OptSlider ('Contrast', Window, 22, 'Gamma Correction', 1)
+
+ OptRadio ('BPP', Window, 5, 37, 'BitsPerPixel', 16)
+ OptRadio ('BPP', Window, 6, 37, 'BitsPerPixel', 24)
+ OptRadio ('BPP', Window, 7, 37, 'BitsPerPixel', 32)
+
+ OptCheckbox ('FullScreen', Window, 9, 38, 'Full Screen', 1)
+
+ OptCheckbox ('TransShadow', Window, 51, 50, 'Translucent Shadows', 1)
+ OptCheckbox ('SoftMirrBlt', Window, 40, 44, 'SoftMirrorBlt' ,1)
+ OptCheckbox ('SoftTransBlt', Window, 41, 46, 'SoftSrcKeyBlt' ,1)
+ OptCheckbox ('SoftStandBlt', Window, 42, 48, 'SoftBltFast' ,1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpFullScreen ():
+ HelpTextArea.SetText (18000)
+ GemRB.SetFullScreen (GemRB.GetVar("Full Screen"))
+
+def DisplayHelpBPP ():
+ HelpTextArea.SetText (17205)
+
+def DisplayHelpBrightness ():
+ HelpTextArea.SetText (17203)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction")/2)
+
+def DisplayHelpContrast ():
+ HelpTextArea.SetText (17204)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction")/2)
+
+def DisplayHelpSoftMirrBlt ():
+ HelpTextArea.SetText (18004)
+
+def DisplayHelpSoftTransBlt ():
+ HelpTextArea.SetText (18006)
+
+def DisplayHelpSoftStandBlt ():
+ HelpTextArea.SetText (18007)
+
+def DisplayHelpTransShadow ():
+ HelpTextArea.SetText (20620)
+
+
+###################################################
+
+def CloseAudioOptionsWindow ():
+ global SubOptionsWindow
+
+ if SubOptionsWindow:
+ SubOptionsWindow.Unload()
+ SubOptionsWindow = None
+ return
+
+def OpenAudioOptionsWindow ():
+ """Open audio options window"""
+ global SubOptionsWindow, HelpTextArea
+
+ if SubOptionsWindow:
+ if SubOptionsWindow:
+ SubOptionsWindow.Unload ()
+ SubOptionsWindow = None
+
+ SubOptionsWindow = Window = GemRB.LoadWindow (7)
+
+ HelpTextArea = OptHelpText ('AudioOptions', Window, 14, 18040)
+
+ OptDone ('AudioOptions', Window, 24)
+ OptCancel ('AudioOptions', Window, 25)
+ OptButton ('CharacterSounds', Window, 13, 17778)
+
+ OptSlider ('AmbientVolume', Window, 1, 'Volume Ambients', 10)
+ OptSlider ('SoundFXVolume', Window, 2, 'Volume SFX', 10)
+ OptSlider ('VoiceVolume', Window, 3, 'Volume Voices', 10)
+ OptSlider ('MusicVolume', Window, 4, 'Volume Music', 10)
+ OptSlider ('MovieVolume', Window, 22, 'Volume Movie', 10)
+
+ OptCheckbox ('CreativeEAX', Window, 26, 28, 'Environmental Audio', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpAmbientVolume ():
+ HelpTextArea.SetText (18008)
+ GemRB.UpdateAmbientsVolume ()
+
+def DisplayHelpSoundFXVolume ():
+ HelpTextArea.SetText (18009)
+
+def DisplayHelpVoiceVolume ():
+ HelpTextArea.SetText (18010)
+
+def DisplayHelpMusicVolume ():
+ HelpTextArea.SetText (18011)
+ GemRB.UpdateMusicVolume ()
+
+def DisplayHelpMovieVolume ():
+ HelpTextArea.SetText (18012)
+
+def DisplayHelpCreativeEAX ():
+ HelpTextArea.SetText (18022)
+
+###################################################
+
+def CloseCharacterSoundsWindow ():
+ global SubSubOptionsWindow
+
+ if SubSubOptionsWindow:
+ SubSubOptionsWindow.Unload ()
+ SubSubOptionsWindow = None
+ return
+
+def OpenCharacterSoundsWindow ():
+ """Open character sounds window"""
+ global SubSubOptionsWindow, HelpTextArea
+
+ if SubSubOptionsWindow:
+ if SubSubOptionsWindow:
+ SubSubOptionsWindow.Unload ()
+ SubSubOptionsWindow = None
+
+ SubSubOptionsWindow = Window = GemRB.LoadWindow (12)
+
+ HelpTextArea = OptHelpText ('CharacterSounds', Window, 16, 18041)
+
+ OptDone ('CharacterSounds', Window, 24)
+ OptCancel ('CharacterSounds', Window, 25)
+
+ OptCheckbox ('Subtitles', Window, 5, 20, 'Subtitles', 1)
+ OptCheckbox ('AttackSounds', Window, 6, 18, 'Attack Sounds', 1)
+ OptCheckbox ('Footsteps', Window, 7, 19, 'Footsteps', 1)
+ OptRadio ('CommandSounds', Window, 8, 21, 'Command Sounds Frequency', 1)
+ OptRadio ('CommandSounds', Window, 9, 21, 'Command Sounds Frequency', 2)
+ OptRadio ('CommandSounds', Window, 10, 21, 'Command Sounds Frequency', 3)
+ OptRadio ('SelectionSounds', Window, 58, 57, 'Selection Sounds Frequency', 1)
+ OptRadio ('SelectionSounds', Window, 59, 57, 'Selection Sounds Frequency', 2)
+ OptRadio ('SelectionSounds', Window, 60, 57, 'Selection Sounds Frequency', 3)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def DisplayHelpSubtitles ():
+ HelpTextArea.SetText (18015)
+
+def DisplayHelpAttackSounds ():
+ HelpTextArea.SetText (18013)
+
+def DisplayHelpFootsteps ():
+ HelpTextArea.SetText (18014)
+
+def DisplayHelpCommandSounds ():
+ HelpTextArea.SetText (18016)
+
+def DisplayHelpSelectionSounds ():
+ HelpTextArea.SetText (11352)
+
+###################################################
+
+def CloseGameplayOptionsWindow ():
+ global SubOptionsWindow
+
+ if SubOptionsWindow:
+ SubOptionsWindow.Unload()
+ SubOptionsWindow = None
+ return
+
+def OpenGameplayOptionsWindow ():
+ """Open gameplay options window"""
+ global SubOptionsWindow, HelpTextArea
+
+ if SubOptionsWindow:
+ if SubOptionsWindow:
+ SubOptionsWindow.Unload ()
+ SubOptionsWindow = None
+
+ SubOptionsWindow = Window = GemRB.LoadWindow (8)
+
+ HelpTextArea = OptHelpText ('GameplayOptions', Window, 40, 18042)
+
+ OptDone ('GameplayOptions', Window, 7)
+ OptCancel ('GameplayOptions', Window, 20)
+
+ OptSlider ('TooltipDelay', Window, 1, 'Tooltips', TOOLTIP_DELAY_FACTOR)
+ OptSlider ('MouseScrollingSpeed', Window, 2, 'Mouse Scroll Speed', 5)
+ OptSlider ('KeyboardScrollingSpeed', Window, 3, 'Keyboard Scroll Speed', 5)
+ OptSlider ('Difficulty', Window, 12, 'Difficulty Level', 0)
+
+ OptCheckbox ('DitherAlways', Window, 14, 25, 'Always Dither', 1)
+ OptCheckbox ('Gore', Window, 19, 27, 'Gore', 1)
+ OptCheckbox ('Infravision', Window, 42, 44, 'Infravision', 1)
+ OptCheckbox ('Weather', Window, 47, 46, 'Weather', 1)
+ if GUICommon.GameIsBG2():
+ OptCheckbox ('RestUntilHealed', Window, 50, 48, 'Heal Party on Rest', 1)
+
+ OptButton ('FeedbackOptions', Window, 5, 17163)
+ OptButton ('AutopauseOptions', Window, 6, 17166)
+ if GUICommon.GameIsBG2():
+ OptButton ('HotkeyOptions', Window, 51, 816)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpTooltipDelay ():
+ HelpTextArea.SetText (18017)
+ GemRB.SetTooltipDelay (GemRB.GetVar ("Tooltips") )
+
+def DisplayHelpMouseScrollingSpeed ():
+ HelpTextArea.SetText (18018)
+ GemRB.SetMouseScrollSpeed (GemRB.GetVar ("Mouse Scroll Speed") )
+
+def DisplayHelpKeyboardScrollingSpeed ():
+ HelpTextArea.SetText (18019)
+
+def DisplayHelpDifficulty ():
+ HelpTextArea.SetText (18020)
+
+def DisplayHelpDitherAlways ():
+ HelpTextArea.SetText (18021)
+
+def DisplayHelpGore ():
+ HelpTextArea.SetText (18023)
+
+def DisplayHelpInfravision ():
+ HelpTextArea.SetText (11797)
+
+def DisplayHelpWeather ():
+ HelpTextArea.SetText (20619)
+
+def DisplayHelpRestUntilHealed ():
+ HelpTextArea.SetText (2242)
+
+###################################################
+
+def CloseFeedbackOptionsWindow ():
+ global SubSubOptionsWindow
+
+ if SubSubOptionsWindow:
+ SubSubOptionsWindow.Unload ()
+ SubSubOptionsWindow = None
+ return
+
+def OpenFeedbackOptionsWindow ():
+ """Open feedback options window"""
+ global SubSubOptionsWindow, HelpTextArea
+
+ if SubSubOptionsWindow:
+ if SubSubOptionsWindow:
+ SubSubOptionsWindow.Unload ()
+ SubSubOptionsWindow = None
+
+ SubSubOptionsWindow = Window = GemRB.LoadWindow (9)
+
+ HelpTextArea = OptHelpText ('FeedbackOptions', Window, 28, 18043)
+
+ OptDone ('FeedbackOptions', Window, 26)
+ OptCancel ('FeedbackOptions', Window, 27)
+
+ OptSlider ('MarkerFeedback', Window, 8, 'GUI Feedback Level', 1)
+ OptSlider ('LocatorFeedback', Window, 9, 'Locator Feedback Level', 1)
+
+ OptCheckbox ('ToHitRolls', Window, 10, 32, 'Rolls', 1)
+ OptCheckbox ('CombatInfo', Window, 11, 33, 'Combat Info', 1)
+ OptCheckbox ('Actions', Window, 12, 34, 'Actions', 1)
+ OptCheckbox ('States', Window, 13, 35, 'State Changes', 1)
+ OptCheckbox ('Selection', Window, 14, 36, 'Selection Text', 1)
+ OptCheckbox ('Miscellaneous', Window, 15, 37, 'Miscellaneous Text', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpMarkerFeedback ():
+ HelpTextArea.SetText (18024)
+
+def DisplayHelpLocatorFeedback ():
+ HelpTextArea.SetText (18025)
+
+def DisplayHelpToHitRolls ():
+ HelpTextArea.SetText (18026)
+
+def DisplayHelpCombatInfo ():
+ HelpTextArea.SetText (18027)
+
+def DisplayHelpActions ():
+ HelpTextArea.SetText (18028)
+
+def DisplayHelpStates ():
+ HelpTextArea.SetText (18029)
+
+def DisplayHelpSelection ():
+ HelpTextArea.SetText (18030)
+
+def DisplayHelpMiscellaneous ():
+ HelpTextArea.SetText (18031)
+
+###################################################
+
+def CloseAutopauseOptionsWindow ():
+ global SubSubOptionsWindow
+
+ if SubSubOptionsWindow:
+ SubSubOptionsWindow.Unload ()
+ SubSubOptionsWindow = None
+ return
+
+def OpenAutopauseOptionsWindow ():
+ """Open autopause options window"""
+ global SubSubOptionsWindow, HelpTextArea
+
+ if SubSubOptionsWindow:
+ if SubSubOptionsWindow:
+ SubSubOptionsWindow.Unload ()
+ SubSubOptionsWindow = None
+
+ SubSubOptionsWindow = Window = GemRB.LoadWindow (10)
+
+ HelpTextArea = OptHelpText ('AutopauseOptions', Window, 15, 18044)
+
+ OptDone ('AutopauseOptions', Window, 11)
+ OptCancel ('AutopauseOptions', Window, 14)
+
+ OptCheckbox ('CharacterHit', Window, 1, 17, 'Auto Pause State', 1)
+ OptCheckbox ('CharacterInjured', Window, 2, 18, 'Auto Pause State', 2)
+ OptCheckbox ('CharacterDead', Window, 3, 19, 'Auto Pause State', 4)
+ OptCheckbox ('CharacterAttacked', Window, 4, 20, 'Auto Pause State', 8)
+ OptCheckbox ('WeaponUnusable', Window, 5, 21, 'Auto Pause State', 16)
+ OptCheckbox ('TargetGone', Window, 13, 22, 'Auto Pause State', 32)
+ OptCheckbox ('EndOfRound', Window, 25, 24, 'Auto Pause State', 64)
+ if Window.HasControl(26, IE_GUI_BUTTON):
+ OptCheckbox ('EnemySighted', Window, 26, 27, 'Auto Pause State', 128)
+ if GUICommon.GameIsBG2():
+ OptCheckbox ('SpellCast', Window, 34, 30, 'Auto Pause State', 256)
+ OptCheckbox ('TrapFound', Window, 31, 33, 'Auto Pause State', 512)
+ OptCheckbox ('CenterOnActor', Window, 31, 33, 'Auto Pause Center', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpCharacterHit ():
+ HelpTextArea.SetText (18032)
+
+def DisplayHelpCharacterInjured ():
+ HelpTextArea.SetText (18033)
+
+def DisplayHelpCharacterDead ():
+ HelpTextArea.SetText (18034)
+
+def DisplayHelpCharacterAttacked ():
+ HelpTextArea.SetText (18035)
+
+def DisplayHelpWeaponUnusable ():
+ HelpTextArea.SetText (18036)
+
+def DisplayHelpTargetGone ():
+ HelpTextArea.SetText (18037)
+
+def DisplayHelpEndOfRound ():
+ HelpTextArea.SetText (10640)
+
+def DisplayHelpEnemySighted ():
+ HelpTextArea.SetText (23514)
+
+def DisplayHelpSpellCast ():
+ HelpTextArea.SetText (58171)
+
+def DisplayHelpTrapFound ():
+ HelpTextArea.SetText (31872)
+
+def DisplayHelpCenterOnActor ():
+ HelpTextArea.SetText (10571)
+
+###################################################
+
+def OpenSaveMsgWindow ():
+ GemRB.SetVar("QuitAfterSave",0)
+ GUISAVE.OpenSaveWindow ()
+ #save the game without quitting
+ return
+
+###################################################
+
+def OpenLoadMsgWindow ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ return
+
+ LoadMsgWindow = Window = GemRB.LoadWindow (4)
+
+ # Load
+ Button = Window.GetControl (0)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadGamePress)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseLoadMsgWindow)
+
+ # Loading a game will destroy ...
+ Text = Window.GetControl (3)
+ Text.SetText (19531)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseLoadMsgWindow ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ LoadMsgWindow.Unload ()
+ LoadMsgWindow = None
+ #OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def LoadGamePress ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ LoadMsgWindow.Unload ()
+ LoadMsgWindow = None
+ GemRB.QuitGame ()
+ OpenOptionsWindow()
+ GemRB.SetNextScript ("GUILOAD")
+ return
+
+#save game AND quit
+def SaveGamePress ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ #we need to set a state: quit after save
+ GemRB.SetVar("QuitAfterSave",1)
+ OpenOptionsWindow()
+ GUISAVE.OpenSaveWindow ()
+ return
+
+def QuitGamePress ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ GemRB.QuitGame ()
+ OpenOptionsWindow()
+ GemRB.SetNextScript ("Start")
+ return
+
+###################################################
+
+def OpenQuitMsgWindow ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ return
+
+ QuitMsgWindow = Window = GemRB.LoadWindow (5)
+
+ # Save
+ Button = Window.GetControl (0)
+ Button.SetText (15589)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SaveGamePress)
+
+ # Quit Game
+ Button = Window.GetControl (1)
+ Button.SetText (15417)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitGamePress)
+
+ # Cancel
+ Button = Window.GetControl (2)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseQuitMsgWindow)
+
+ # Do you wish to save the game ....
+ Text = Window.GetControl (3)
+ Text.SetText (16456)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseQuitMsgWindow ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+###################################################
+#TODO
+def OpenHotkeyOptionsWindow ():
+ return
+
+def CloseHotkeyOptionsWindow ():
+ return
+
+###################################################
+# These functions help to setup controls found
+# in Video, Audio, Gameplay, Feedback and Autopause
+# options windows
+
+# These controls are usually made from an active
+# control (button, slider ...) and a label
+
+
+def OptSlider (name, window, slider_id, variable, value):
+ """Standard slider for option windows"""
+ slider = window.GetControl (slider_id)
+ slider.SetVarAssoc (variable, value)
+ slider.SetEvent (IE_GUI_SLIDER_ON_CHANGE, eval("DisplayHelp" + name))
+ return slider
+
+def OptRadio (name, window, button_id, label_id, variable, value):
+ """Standard radio button for option windows"""
+
+ button = window.GetControl (button_id)
+ button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("DisplayHelp" + name))
+ button.SetVarAssoc (variable, value)
+
+ label = window.GetControl (label_id)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+
+ return button
+
+def OptCheckbox (name, window, button_id, label_id, variable, value):
+ """Standard checkbox for option windows"""
+
+ button = window.GetControl (button_id)
+ button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("DisplayHelp" + name))
+ button.SetVarAssoc (variable, value)
+
+ label = window.GetControl (label_id)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+
+ return button
+
+def OptButton (name, window, button_id, button_strref):
+ """Standard subwindow button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Open%sWindow" %name))
+ button.SetText (button_strref)
+
+def OptDone (name, window, button_id):
+ """Standard `Done' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (11973) # Done
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Close%sWindow" %name))
+ button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+def OptCancel (name, window, button_id):
+ """Standard `Cancel' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (13727) # Cancel
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Close%sWindow" %name))
+ button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+def OptHelpText (name, window, text_id, text_strref):
+ """Standard textarea with context help for option windows"""
+ text = window.GetControl (text_id)
+ text.SetText (text_strref)
+ return text
+
+###################################################
+# End of file GUIOPT.py
diff --git a/gemrb/GUIScripts/bg1/GUIPR.py b/gemrb/GUIScripts/bg1/GUIPR.py
new file mode 100644
index 0000000..c247b9f
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIPR.py
@@ -0,0 +1,359 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIPR.py - scripts to control priest spells windows from GUIPR winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+from ie_action import ACT_CAST
+
+PriestWindow = None
+PriestSpellInfoWindow = None
+PriestSpellLevel = 0
+PriestSpellUnmemorizeWindow = None
+OldOptionsWindow = None
+PortraitWindow = None
+OldPortraitWindow = None
+
+
+def OpenPriestWindow ():
+ global PriestWindow, OptionsWindow
+ global OldOptionsWindow, PortraitWindow, OldPortraitWindow
+
+
+ if GUICommon.CloseOtherWindow (OpenPriestWindow):
+ if PriestWindow:
+ PriestWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ PriestWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.SetSelectionChangeHandler(None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIPR", 640, 480)
+ PriestWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", PriestWindow.ID)
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenPriestWindow)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ OptionsWindow.SetFrame ()
+
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestPrevLevelPress)
+
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestNextLevelPress)
+
+## #setup level buttons
+## for i in range (7):
+## Button = Window.GetControl (55 + i)
+## Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RefreshPriestLevel)
+## Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+## for i in range (7):
+## Button = Window.GetControl (55 + i)
+## Button.SetVarAssoc ("PriestSpellLevel", i)
+
+ # Setup memorized spells buttons
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ Button.SetBorder (0,0,0,0,0,0,0,0,64,0,1)
+ Button.SetSprites ("SPELFRAM",0,0,0,0,0)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Setup book spells buttons
+ for i in range (20):
+ Button = Window.GetControl (27 + i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdatePriestWindow)
+ UpdatePriestWindow ()
+
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+
+def UpdatePriestWindow ():
+ global PriestMemorizedSpellList, PriestKnownSpellList
+
+ PriestMemorizedSpellList = []
+ PriestKnownSpellList = []
+
+ Window = PriestWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Type = IE_SPELL_TYPE_PRIEST
+ level = PriestSpellLevel
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, Type, level)
+
+ Label = Window.GetControl (0x10000032)
+ GemRB.SetToken ('LEVEL', str (level + 1))
+ Label.SetText (12137)
+
+ Name = GemRB.GetPlayerName (pc, 0)
+ Label = Window.GetControl (0x10000035)
+ Label.SetText (Name)
+
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, Type, level)
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ if i < mem_cnt:
+ ms = GemRB.GetMemorizedSpell (pc, Type, level, i)
+ Button.SetSpellIcon (ms['SpellResRef'])
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if ms['Flags']:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellUnmemorizeWindow)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestUnmemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenPriestSpellInfoWindow)
+ spell = GemRB.GetSpell (ms['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ PriestMemorizedSpellList.append (ms['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", i)
+ Button.EnableBorder (0, ms['Flags'] == 0)
+ else:
+ if i < max_mem_cnt:
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+
+ known_cnt = GemRB.GetKnownSpellsCount (pc, Type, level)
+ for i in range (20):
+ Button = Window.GetControl (27 + i)
+ if i < known_cnt:
+ ks = GemRB.GetKnownSpell (pc, Type, level, i)
+ Button.SetSpellIcon (ks['SpellResRef'])
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestMemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenPriestSpellInfoWindow)
+ spell = GemRB.GetSpell (ks['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ PriestKnownSpellList.append (ks['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", 100 + i)
+
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ DivineCaster = CommonTables.ClassSkills.GetValue (Class, 1)
+ if DivineCaster == "*":
+ # also check the DRUIDSPELL column
+ DivineCaster = CommonTables.ClassSkills.GetValue (Class, 0)
+ CantCast = DivineCaster == "*" or GemRB.GetPlayerStat(pc, IE_DISABLEDBUTTON)&(1<<ACT_CAST)
+ if CantCast or GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def PriestPrevLevelPress ():
+ global PriestSpellLevel
+
+ if PriestSpellLevel > 0:
+ PriestSpellLevel = PriestSpellLevel - 1
+ UpdatePriestWindow ()
+ return
+
+def PriestNextLevelPress ():
+ global PriestSpellLevel
+
+ if PriestSpellLevel < 6:
+ PriestSpellLevel = PriestSpellLevel + 1
+ UpdatePriestWindow ()
+ return
+
+def RefreshPriestLevel ():
+ global PriestSpellLevel
+
+ PriestSpellLevel = GemRB.GetVar ("PriestSpellLevel")
+ UpdatePriestWindow ()
+ return
+
+def OpenPriestSpellInfoWindow ():
+ global PriestSpellInfoWindow
+
+ if PriestSpellInfoWindow != None:
+ if PriestSpellInfoWindow:
+ PriestSpellInfoWindow.Unload ()
+ PriestSpellInfoWindow = None
+ return
+
+ PriestSpellInfoWindow = Window = GemRB.LoadWindow (3)
+
+ #back
+ Button = Window.GetControl (5)
+ Button.SetText (15416)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellInfoWindow)
+
+ index = GemRB.GetVar ("SpellButton")
+ if index < 100:
+ ResRef = PriestMemorizedSpellList[index]
+ else:
+ ResRef = PriestKnownSpellList[index - 100]
+
+ spell = GemRB.GetSpell (ResRef)
+
+ #Label = Window.GetControl (0x0fffffff)
+ #Label.SetText (spell['SpellName'])
+
+ Label = Window.GetControl (0x10000000)
+ Label.SetText (spell['SpellName'])
+
+ Button = Window.GetControl (2)
+ Button.SetSpellIcon (ResRef, 1)
+
+ Text = Window.GetControl (3)
+ Text.SetText (spell['SpellDesc'])
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnPriestMemorizeSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ Type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ if GemRB.MemorizeSpell (pc, Type, level, index):
+ UpdatePriestWindow ()
+ return
+
+def OpenPriestSpellRemoveWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ PriestSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (11824)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestRemoveSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellRemoveWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ClosePriestSpellUnmemorizeWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ if PriestSpellUnmemorizeWindow:
+ PriestSpellUnmemorizeWindow.Unload ()
+ PriestSpellUnmemorizeWindow = None
+ return
+
+def OpenPriestSpellUnmemorizeWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ PriestSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (11824)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestUnmemorizeSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClosePriestSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnPriestUnmemorizeSpell ():
+ if PriestSpellUnmemorizeWindow:
+ ClosePriestSpellUnmemorizeWindow ()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ Type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton")
+
+ if GemRB.UnmemorizeSpell (pc, Type, level, index):
+ UpdatePriestWindow ()
+ return
+
+def OnPriestRemoveSpell ():
+ ClosePriestSpellUnmemorizeWindow()
+ OpenPriestSpellInfoWindow()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ Type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton")-100
+
+ #remove spell from memory
+ GemRB.RemoveSpell (pc, Type, level, index)
+ UpdatePriestWindow ()
+ return
+
+###################################################
+# End of file GUIPR.py
diff --git a/gemrb/GUIScripts/bg1/GUIREC.py b/gemrb/GUIScripts/bg1/GUIREC.py
new file mode 100644
index 0000000..6174578
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIREC.py
@@ -0,0 +1,1449 @@
+# -*-python-*-
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2003-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.
+#
+
+
+# GUIREC.py - scripts to control stats/records windows from the GUIREC winpack
+###################################################
+import GemRB
+import GUICommon
+import CommonTables
+from GUIDefines import *
+from ie_stats import *
+from ie_restype import *
+import LUCommon
+import LevelUp
+import GUIWORLD
+import GUICG19
+import DualClass
+###################################################
+RecordsWindow = None
+InformationWindow = None
+BiographyWindow = None
+OptionsWindow = None
+CustomizeWindow = None
+OldOptionsWindow = None
+ExportDoneButton = None
+ExportFileName = ""
+PortraitsTable = None
+ScriptsTable = None
+ColorTable = None
+ColorIndex = None
+ScriptTextArea = None
+SelectedTextArea = None
+PortraitWindow = None
+OldPortraitWindow = None
+OldVoiceSet = None
+
+# the available sounds
+SoundSequence = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', \
+ 'm', 's', 't', 'u', 'v', '_', 'x', 'y', 'z', '0', '1', '2', \
+ '3', '4', '5', '6', '7', '8', '9']
+SoundIndex = 0
+
+###################################################
+def OpenRecordsWindow ():
+ import GUICommonWindows
+ global RecordsWindow, OptionsWindow
+ global OldOptionsWindow, PortraitWindow, OldPortraitWindow
+
+ if GUICommon.CloseOtherWindow (OpenRecordsWindow):
+ if InformationWindow: OpenInformationWindow ()
+
+ if RecordsWindow:
+ RecordsWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ RecordsWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ GUICommonWindows.UpdatePortraitWindow ()
+ OldPortraitWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIREC", 640, 480)
+ RecordsWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", RecordsWindow.ID)
+ # saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenRecordsWindow)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ OptionsWindow.SetFrame ()
+
+ # dual class
+ Button = Window.GetControl (0)
+ Button.SetText (7174)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DualClass.DualClassWindow)
+
+ # levelup
+ Button = Window.GetControl (37)
+ Button.SetText (7175)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LevelUp.OpenLevelUpWindow)
+
+ # information
+ Button = Window.GetControl (1)
+ Button.SetText (11946)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenInformationWindow)
+
+ # reform party
+ Button = Window.GetControl (51)
+ Button.SetText (16559)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenRecReformPartyWindow)
+
+ # customize
+ Button = Window.GetControl (50)
+ Button.SetText (10645)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenCustomizeWindow)
+
+ # export
+ Button = Window.GetControl (36)
+ Button.SetText (13956)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenExportWindow)
+
+## # kit info
+## Button = Window.GetControl (52)
+## Button.SetText (61265)
+## Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, KitInfoWindow)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateRecordsWindow)
+ UpdateRecordsWindow ()
+
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+#original returns to game before continuing...
+def OpenRecReformPartyWindow ():
+ OpenRecordsWindow ()
+ GemRB.SetTimedEvent (GUIWORLD.OpenReformPartyWindow, 1)
+ return
+
+def UpdateRecordsWindow ():
+ global stats_overview, alignment_help
+
+ Window = RecordsWindow
+ if not RecordsWindow:
+ print "SelectionChange handler points to non existing window\n"
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # exportable
+ Button = Window.GetControl (36)
+ if GemRB.GetPlayerStat (pc, IE_MC_FLAGS)&MC_EXPORTABLE:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # dual-classable
+ Button = Window.GetControl (0)
+ if GUICommon.CanDualClass (pc):
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # levelup
+ Button = Window.GetControl (37)
+ if LUCommon.CanLevelUp (pc):
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # name
+ Label = Window.GetControl (0x1000000e)
+ Label.SetText (GemRB.GetPlayerName (pc, 0))
+
+ # portrait
+ Button = Window.GetControl (2)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetPicture (GemRB.GetPlayerPortrait (pc,0), "NOPORTLG")
+
+ # armorclass
+ Label = Window.GetControl (0x10000028)
+ ac = GemRB.GetPlayerStat (pc, IE_ARMORCLASS)
+ ac += GemRB.GetAbilityBonus (IE_DEX, 2, GemRB.GetPlayerStat (pc, IE_DEX) )
+ Label.SetText (str (ac))
+ Label.SetTooltip (17183)
+
+ # hp now
+ Label = Window.GetControl (0x10000029)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_HITPOINTS)))
+ Label.SetTooltip (17184)
+
+ # hp max
+ Label = Window.GetControl (0x1000002a)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)))
+ Label.SetTooltip (17378)
+
+ # stats
+
+ sstr = GemRB.GetPlayerStat (pc, IE_STR)
+ sstrx = GemRB.GetPlayerStat (pc, IE_STREXTRA)
+ cstr = GetStatColor (pc, IE_STR)
+ if sstrx > 0 and sstr==18:
+ sstr = "%d/%02d" %(sstr, sstrx % 100)
+ else:
+ sstr = str (sstr)
+
+ sint = str (GemRB.GetPlayerStat (pc, IE_INT))
+ cint = GetStatColor (pc, IE_INT)
+ swis = str (GemRB.GetPlayerStat (pc, IE_WIS))
+ cwis = GetStatColor (pc, IE_WIS)
+ sdex = str (GemRB.GetPlayerStat (pc, IE_DEX))
+ cdex = GetStatColor (pc, IE_DEX)
+ scon = str (GemRB.GetPlayerStat (pc, IE_CON))
+ ccon = GetStatColor (pc, IE_CON)
+ schr = str (GemRB.GetPlayerStat (pc, IE_CHR))
+ cchr = GetStatColor (pc, IE_CHR)
+
+ Label = Window.GetControl (0x1000002f)
+ Label.SetText (sstr)
+ Label.SetTextColor (cstr[0], cstr[1], cstr[2])
+
+ Label = Window.GetControl (0x10000009)
+ Label.SetText (sdex)
+ Label.SetTextColor (cdex[0], cdex[1], cdex[2])
+
+ Label = Window.GetControl (0x1000000a)
+ Label.SetText (scon)
+ Label.SetTextColor (ccon[0], ccon[1], ccon[2])
+
+ Label = Window.GetControl (0x1000000b)
+ Label.SetText (sint)
+ Label.SetTextColor (cint[0], cint[1], cint[2])
+
+ Label = Window.GetControl (0x1000000c)
+ Label.SetText (swis)
+ Label.SetTextColor (cwis[0], cwis[1], cwis[2])
+
+ Label = Window.GetControl (0x1000000d)
+ Label.SetText (schr)
+ Label.SetTextColor (cchr[0], cchr[1], cchr[2])
+
+ # class
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ Label = Window.GetControl (0x10000030)
+ Label.SetText (ClassTitle)
+
+ # race
+ text = CommonTables.Races.GetValue (CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (pc, IE_RACE)) ,
+ 0)
+
+ Label = Window.GetControl (0x1000000f)
+ Label.SetText (text)
+
+ Table = GemRB.LoadTable ("aligns")
+
+ text = Table.GetValue (Table.FindValue ( 3, GemRB.GetPlayerStat (pc, IE_ALIGNMENT) ), 0)
+ Label = Window.GetControl (0x10000010)
+ Label.SetText (text)
+
+ Label = Window.GetControl (0x10000011)
+ if GemRB.GetPlayerStat (pc, IE_SEX) == 1:
+ Label.SetText (7198)
+ else:
+ Label.SetText (7199)
+
+ # help, info textarea
+ stats_overview = GetStatOverview (pc)
+ Text = Window.GetControl (45)
+ Text.SetText (stats_overview)
+ #TODO: making window visible/shaded depending on the pc's state
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def GetStatColor (pc, stat):
+ a = GemRB.GetPlayerStat (pc, stat)
+ b = GemRB.GetPlayerStat (pc, stat, 1)
+ if a==b:
+ return (255,255,255)
+ if a<b:
+ return (255,255,0)
+ return (0,255,0)
+
+# GemRB.GetPlayerStat wrapper that only returns nonnegative values
+def GSNN (pc, stat):
+ val = GemRB.GetPlayerStat (pc, stat)
+ if val >= 0:
+ return val
+ else:
+ return 0
+
+# LevelDiff is used only from the level up code and holds the level
+# difference for each class
+def GetStatOverview (pc, LevelDiff=[0,0,0]):
+ StateTable = GemRB.LoadTable ("statdesc")
+
+ GS = lambda s, pc=pc: GemRB.GetPlayerStat (pc, s)
+ GB = lambda s, pc=pc: GemRB.GetPlayerStat (pc, s, 1)
+ GA = lambda s, col, pc=pc: GemRB.GetAbilityBonus (s, col, GS (s) )
+
+ stats = []
+ # class levels
+ # 16480 <CLASS>: Level <LEVEL>
+ # Experience: <EXPERIENCE>
+ # Next Level: <NEXTLEVEL>
+
+ # collecting tokens for stat overview
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ GemRB.SetToken ("CLASS", ClassTitle)
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ Class = CommonTables.Classes.GetRowName (Class)
+ Dual = GUICommon.IsDualClassed (pc, 1)
+ Multi = GUICommon.IsMultiClassed (pc, 1)
+ XP = GemRB.GetPlayerStat (pc, IE_XP)
+ LevelDrain = GS (IE_LEVELDRAIN)
+
+ if GS (IE_STATE_ID) & STATE_DEAD:
+ stats.append ( (11829,1,'c') ) # DEAD
+ stats.append (None)
+
+ if Multi[0] > 1: # we're multiclassed
+ print "\tMulticlassed"
+ Levels = [GemRB.GetPlayerStat (pc, IE_LEVEL), GemRB.GetPlayerStat (pc, IE_LEVEL2), GemRB.GetPlayerStat (pc, IE_LEVEL3)]
+
+ stats.append ( (19721,1,'c') )
+ stats.append (None)
+ for i in range (Multi[0]):
+ ClassIndex = CommonTables.Classes.FindValue (5, Multi[i+1])
+ ClassTitle = GemRB.GetString (CommonTables.Classes.GetValue (ClassIndex, 2))
+ GemRB.SetToken ("CLASS", ClassTitle)
+ Class = CommonTables.Classes.GetRowName (ClassIndex)
+ GemRB.SetToken ("LEVEL", str (Levels[i]+LevelDiff[i]-int(LevelDrain/Multi[0])) )
+ GemRB.SetToken ("EXPERIENCE", str (XP/Multi[0]) )
+ if LevelDrain:
+ stats.append ( (GemRB.GetString (19720),1,'d') )
+ stats.append ( (GemRB.GetString (57435),1,'d') ) # LEVEL DRAINED
+ else:
+ GemRB.SetToken ("NEXTLEVEL", LUCommon.GetNextLevelExp (Levels[i]+LevelDiff[i], Class) )
+ stats.append ( (GemRB.GetString (16480),"",'d') )
+ stats.append (None)
+ print "\t\tClass (Level):",Class,"(",Levels[i],")"
+
+ elif Dual[0] > 0: # dual classed; first show the new class
+ print "\tDual classed"
+ stats.append ( (19722,1,'c') )
+ stats.append (None)
+
+ Levels = [GemRB.GetPlayerStat (pc, IE_LEVEL), GemRB.GetPlayerStat (pc, IE_LEVEL2), GemRB.GetPlayerStat (pc, IE_LEVEL3)]
+
+ # the levels are stored in the class order (eg. FIGHTER_MAGE)
+ # the current active class does not matter!
+ if GUICommon.IsDualSwap (pc):
+ Levels = [Levels[1], Levels[0], Levels[2]]
+
+ Levels[0] += LevelDiff[0]
+
+ ClassTitle = GemRB.GetString (CommonTables.Classes.GetValue (Dual[2], 2))
+ GemRB.SetToken ("CLASS", ClassTitle)
+ GemRB.SetToken ("LEVEL", str (Levels[0]-LevelDrain))
+ Class = CommonTables.Classes.GetRowName (Dual[2])
+ XP2 = GemRB.GetPlayerStat (pc, IE_XP)
+ GemRB.SetToken ("EXPERIENCE", str (XP2) )
+ if LevelDrain:
+ stats.append ( (GemRB.GetString (19720),1,'d') )
+ stats.append ( (GemRB.GetString (57435),1,'d') ) # LEVEL DRAINED
+ else:
+ GemRB.SetToken ("NEXTLEVEL", LUCommon.GetNextLevelExp (Levels[0], Class) )
+ stats.append ( (GemRB.GetString (16480),"",'d') )
+ stats.append (None)
+ # the first class (shown second)
+ if Dual[0] == 1:
+ ClassTitle = GemRB.GetString (CommonTables.KitList.GetValue (Dual[1], 2))
+ elif Dual[0] == 2:
+ ClassTitle = GemRB.GetString (CommonTables.Classes.GetValue (Dual[1], 2))
+ GemRB.SetToken ("CLASS", ClassTitle)
+ GemRB.SetToken ("LEVEL", str (Levels[1]) )
+
+ # the xp table contains only classes, so we have to determine the base class for kits
+ if Dual[0] == 2:
+ BaseClass = CommonTables.Classes.GetRowName (Dual[1])
+ else:
+ BaseClass = GUICommon.GetKitIndex (pc)
+ BaseClass = CommonTables.KitList.GetValue (BaseClass, 7)
+ BaseClass = CommonTables.Classes.FindValue (5, BaseClass)
+ BaseClass = CommonTables.Classes.GetRowName (BaseClass)
+ # the first class' XP is discarded and set to the minimum level
+ # requirement, so if you don't dual class right after a levelup,
+ # the game would eat some of your XP
+ XP1 = CommonTables.NextLevel.GetValue (BaseClass, str (Levels[1]))
+ GemRB.SetToken ("EXPERIENCE", str (XP1) )
+
+ # inactive until the new class SURPASSES the former
+ if Levels[0] <= Levels[1]:
+ # inactive
+ stats.append ( (19719,1,'c') )
+ else:
+ stats.append ( (19720,1,'c') )
+ stats.append (None)
+ else: # single classed
+ print "\tSingle classed"
+ Level = GemRB.GetPlayerStat (pc, IE_LEVEL) + LevelDiff[0]
+ GemRB.SetToken ("LEVEL", str (Level-LevelDrain))
+ GemRB.SetToken ("EXPERIENCE", str (XP) )
+ if LevelDrain:
+ stats.append ( (19720,1,'c') )
+ stats.append ( (57435,1,'c') ) # LEVEL DRAINED
+ else:
+ GemRB.SetToken ("NEXTLEVEL", LUCommon.GetNextLevelExp (Level, Class) )
+ stats.append ( (16480,1,'c') )
+ stats.append (None)
+ print "\t\tClass (Level):",Class,"(",Level,")"
+
+ # check to see if we have a level diff anywhere
+ if sum (LevelDiff) == 0:
+ effects = GemRB.GetPlayerStates (pc)
+ if len (effects):
+ for c in effects:
+ tmp = StateTable.GetValue (ord(c)-66, 0)
+ stats.append ( (tmp,c,'a') )
+ stats.append (None)
+
+ stats.append (None)
+
+ #proficiencies
+ stats.append ( (8442,1,'c') )
+
+ stats.append ( (9457, str(GS (IE_TOHIT))+" ("+str(GemRB.GetCombatDetails(pc, 0)["ToHit"])+")", '0') )
+ tmp = GS (IE_NUMBEROFATTACKS)
+ if (tmp&1):
+ tmp2 = str (tmp/2) + chr (188)
+ else:
+ tmp2 = str (tmp/2)
+ stats.append ( (9458, tmp2, '') )
+ stats.append ( (9459, GSNN (pc, IE_LORE), '0') )
+ stats.append ( (19224, GS (IE_RESISTMAGIC), '') )
+
+ # party's reputation
+ reptxt = GetReputation (GemRB.GameGetReputation ()/10)
+ stats.append ( (9465, reptxt, '') )
+ stats.append ( (9460, GSNN (pc, IE_LOCKPICKING), '') )
+ stats.append ( (9462, GSNN (pc, IE_TRAPS), '') )
+ stats.append ( (9463, GSNN (pc, IE_PICKPOCKET), '') )
+ stats.append ( (9461, GSNN (pc, IE_STEALTH), '') )
+ HatedRace = GS (IE_HATEDRACE)
+ if HatedRace:
+ HateTable = GemRB.LoadTable ("haterace")
+ Racist = HateTable.FindValue (1, HatedRace)
+ if Racist != -1:
+ HatedRace = HateTable.GetValue (Racist, 0)
+ stats.append ( (15982, GemRB.GetString (HatedRace), '') )
+
+ #stats.append ( (34120, GSNN (pc, IE_HIDEINSHADOWS), '') )
+ #stats.append ( (34121, GSNN (pc, IE_DETECTILLUSIONS), '') )
+ #stats.append ( (34122, GSNN (pc, IE_SETTRAPS), '') )
+ stats.append ( (12128, GS (IE_BACKSTABDAMAGEMULTIPLIER), 'x') )
+ stats.append ( (12126, GS (IE_TURNUNDEADLEVEL), '') )
+
+ #this hack only displays LOH if we know the spell
+ #TODO: the core should just not set LOH if the paladin can't learn it
+ if (GUICommon.HasSpell (pc, IE_SPELL_TYPE_INNATE, 0, "SPCL211") >= 0):
+ stats.append ( (12127, GS (IE_LAYONHANDSAMOUNT), '') )
+
+ #script
+ aiscript = GemRB.GetPlayerScript (pc )
+ stats.append ( (2078, aiscript, '') )
+ stats.append (None)
+
+ # 17379 Saving throws
+ stats.append (17379)
+ # 17380 Paralyze/Poison/Death
+ stats.append ( (17380, IE_SAVEVSDEATH, 's') )
+ # 17381 Rod/Staff/Wand
+ stats.append ( (17381, IE_SAVEVSWANDS, 's') )
+ # 17382 Petrify/Polymorph
+ stats.append ( (17382, IE_SAVEVSPOLY, 's') )
+ # 17383 Breath weapon
+ stats.append ( (17383, IE_SAVEVSBREATH, 's') )
+ # 17384 Spells
+ stats.append ( (17384, IE_SAVEVSSPELL, 's') )
+ stats.append (None)
+
+ # 9466 Weapon proficiencies
+ stats.append (9466)
+ table = GemRB.LoadTable ("weapprof")
+ RowCount = table.GetRowCount ()
+ for i in range (RowCount):
+ text = table.GetValue (i, 1)
+ stat = table.GetValue (i, 0) + IE_PROFICIENCYBASTARDSWORD
+ stats.append ( (text, GS (stat)&0x07, '+') )
+ stats.append (None)
+
+ # 11766 AC Bonuses
+ stats.append (11766)
+ # 11770 AC vs. Crushing
+ stats.append ((11770, GS (IE_ACCRUSHINGMOD), ''))
+ # 11767 AC vs. Missile
+ stats.append ((11767, GS (IE_ACMISSILEMOD), ''))
+ # 11769 AC vs. Piercing
+ stats.append ((11769, GS (IE_ACPIERCINGMOD), ''))
+ # 11768 AC vs. Slashing
+ stats.append ((11768, GS (IE_ACSLASHINGMOD), ''))
+ stats.append (None)
+
+ # 10315 Ability bonuses
+ stats.append (10315)
+ value = GemRB.GetPlayerStat (pc, IE_STR)
+ ex = GemRB.GetPlayerStat (pc, IE_STREXTRA)
+ # 10332 to hit
+ stats.append ( (10332, GemRB.GetAbilityBonus (IE_STR,0,value,ex), '0') )
+ # 10336 damage
+ stats.append ( (10336, GemRB.GetAbilityBonus (IE_STR,1,value,ex), '0') )
+ # 10337 open doors (bend bars lift gates)
+ stats.append ( (10337, GemRB.GetAbilityBonus (IE_STR,2,value,ex), '0') )
+ # 10338 weight allowance
+ stats.append ( (10338, GemRB.GetAbilityBonus (IE_STR,3,value,ex), '0') )
+ # 10339 AC
+ stats.append ( (10339, GA (IE_DEX,2), '0') )
+ # 10340 Missile adjustment
+ stats.append ( (10340, GA (IE_DEX,1), '0') )
+ # 10341 Reaction adjustment
+ stats.append ( (10341, GA (IE_DEX,0), '0') )
+ # 10342 CON HP Bonus/Level
+ stats.append ( (10342, GA (IE_CON,0), 'p') )
+ # 10343 Chance To Learn spell
+ if GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_WIZARD, 0, 0)>0:
+ stats.append ( (10343, GA (IE_INT,0), '%' ) )
+ # 10347 Reaction
+ stats.append ( (10347, GA (IE_REPUTATION,0), '0') )
+ stats.append (None)
+
+ # 10344 Bonus Priest spells
+ if GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, 0, 0)>0:
+ stats.append (10344)
+ for level in range (7):
+ GemRB.SetToken ("SPELLLEVEL", str (level+1) )
+ #get the bonus spell count
+ base = GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, level, 0)
+ if base:
+ count = GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, level)
+ stats.append ( (GemRB.GetString (10345), count-base, 'b') )
+ stats.append (None)
+
+ res = []
+ lines = 0
+ for s in stats:
+ try:
+ strref, val, type = s
+ if val == 0 and type != '0':
+ continue
+ if type == '+': #pluses
+ res.append ("[capital=0]"+GemRB.GetString (strref) + ' '+ '+' * val)
+ elif type == 'p': #a plus prefix if positive
+ if val > 0:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ' +' + str (val) )
+ else:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ' ' + str (val) )
+ elif type == 's': #both base and (modified) stat, but only if they differ
+ base = str (GB (val))
+ stat = str (GS (val))
+ if base == stat:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ': ' + base)
+ else:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ': ' + base + " (" + stat + ")")
+ elif type == 'x': #x character before value
+ res.append ("[capital=0]"+GemRB.GetString (strref) +': x' + str (val) )
+ elif type == 'a': #value (portrait icon) + string
+ res.append ("[capital=2]"+val+" "+GemRB.GetString (strref))
+ elif type == 'b': #strref is an already resolved string
+ res.append ("[capital=0]"+strref+": "+str (val))
+ elif type == 'c': #normal string
+ res.append ("[capital=0]"+GemRB.GetString (strref))
+ elif type == 'd': #strref is an already resolved string
+ res.append ("[capital=0]"+strref)
+ elif type == '0': #normal value
+ res.append (GemRB.GetString (strref) + ': ' + str (val))
+ else: #normal value + type character, for example percent sign
+ res.append ("[capital=0]"+GemRB.GetString (strref) + ': ' + str (val) + type)
+ lines = 1
+ except:
+ if s != None:
+ res.append ( GemRB.GetString (s) )
+ lines = 0
+ else:
+ if lines:
+ res.append ("")
+ lines = 0
+
+ return "\n".join (res)
+
+def GetReputation (repvalue):
+ table = GemRB.LoadTable ("reptxt")
+ if repvalue>20:
+ repvalue=20
+ txt = GemRB.GetString (table.GetValue (repvalue, 0) )
+ return txt+"("+str (repvalue)+")"
+
+def OpenInformationWindow ():
+ global InformationWindow
+
+ if InformationWindow != None:
+ if BiographyWindow: OpenBiographyWindow ()
+
+ return
+
+ InformationWindow = Window = GemRB.LoadWindow (4)
+
+ # Biography
+ Button = Window.GetControl (26)
+ Button.SetText (18003)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyWindow)
+
+ # Done
+ Button = Window.GetControl (24)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseInformationWindow)
+
+ TotalPartyExp = 0
+ ChapterPartyExp = 0
+ TotalCount = 0
+ ChapterCount = 0
+ for i in range (1, GemRB.GetPartySize () + 1):
+ stat = GemRB.GetPCStats(i)
+ TotalPartyExp = TotalPartyExp + stat['KillsTotalXP']
+ ChapterPartyExp = ChapterPartyExp + stat['KillsChapterXP']
+ TotalCount = TotalCount + stat['KillsTotalCount']
+ ChapterCount = ChapterCount + stat['KillsChapterCount']
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+ stat = GemRB.GetPCStats (pc)
+
+ Label = Window.GetControl (0x10000000)
+ Label.SetText (GemRB.GetPlayerName (pc, 1))
+ # class
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ Label = Window.GetControl (0x10000018)
+ Label.SetText (ClassTitle)
+
+ #most powerful vanquished
+ Label = Window.GetControl (0x10000005)
+ #we need getstring, so -1 will translate to empty string
+ Label.SetText (GemRB.GetString (stat['BestKilledName']))
+
+ # NOTE: currentTime is in seconds, joinTime is in seconds * 15
+ # (script updates???). In each case, there are 60 seconds
+ # in a minute, 24 hours in a day, but ONLY 5 minutes in an hour!!
+ # Hence currentTime (and joinTime after div by 15) has
+ # 7200 secs a day (60 * 5 * 24)
+ currentTime = GemRB.GetGameTime ()
+ joinTime = stat['JoinDate'] - stat['AwayTime']
+
+ party_time = currentTime - (joinTime / 15)
+ days = party_time / 7200
+ hours = (party_time % 7200) / 300
+
+ GemRB.SetToken ('GAMEDAYS', str (days))
+ GemRB.SetToken ('HOUR', str (hours))
+ Label = Window.GetControl (0x10000006)
+ #actually it is 16043 <DURATION>, but duration is translated to
+ #16041, hopefully this won't cause problem with international version
+ Label.SetText (16041)
+
+ #favourite spell
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (stat['FavouriteSpell'])
+
+ #favourite weapon
+ Label = Window.GetControl (0x10000008)
+ #actually it is 10479 <WEAPONNAME>, but weaponname is translated to
+ #the real weapon name (which we should set using SetToken)
+ #there are other strings like bow+wname/xbow+wname/sling+wname
+ #are they used?
+ Label.SetText (stat['FavouriteWeapon'])
+
+ #total party xp
+ Label = Window.GetControl (0x10000013)
+ if TotalPartyExp != 0:
+ PartyExp = int ((stat['KillsTotalXP'] * 100) / TotalPartyExp)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ # chapter party xp
+ Label = Window.GetControl (0x1000000f)
+ if ChapterPartyExp != 0:
+ PartyExp = int ((stat['KillsChapterXP'] * 100) / ChapterPartyExp)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ #total xp
+ Label = Window.GetControl (0x10000014)
+ if TotalCount != 0:
+ PartyExp = int ((stat['KillsTotalCount'] * 100) / TotalCount)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ # chapter xp
+ Label = Window.GetControl (0x10000010)
+ if ChapterCount != 0:
+ PartyExp = int ((stat['KillsChapterCount'] * 100) / ChapterCount)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ Label = Window.GetControl (0x10000011)
+ Label.SetText (str (stat['KillsChapterXP']))
+ Label = Window.GetControl (0x10000015)
+ Label.SetText (str (stat['KillsTotalXP']))
+
+ #count of kills in chapter/game
+ Label = Window.GetControl (0x10000012)
+ Label.SetText (str (stat['KillsChapterCount']))
+ Label = Window.GetControl (0x10000016)
+ Label.SetText (str (stat['KillsTotalCount']))
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseInformationWindow ():
+ import GUICommonWindows
+ global InformationWindow
+
+ if InformationWindow:
+ InformationWindow.Unload ()
+ InformationWindow = None
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ RecordsWindow.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenBiographyWindow ():
+ global BiographyWindow
+
+ if BiographyWindow != None:
+ return
+
+ BiographyWindow = Window = GemRB.LoadWindow (12)
+ GemRB.SetVar ("FloatWindow", BiographyWindow.ID)
+
+ TextArea = Window.GetControl (0)
+ pc = GemRB.GameGetSelectedPCSingle ()
+ TextArea.SetText (GemRB.GetPlayerString (pc, 74) )
+
+ # Done
+ Button = Window.GetControl (2)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseBiographyWindow)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseBiographyWindow ():
+ global BiographyWindow
+
+ if BiographyWindow:
+ BiographyWindow.Unload ()
+ BiographyWindow = None
+ InformationWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenExportWindow ():
+ global ExportWindow, NameField, ExportDoneButton
+
+ ExportWindow = GemRB.LoadWindow (13)
+
+ TextArea = ExportWindow.GetControl (2)
+ TextArea.SetText (10962)
+
+ TextArea = ExportWindow.GetControl (0)
+ TextArea.GetCharacters ()
+
+ ExportDoneButton = ExportWindow.GetControl (4)
+ ExportDoneButton.SetText (11973)
+ ExportDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = ExportWindow.GetControl (5)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ NameField = ExportWindow.GetControl (6)
+
+ ExportDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExportDonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExportCancelPress)
+ NameField.SetEvent (IE_GUI_EDIT_ON_CHANGE, ExportEditChanged)
+ ExportWindow.ShowModal (MODAL_SHADOW_GRAY)
+ NameField.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def ExportDonePress():
+ if ExportWindow:
+ ExportWindow.Unload()
+ #save file under name from EditControl
+ return
+
+def ExportCancelPress():
+ if ExportWindow:
+ ExportWindow.Unload()
+ return
+
+def ExportEditChanged():
+ ExportFileName = NameField.QueryText ()
+ if ExportFileName == "":
+ ExportDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ ExportDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def OpenCustomizeWindow ():
+ global CustomizeWindow
+ global PortraitsTable, ScriptsTable, ColorTable
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ if GemRB.GetPlayerStat (pc, IE_MC_FLAGS)&MC_EXPORTABLE:
+ Exportable = 1
+ else:
+ Exportable = 0
+
+ PortraitsTable = GemRB.LoadTable ("PICTURES")
+ ScriptsTable = GemRB.LoadTable ("SCRPDESC")
+ ColorTable = GemRB.LoadTable ("CLOWNCOL")
+ CustomizeWindow = GemRB.LoadWindow (17)
+
+ PortraitSelectButton = CustomizeWindow.GetControl (0)
+ PortraitSelectButton.SetText (11961)
+ if not Exportable:
+ PortraitSelectButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ SoundButton = CustomizeWindow.GetControl (1)
+ SoundButton.SetText (10647)
+ if not Exportable:
+ SoundButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ColorButton = CustomizeWindow.GetControl (2)
+ ColorButton.SetText (10646)
+ if not Exportable:
+ ColorButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ScriptButton = CustomizeWindow.GetControl (3)
+ ScriptButton.SetText (17111)
+
+ #This button exists only in bg2/iwd, theoretically we could create it here
+ #BiographyButton = CustomizeWindow.GetControl (9)
+ #BiographyButton.SetText (18003)
+ #if not Exportable:
+ # BiographyButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ TextArea = CustomizeWindow.GetControl (5)
+ TextArea.SetText (11327)
+
+ CustomizeDoneButton = CustomizeWindow.GetControl (7)
+ CustomizeDoneButton.SetText (11973)
+ CustomizeDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ CancelButton = CustomizeWindow.GetControl (8);
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PortraitSelectButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPortraitSelectWindow)
+ SoundButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSoundWindow)
+ ColorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenColorWindow)
+ ScriptButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenScriptWindow)
+ #BiographyButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyEditWindow)
+ CustomizeDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomizeDonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomizeCancelPress)
+
+ CustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CustomizeDonePress ():
+ CloseCustomizeWindow ()
+ UpdateRecordsWindow ()
+ return
+
+def CustomizeCancelPress ():
+ CloseCustomizeWindow ()
+ UpdateRecordsWindow ()
+ return
+
+def CloseCustomizeWindow ():
+ global CustomizeWindow
+
+ if CustomizeWindow:
+ CustomizeWindow.Unload ()
+ CustomizeWindow = None
+ return
+
+def OpenPortraitSelectWindow ():
+ global SubCustomizeWindow
+ global PortraitButton
+ global Gender, LastPortrait
+
+ SubCustomizeWindow = GemRB.LoadWindow (18)
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Gender = GemRB.GetPlayerStat (pc, IE_SEX, 1)
+ PortraitName = GemRB.GetPlayerPortrait (pc, 0)
+ LastPortrait = PortraitsTable.GetRowIndex (PortraitName[0:len(PortraitName)-1])
+
+ PortraitButton = SubCustomizeWindow.GetControl (0)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ LeftButton = SubCustomizeWindow.GetControl (1)
+ RightButton = SubCustomizeWindow.GetControl (2)
+
+ DoneButton = SubCustomizeWindow.GetControl (3)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (4)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ CustomPortraitButton = SubCustomizeWindow.GetControl (5)
+ CustomPortraitButton.SetText (17545)
+
+ LeftButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitLeftPress)
+ RightButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitRightPress)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitDonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ CustomPortraitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenCustomPortraitWindow)
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+ while True:
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ UpdatePortrait ()
+ break
+ LastPortrait = LastPortrait + 1
+
+ return
+
+def PortraitDonePress ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Name = PortraitsTable.GetRowName (LastPortrait)
+ GemRB.FillPlayerInfo (pc, Name + "L", Name + "S")
+ CloseSubCustomizeWindow ()
+ return
+
+def PortraitRightPress():
+ global LastPortrait
+
+ while True:
+ LastPortrait = LastPortrait + 1
+ if LastPortrait >= PortraitsTable.GetRowCount ():
+ LastPortrait = 0
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ UpdatePortrait ()
+ return
+
+ return
+
+def PortraitLeftPress():
+ global LastPortrait
+
+ while True:
+ LastPortrait = LastPortrait - 1
+ if LastPortrait < 0:
+ LastPortrait = PortraitsTable.GetRowCount ()-1
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ UpdatePortrait ()
+ return
+
+ return
+
+def UpdatePortrait ():
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"G"
+ PortraitButton.SetPicture (PortraitName, "NOPORTLG")
+ return
+
+def OpenCustomPortraitWindow ():
+ global SubSubCustomizeWindow
+ global PortraitList1, PortraitList2
+ global RowCount1, RowCount2
+
+ SubSubCustomizeWindow = GemRB.LoadWindow (19)
+
+ SmallPortraitButton = SubSubCustomizeWindow.GetControl (1)
+ SmallPortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+
+ LargePortraitButton = SubSubCustomizeWindow.GetControl (0)
+ LargePortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+
+ DoneButton = SubSubCustomizeWindow.GetControl (10)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = SubSubCustomizeWindow.GetControl (11)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Portrait List Large
+ PortraitList1 = SubSubCustomizeWindow.GetControl (2)
+ RowCount1 = PortraitList1.GetPortraits (0)
+ PortraitList1.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, LargeCustomPortrait)
+ GemRB.SetVar ("Row1", RowCount1)
+ PortraitList1.SetVarAssoc ("Row1",RowCount1)
+
+ # Portrait List Small
+ PortraitList2 = SubSubCustomizeWindow.GetControl (3)
+ RowCount2 = PortraitList2.GetPortraits (1)
+ PortraitList2.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, SmallCustomPortrait)
+ GemRB.SetVar ("Row2", RowCount2)
+ PortraitList2.SetVarAssoc ("Row2",RowCount2)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomPortraitDonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubSubCustomizeWindow)
+
+ SubSubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CustomPortraitDonePress ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.FillPlayerInfo (pc, PortraitList1.QueryText () , PortraitList2.QueryText ())
+
+ CloseSubSubCustomizeWindow ()
+ #closing the generic portraits, because we just set a custom one
+ CloseSubCustomizeWindow ()
+ return
+
+def LargeCustomPortrait ():
+ Window = SubSubCustomizeWindow
+
+ Portrait = PortraitList1.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row1") == RowCount1:
+ return
+
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (10)
+ if Portrait=="":
+ Portrait = "NOPORTMD"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList2.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (0)
+ Button.SetPicture (Portrait, "NOPORTMD")
+ return
+
+def SmallCustomPortrait ():
+ Window = SubSubCustomizeWindow
+
+ Portrait = PortraitList2.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row2") == RowCount2:
+ return
+
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (10)
+ if Portrait=="":
+ Portrait = "NOPORTSM"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList1.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (1)
+ Button.SetPicture (Portrait, "NOPORTSM")
+ return
+
+def OpenSoundWindow ():
+ global SubCustomizeWindow
+ global VoiceList
+ global Gender
+ global OldVoiceSet
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ OldVoiceSet = GemRB.GetPlayerSound (pc)
+ SubCustomizeWindow = GemRB.LoadWindow (20)
+
+ VoiceList = SubCustomizeWindow.GetControl (5)
+ VoiceList.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ Gender = GemRB.GetPlayerStat (pc, IE_SEX, 1)
+
+ VoiceList.SetVarAssoc ("Selected", 0)
+ VoiceList.GetCharSounds()
+ VoiceList.SelectText (OldVoiceSet)
+
+ PlayButton = SubCustomizeWindow.GetControl (7)
+ PlayButton.SetText (17318)
+
+ TextArea = SubCustomizeWindow.GetControl (8)
+ TextArea.SetText (11315)
+
+ DoneButton = SubCustomizeWindow.GetControl (10)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (11)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlaySoundPressed)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneSoundWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSoundWindow)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseSoundWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerSound (pc, OldVoiceSet)
+ CloseSubCustomizeWindow ()
+ return
+
+def DoneSoundWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ CharSound = VoiceList.QueryText ()
+ GemRB.SetPlayerSound (pc, CharSound)
+
+ CloseSubCustomizeWindow ()
+ return
+
+def PlaySoundPressed():
+ global CharSoundWindow, SoundIndex, SoundSequence
+
+ CharSound = VoiceList.QueryText ()
+ tmp = SoundIndex
+ while (not GemRB.HasResource (CharSound + SoundSequence[SoundIndex], RES_WAV)):
+ NextSound()
+ if SoundIndex == tmp:
+ break
+ else:
+ NextSound()
+ GemRB.PlaySound (CharSound + SoundSequence[SoundIndex], 0, 0, 5)
+ return
+
+def NextSound():
+ global SoundIndex, SoundSequence
+ SoundIndex += 1
+ if SoundIndex >= len(SoundSequence):
+ SoundIndex = 0
+ return
+
+def OpenColorWindow ():
+ global SubCustomizeWindow
+ global PortraitWindow
+ global PortraitButton
+ global HairButton, SkinButton, MajorButton, MinorButton
+ global HairColor, SkinColor, MajorColor, MinorColor
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ MinorColor = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR)
+ MajorColor = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR)
+ SkinColor = GemRB.GetPlayerStat (pc, IE_SKIN_COLOR)
+ HairColor = GemRB.GetPlayerStat (pc, IE_HAIR_COLOR)
+ SubCustomizeWindow = GemRB.LoadWindow (21)
+
+ PortraitButton = SubCustomizeWindow.GetControl (0)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ HairButton = SubCustomizeWindow.GetControl (3)
+ SkinButton = SubCustomizeWindow.GetControl (4)
+ MajorButton = SubCustomizeWindow.GetControl (5)
+ MinorButton = SubCustomizeWindow.GetControl (6)
+
+ DoneButton = SubCustomizeWindow.GetControl (12)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (13)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ HairButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetHairColor)
+ SkinButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetSkinColor)
+ MajorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetMajorColor)
+ MinorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetMinorColor)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneColorWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ UpdatePaperDoll ()
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DoneColorWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerStat (pc, IE_MINOR_COLOR, MinorColor)
+ GemRB.SetPlayerStat (pc, IE_MAJOR_COLOR, MajorColor)
+ GemRB.SetPlayerStat (pc, IE_SKIN_COLOR, SkinColor)
+ GemRB.SetPlayerStat (pc, IE_HAIR_COLOR, HairColor)
+ CloseSubCustomizeWindow ()
+ return
+
+def UpdatePaperDoll ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Color1 = GemRB.GetPlayerStat (pc, IE_METAL_COLOR)
+ MinorButton.SetBAM ("COLGRAD", 0, 0, MinorColor&0xff)
+ MajorButton.SetBAM ("COLGRAD", 0, 0, MajorColor&0xff)
+ SkinButton.SetBAM ("COLGRAD", 0, 0, SkinColor&0xff)
+ Color5 = GemRB.GetPlayerStat (pc, IE_LEATHER_COLOR)
+ Color6 = GemRB.GetPlayerStat (pc, IE_ARMOR_COLOR)
+ HairButton.SetBAM ("COLGRAD", 0, 0, HairColor&0xff)
+ PortraitButton.SetPLT (GUICommon.GetActorPaperDoll (pc),
+ Color1, MinorColor, MajorColor, SkinColor, Color5, Color6, HairColor, 0, 0)
+ return
+
+def SetHairColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 0
+ PickedColor = HairColor
+ OpenColorPicker ()
+ return
+
+def SetSkinColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 1
+ PickedColor = SkinColor
+ OpenColorPicker ()
+ return
+
+def SetMinorColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 2
+ PickedColor = MinorColor
+ OpenColorPicker ()
+ return
+
+def SetMajorColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 3
+ PickedColor = MajorColor
+ OpenColorPicker ()
+ return
+
+def OpenColorPicker ():
+ global SubSubCustomizeWindow
+ #global Selected
+
+ SubSubCustomizeWindow = GemRB.LoadWindow (22)
+
+ GemRB.SetVar ("Selected",-1)
+ for i in range (1,35):
+ Button = SubSubCustomizeWindow.GetControl (i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+
+ #Selected = -1
+ for i in range (34):
+ MyColor = ColorTable.GetValue (ColorIndex, i)
+ if MyColor == "*":
+ break
+ Button = SubSubCustomizeWindow.GetControl (i+1)
+ Button.SetBAM("COLGRAD", 2, 0, MyColor)
+ if PickedColor == MyColor:
+ GemRB.SetVar ("Selected",i)
+ #Selected = i
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetVarAssoc("Selected",i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePress)
+
+ SubSubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DonePress():
+ global HairColor, SkinColor, MajorColor, MinorColor
+ global PickedColor
+
+ CloseSubSubCustomizeWindow ()
+ PickedColor=ColorTable.GetValue (ColorIndex, GemRB.GetVar ("Selected"))
+ if ColorIndex==0:
+ HairColor=PickedColor
+ UpdatePaperDoll ()
+ return
+ if ColorIndex==1:
+ SkinColor=PickedColor
+ UpdatePaperDoll ()
+ return
+ if ColorIndex==2:
+ MinorColor=PickedColor
+ UpdatePaperDoll ()
+ return
+
+ MajorColor=PickedColor
+ UpdatePaperDoll ()
+ return
+
+def OpenScriptWindow ():
+ global SubCustomizeWindow
+ global ScriptTextArea, SelectedTextArea
+
+ SubCustomizeWindow = GemRB.LoadWindow (11)
+
+ ScriptTextArea = SubCustomizeWindow.GetControl (2)
+ ScriptTextArea.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ FillScriptList ()
+ pc = GemRB.GameGetSelectedPCSingle ()
+ script = GemRB.GetPlayerScript (pc)
+ scriptindex = ScriptsTable.GetRowIndex (script)
+ GemRB.SetVar ("Selected", scriptindex)
+ ScriptTextArea.SetVarAssoc ("Selected", scriptindex)
+
+ SelectedTextArea = SubCustomizeWindow.GetControl (4)
+ UpdateScriptSelection ()
+
+ DoneButton = SubCustomizeWindow.GetControl (5)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (6)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneScriptWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ ScriptTextArea.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, UpdateScriptSelection)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def FillScriptList ():
+ ScriptTextArea.Clear ()
+ row = ScriptsTable.GetRowCount ()
+ for i in range (row):
+ GemRB.SetToken ("script", ScriptsTable.GetRowName (i) )
+ title = ScriptsTable.GetValue (i,0)
+ if title!=-1:
+ desc = ScriptsTable.GetValue (i,1)
+ txt = GemRB.GetString (title)
+
+ if (desc!=-1):
+ txt += GemRB.GetString (desc)
+
+ ScriptTextArea.Append (txt+"\n", -1)
+
+ else:
+ ScriptTextArea.Append (ScriptsTable.GetRowName (i)+"\n" ,-1)
+
+ return
+
+def DoneScriptWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ script = ScriptsTable.GetRowName (GemRB.GetVar ("Selected") )
+ GemRB.SetPlayerScript (pc, script)
+ CloseSubCustomizeWindow ()
+ return
+
+def UpdateScriptSelection():
+ text = ScriptTextArea.QueryText ()
+ SelectedTextArea.SetText (text)
+ return
+
+def OpenBiographyEditWindow ():
+ global SubCustomizeWindow
+ global BioStrRef
+ global TextArea
+
+ Changed = 0
+ pc = GemRB.GameGetSelectedPCSingle ()
+ BioStrRef = GemRB.GetPlayerString (pc, 74)
+ if BioStrRef != 33347:
+ Changed = 1
+
+ SubCustomizeWindow = GemRB.LoadWindow (23)
+
+ ClearButton = SubCustomizeWindow.GetControl (5)
+ ClearButton.SetText (34881)
+
+ DoneButton = SubCustomizeWindow.GetControl (1)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ RevertButton = SubCustomizeWindow.GetControl (3)
+ RevertButton.SetText (2240)
+ if not Changed:
+ RevertButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = SubCustomizeWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ TextArea = SubCustomizeWindow.GetControl (4)
+ TextArea.SetBufferLength (65535)
+ TextArea.SetText (BioStrRef)
+
+ ClearButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClearBiography)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneBiographyWindow)
+ RevertButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RevertBiography)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ClearBiography():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ BioStrRef = 62015+pc
+ #GemRB.CreateString (BioStrRef, "")
+ TextArea.SetText ("")
+ return
+
+def DoneBiographyWindow ():
+ global BioStrRef
+
+ #TODO set bio
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #pc is 1 based
+ BioStrRef = 62015+pc
+ GemRB.CreateString (BioStrRef, TextArea.QueryText())
+ GemRB.SetPlayerString (pc, 74, BioStrRef)
+ CloseSubCustomizeWindow ()
+ return
+
+def RevertBiography():
+ global BioStrRef
+
+ BioStrRef = 33347
+ TextArea.SetText (33347)
+ return
+
+def CloseSubCustomizeWindow ():
+ global SubCustomizeWindow
+
+ if SubCustomizeWindow:
+ SubCustomizeWindow.Unload ()
+ SubCustomizeWindow = None
+ return
+
+def CloseSubSubCustomizeWindow ():
+ global SubSubCustomizeWindow
+
+ if SubSubCustomizeWindow:
+ SubSubCustomizeWindow.Unload ()
+ SubSubCustomizeWindow = None
+ return
+
+###################################################
+# End of file GUIREC.py
diff --git a/gemrb/GUIScripts/bg1/GUISAVE.py b/gemrb/GUIScripts/bg1/GUISAVE.py
new file mode 100644
index 0000000..d5673e7
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUISAVE.py
@@ -0,0 +1,279 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUISAVE.py - Save window
+
+###################################################
+
+import GemRB
+import GUICommon
+import LoadScreen
+from GUIDefines import *
+
+SaveWindow = 0
+ConfirmWindow = 0
+NameField = 0
+SaveButton = 0
+TextAreaControl = 0
+Games = ()
+ScrollBar = 0
+
+def OpenSaveWindow ():
+ global SaveWindow, TextAreaControl, Games, ScrollBar
+
+ if GUICommon.CloseOtherWindow (OpenSaveWindow):
+ CloseSaveWindow ()
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUISAVE", 640, 480)
+ Window = SaveWindow = GemRB.LoadWindow (0)
+ Window.SetFrame ()
+ CancelButton=Window.GetControl (34)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSaveWindow)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ GemRB.SetVar ("LoadIdx",0)
+
+ for i in range(4):
+ Button = Window.GetControl (26+i)
+ Button.SetText (15588)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SavePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ Button = Window.GetControl (30+i)
+ Button.SetText (13957)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ #area previews
+ Button = Window.GetControl (1+i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ #PC portraits
+ for j in range(PARTY_SIZE):
+ Button = Window.GetControl (40+i*PARTY_SIZE+j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ ScrollBar=Window.GetControl (25)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games=GemRB.GetSaveGames ()
+ TopIndex = max (0, len(Games) - 4 + 1) #one more for the 'new game'
+ GemRB.SetVar ("TopIndex",TopIndex)
+ ScrollBar.SetVarAssoc ("TopIndex", TopIndex+1)
+ ScrollBar.SetDefaultScrollBar ()
+ ScrollBarPress ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ScrollBarPress():
+ Window = SaveWindow
+
+ #draw load game portraits
+ Pos = GemRB.GetVar ("TopIndex")
+ for i in range(4):
+ ActPos = Pos + i
+
+ Button1 = Window.GetControl (26+i)
+ Button2 = Window.GetControl (30+i)
+ if ActPos<=len(Games):
+ Button1.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetName()
+ Button2.SetState (IE_GUI_BUTTON_ENABLED)
+ elif ActPos == len(Games):
+ Slotname = 15304
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Slotname = ""
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Label = Window.GetControl (0x10000008+i)
+ Label.SetText (Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = Window.GetControl (0x10000010+i)
+ Label.SetText (Slotname)
+
+ Button=Window.GetControl (1+i)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture("")
+ for j in range(PARTY_SIZE):
+ Button=Window.GetControl (40+i*PARTY_SIZE+j)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture("")
+ return
+
+def AbortedSaveGame():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ConfirmedSaveGame():
+ global ConfirmWindow
+
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ Label = ConfirmWindow.GetControl (3)
+ Slotname = Label.QueryText ()
+ LoadScreen.StartLoadScreen()
+ if Pos<len(Games):
+ GemRB.SaveGame(Games[Pos], Slotname)
+ else:
+ GemRB.SaveGame(None, Slotname)
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ OpenSaveWindow() # close window
+ return
+
+def SavePress():
+ global ConfirmWindow, NameField, SaveButton
+
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ ConfirmWindow = GemRB.LoadWindow (1)
+
+ #slot name
+ if Pos<len(Games):
+ Slotname = Games[Pos].GetName()
+ save_strref = 15306
+ else:
+ Slotname = ""
+ save_strref = 15588
+ NameField = ConfirmWindow.GetControl (3)
+ NameField.SetText (Slotname)
+ NameField.SetEvent (IE_GUI_EDIT_ON_CHANGE, EditChange)
+
+ #game hours (should be generated from game)
+ if Pos<len(Games):
+ Slotname = Games[Pos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = ConfirmWindow.GetControl (0x10000004)
+ Label.SetText (Slotname)
+
+ #areapreview
+ Button=ConfirmWindow.GetControl (0)
+ if Pos<len(Games):
+ Button.SetSprite2D(Games[Pos].GetPreview())
+ else:
+ Button.SetPicture("")
+
+ #portraits
+ for j in range(PARTY_SIZE):
+ Button=ConfirmWindow.GetControl (40+j)
+ if Pos<len(Games):
+ Button.SetSprite2D(Games[Pos].GetPortrait(j))
+ else:
+ Button.SetPicture("")
+
+ #save
+ SaveButton=ConfirmWindow.GetControl (7)
+ SaveButton.SetText (save_strref)
+ SaveButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ConfirmedSaveGame)
+ SaveButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ #SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+ if Slotname == "":
+ SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ #cancel
+ CancelButton=ConfirmWindow.GetControl (8)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbortedSaveGame)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ NameField.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def EditChange():
+ Name = NameField.QueryText ()
+ if len(Name)==0:
+ SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ SaveButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def DeleteGameConfirm():
+ global Games
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Pos = TopIndex +GemRB.GetVar ("LoadIdx")
+ GemRB.DeleteSaveGame(Games[Pos])
+ if TopIndex>0:
+ GemRB.SetVar ("TopIndex",TopIndex-1)
+ del Games[pos]
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress()
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress():
+ global ConfirmWindow
+
+ SaveWindow.SetVisible (WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow (2)
+ Text=ConfirmWindow.GetControl (0)
+ Text.SetText (15305)
+ DeleteButton=ConfirmWindow.GetControl (1)
+ DeleteButton.SetText (13957)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ CancelButton=ConfirmWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CloseSaveWindow ():
+ if SaveWindow:
+ SaveWindow.Unload ()
+ if GemRB.GetVar ("QuitAfterSave"):
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("Start")
+ return
+
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE) #enabling the game control screen
+ GemRB.UnhideGUI () #enabling the other windows
+ return
diff --git a/gemrb/GUIScripts/bg1/GUISONGS.py b/gemrb/GUIScripts/bg1/GUISONGS.py
new file mode 100644
index 0000000..ba221df
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUISONGS.py
@@ -0,0 +1,69 @@
+# 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.
+#
+#
+#instead of credits, you can listen the songs of the game :)
+import GemRB
+import GUICommon
+
+MovieWindow = 0
+TextAreaControl = 0
+MoviesTable = 0
+
+def OnLoad():
+ global MovieWindow, TextAreaControl, MoviesTable
+
+ GemRB.LoadWindowPack("GUIMOVIE", 640, 480)
+ MovieWindow = GemRB.LoadWindow(0)
+ MovieWindow.SetFrame ()
+ TextAreaControl = MovieWindow.GetControl(0)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ PlayButton = MovieWindow.GetControl(2)
+ CreditsButton = MovieWindow.GetControl(3)
+ DoneButton = MovieWindow.GetControl(4)
+ MoviesTable = GemRB.LoadTable("MUSIC")
+ for i in range(1,MoviesTable.GetRowCount() ):
+ s = MoviesTable.GetValue(i, 0)
+ TextAreaControl.Append(s,-1)
+ TextAreaControl.SetVarAssoc("MovieIndex",0)
+ PlayButton.SetText(17318)
+ CreditsButton.SetText(15591)
+ DoneButton.SetText(11973)
+ PlayButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ CreditsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CreditsPress)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+ MovieWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def PlayPress():
+ s = GemRB.GetVar("MovieIndex")+1
+ t = MoviesTable.GetValue(s, 0)
+ GemRB.LoadMusicPL(t,1)
+ return
+
+def CreditsPress():
+ GemRB.PlayMovie("credits", 1)
+ return
+
+def DonePress():
+ if MovieWindow:
+ MovieWindow.Unload()
+ if GUICommon.HasTOB():
+ GemRB.SetNextScript ("Start2")
+ else:
+ GemRB.SetNextScript ("Start")
+ return
diff --git a/gemrb/GUIScripts/bg1/GUIWORLD.py b/gemrb/GUIScripts/bg1/GUIWORLD.py
new file mode 100644
index 0000000..dbe8bf8
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/GUIWORLD.py
@@ -0,0 +1,336 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIW.py - scripts to control some windows from GUIWORLD winpack
+# except of Actions, Portrait, Options and Dialog windows
+#################################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+import GUIClasses
+from GUIDefines import *
+from ie_stats import *
+from ie_restype import *
+import MessageWindow
+import CommonWindow
+
+FRAME_PC_SELECTED = 0
+FRAME_PC_TARGET = 1
+
+ContinueWindow = None
+ReformPartyWindow = None
+OldActionsWindow = None
+OldMessageWindow = None
+
+removable_pcs = []
+
+def DialogStarted ():
+ global ContinueWindow, OldActionsWindow
+
+ # try to force-close anything which is open
+ GUICommon.CloseOtherWindow(None)
+ CommonWindow.CloseContainerWindow()
+
+ # we need GUI for dialogs
+ GemRB.UnhideGUI()
+
+ # opening control size to maximum, enabling dialog window
+ GemRB.GameSetScreenFlags(GS_HIDEGUI, OP_NAND)
+ GemRB.GameSetScreenFlags(GS_DIALOG, OP_OR)
+
+ if GUICommonWindows.PortraitWindow:
+ GUICommonWindows.UpdatePortraitWindow ()
+
+ # we want this to happen before we start fiddling with the GUI
+ MessageWindow.UpdateControlStatus()
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ContinueWindow = Window = GemRB.LoadWindow (9)
+
+ GUICommonWindows.EmptyControls()
+ OldActionsWindow = GUICommonWindows.ActionsWindow
+ #GUICommonWindows.ActionsWindow = None
+ OldActionsWindow.SetVisible(WINDOW_INVISIBLE)
+ GemRB.SetVar ("ActionsWindow", -1)
+
+def DialogEnded ():
+ global ContinueWindow, OldActionsWindow
+
+ # TODO: why is this being called at game start?!
+ if not ContinueWindow:
+ return
+
+ #GUICommonWindows.ActionsWindow = OldActionsWindow
+ OldActionsWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.SetVar ("ActionsWindow", OldActionsWindow.ID)
+ GUICommonWindows.UpdateActionsWindow()
+
+ ContinueWindow.Unload ()
+ ContinueWindow = None
+ OldActionsWindow = None
+
+ if GUICommonWindows.PortraitWindow:
+ GUICommonWindows.UpdatePortraitWindow ()
+
+def CloseContinueWindow ():
+ # don't close the actual window now to avoid flickering: we might still want it open
+ GemRB.SetVar ("DialogChoose", GemRB.GetVar ("DialogOption"))
+
+def NextDialogState ():
+ if not ContinueWindow:
+ return
+
+ ContinueWindow.SetVisible(WINDOW_INVISIBLE)
+ OldActionsWindow.SetVisible(WINDOW_VISIBLE)
+
+ MessageWindow.TMessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenEndMessageWindow ():
+ ContinueWindow.SetVisible(WINDOW_VISIBLE)
+ OldActionsWindow.SetVisible(WINDOW_INVISIBLE)
+ Button = ContinueWindow.GetControl (0)
+ Button.SetText (9371)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenContinueMessageWindow ():
+ ContinueWindow.SetVisible(WINDOW_VISIBLE)
+ OldActionsWindow.SetVisible(WINDOW_INVISIBLE)
+ #continue
+ Button = ContinueWindow.GetControl (0)
+ Button.SetText (9372)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def UpdateReformWindow ():
+ Window = ReformPartyWindow
+
+ select = GemRB.GetVar ("Selected")
+
+ need_to_drop = GemRB.GetPartySize ()-PARTY_SIZE
+ if need_to_drop<0:
+ need_to_drop = 0
+
+ #excess player number
+ Label = Window.GetControl (0x1000000f)
+ Label.SetText (str(need_to_drop) )
+
+ #done
+ Button = Window.GetControl (8)
+ if need_to_drop:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ #remove
+ Button = Window.GetControl (15)
+ if select:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ for i in range (PARTY_SIZE+1):
+ Button = Window.GetControl (i)
+ if i+1 not in removable_pcs:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ continue
+
+ for i in removable_pcs:
+ Button = Window.GetControl (removable_pcs.index(i))
+ Button.EnableBorder (FRAME_PC_SELECTED, select == i )
+ pic = GemRB.GetPlayerPortrait (i, 1)
+ if not pic:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ continue
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ALIGN_BOTTOM|IE_GUI_BUTTON_ALIGN_LEFT, OP_SET)
+ Button.SetPicture (pic, "NOPORTSM")
+ GUICommonWindows.UpdatePortraitWindow ()
+ return
+
+def RemovePlayer ():
+ global ReformPartyWindow
+
+ hideflag = GemRB.HideGUI ()
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+ ReformPartyWindow = Window = GemRB.LoadWindow (25)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+
+ #are you sure
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (17518)
+
+ #confirm
+ Button = Window.GetControl (1)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemovePlayerConfirm)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ #cancel
+ Button = Window.GetControl (2)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemovePlayerCancel)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.SetVar ("OtherWindow", Window.ID)
+ GemRB.SetVar ("ActionsWindow", -1)
+ if hideflag:
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def RemovePlayerConfirm ():
+ slot = GemRB.GetVar("Selected")
+ if GemRB.GetPlayerStat(slot, IE_HITPOINTS) > 0:
+ GemRB.ExecuteString("Dialogue([PC])", slot)
+ GemRB.LeaveParty (slot, 1)
+ OpenReformPartyWindow ()
+ return
+
+def RemovePlayerCancel ():
+ #Once for getting rid of the confirmation window
+ OpenReformPartyWindow ()
+ #and once for reopening the reform party window
+ OpenReformPartyWindow ()
+ return
+
+def OpenReformPartyWindow ():
+ global ReformPartyWindow, OldActionsWindow, OldMessageWindow
+ global removable_pcs
+
+ GemRB.SetVar ("Selected", 0)
+ hideflag = GemRB.HideGUI ()
+
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+ GemRB.SetVar ("ActionsWindow", OldActionsWindow.ID)
+ GemRB.SetVar ("MessageWindow", OldMessageWindow.ID)
+ GemRB.SetVar ("OtherWindow", -1)
+
+ OldActionsWindow = None
+ OldMessageWindow = None
+ ReformPartyWindow = None
+
+ #GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ if hideflag:
+ GemRB.UnhideGUI ()
+ #re-enabling party size control
+ GemRB.GameSetPartySize (PARTY_SIZE)
+ GUICommonWindows.UpdatePortraitWindow()
+ return
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ReformPartyWindow = Window = GemRB.LoadWindow (24)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+
+ # skip exportable party members (usually only the protagonist)
+ removable_pcs = []
+ for i in range (1, GemRB.GetPartySize()+1):
+ if not GemRB.GetPlayerStat (i, IE_MC_FLAGS)&MC_EXPORTABLE:
+ removable_pcs.append(i)
+
+ #PC portraits
+ for j in range (PARTY_SIZE+1):
+ Button = Window.GetControl (j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+ Button.SetBorder (FRAME_PC_SELECTED, 1, 1, 2, 2, 0, 255, 0, 255)
+ if j < len(removable_pcs):
+ Button.SetVarAssoc ("Selected", removable_pcs[j])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateReformWindow)
+
+ # Remove
+ Button = Window.GetControl (15)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemovePlayer)
+
+ # Done
+ Button = Window.GetControl (8)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenReformPartyWindow)
+
+ OldActionsWindow = GUIClasses.GWindow( GemRB.GetVar ("ActionsWindow") )
+ OldMessageWindow = GUIClasses.GWindow( GemRB.GetVar ("MessageWindow") )
+ GemRB.SetVar ("ActionsWindow", -1)
+ GemRB.SetVar ("MessageWindow", -1)
+
+ # if nobody can be removed, just close the window
+ if not removable_pcs:
+ OpenReformPartyWindow ()
+ if hideflag:
+ GemRB.UnhideGUI ()
+ return
+
+ UpdateReformWindow ()
+ if hideflag:
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DeathWindow ():
+ GemRB.HideGUI ()
+ GemRB.SetTimedEvent (DeathWindowEnd, 10)
+ return
+
+def DeathWindowEnd ():
+ #playing death movie before continuing
+ GemRB.PlayMovie ("deathand",1)
+ GemRB.GamePause (1,1)
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ Window = GemRB.LoadWindow (17)
+
+ #reason for death
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (16498)
+
+ #load
+ Button = Window.GetControl (1)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadPress)
+
+ #quit
+ Button = Window.GetControl (2)
+ Button.SetText (15417)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitPress)
+
+ GemRB.HideGUI ()
+ GemRB.SetVar ("MessageWindow", -1)
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def QuitPress():
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("Start")
+ return
+
+def LoadPress():
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("GUILOAD")
+ return
diff --git a/gemrb/GUIScripts/bg1/ImportFile.py b/gemrb/GUIScripts/bg1/ImportFile.py
new file mode 100644
index 0000000..23628fc
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/ImportFile.py
@@ -0,0 +1,75 @@
+# 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.
+#
+#
+#character generation, import (GUICG20)
+import GemRB
+from GUIDefines import *
+import GUICommon
+import CharGenCommon
+
+#import from a character sheet
+ImportWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global ImportWindow, TextAreaControl
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ ImportWindow = GemRB.LoadWindow(20)
+
+ GUICommon.CloseOtherWindow(ImportWindow.Unload)
+
+ TextAreaControl = ImportWindow.GetControl(4)
+ TextAreaControl.SetText(10963)
+
+ TextAreaControl = ImportWindow.GetControl(2)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ TextAreaControl.GetCharacters()
+
+ DoneButton = ImportWindow.GetControl(0)
+ DoneButton.SetText (11973)
+
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = ImportWindow.GetControl(1)
+ CancelButton.SetText (13727)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ TextAreaControl.SetEvent(IE_GUI_TEXTAREA_ON_CHANGE, SelectPress)
+ ImportWindow.ShowModal(MODAL_SHADOW_NONE)
+ return
+
+def SelectPress():
+ DoneButton = ImportWindow.GetControl(0)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def DonePress():
+ FileName = TextAreaControl.QueryText()
+ Slot = GemRB.GetVar("Slot")
+ GemRB.CreatePlayer(FileName, Slot| 0x8000, 1)
+ GemRB.SetToken("SmallPortrait", GemRB.GetPlayerPortrait (Slot, 1) )
+ GemRB.SetToken("LargePortrait", GemRB.GetPlayerPortrait (Slot, 0) )
+ CharGenCommon.jumpTo("appearance")
+ return
+
+def CancelPress():
+ GUICommon.CloseOtherWindow(None)
+ GemRB.SetNextScript(GemRB.GetToken("NextScript"))
+ return
diff --git a/gemrb/GUIScripts/bg1/LoadScreen.py b/gemrb/GUIScripts/bg1/LoadScreen.py
new file mode 100644
index 0000000..2ccb0b7
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/LoadScreen.py
@@ -0,0 +1,58 @@
+# -*-python-*-
+# 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.
+#
+
+# LoadScreen.py - display Loading screen
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+
+LoadScreen = None
+
+def SetLoadScreen ():
+ return
+
+def StartLoadScreen ():
+ global LoadScreen
+
+ GemRB.LoadWindowPack ("guils", 640, 480)
+ LoadScreen = GemRB.LoadWindow (0)
+ LoadScreen.SetFrame ()
+ Middle = LoadScreen.GetControl (4)
+ #LoadPic = GemRB.GetGameString (STR_LOADMOS)
+ #if LoadPic=="":
+ LoadPic = "GTRSK00"+str(GemRB.Roll(1,5,0))
+ Middle.SetMOS (LoadPic)
+ Bar = LoadScreen.GetControl (0)
+ Progress = 0
+ GemRB.SetVar ("Progress", Progress)
+ Bar.SetVarAssoc ("Progress", Progress)
+ Bar.SetEvent (IE_GUI_PROGRESS_END_REACHED, EndLoadScreen)
+ Skull = LoadScreen.GetControl (3)
+ Skull.SetMOS ("GTRBPSK")
+ LoadScreen.SetVisible (WINDOW_VISIBLE)
+ return
+
+def EndLoadScreen ():
+ Skull = LoadScreen.GetControl (3)
+ Skull.SetMOS ("GTRBPSK2")
+ LoadScreen.SetVisible (WINDOW_VISIBLE)
+ LoadScreen.Unload()
+ return
diff --git a/gemrb/GUIScripts/bg1/Makefile.am b/gemrb/GUIScripts/bg1/Makefile.am
new file mode 100644
index 0000000..b94a836
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/Makefile.am
@@ -0,0 +1,4 @@
+bg1script_DATA = *.py
+bg1scriptdir = $(moddir)/GUIScripts/bg1/
+EXTRA_DIST = *.py
+MOSTLYCLEANFILES = *.pyc
diff --git a/gemrb/GUIScripts/bg1/MessageWindow.py b/gemrb/GUIScripts/bg1/MessageWindow.py
new file mode 100644
index 0000000..59c4a89
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/MessageWindow.py
@@ -0,0 +1,149 @@
+# -*-python-*-
+# 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.
+#
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+import CommonWindow
+import GUIClasses
+from GUIDefines import *
+
+MessageWindow = 0
+PortraitWindow = 0
+OptionsWindow = 0
+ExpandButton = 0
+ContractButton = 0
+TMessageTA = 0 # for dialog code
+
+def OnLoad():
+ global PortraitWindow, OptionsWindow
+
+ GemRB.GameSetPartySize(PARTY_SIZE)
+ GemRB.GameSetProtagonistMode(1)
+ GemRB.LoadWindowPack(GUICommon.GetWindowPack())
+
+ GUICommonWindows.PortraitWindow = None
+ GUICommonWindows.ActionsWindow = None
+ GUICommonWindows.OptionsWindow = None
+
+ ActionsWindow = GemRB.LoadWindow(3)
+ OptionsWindow = GemRB.LoadWindow(0)
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow(1)
+
+ GemRB.SetVar("PortraitWindow", PortraitWindow.ID)
+ GemRB.SetVar("ActionsWindow", ActionsWindow.ID)
+ GemRB.SetVar("OptionsWindow", OptionsWindow.ID)
+ GemRB.SetVar("TopWindow", -1)
+ GemRB.SetVar("OtherWindow", -1)
+ GemRB.SetVar("FloatWindow", -1)
+ GemRB.SetVar("PortraitPosition", 2) #Right
+ GemRB.SetVar("ActionsPosition", 4) #BottomAdded
+ GemRB.SetVar("OptionsPosition", 0) #Left
+ GemRB.SetVar("MessagePosition", 4) #BottomAdded
+ GemRB.SetVar("OtherPosition", 5) #Inactivating
+ GemRB.SetVar("TopPosition", 5) #Inactivating
+
+ GUICommonWindows.OpenActionsWindowControls (ActionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 1, None)
+
+ UpdateControlStatus()
+
+def ScrollUp ():
+ TMessageWindow = GemRB.GetVar("MessageWindow")
+ TMessageTA = GUIClasses.GTextArea(TMessageWindow,GemRB.GetVar("MessageTextArea"))
+ TMessageTA.Scroll(-1)
+
+def ScrollDown ():
+ TMessageWindow = GemRB.GetVar("MessageWindow")
+ TMessageTA = GUIClasses.GTextArea(TMessageWindow,GemRB.GetVar("MessageTextArea"))
+ TMessageTA.Scroll(1)
+
+def UpdateControlStatus():
+ global MessageWindow, ExpandButton, ContractButton, TMessageTA
+
+ TMessageWindow = 0
+ TMessageTA = 0
+ GSFlags = GemRB.GetMessageWindowSize()
+ Expand = GSFlags&GS_DIALOGMASK
+ Override = GSFlags&GS_DIALOG
+ GSFlags = GSFlags-Expand
+
+ #a dialogue is running, setting messagewindow size to maximum
+ if Override:
+ Expand = GS_LARGEDIALOG
+
+ MessageWindow = GemRB.GetVar("MessageWindow")
+
+ GemRB.LoadWindowPack(GUICommon.GetWindowPack())
+
+ if Expand == GS_MEDIUMDIALOG:
+ TMessageWindow = GemRB.LoadWindow(12)
+ TMessageTA = TMessageWindow.GetControl(1)
+ ExpandButton = TMessageWindow.GetControl(0)
+ ExpandButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnIncreaseSize)
+ ContractButton = TMessageWindow.GetControl(3)
+ ContractButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnDecreaseSize)
+
+ elif Expand == GS_LARGEDIALOG:
+ TMessageWindow = GemRB.LoadWindow(7)
+ TMessageTA = TMessageWindow.GetControl(1)
+ ContractButton = TMessageWindow.GetControl(0)
+ ContractButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnDecreaseSize)
+
+ else:
+ TMessageWindow = GemRB.LoadWindow(4)
+ TMessageTA = TMessageWindow.GetControl(3)
+ ExpandButton = TMessageWindow.GetControl(2)
+ ExpandButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnIncreaseSize)
+ #up button (instead of scrollbar)
+ Button = TMessageWindow.GetControl(0)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ScrollUp)
+ #down button (instead of scrollbar)
+ Button = TMessageWindow.GetControl(1)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ScrollDown)
+
+ TMessageTA.SetFlags(IE_GUI_TEXTAREA_AUTOSCROLL)
+ TMessageTA.SetHistory(100)
+
+ hideflag = GemRB.HideGUI()
+ MessageTA = GUIClasses.GTextArea(MessageWindow,GemRB.GetVar("MessageTextArea"))
+ if MessageWindow>0 and MessageWindow!=TMessageWindow.ID:
+ MessageTA.MoveText(TMessageTA)
+ GUIClasses.GWindow(MessageWindow).Unload()
+
+ GemRB.SetVar("MessageWindow", TMessageWindow.ID)
+ GemRB.SetVar("MessageTextArea", TMessageTA.ID)
+ if Override:
+ TMessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ else:
+ GUICommon.GameControl.SetStatus(IE_GUI_CONTROL_FOCUSED)
+
+ if GSFlags & GS_OPTIONPANE:
+ GemRB.SetVar("OptionsWindow", -1)
+ else:
+ GemRB.SetVar("OptionsWindow", OptionsWindow.ID)
+
+ if GSFlags & GS_PORTRAITPANE:
+ GemRB.SetVar("PortraitWindow", -1)
+ else:
+ GemRB.SetVar("PortraitWindow", PortraitWindow.ID)
+
+ if hideflag:
+ GemRB.UnhideGUI()
+
diff --git a/gemrb/GUIScripts/bg1/QuitGame.py b/gemrb/GUIScripts/bg1/QuitGame.py
new file mode 100644
index 0000000..0c3e95d
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/QuitGame.py
@@ -0,0 +1,29 @@
+# -*-python-*-
+# 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.
+#
+
+# QuitGame.py - display EndGame sequence
+
+###################################################
+
+import GemRB
+
+def OnLoad ():
+ GemRB.HideGUI ()
+ GemRB.QuitGame ()
+ GemRB.SetNextScript("Start")
diff --git a/gemrb/GUIScripts/bg1/Start.py b/gemrb/GUIScripts/bg1/Start.py
new file mode 100644
index 0000000..ecdf9c3
--- /dev/null
+++ b/gemrb/GUIScripts/bg1/Start.py
@@ -0,0 +1,186 @@
+# 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.
+#
+#
+import GemRB
+from GUIDefines import *
+
+StartWindow = 0
+QuitWindow = 0
+ExitButton = 0
+SinglePlayerButton = 0
+MultiPlayerButton = 0
+MoviesButton = 0
+
+def OnLoad():
+ global StartWindow, QuitWindow
+ global ExitButton, MultiPlayerButton, MoviesButton, SinglePlayerButton
+
+ skip_videos = GemRB.GetVar ("SkipIntroVideos")
+ if not skip_videos:
+ GemRB.PlayMovie ('BG4LOGO',1)
+ GemRB.PlayMovie ('TSRLOGO',1)
+ GemRB.PlayMovie ('BILOGO',1)
+ GemRB.PlayMovie ('INFELOGO',1)
+ GemRB.PlayMovie ('INTRO',1)
+ GemRB.SetVar ("SkipIntroVideos", 1)
+
+ GemRB.LoadWindowPack("START", 640, 480)
+
+ #quit subwindow
+ QuitWindow = GemRB.LoadWindow(3)
+ QuitTextArea = QuitWindow.GetControl(0)
+ CancelButton = QuitWindow.GetControl(2)
+ ConfirmButton = QuitWindow.GetControl(1)
+ QuitTextArea.SetText(19532)
+ CancelButton.SetText(13727)
+ ConfirmButton.SetText(15417)
+ ConfirmButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitConfirmed)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitCancelled)
+ ConfirmButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ #main window
+ StartWindow = GemRB.LoadWindow(0)
+ SinglePlayerButton = StartWindow.GetControl(0)
+ MultiPlayerButton = StartWindow.GetControl(1)
+ MoviesButton = StartWindow.GetControl(2)
+ ExitButton = StartWindow.GetControl(3)
+
+ BackToMain()
+
+ GemRB.LoadMusicPL("Theme.mus")
+ return
+
+def SinglePlayerPress():
+
+ SinglePlayerButton.SetText(13728)
+ MultiPlayerButton.SetText(13729)
+ MoviesButton.SetText(24110)
+ ExitButton.SetText(15416)
+ MultiPlayerButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, LoadSingle)
+ SinglePlayerButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NewSingle)
+ MoviesButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MissionPack)
+ ExitButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackToMain)
+ ExitButton.SetFlags(IE_GUI_BUTTON_CANCEL, OP_OR)
+ if GemRB.GetString(24110) == "": # TODO: better way to detect lack of mission pack?
+ MoviesButton.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ return
+
+def MultiPlayerPress():
+
+ SinglePlayerButton.SetText(11825)
+ MultiPlayerButton.SetText(20642)
+ MoviesButton.SetText(15416)
+ ExitButton.SetText("")
+ SinglePlayerButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, PregenPress)
+ MultiPlayerButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ConnectPress)
+ MoviesButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackToMain)
+ MoviesButton.SetFlags(IE_GUI_BUTTON_CANCEL, OP_OR)
+ ExitButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, None)
+ ExitButton.SetStatus(IE_GUI_BUTTON_DISABLED)
+ ExitButton.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ return
+
+def ConnectPress():
+#well...
+ return
+
+def PregenPress():
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ GemRB.SetVar("PlayMode",0) #loadgame needs this hack
+ GemRB.SetVar("Slot",1)
+ GemRB.LoadGame(None)
+ GemRB.SetVar("PlayMode",-1)
+ GemRB.SetNextScript("CharGen")
+ return
+
+def LoadSingle():
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ GemRB.SetVar("PlayMode",0)
+ GemRB.SetNextScript("GUILOAD")
+ return
+
+def MissionPack():
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ GemRB.SetVar("PlayMode",3) #use mpsave for saved games
+ GemRB.SetNextScript("GUILOAD")
+ return
+
+def NewSingle():
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ GemRB.SetVar("PlayMode",0)
+ GemRB.SetVar("Slot",1)
+ GemRB.LoadGame(None)
+ GemRB.SetNextScript("CharGen") #temporarily
+ return
+
+def ExitPress():
+ StartWindow.SetVisible(WINDOW_INVISIBLE)
+ QuitWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ExitConfirmed():
+ GemRB.Quit()
+ return
+
+def MoviesPress():
+#apparently the order is important
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ GemRB.SetNextScript("GUIMOVIE")
+ return
+
+def ExitCancelled():
+ QuitWindow.SetVisible(WINDOW_INVISIBLE)
+ StartWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def BackToMain():
+ SinglePlayerButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ MultiPlayerButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ MoviesButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ ExitButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ SinglePlayerButton.SetText(15413)
+ MultiPlayerButton.SetText(15414)
+ MoviesButton.SetText(15415)
+ ExitButton.SetText(15417)
+ SinglePlayerButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SinglePlayerPress)
+ MultiPlayerButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MultiPlayerPress)
+ MoviesButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MoviesPress)
+ ExitButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitPress)
+ MoviesButton.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ ExitButton.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ ExitButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ QuitWindow.SetVisible(WINDOW_INVISIBLE)
+ StartWindow.SetVisible(WINDOW_VISIBLE)
+ return
diff --git a/gemrb/GUIScripts/bg2/Autodetect.py b/gemrb/GUIScripts/bg2/Autodetect.py
new file mode 100644
index 0000000..70d0541
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/Autodetect.py
@@ -0,0 +1,45 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2010 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.
+#
+
+import GemRB
+from ie_restype import *
+from AutodetectCommon import CheckFiles
+
+files = (
+ ("START", "CHU", RES_CHU),
+ ("STARTPOS", "2DA", RES_2DA),
+ ("STARTARE", "2DA", RES_2DA),
+
+ ("KITLIST", "2DA", RES_2DA),
+ ("SONGLIST", "2DA", RES_2DA),
+)
+
+# using a dud resource type, since we rely on the manual GamePath searching
+# in AutodetectCommon
+demo_files = (
+ ("benevent", "TTF", 1),
+ ("feine22", "TTF", 1),
+)
+
+GemRB.BG2Demo = False
+if CheckFiles(files):
+ GemRB.AddGameTypeHint ("bg2", 90)
+ if CheckFiles(demo_files):
+ GemRB.BG2Demo = True
diff --git a/gemrb/GUIScripts/bg2/CMakeLists.txt b/gemrb/GUIScripts/bg2/CMakeLists.txt
new file mode 100644
index 0000000..577d1c2
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CMakeLists.txt
@@ -0,0 +1,3 @@
+FILE( GLOB FILES_TO_INSTALL *.py )
+
+INSTALL( FILES ${FILES_TO_INSTALL} DESTINATION ${DATA_DIR}/GUIScripts/bg2 )
diff --git a/gemrb/GUIScripts/bg2/CharGen.py b/gemrb/GUIScripts/bg2/CharGen.py
new file mode 100644
index 0000000..0059419
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGen.py
@@ -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.
+#
+#
+#character generation (GUICG 0)
+import GemRB
+import CharGenCommon
+import GUICommonWindows
+
+def OnLoad():
+ GemRB.SetVar("Gender",0) #gender
+ GemRB.SetVar("Race",0) #race
+ GemRB.SetVar("Class",0) #class
+ GemRB.SetVar("Class Kit",0) #class
+ GemRB.SetVar("Alignment",-1) #alignment
+
+ GUICommonWindows.PortraitWindow = None
+
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.CreatePlayer ("charbase", MyChar | 0x8000, 0, 11 ) # 11 = force bg2
+ CharGenCommon.DisplayOverview (1)
+
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGen2.py b/gemrb/GUIScripts/bg2/CharGen2.py
new file mode 100644
index 0000000..8df6fb4
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGen2.py
@@ -0,0 +1,31 @@
+# -*-python-*-
+# 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.
+#
+# character generation - gender; next race (CharGen2)
+import GemRB
+import CharGenCommon
+from ie_stats import IE_RACE
+
+def OnLoad():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetVar ("Race",0) #race
+ GemRB.SetPlayerStat (MyChar, IE_RACE, 0)
+
+ CharGenCommon.DisplayOverview (2)
+
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGen3.py b/gemrb/GUIScripts/bg2/CharGen3.py
new file mode 100644
index 0000000..6c72d73
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGen3.py
@@ -0,0 +1,38 @@
+# -*-python-*-
+# 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.
+#
+# character generation - race; next class/kit (CharGen3)
+import GemRB
+import CharGenCommon
+from ie_stats import *
+
+def OnLoad():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetVar("Class",0) #class
+ GemRB.SetVar("Class Kit",0) #class kit
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, 0)
+ GemRB.SetPlayerStat (MyChar, IE_KIT, 0)
+
+ #reset all the levels (assigned in CharGen4)
+ GemRB.SetPlayerStat (MyChar, IE_LEVEL, 0)
+ GemRB.SetPlayerStat (MyChar, IE_LEVEL2, 0)
+ GemRB.SetPlayerStat (MyChar, IE_LEVEL3, 0)
+
+ CharGenCommon.DisplayOverview (3)
+
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGen4.py b/gemrb/GUIScripts/bg2/CharGen4.py
new file mode 100644
index 0000000..67a3d94
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGen4.py
@@ -0,0 +1,32 @@
+# -*-python-*-
+# 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.
+#
+# character generation - classes+kits; next alignment/reputation(CharGen4.py)
+import GemRB
+import CharGenCommon
+from ie_stats import IE_ALIGNMENT, IE_REPUTATION
+
+def OnLoad():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetVar ("Alignment", -1) #alignment
+ GemRB.SetPlayerStat (MyChar, IE_ALIGNMENT, 0)
+ GemRB.SetPlayerStat (MyChar, IE_REPUTATION, 0)
+
+ CharGenCommon.DisplayOverview (4)
+
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGen5.py b/gemrb/GUIScripts/bg2/CharGen5.py
new file mode 100644
index 0000000..6c619c1
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGen5.py
@@ -0,0 +1,40 @@
+# -*-python-*-
+# 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.
+#
+#character generation - alignment; next abilities (GUICG 0)
+import GemRB
+import CharGenCommon
+from ie_stats import IE_STREXTRA
+
+def OnLoad():
+ MyChar = GemRB.GetVar ("Slot")
+ AbilityTable = GemRB.LoadTable ("ability")
+ AbilityCount = AbilityTable.GetRowCount ()
+
+ # set all our abilites to zero
+ GemRB.SetVar ("Ability -1", 0)
+ GemRB.SetVar ("StrExtra", 0)
+ GemRB.SetPlayerStat (MyChar, IE_STREXTRA, 0)
+ for i in range(AbilityCount):
+ GemRB.SetVar ("Ability "+str(i), 0)
+ StatID = AbilityTable.GetValue (i, 3)
+ GemRB.SetPlayerStat (MyChar, StatID, 0)
+
+ CharGenCommon.DisplayOverview (5)
+
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGen6.py b/gemrb/GUIScripts/bg2/CharGen6.py
new file mode 100644
index 0000000..bab6376
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGen6.py
@@ -0,0 +1,98 @@
+# -*-python-*-
+# 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.
+#
+# character generation - ability; next skills/profs/spells (CharGen6)
+import GemRB
+import GUICommon
+import CommonTables
+import CharGenCommon
+import LUSkillsSelection
+import LUProfsSelection
+from GUIDefines import IE_SPELL_TYPE_PRIEST, IE_SPELL_TYPE_WIZARD
+from ie_stats import *
+
+def OnLoad():
+ MyChar = GemRB.GetVar ("Slot")
+
+ # nullify our thieving skills
+ LUSkillsSelection.SkillsNullify ()
+ LUSkillsSelection.SkillsSave (MyChar)
+
+ # nullify our proficiencies
+ LUProfsSelection.ProfsNullify ()
+
+ # nully other variables
+ GemRB.SetVar ("HatedRace", 0)
+
+ # save our previous stats:
+ # abilities
+ AbilityTable = GemRB.LoadTable ("ability")
+ AbilityCount = AbilityTable.GetRowCount ()
+
+ # print our diagnostic as we loop (so as not to duplicate)
+ print "CharGen6 output:"
+
+ #remove all known spells and nullify the memorizable counts
+ GUICommon.RemoveKnownSpells (MyChar, IE_SPELL_TYPE_WIZARD, 1,9, 1)
+ GUICommon.RemoveKnownSpells (MyChar, IE_SPELL_TYPE_PRIEST, 1,7, 1)
+
+ # learn divine spells if appropriate
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ TableName = CommonTables.ClassSkills.GetValue (Class, 1, 0) # cleric spells
+
+ if TableName == "*": # only druid spells or no spells at all
+ TableName = CommonTables.ClassSkills.GetValue (Class, 0, 0)
+ ClassFlag = 0x8000
+ elif CommonTables.ClassSkills.GetValue (Class, 0, 0) != "*": # cleric and druid spells
+ ClassFlag = 0
+ else: # only cleric spells
+ ClassFlag = 0x4000
+
+ if TableName != "*":
+ #figure out which class casts spells and use the level of the class
+ #to setup the priest spells
+ Levels = [GemRB.GetPlayerStat (MyChar, IE_LEVEL), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL2), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL3)]
+ index = 0
+ IsMulti = GUICommon.IsMultiClassed (MyChar, 1)
+ if IsMulti[0]>1:
+ #loop through each multiclass until we come across the class that gives
+ #divine spells; because clerics have a lower id than rangers, they should
+ #be looked at first in Cleric/Ranger multi's, which is correct
+ foundindex = 0
+ for i in range (IsMulti[0]):
+ ClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, IsMulti[i+1]) )
+ for table in "CLERICSPELL", "DRUIDSPELL":
+ if CommonTables.ClassSkills.GetValue (ClassName, table) != "*":
+ index = i
+ foundindex = 1
+ break
+ if foundindex:
+ break
+
+ #set our memorizable counts
+ GUICommon.SetupSpellLevels (MyChar, TableName, IE_SPELL_TYPE_PRIEST, Levels[index])
+
+ #learn all our priest spells up to the level we can learn
+ for level in range (8):
+ if GemRB.GetMemorizableSpellsCount (MyChar, IE_SPELL_TYPE_PRIEST, level, 0) <= 0:
+ GUICommon.LearnPriestSpells (MyChar, level, ClassFlag)
+ break
+ CharGenCommon.DisplayOverview (6)
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGen7.py b/gemrb/GUIScripts/bg2/CharGen7.py
new file mode 100644
index 0000000..d2ef7c4
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGen7.py
@@ -0,0 +1,24 @@
+# -*-python-*-
+# 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.
+#
+#character generation - skills/profs/spells; next apearance/sound (CharGen7)
+import CharGenCommon
+
+def OnLoad():
+ CharGenCommon.DisplayOverview (7)
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGen8.py b/gemrb/GUIScripts/bg2/CharGen8.py
new file mode 100644
index 0000000..b67dc2e
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGen8.py
@@ -0,0 +1,25 @@
+# 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.
+#
+#
+#character generation (GUICG 0)
+import CharGenCommon
+
+def OnLoad():
+ CharGenCommon.DisplayOverview (8)
+
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGen9.py b/gemrb/GUIScripts/bg2/CharGen9.py
new file mode 100644
index 0000000..a59568f
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGen9.py
@@ -0,0 +1,25 @@
+# -*-python-*-
+# 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.
+#
+# character generation (CharGen9.py)
+import CharGenCommon
+
+def OnLoad():
+ CharGenCommon.DisplayOverview (9)
+
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGenCommon.py b/gemrb/GUIScripts/bg2/CharGenCommon.py
new file mode 100644
index 0000000..dbf1d3b
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGenCommon.py
@@ -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.
+#
+#
+# common character generation display code
+import GemRB
+from ie_stats import *
+from GUIDefines import *
+import GUICommon
+import CommonTables
+from ie_restype import RES_BMP
+
+CharGenWindow = 0
+TextAreaControl = 0
+PortraitName = ""
+
+def DisplayOverview(step):
+ """Sets up the primary character generation window."""
+
+ global CharGenWindow, TextAreaControl, PortraitName
+
+ GemRB.LoadWindowPack ("GUICG", 640, 480)
+ CharGenWindow = GemRB.LoadWindow (0)
+ CharGenWindow.SetFrame ()
+ GemRB.SetVar ("Step", step)
+
+ ###
+ # Buttons
+ ###
+ PortraitButton = CharGenWindow.GetControl (12)
+ PortraitButton.SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitName = GemRB.GetToken ("LargePortrait")
+ if PortraitName != "":
+ if GemRB.HasResource (PortraitName, RES_BMP, 1) or GemRB.HasResource ("NOPORTMD", RES_BMP, 1):
+ PortraitButton.SetPicture (PortraitName, "NOPORTMD")
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ GenderButton = CharGenWindow.GetControl (0)
+ GenderButton.SetText (11956)
+ SetButtonStateFromStep ("GenderButton", GenderButton, step)
+
+ RaceButton = CharGenWindow.GetControl (1)
+ RaceButton.SetText (11957)
+ SetButtonStateFromStep ("RaceButton", RaceButton, step)
+
+ ClassButton = CharGenWindow.GetControl (2)
+ ClassButton.SetText (11959)
+ SetButtonStateFromStep ("ClassButton", ClassButton, step)
+
+ AlignmentButton = CharGenWindow.GetControl (3)
+ AlignmentButton.SetText (11958)
+ SetButtonStateFromStep ("AlignmentButton", AlignmentButton, step)
+
+ AbilitiesButton = CharGenWindow.GetControl (4)
+ AbilitiesButton.SetText (11960)
+ SetButtonStateFromStep ("AbilitiesButton", AbilitiesButton, step)
+
+ SkillButton = CharGenWindow.GetControl (5)
+ SkillButton.SetText (17372)
+ SetButtonStateFromStep ("SkillButton", SkillButton, step)
+
+ AppearanceButton = CharGenWindow.GetControl (6)
+ AppearanceButton.SetText (11961)
+ SetButtonStateFromStep ("AppearanceButton", AppearanceButton, step)
+
+ NameButton = CharGenWindow.GetControl (7)
+ NameButton.SetText (11963)
+ SetButtonStateFromStep ("NameButton", NameButton, step)
+
+ BackButton = CharGenWindow.GetControl (11)
+ BackButton.SetText (15416)
+ BackButton.SetState (IE_GUI_BUTTON_ENABLED)
+ BackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BackPress)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ AcceptButton = CharGenWindow.GetControl (8)
+ playmode = GemRB.GetVar ("PlayMode")
+ if playmode>=0:
+ AcceptButton.SetText (11962)
+ else:
+ AcceptButton.SetText (13956)
+ SetButtonStateFromStep ("AcceptButton", AcceptButton, step)
+ #AcceptButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ AcceptButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, NextPress)
+
+ ScrollBar = CharGenWindow.GetControl (10)
+ ScrollBar.SetDefaultScrollBar ()
+
+ ImportButton = CharGenWindow.GetControl (13)
+ ImportButton.SetText (13955)
+ ImportButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ImportButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ImportPress)
+
+ CancelButton = CharGenWindow.GetControl (15)
+ if step == 1:
+ CancelButton.SetText (13727) # Cancel
+ else:
+ CancelButton.SetText (8159) # Start over
+ CancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+
+ BiographyButton = CharGenWindow.GetControl (16)
+ BiographyButton.SetText (18003)
+ BiographyButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BiographyPress)
+ if step == 9:
+ BiographyButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ BiographyButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ###
+ # Stat overview
+ ###
+ AlignmentTable = GemRB.LoadTable ("aligns")
+ AbilityTable = GemRB.LoadTable ("ability")
+
+ MyChar = GemRB.GetVar ("Slot")
+
+ for part in range(1, step+1):
+ if part == 1:
+ TextAreaControl= CharGenWindow.GetControl (9)
+ if step == 1:
+ TextAreaControl.SetText ("[capital=0]" + GemRB.GetString(16575))
+ elif step == 9:
+ TextAreaControl.SetText ("[capital=0]" + GemRB.GetString(1047))
+ else:
+ TextAreaControl.SetText ("[capital=0]" + GemRB.GetString(12135))
+ elif part == 2:
+ TextAreaControl.Append (": ")
+ if step == 9:
+ TextAreaControl.Append (GemRB.GetToken ("CHARNAME") )
+ TextAreaControl.Append (12135, -1)
+ TextAreaControl.Append (": ")
+ if GemRB.GetPlayerStat (MyChar, IE_SEX) == 1:
+ TextAreaControl.Append (1050)
+ else:
+ TextAreaControl.Append (1051)
+ elif part == 3:
+ TextAreaControl.Append (1048, -1) # new line
+ TextAreaControl.Append (": ")
+ stat = GemRB.GetPlayerStat(MyChar, IE_RACE)
+ v = CommonTables.Races.FindValue (3, stat)
+ TextAreaControl.Append (CommonTables.Races.GetValue (v,2) )
+ elif part == 4:
+ TextAreaControl.Append (12136, -1)
+ TextAreaControl.Append (": ")
+ ClassTitle = GUICommon.GetActorClassTitle (MyChar)
+ TextAreaControl.Append (ClassTitle)
+ elif part == 5:
+ TextAreaControl.Append (1049, -1)
+ TextAreaControl.Append (": ")
+ stat = GemRB.GetPlayerStat (MyChar, IE_ALIGNMENT)
+ v = AlignmentTable.FindValue (3, stat)
+ TextAreaControl.Append (AlignmentTable.GetValue (v,2))
+ elif part == 6:
+ TextAreaControl.Append ("\n")
+ ClassID = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, ClassID)
+ hasextra = CommonTables.Classes.GetValue (Class, 3)=="SAVEWAR"
+ strextra = GemRB.GetPlayerStat (MyChar, IE_STREXTRA)
+ for i in range(6):
+ v = AbilityTable.GetValue (i, 2)
+ TextAreaControl.Append (v, -1)
+ StatID = AbilityTable.GetValue (i, 3)
+ stat = GemRB.GetPlayerStat (MyChar, StatID)
+ if (i == 0) and hasextra and (stat==18):
+ TextAreaControl.Append (": " + str(stat) +"/"+str(strextra) )
+ else:
+ TextAreaControl.Append (": " + str(stat) )
+ elif part == 7:
+ TextAreaControl.Append ("\n\n")
+ # thieving and other skills
+ info = ""
+ SkillTable = GemRB.LoadTable ("skills")
+ ClassID = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, ClassID)
+ ClassName = CommonTables.Classes.GetRowName (Class)
+ RangerSkills = CommonTables.ClassSkills.GetValue (ClassName, "RANGERSKILL")
+ BardSkills = CommonTables.ClassSkills.GetValue (ClassName, "BARDSKILL")
+ KitName = GUICommon.GetKitIndex (MyChar)
+ if KitName == 0:
+ KitName = ClassName
+ else:
+ KitName = CommonTables.KitList.GetValue (KitName, 0)
+
+ if SkillTable.GetValue ("RATE", KitName) != -1:
+ for skill in range(SkillTable.GetRowCount () - 2):
+ name = GemRB.GetString (SkillTable.GetValue (skill+2, 1))
+ available = SkillTable.GetValue (SkillTable.GetRowName (skill+2), KitName)
+ statID = SkillTable.GetValue (skill+2, 2)
+ value = GemRB.GetPlayerStat (MyChar, statID, 1)
+ if value >= 0 and available != -1:
+ info += name + ": " + str(value) + "\n"
+ elif BardSkills != "*" or RangerSkills != "*":
+ for skill in range(SkillTable.GetRowCount () - 2):
+ name = GemRB.GetString (SkillTable.GetValue (skill+2, 1))
+ StatID = SkillTable.GetValue (skill+2, 2)
+ value = GemRB.GetPlayerStat (MyChar, StatID, 1)
+ if value > 0:
+ info += name + ": " + str(value) + "\n"
+ if info != "":
+ info = "\n" + info + "\n"
+ TextAreaControl.Append (8442)
+ TextAreaControl.Append (info)
+
+ # arcane spells
+ info = ""
+ for level in range(0, 9):
+ for j in range(0, GemRB.GetKnownSpellsCount (MyChar, IE_SPELL_TYPE_WIZARD, level) ):
+ Spell = GemRB.GetKnownSpell (MyChar, IE_SPELL_TYPE_WIZARD, level, j)
+ Spell = GemRB.GetSpell (Spell['SpellResRef'], 1)['SpellName']
+ info += GemRB.GetString (Spell) + "\n"
+ if info != "":
+ info = "\n" + info + "\n"
+ TextAreaControl.Append (11027)
+ TextAreaControl.Append (info)
+
+ # divine spells
+ info = ""
+ for level in range(0, 7):
+ for j in range(0, GemRB.GetKnownSpellsCount (MyChar, IE_SPELL_TYPE_PRIEST, level) ):
+ Spell = GemRB.GetKnownSpell (MyChar, IE_SPELL_TYPE_PRIEST, level, j)
+ Spell = GemRB.GetSpell (Spell['SpellResRef'], 1)['SpellName']
+ info += GemRB.GetString (Spell) + "\n"
+ if info != "":
+ info = "\n" + info + "\n"
+ TextAreaControl.Append (11028)
+ TextAreaControl.Append (info)
+
+ # racial enemy
+ info = ""
+ Race = GemRB.GetVar ("HatedRace")
+ if Race:
+ HateRaceTable = GemRB.LoadTable ("HATERACE")
+ Row = HateRaceTable.FindValue (1, Race)
+ info = GemRB.GetString (HateRaceTable.GetValue(Row, 0))
+ if info != "":
+ info = "\n" + info + "\n\n"
+ TextAreaControl.Append (15982)
+ TextAreaControl.Append (info)
+
+ # weapon proficiencies
+ TextAreaControl.Append (9466)
+ TextAreaControl.Append ("\n")
+ TmpTable=GemRB.LoadTable ("weapprof")
+ ProfCount = TmpTable.GetRowCount ()
+ #bg2 weapprof.2da contains the bg1 proficiencies too, skipping those
+ for i in range(ProfCount-8):
+ # 4294967296 overflows to -1 on some arches, so we use a smaller invalid strref
+ strref = TmpTable.GetValue (i+8, 1)
+ if strref == -1 or strref > 500000:
+ continue
+ Weapon = GemRB.GetString (strref)
+ StatID = TmpTable.GetValue (i+8, 0)
+ Value = GemRB.GetPlayerStat (MyChar, StatID )
+ if Value:
+ pluses = " "
+ for plus in range(0, Value):
+ pluses += "+"
+ TextAreaControl.Append (Weapon + pluses + "\n")
+
+ elif part == 8:
+ break
+
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def SetButtonStateFromStep (buttonName, button, step):
+ """Updates selectable buttons based upon current step."""
+
+ global CharGenWindow
+
+ state = IE_GUI_BUTTON_DISABLED
+ if buttonName == "GenderButton":
+ if step == 1:
+ state = IE_GUI_BUTTON_ENABLED
+ elif buttonName == "RaceButton":
+ if step == 2:
+ state = IE_GUI_BUTTON_ENABLED
+ elif buttonName == "ClassButton":
+ if step == 3:
+ state = IE_GUI_BUTTON_ENABLED
+ elif buttonName == "AlignmentButton":
+ if step == 4:
+ state = IE_GUI_BUTTON_ENABLED
+ elif buttonName == "AbilitiesButton":
+ if step == 5:
+ state = IE_GUI_BUTTON_ENABLED
+ elif buttonName == "SkillButton":
+ if step == 6:
+ state = IE_GUI_BUTTON_ENABLED
+ elif buttonName == "AppearanceButton":
+ if step == 7:
+ state = IE_GUI_BUTTON_ENABLED
+ elif buttonName == "NameButton":
+ if step == 8:
+ state = IE_GUI_BUTTON_ENABLED
+ elif buttonName == "AcceptButton":
+ if step == 9:
+ state = IE_GUI_BUTTON_ENABLED
+ button.SetState (state)
+
+ if state == IE_GUI_BUTTON_ENABLED:
+ button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, NextPress)
+ return
+
+def CancelPress():
+ """Revert back to the first step; if there, free the actor."""
+
+ global CharGenWindow
+ if CharGenWindow:
+ CharGenWindow.Unload ()
+
+ step = GemRB.GetVar ("Step")
+ if step == 1:
+ #free up the slot before exiting
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.CreatePlayer ("", MyChar | 0x8000 )
+ GemRB.SetNextScript ("Start")
+ else:
+ GemRB.SetNextScript ("CharGen")
+ return
+
+def ImportPress():
+ """Opens the character import window."""
+
+ global CharGenWindow
+ if CharGenWindow:
+ CharGenWindow.Unload ()
+
+ step = GemRB.GetVar ("Step")
+ # TODO: check why this is handled differently
+ if step == 1:
+ GemRB.SetNextScript("GUICG24")
+ else:
+ GemRB.SetToken ("NextScript", "CharGen9")
+ GemRB.SetNextScript ("ImportFile") #import
+ return
+
+def BackPress():
+ """Moves to the previous step."""
+
+ global CharGenWindow
+ if CharGenWindow:
+ CharGenWindow.Unload ()
+
+ step = GemRB.GetVar ("Step")
+ if step == 1:
+ GemRB.SetNextScript ("Start")
+ elif step == 2:
+ GemRB.SetNextScript ("CharGen")
+ else:
+ GemRB.SetNextScript ("CharGen" + str(step-1))
+ return
+
+def NextPress():
+ """Moves to the next step."""
+
+ global CharGenWindow
+ if CharGenWindow:
+ CharGenWindow.Unload ()
+
+ step = GemRB.GetVar ("Step")
+ if step == 1:
+ GemRB.SetNextScript ("GUICG1")
+ elif step == 2:
+ GemRB.SetNextScript ("GUICG8")
+ elif step == 6:
+ GemRB.SetNextScript ("GUICG15")
+ elif step == 7:
+ GemRB.SetNextScript ("GUICG13")
+ elif step == 8:
+ GemRB.SetNextScript ("GUICG5")
+ elif step == 9:
+ GemRB.SetNextScript ("CharGenEnd")
+ else: # 3, 4, 5
+ GemRB.SetNextScript ("GUICG" + str(step-1))
+ return
+
+def BiographyPress():
+ """Opens the biography window."""
+ if CharGenWindow:
+ CharGenWindow.Unload()
+ GemRB.SetNextScript("GUICG23") #biography
+ return
diff --git a/gemrb/GUIScripts/bg2/CharGenEnd.py b/gemrb/GUIScripts/bg2/CharGenEnd.py
new file mode 100644
index 0000000..65a4d72
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/CharGenEnd.py
@@ -0,0 +1,215 @@
+# -*-python-*-
+# 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.
+#
+# character generation end
+import GemRB
+import GUICommon
+import CommonTables
+import LUCommon
+from GUIDefines import *
+from ie_slots import *
+from ie_stats import *
+from ie_spells import *
+from ie_restype import RES_2DA
+
+def OnLoad():
+ # Lay on hands, turn undead and backstab multiplier get set by the core
+ # set my character up
+ MyChar = GemRB.GetVar ("Slot")
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ ClassName = CommonTables.Classes.GetRowName (ClassIndex)
+ IsMulti = GUICommon.IsMultiClassed (MyChar, 1)
+ Levels = [GemRB.GetPlayerStat (MyChar, IE_LEVEL), GemRB.GetPlayerStat (MyChar, IE_LEVEL2), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL3)]
+
+ # weapon proficiencies
+ # set the base number of attacks; effects will add the proficiency bonus
+ # 2 means 1 attack, because this is the number of attacks in 2 rounds
+ GemRB.SetPlayerStat (MyChar, IE_NUMBEROFATTACKS, 2)
+
+ #lore, thac0, hp, and saves
+ GemRB.SetPlayerStat (MyChar, IE_MAXHITPOINTS, 0)
+ GemRB.SetPlayerStat (MyChar, IE_HITPOINTS, 0)
+ LUCommon.SetupSavingThrows (MyChar)
+ LUCommon.SetupThaco (MyChar)
+ LUCommon.SetupLore (MyChar)
+ LUCommon.SetupHP (MyChar)
+
+ # mage spells
+ TableName = CommonTables.ClassSkills.GetValue (Class, 2, 0)
+ if TableName != "*":
+ index = 0
+ if IsMulti[0]>1:
+ #find out which class gets mage spells
+ for i in range (IsMulti[0]):
+ if CommonTables.ClassSkills.GetValue (IsMulti[i+1], 2, 0) != "*":
+ index = i
+ break
+ GUICommon.SetupSpellLevels(MyChar, TableName, IE_SPELL_TYPE_WIZARD, Levels[index])
+
+ # apply class/kit abilities
+ KitIndex = GUICommon.GetKitIndex (MyChar)
+ if IsMulti[0]>1:
+ #get the class abilites for each class
+ for i in range (IsMulti[0]):
+ TmpClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, IsMulti[i+1]) )
+ ABTable = CommonTables.ClassSkills.GetValue (TmpClassName, "ABILITIES")
+ if ABTable != "*" and GemRB.HasResource (ABTable, RES_2DA, 1):
+ GUICommon.AddClassAbilities (MyChar, ABTable, Levels[i], Levels[i])
+ else:
+ if KitIndex:
+ ABTable = CommonTables.KitList.GetValue (str(KitIndex), "ABILITIES")
+ else:
+ ABTable = CommonTables.ClassSkills.GetValue (ClassName, "ABILITIES")
+ if ABTable != "*" and GemRB.HasResource (ABTable, RES_2DA, 1):
+ GUICommon.AddClassAbilities (MyChar, ABTable, Levels[0], Levels[0])
+
+ # apply starting (alignment dictated) abilities
+ # pc, table, new level, level diff, alignment
+ AlignmentTable = GemRB.LoadTable ("aligns")
+ AlignmentAbbrev = AlignmentTable.FindValue (3, GemRB.GetPlayerStat (MyChar, IE_ALIGNMENT))
+ GUICommon.AddClassAbilities (MyChar, "abstart", 6,6, AlignmentAbbrev)
+
+ # setup starting gold (uses a roll dictated by class
+ TmpTable = GemRB.LoadTable ("strtgold")
+ temp = GemRB.Roll (TmpTable.GetValue (Class, 1),TmpTable.GetValue (Class, 0), TmpTable.GetValue (Class, 2))
+ GemRB.SetPlayerStat (MyChar, IE_GOLD, temp * TmpTable.GetValue (Class, 3))
+
+ # save the appearance
+ GUICommon.SetColorStat (MyChar, IE_HAIR_COLOR, GemRB.GetVar ("HairColor") )
+ GUICommon.SetColorStat (MyChar, IE_SKIN_COLOR, GemRB.GetVar ("SkinColor") )
+ GUICommon.SetColorStat (MyChar, IE_MAJOR_COLOR, GemRB.GetVar ("MajorColor") )
+ GUICommon.SetColorStat (MyChar, IE_MINOR_COLOR, GemRB.GetVar ("MinorColor") )
+ #GUICommon.SetColorStat (MyChar, IE_METAL_COLOR, 0x1B )
+ #GUICommon.SetColorStat (MyChar, IE_LEATHER_COLOR, 0x16 )
+ #GUICommon.SetColorStat (MyChar, IE_ARMOR_COLOR, 0x17 )
+ GemRB.SetPlayerStat (MyChar, IE_EA, 2 )
+
+ # save the name and starting xp (can level right away in game)
+ GemRB.SetPlayerName (MyChar, GemRB.GetToken ("CHARNAME"), 0)
+
+ # does all the rest
+ LargePortrait = GemRB.GetToken ("LargePortrait")
+ SmallPortrait = GemRB.GetToken ("SmallPortrait")
+ GemRB.FillPlayerInfo (MyChar, LargePortrait, SmallPortrait)
+
+ if GUICommon.GameIsTOB():
+ # add the starting inventory for tob
+ GiveEquipment(MyChar, ClassName, KitIndex)
+
+ playmode = GemRB.GetVar ("PlayMode")
+ if playmode >=0:
+ if GemRB.GetVar("GUIEnhancements"):
+ GemRB.SaveCharacter ( MyChar, "gembak" )
+ #LETS PLAY!!
+ GemRB.EnterGame()
+ GemRB.ExecuteString ("EquipMostDamagingMelee()", MyChar)
+ else:
+ #leaving multi player pregen
+ if CharGenWindow:
+ CharGenWindow.Unload ()
+ #when export is done, go to start
+ if GUICommon.HasTOB():
+ GemRB.SetToken ("NextScript","Start2")
+ else:
+ GemRB.SetToken ("NextScript","Start")
+ GemRB.SetNextScript ("ExportFile") #export
+ return
+
+def GiveEquipment(MyChar, ClassName, KitIndex):
+ # get the kit (or use class if no kit) to load the start table
+ if KitIndex == 0:
+ EquipmentColName = ClassName
+ # sorcerers are missing from the table, use the mage equipment instead
+ if EquipmentColName == "SORCERER":
+ EquipmentColName = "MAGE"
+ else:
+ EquipmentColName = CommonTables.KitList.GetValue (KitIndex, 0)
+
+ EquipmentTable = GemRB.LoadTable ("25stweap")
+
+ # a map of slots in the table to the real slots
+ # SLOT_BAG is invalid, so use the inventory (first occurence)
+ # SLOT_INVENTORY: use -1 instead, that's what CreateItem expects
+ RealSlots = [ SLOT_ARMOUR, SLOT_SHIELD, SLOT_HELM, -1, SLOT_RING, \
+ SLOT_RING, SLOT_CLOAK, SLOT_BOOT, SLOT_AMULET, SLOT_GLOVE, \
+ SLOT_BELT, SLOT_QUIVER, SLOT_QUIVER, SLOT_QUIVER, \
+ SLOT_ITEM, SLOT_ITEM, SLOT_ITEM, SLOT_WEAPON, SLOT_WEAPON, SLOT_WEAPON ]
+ inventory_exclusion = 0
+
+ #loop over rows - item slots
+ for slot in range(0, EquipmentTable.GetRowCount ()):
+ slotname = EquipmentTable.GetRowName (slot)
+ item_resref = EquipmentTable.GetValue (slotname, EquipmentColName)
+
+ # no item - go to next
+ if item_resref == "*":
+ continue
+
+ # the table has typos for kitted bard's armor
+ if item_resref == "LEATH14":
+ item_resref = "LEAT14"
+
+ # get empty slots of the requested type
+ realslot = GemRB.GetSlots (MyChar, RealSlots[slot], -1)
+ if RealSlots[slot] == SLOT_WEAPON:
+ # exclude the shield slot, so the offhand stays empty
+ realslot = realslot[1:]
+
+ if realslot == (): # fallback to the inventory
+ realslot = GemRB.GetSlots (MyChar, -1, -1)
+
+ if realslot == (): # this shouldn't happen!
+ print "Eeek! No free slots for", item_resref
+ continue
+
+ # if an item contains a comma, the rest of the value is the stack
+ if "," in item_resref:
+ item_resref = item_resref.split(",")
+ count = int(item_resref[1])
+ item_resref = item_resref[0]
+ else:
+ count = 0
+
+ targetslot = realslot[0]
+ SlotType = GemRB.GetSlotType (targetslot, MyChar)
+ i = 1
+ item = GemRB.GetItem (item_resref)
+
+ if inventory_exclusion & item['Exclusion']:
+ # oops, too many magic items to equip, so just dump it to the inventory
+ targetslot = GemRB.GetSlots (MyChar, -1, -1)[0]
+ SlotType = -1
+ else:
+ # if there are no free slots, CreateItem will create the item on the ground
+ while not GemRB.CanUseItemType (SlotType["Type"], item_resref, MyChar) \
+ and i < len(realslot):
+ targetslot = realslot[i]
+ SlotType = GemRB.GetSlotType (targetslot, MyChar)
+ i = i + 1
+
+ GemRB.CreateItem(MyChar, item_resref, targetslot, count, 0, 0)
+ GemRB.ChangeItemFlag (MyChar, targetslot, IE_INV_ITEM_IDENTIFIED, OP_OR)
+ inventory_exclusion |= item['Exclusion']
+
+ # grant the slayer change ability to the protagonist
+ if MyChar == 1:
+ GemRB.LearnSpell (MyChar, "SPIN822", LS_MEMO)
+ return
+
diff --git a/gemrb/GUIScripts/bg2/ExportFile.py b/gemrb/GUIScripts/bg2/ExportFile.py
new file mode 100644
index 0000000..0a63112
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/ExportFile.py
@@ -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.
+#
+#
+#character generation, import (GUICG24)
+import GemRB
+
+#import from a character sheet
+ImportWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global ImportWindow, TextAreaControl
+
+ GemRB.LoadWindowPack ("GUICG",640,480)
+ ImportWindow = GemRB.LoadWindow (21)
+
+ TextAreaControl = ImportWindow.GetControl (4)
+ TextAreaControl.SetText (10962)
+
+ TextAreaControl = ImportWindow.GetControl (2)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ TextAreaControl.GetCharacters ()
+
+ DoneButton = ImportWindow.GetControl (0)
+ DoneButton.SetText (2610)
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = ImportWindow.GetControl (1)
+ CancelButton.SetText (15416)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ TextAreaControl.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, SelectPress)
+ ImportWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def SelectPress ():
+ DoneButton = ImportWindow.GetControl (0)
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def DonePress ():
+ FileName = TextAreaControl.QueryText ()
+ Slot = GemRB.GetVar ("Slot")
+ GemRB.SaveCharacter (Slot, FileName)
+ if ImportWindow:
+ ImportWindow.Unload ()
+ GemRB.SetNextScript (GemRB.GetToken("NextScript"))
+ return
+
+def CancelPress ():
+ if ImportWindow:
+ ImportWindow.Unload ()
+ GemRB.SetNextScript (GemRB.GetToken("NextScript"))
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG1.py b/gemrb/GUIScripts/bg2/GUICG1.py
new file mode 100644
index 0000000..7721708
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG1.py
@@ -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.
+#
+#
+#character generation, gender (GUICG1)
+import GemRB
+from ie_stats import *
+from GUIDefines import *
+
+GenderWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+MyChar = 0
+
+def OnLoad():
+ global GenderWindow, TextAreaControl, DoneButton, MyChar
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ MyChar = GemRB.GetVar ("Slot")
+ #this hack will redraw the base CG window
+ GenderWindow = GemRB.LoadWindow(0)
+ PortraitButton = GenderWindow.GetControl(12)
+ PortraitButton.SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ ImportButton = GenderWindow.GetControl(13)
+ ImportButton.SetText(13955)
+ ImportButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = GenderWindow.GetControl(15)
+ CancelButton.SetText(13727)
+ CancelButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ BiographyButton = GenderWindow.GetControl(16)
+ BiographyButton.SetText(18003)
+ BiographyButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ GenderWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.DrawWindows()
+ if GenderWindow:
+ GenderWindow.Unload()
+ GenderWindow = GemRB.LoadWindow(1)
+
+ BackButton = GenderWindow.GetControl(6)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = GenderWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = GenderWindow.GetControl(5)
+ TextAreaControl.SetText(17236)
+
+ MaleButton = GenderWindow.GetControl(2)
+ MaleButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ FemaleButton = GenderWindow.GetControl(3)
+ FemaleButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ MaleButton.SetVarAssoc("Gender",1)
+ FemaleButton.SetVarAssoc("Gender",2)
+ MaleButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClickedMale)
+ FemaleButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClickedFemale)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ GenderWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ClickedMale():
+ TextAreaControl.SetText(13083)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def ClickedFemale():
+ TextAreaControl.SetText(13084)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def BackPress():
+ if GenderWindow:
+ GenderWindow.Unload()
+ GemRB.SetNextScript("CharGen")
+ GemRB.SetVar("Gender",0) #scrapping the gender value
+ return
+
+def NextPress():
+ if GenderWindow:
+ GenderWindow.Unload()
+
+ Gender = GemRB.GetVar ("Gender")
+ GemRB.SetPlayerStat (MyChar, IE_SEX, Gender)
+ GemRB.SetNextScript("GUICG12") #appearance
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG10.py b/gemrb/GUIScripts/bg2/GUICG10.py
new file mode 100644
index 0000000..130474a
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG10.py
@@ -0,0 +1,116 @@
+# 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.
+#
+#
+#character generation, multi-class (GUICG10)
+import GemRB
+import CommonTables
+from ie_stats import *
+from GUIDefines import *
+import GUICG2
+
+ClassWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+MyChar = 0
+
+def OnLoad():
+ global ClassWindow, TextAreaControl, DoneButton, MyChar
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ ClassWindow = GemRB.LoadWindow(10)
+
+ MyChar = GemRB.GetVar ("Slot")
+ ClassCount = CommonTables.Classes.GetRowCount()+1
+ Race = GemRB.GetPlayerStat (MyChar, IE_RACE)
+ RaceName = CommonTables.Races.GetRowName(CommonTables.Races.FindValue (3, Race) )
+
+ j=0
+ for i in range(1,ClassCount):
+ if CommonTables.Classes.GetValue(i-1,4)==0:
+ continue
+ if j>11:
+ Button = ClassWindow.GetControl(j+7)
+ else:
+ Button = ClassWindow.GetControl(j+2)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ j = j + 1
+ j=0
+ for i in range(1,ClassCount):
+ ClassName = CommonTables.Classes.GetRowName(i-1)
+ Allowed = CommonTables.Classes.GetValue(ClassName, RaceName)
+ if CommonTables.Classes.GetValue(i-1,4)==0:
+ continue
+ if j>11:
+ Button = ClassWindow.GetControl(j+7)
+ else:
+ Button = ClassWindow.GetControl(j+2)
+
+ t = CommonTables.Classes.GetValue(i-1, 0)
+ Button.SetText(t )
+ j=j+1
+ if Allowed ==0:
+ continue
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClassPress)
+ Button.SetVarAssoc("Class", i) #multiclass, actually
+
+ BackButton = ClassWindow.GetControl(14)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = ClassWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ TextAreaControl = ClassWindow.GetControl(12)
+ TextAreaControl.SetText(17244)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ ClassWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ClassPress():
+ GUICG2.SetClass()
+ Class = GemRB.GetVar("Class")-1
+ TextAreaControl.SetText(CommonTables.Classes.GetValue(Class,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def BackPress():
+ GemRB.SetVar("Class",0) # scrapping it
+ if ClassWindow:
+ ClassWindow.Unload()
+ GemRB.SetNextScript("GUICG2")
+ return
+
+def NextPress():
+ GUICG2.SetClass()
+ if ClassWindow:
+ ClassWindow.Unload()
+
+ # find the class from the class table
+ ClassIndex = GemRB.GetVar ("Class") - 1
+ Class = CommonTables.Classes.GetValue (ClassIndex, 5)
+ #protect against barbarians
+ ClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, Class) )
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, Class)
+
+ GemRB.SetNextScript("CharGen4") #alignment
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG12.py b/gemrb/GUIScripts/bg2/GUICG12.py
new file mode 100644
index 0000000..0929348
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG12.py
@@ -0,0 +1,242 @@
+# 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.
+#
+#
+#character generation, appearance (GUICG12)
+import GemRB
+from ie_restype import RES_BMP
+
+AppearanceWindow = 0
+CustomWindow = 0
+PortraitButton = 0
+PortraitsTable = 0
+LastPortrait = 0
+Gender = 0
+
+def SetPicture ():
+ global PortraitsTable, LastPortrait
+
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"L"
+ PortraitButton.SetPicture (PortraitName, "NOPORTLG")
+ return
+
+def OnLoad():
+ global AppearanceWindow, PortraitButton, PortraitsTable, LastPortrait
+ global Gender
+
+ Gender=GemRB.GetVar ("Gender")
+
+ GemRB.LoadWindowPack ("GUICG", 640, 480)
+ AppearanceWindow = GemRB.LoadWindow (11)
+
+ #Load the Portraits Table
+ PortraitsTable = GemRB.LoadTable ("PICTURES")
+ PortraitsStart = PortraitsTable.FindValue (0, 2)
+ FemaleCount = PortraitsTable.GetRowCount () - PortraitsStart + 1
+ if Gender == 2:
+ LastPortrait = GemRB.Roll (1, FemaleCount, PortraitsStart-1)
+ else:
+ LastPortrait = GemRB.Roll (1, PortraitsTable.GetRowCount()-FemaleCount, 0)
+
+ #this control doesn't exist in the demo version (it is unused, so lets just skip it)
+ if AppearanceWindow.HasControl (7):
+ TextAreaControl = AppearanceWindow.GetControl (7)
+ TextAreaControl.SetText ("")
+
+ PortraitButton = AppearanceWindow.GetControl (1)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ LeftButton = AppearanceWindow.GetControl (2)
+ RightButton = AppearanceWindow.GetControl (3)
+
+ BackButton = AppearanceWindow.GetControl (5)
+ BackButton.SetText (15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ CustomButton = AppearanceWindow.GetControl (6)
+ CustomButton.SetText (17545)
+
+ DoneButton = AppearanceWindow.GetControl (0)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ RightButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RightPress)
+ LeftButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LeftPress)
+ BackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BackPress)
+ CustomButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomPress)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, NextPress)
+
+ Flag = False
+ while True:
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ SetPicture ()
+ break
+ LastPortrait = LastPortrait + 1
+ if LastPortrait >= PortraitsTable.GetRowCount ():
+ LastPortrait = 0
+ if Flag:
+ SetPicture ()
+ break
+ Flag = True
+
+ AppearanceWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def RightPress():
+ global LastPortrait
+ while True:
+ LastPortrait = LastPortrait + 1
+ if LastPortrait >= PortraitsTable.GetRowCount ():
+ LastPortrait = 0
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ SetPicture ()
+ return
+
+def LeftPress():
+ global LastPortrait
+ while True:
+ LastPortrait = LastPortrait - 1
+ if LastPortrait < 0:
+ LastPortrait = PortraitsTable.GetRowCount ()-1
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ SetPicture ()
+ return
+
+def BackPress():
+ if AppearanceWindow:
+ AppearanceWindow.Unload ()
+ GemRB.SetNextScript ("GUICG1")
+ GemRB.SetVar ("Gender",0) #scrapping the gender value
+ return
+
+def CustomDone():
+ Window = CustomWindow
+
+ Portrait = PortraitList1.QueryText ()
+ GemRB.SetToken ("LargePortrait", Portrait)
+ Portrait = PortraitList2.QueryText ()
+ GemRB.SetToken ("SmallPortrait", Portrait)
+ if Window:
+ Window.Unload ()
+ if AppearanceWindow:
+ AppearanceWindow.Unload ()
+ GemRB.SetNextScript ("CharGen2")
+ return
+
+def CustomAbort():
+ if CustomWindow:
+ CustomWindow.Unload ()
+ return
+
+def LargeCustomPortrait():
+ Window = CustomWindow
+
+ Portrait = PortraitList1.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row1") == RowCount1:
+ return
+
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (6)
+ if Portrait=="":
+ Portrait = "NOPORTMD"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList2.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (0)
+ Button.SetPicture (Portrait, "NOPORTMD")
+ return
+
+def SmallCustomPortrait():
+ Window = CustomWindow
+
+ Portrait = PortraitList2.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row2") == RowCount2:
+ return
+
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (6)
+ if Portrait=="":
+ Portrait = "NOPORTSM"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList1.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (1)
+ Button.SetPicture (Portrait, "NOPORTSM")
+ return
+
+def CustomPress():
+ global PortraitList1, PortraitList2
+ global RowCount1, RowCount2
+ global CustomWindow
+
+ CustomWindow = Window = GemRB.LoadWindow (18)
+ PortraitList1 = Window.GetControl (2)
+ RowCount1 = PortraitList1.GetPortraits (0)
+ PortraitList1.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, LargeCustomPortrait)
+ GemRB.SetVar ("Row1", RowCount1)
+ PortraitList1.SetVarAssoc ("Row1",RowCount1)
+
+ PortraitList2 = Window.GetControl (4)
+ RowCount2 = PortraitList2.GetPortraits (1)
+ PortraitList2.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, SmallCustomPortrait)
+ GemRB.SetVar ("Row2", RowCount2)
+ PortraitList2.SetVarAssoc ("Row2",RowCount2)
+
+ Button = Window.GetControl (6)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomDone)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Button = Window.GetControl (7)
+ Button.SetText (15416)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomAbort)
+
+ Button = Window.GetControl (0)
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"M"
+ if GemRB.HasResource (PortraitName, RES_BMP, 1) or GemRB.HasResource ("NOPORTMD", RES_BMP, 1):
+ Button.SetPicture (PortraitName, "NOPORTMD")
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Button = Window.GetControl (1)
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"S"
+ Button.SetPicture (PortraitName, "NOPORTSM")
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Window.ShowModal (MODAL_SHADOW_NONE)
+ return
+
+def NextPress():
+ if AppearanceWindow:
+ AppearanceWindow.Unload ()
+ PortraitTable = GemRB.LoadTable ("pictures")
+ PortraitName = PortraitTable.GetRowName (LastPortrait )
+ GemRB.SetToken ("SmallPortrait", PortraitName+"S")
+ GemRB.SetToken ("LargePortrait", PortraitName+"M")
+ GemRB.SetNextScript ("CharGen2") #Before race
+ return
+
diff --git a/gemrb/GUIScripts/bg2/GUICG13.py b/gemrb/GUIScripts/bg2/GUICG13.py
new file mode 100644
index 0000000..93ab7c9
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG13.py
@@ -0,0 +1,208 @@
+# 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.
+#
+#
+#character generation, color (GUICG13)
+import GemRB
+import BGCommon
+from GUIDefines import *
+from ie_stats import *
+
+ColorTable = 0
+ColorWindow = 0
+ColorPicker = 0
+DoneButton = 0
+ColorIndex = 0
+PickedColor = 0
+HairButton = 0
+SkinButton = 0
+MajorButton = 0
+MinorButton = 0
+HairColor = 0
+SkinColor = 0
+MajorColor = 0
+MinorColor = 0
+PDollButton = 0
+
+def OnLoad():
+ global ColorWindow, DoneButton, PDollButton, ColorTable
+ global HairButton, SkinButton, MajorButton, MinorButton
+ global HairColor, SkinColor, MajorColor, MinorColor
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ ColorWindow=GemRB.LoadWindow(13)
+
+ ColorTable = GemRB.LoadTable("clowncol")
+ #set these colors to some default
+ PortraitTable = GemRB.LoadTable("pictures")
+ PortraitName = GemRB.GetToken("LargePortrait")
+ PortraitName = PortraitName[0:len(PortraitName)-1]
+ PortraitIndex = PortraitTable.GetRowIndex(PortraitName)
+ if PortraitIndex<0:
+ HairColor=PortraitTable.GetValue(0,1)
+ SkinColor=PortraitTable.GetValue(0,2)
+ MinorColor=PortraitTable.GetValue(0,3)
+ MajorColor=PortraitTable.GetValue(0,4)
+ else:
+ HairColor=PortraitTable.GetValue(PortraitIndex,1)
+ SkinColor=PortraitTable.GetValue(PortraitIndex,2)
+ MinorColor=PortraitTable.GetValue(PortraitIndex,3)
+ MajorColor=PortraitTable.GetValue(PortraitIndex,4)
+
+ PDollButton = ColorWindow.GetControl(1)
+ PDollButton.SetState (IE_GUI_BUTTON_LOCKED)
+ PDollButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+
+ HairButton = ColorWindow.GetControl(2)
+ HairButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ HairButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, HairPress)
+ HairButton.SetBAM("COLGRAD", 1, 0, HairColor)
+
+ SkinButton = ColorWindow.GetControl(3)
+ SkinButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ SkinButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SkinPress)
+ SkinButton.SetBAM("COLGRAD", 1, 0, SkinColor)
+
+ MajorButton = ColorWindow.GetControl(5)
+ MajorButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ MajorButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MajorPress)
+ MajorButton.SetBAM("COLGRAD", 1, 0, MinorColor)
+
+ MinorButton = ColorWindow.GetControl(4)
+ MinorButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ MinorButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MinorPress)
+ MinorButton.SetBAM("COLGRAD", 1, 0, MajorColor)
+
+ BackButton = ColorWindow.GetControl(13)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = ColorWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ ColorWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def DonePress():
+ global HairColor, SkinColor, MajorColor, MinorColor
+
+ if ColorPicker:
+ ColorPicker.Unload()
+ ColorWindow.SetVisible(WINDOW_VISIBLE)
+ PickedColor=ColorTable.GetValue(ColorIndex, GemRB.GetVar("Selected"))
+ if ColorIndex==0:
+ HairColor=PickedColor
+ HairButton.SetBAM("COLGRAD", 1, 0, HairColor)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ return
+ if ColorIndex==1:
+ SkinColor=PickedColor
+ SkinButton.SetBAM("COLGRAD", 1, 0, SkinColor)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ return
+ if ColorIndex==2:
+ MinorColor=PickedColor
+ MajorButton.SetBAM("COLGRAD", 1, 0, MinorColor)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ return
+
+ MajorColor=PickedColor
+ MinorButton.SetBAM("COLGRAD", 1, 0, MajorColor)
+ BGCommon.RefreshPDoll (PDollButton, MinorColor, MajorColor, SkinColor, HairColor)
+ return
+
+def GetColor():
+ global ColorPicker
+
+ ColorPicker=GemRB.LoadWindow(14)
+ GemRB.SetVar("Selected",-1)
+ for i in range(34):
+ Button = ColorPicker.GetControl(i)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ Selected = -1
+ for i in range(34):
+ MyColor = ColorTable.GetValue(ColorIndex, i)
+ if MyColor == "*":
+ break
+ Button = ColorPicker.GetControl(i)
+ Button.SetBAM("COLGRAD", 2, 0, MyColor)
+ if PickedColor == MyColor:
+ GemRB.SetVar("Selected",i)
+ Selected = i
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetVarAssoc("Selected",i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+
+ ColorPicker.SetVisible(WINDOW_VISIBLE)
+ return
+
+def HairPress():
+ global ColorIndex, PickedColor
+
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 0
+ PickedColor = HairColor
+ GetColor()
+ return
+
+def SkinPress():
+ global ColorIndex, PickedColor
+
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 1
+ PickedColor = SkinColor
+ GetColor()
+ return
+
+def MajorPress():
+ global ColorIndex, PickedColor
+
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 2
+ PickedColor = MinorColor
+ GetColor()
+ return
+
+def MinorPress():
+ global ColorIndex, PickedColor
+
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 3
+ PickedColor = MajorColor
+ GetColor()
+ return
+
+def BackPress():
+ if ColorWindow:
+ ColorWindow.Unload()
+ GemRB.SetNextScript("CharGen7")
+ return
+
+def NextPress():
+ if ColorWindow:
+ ColorWindow.Unload()
+ GemRB.SetVar("HairColor",HairColor)
+ GemRB.SetVar("SkinColor",SkinColor)
+ GemRB.SetVar("MinorColor",MinorColor)
+ GemRB.SetVar("MajorColor",MajorColor)
+ GemRB.SetNextScript("GUICG19") #sounds
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG15.py b/gemrb/GUIScripts/bg2/GUICG15.py
new file mode 100644
index 0000000..5f98247
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG15.py
@@ -0,0 +1,122 @@
+# 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.
+#
+#
+#character generation, racial enemy (GUICG15)
+import GemRB
+import CommonTables
+from GUIDefines import *
+from ie_stats import *
+
+RaceWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+RaceTable = 0
+RaceCount = 0
+TopIndex = 0
+MyChar = 0
+#the size of the selection list
+LISTSIZE = 11
+
+def DisplayRaces():
+ global TopIndex
+
+ TopIndex=GemRB.GetVar("TopIndex")
+ for i in range(LISTSIZE):
+ Button = RaceWindow.GetControl(i+6)
+ Val = RaceTable.GetValue(i+TopIndex,0)
+ if Val==0:
+ Button.SetText("")
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetText(Val)
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RacePress)
+ Button.SetVarAssoc("HatedRace",RaceTable.GetValue(i+TopIndex,1) )
+ return
+
+def OnLoad():
+ global RaceWindow, TextAreaControl, DoneButton
+ global RaceTable, RaceCount, TopIndex, MyChar
+
+ MyChar = GemRB.GetVar ("Slot")
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ ClassName = CommonTables.Classes.GetRowName(Class)
+ TableName = CommonTables.ClassSkills.GetValue(ClassName, "HATERACE")
+ if TableName == "*":
+ GemRB.SetNextScript("GUICG7")
+ return
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ RaceWindow = GemRB.LoadWindow(15)
+ RaceTable = GemRB.LoadTable(TableName)
+ RaceCount = RaceTable.GetRowCount()-LISTSIZE+1
+ if RaceCount<0:
+ RaceCount=0
+
+ TopIndex = 0
+ GemRB.SetVar("TopIndex", 0)
+ ScrollBarControl = RaceWindow.GetControl(1)
+ ScrollBarControl.SetVarAssoc("TopIndex", RaceCount)
+ ScrollBarControl.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, DisplayRaces)
+ ScrollBarControl.SetDefaultScrollBar ()
+
+ for i in range(LISTSIZE):
+ Button = RaceWindow.GetControl(i+6)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ GemRB.SetVar("HatedRace",0)
+
+ BackButton = RaceWindow.GetControl(4)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = RaceWindow.GetControl(5)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ TextAreaControl = RaceWindow.GetControl(2)
+ TextAreaControl.SetText(17256)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ RaceWindow.SetVisible(WINDOW_VISIBLE)
+ DisplayRaces()
+ return
+
+def RacePress():
+ Race = GemRB.GetVar("HatedRace")
+ Row = RaceTable.FindValue(1, Race)
+ TextAreaControl.SetText(RaceTable.GetValue(Row, 2) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def BackPress():
+ if RaceWindow:
+ RaceWindow.Unload()
+ GemRB.SetPlayerStat (MyChar, IE_HATEDRACE, 0) #scrapping the race value
+ GemRB.SetNextScript("CharGen6")
+ return
+
+def NextPress():
+ if RaceWindow:
+ RaceWindow.Unload()
+ # save the hated race
+ GemRB.SetPlayerStat (MyChar, IE_HATEDRACE, GemRB.GetVar ("HatedRace"))
+
+ GemRB.SetNextScript("GUICG7") #mage spells
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG19.py b/gemrb/GUIScripts/bg2/GUICG19.py
new file mode 100644
index 0000000..cb425f5
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG19.py
@@ -0,0 +1,112 @@
+# 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.
+#
+#
+#character generation, sounds (GUICG19)
+from ie_restype import *
+import GemRB
+
+VoiceList = 0
+CharSoundWindow = 0
+
+# the available sounds
+SoundSequence = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', \
+ 'm', 's', 't', 'u', 'v', '_', 'x', 'y', 'z', '0', '1', '2', \
+ '3', '4', '5', '6', '7', '8', '9']
+SoundIndex = 0
+
+def OnLoad():
+ global CharSoundWindow, VoiceList
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ CharSoundWindow=GemRB.LoadWindow(19)
+
+ VoiceList = CharSoundWindow.GetControl (45)
+ VoiceList.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ if GemRB.GetVar ("Gender")==1:
+ GemRB.SetVar ("Selected", 4)
+ else:
+ GemRB.SetVar ("Selected", 0)
+
+ VoiceList.SetVarAssoc ("Selected", 0)
+ RowCount=VoiceList.GetCharSounds()
+
+ PlayButton = CharSoundWindow.GetControl (47)
+ PlayButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ PlayButton.SetText (17318)
+
+ TextArea = CharSoundWindow.GetControl (50)
+ TextArea.SetText (11315)
+
+ BackButton = CharSoundWindow.GetControl(10)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = CharSoundWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ VoiceList.SetEvent(IE_GUI_TEXTAREA_ON_CHANGE, ChangeVoice)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ CharSoundWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def PlayPress():
+ global CharSoundWindow, SoundIndex, SoundSequence
+
+ CharSound = VoiceList.QueryText()
+ # SClassID.h -> IE_WAV_CLASS_ID = 0x00000004
+ while (not GemRB.HasResource (CharSound + SoundSequence[SoundIndex], RES_WAV)):
+ NextSound()
+ # play the sound like it was a speech, so any previous yells are quieted
+ GemRB.PlaySound (CharSound + SoundSequence[SoundIndex], 0, 0, 4)
+ NextSound()
+ return
+
+def NextSound():
+ global SoundIndex, SoundSequence
+ SoundIndex += 1
+ if SoundIndex >= len(SoundSequence):
+ SoundIndex = 0
+ return
+
+# When a new voice is selected, play the sounds again from the beginning of the sequence
+def ChangeVoice():
+ global SoundIndex
+ SoundIndex = 0
+ return
+
+def BackPress():
+ global CharSoundWindow
+
+ if CharSoundWindow:
+ CharSoundWindow.Unload()
+ GemRB.SetNextScript("GUICG13")
+ return
+
+def NextPress():
+ global CharSoundWindow
+
+ CharSound = VoiceList.QueryText ()
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerSound (MyChar, CharSound)
+
+ if CharSoundWindow:
+ CharSoundWindow.Unload()
+ GemRB.SetNextScript("CharGen8") #name
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG2.py b/gemrb/GUIScripts/bg2/GUICG2.py
new file mode 100644
index 0000000..6373d38
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG2.py
@@ -0,0 +1,170 @@
+# 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.
+#
+#
+#character generation, class (GUICG2)
+import GemRB
+import GUICommon
+import CommonTables
+import LUCommon
+from ie_stats import *
+from GUIDefines import *
+
+ClassWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+MyChar = 0
+
+def OnLoad():
+ global ClassWindow, TextAreaControl, DoneButton, MyChar
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ ClassWindow = GemRB.LoadWindow(2)
+
+ MyChar = GemRB.GetVar ("Slot")
+ Race = CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (MyChar, IE_RACE) )
+ RaceName = CommonTables.Races.GetRowName(Race)
+
+ ClassCount = CommonTables.Classes.GetRowCount()+1
+
+ j = 0
+ #radiobutton groups must be set up before doing anything else to them
+ for i in range(1,ClassCount):
+ if CommonTables.Classes.GetValue(i-1,4):
+ continue
+ if j>7:
+ Button = ClassWindow.GetControl(j+7)
+ else:
+ Button = ClassWindow.GetControl(j+2)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ j = j+1
+
+ j = 0
+ GemRB.SetVar("MAGESCHOOL",0)
+ HasMulti = 0
+ for i in range(1,ClassCount):
+ ClassName = CommonTables.Classes.GetRowName(i-1)
+ Allowed = CommonTables.Classes.GetValue(ClassName, RaceName)
+ if CommonTables.Classes.GetValue(i-1,4):
+ if Allowed!=0:
+ HasMulti = 1
+ continue
+ if j>7:
+ Button = ClassWindow.GetControl(j+7)
+ else:
+ Button = ClassWindow.GetControl(j+2)
+ j = j+1
+ t = CommonTables.Classes.GetValue(i-1, 0)
+ Button.SetText(t )
+
+ if Allowed==0:
+ continue
+ if Allowed==2:
+ GemRB.SetVar("MAGESCHOOL",5) #illusionist
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClassPress)
+ Button.SetVarAssoc("Class", i)
+
+ MultiClassButton = ClassWindow.GetControl(10)
+ MultiClassButton.SetText(11993)
+ if HasMulti == 0:
+ MultiClassButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ BackButton = ClassWindow.GetControl(14)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = ClassWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ TextAreaControl = ClassWindow.GetControl(13)
+
+ Class = GemRB.GetVar("Class")-1
+ if Class<0:
+ TextAreaControl.SetText(17242)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ TextAreaControl.SetText(CommonTables.Classes.GetValue(Class,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+
+ MultiClassButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MultiClassPress)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ ClassWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def BackPress():
+ if ClassWindow:
+ ClassWindow.Unload()
+ GemRB.SetNextScript("CharGen3")
+ GemRB.SetVar("Class",0) #scrapping the class value
+ return
+
+def SetClass():
+ # find the class from the class table
+ ClassIndex = GemRB.GetVar ("Class") - 1
+ Class = CommonTables.Classes.GetValue (ClassIndex, 5)
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, Class)
+ ClassName = CommonTables.Classes.GetRowName (CommonTables.Classes.FindValue (5, Class))
+ # protect against barbarians; this stat will be overwritten later
+ GemRB.SetPlayerStat (MyChar, IE_HITPOINTS, ClassIndex)
+
+ #assign the correct XP
+ if GUICommon.GameIsTOB():
+ GemRB.SetPlayerStat (MyChar, IE_XP, CommonTables.ClassSkills.GetValue (ClassName, "STARTXP2"))
+ else:
+ GemRB.SetPlayerStat (MyChar, IE_XP, CommonTables.ClassSkills.GetValue (ClassName, "STARTXP"))
+
+ #create an array to get all the classes from
+ NumClasses = 1
+ IsMulti = GUICommon.IsMultiClassed (MyChar, 1)
+ if IsMulti[0] > 1:
+ NumClasses = IsMulti[0]
+ Classes = [IsMulti[1], IsMulti[2], IsMulti[3]]
+ else:
+ Classes = [GemRB.GetPlayerStat (MyChar, IE_CLASS)]
+
+ #loop through each class and update it's level
+ xp = GemRB.GetPlayerStat (MyChar, IE_XP)/NumClasses
+ for i in range (NumClasses):
+ CurrentLevel = LUCommon.GetNextLevelFromExp (xp, Classes[i])
+ if i == 0:
+ GemRB.SetPlayerStat (MyChar, IE_LEVEL, CurrentLevel)
+ elif i <= 2:
+ GemRB.SetPlayerStat (MyChar, IE_LEVEL2+i-1, CurrentLevel)
+
+def MultiClassPress():
+ GemRB.SetVar("Class Kit",0)
+ if ClassWindow:
+ ClassWindow.Unload()
+ GemRB.SetNextScript("GUICG10")
+ return
+
+def ClassPress():
+ SetClass()
+ if ClassWindow:
+ ClassWindow.Unload()
+ GemRB.SetNextScript("GUICG22")
+ return
+
+def NextPress ():
+ SetClass()
+ if ClassWindow:
+ ClassWindow.Unload()
+ GemRB.SetNextScript("CharGen4") #alignment
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG22.py b/gemrb/GUIScripts/bg2/GUICG22.py
new file mode 100644
index 0000000..d0e4920
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG22.py
@@ -0,0 +1,249 @@
+# -*-python-*-
+# 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.
+#
+#character generation, class kit (GUICG22)
+
+import GemRB
+import CommonTables
+from ie_stats import *
+from GUIDefines import *
+
+KitWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+SchoolList = 0
+ClassID = 0
+TopIndex = 0
+RowCount = 10
+KitTable = 0
+Init = 0
+MyChar = 0
+KitSelected = 0 #store clicked kit on redraw as number within RowCount
+EnhanceGUI = GemRB.GetVar("GUIEnhancements") #extra kit button and scroll bar toggle
+
+def OnLoad():
+ global KitWindow, TextAreaControl, DoneButton
+ global SchoolList, ClassID
+ global RowCount, TopIndex, KitTable, Init, MyChar
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ MyChar = GemRB.GetVar ("Slot")
+ Race = GemRB.GetPlayerStat (MyChar, IE_RACE)
+ RaceName = CommonTables.Races.GetRowName(CommonTables.Races.FindValue (3, Race) )
+
+ ClassID = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ ClassName = CommonTables.Classes.GetRowName (GemRB.GetPlayerStat (MyChar, IE_HITPOINTS)) # barbarian hack
+
+ KitTable = GemRB.LoadTable("kittable")
+ KitTableName = KitTable.GetValue(ClassName, RaceName)
+ KitTable = GemRB.LoadTable(KitTableName,1)
+
+ SchoolList = GemRB.LoadTable("magesch")
+
+ #there is a specialist mage window, but it is easier to use
+ #the class kit window for both
+ KitWindow = GemRB.LoadWindow(22)
+ if ClassID == 1:
+ Label = KitWindow.GetControl(0xfffffff)
+ Label.SetText(595)
+
+ for i in range(10):
+ if i<4:
+ Button = KitWindow.GetControl(i+1)
+ else:
+ Button = KitWindow.GetControl(i+5)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ if not KitTable: # sorcerer or monk
+ RowCount = 1
+ else:
+ if ClassID == 1: # mages
+ RowCount = SchoolList.GetRowCount()
+ else:
+ RowCount = KitTable.GetRowCount()
+
+ TopIndex = 0
+ GemRB.SetVar("TopIndex", 0)
+ if EnhanceGUI:
+ tmpRowCount = RowCount
+ if RowCount>10: #create 11 kit button
+ KitWindow.CreateButton (15, 18, 250, 271, 20)
+ extrakit = KitWindow.GetControl(15)
+ extrakit.SetState(IE_GUI_BUTTON_DISABLED)
+ extrakit.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ extrakit.SetSprites("GUICGBC",0, 0,1,2,3)
+ RowCount = 11
+ if tmpRowCount>11: #create scroll bar
+ KitWindow.CreateScrollBar(1000, 290, 50, 16, 220)
+ ScrollBar = KitWindow.GetControl (1000)
+ ScrollBar.SetSprites("GUISCRCW", 0, 0,1,2,3,5,4)
+ ScrollBar.SetVarAssoc("TopIndex",tmpRowCount-10)
+ ScrollBar.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, RedrawKits)
+ ScrollBar.SetDefaultScrollBar()
+ elif not EnhanceGUI and RowCount>10:
+ RowCount = 10
+
+ for i in range(RowCount):
+ if i<4:
+ Button = KitWindow.GetControl(i+1)
+ else:
+ Button = KitWindow.GetControl(i+5)
+ Button.SetVarAssoc("ButtonPressed", i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, KitPress)
+
+ BackButton = KitWindow.GetControl(8)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = KitWindow.GetControl(7)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = KitWindow.GetControl(5)
+ TextAreaControl.SetText(17247)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ Init = 1
+ RedrawKits()
+ KitPress()
+ KitWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def RedrawKits():
+ global TopIndex, Init, KitSelected
+
+ TopIndex=GemRB.GetVar("TopIndex")
+ EnabledButtons = []
+ for i in range(RowCount):
+ if i<4:
+ Button = KitWindow.GetControl(i+1)
+ else:
+ Button = KitWindow.GetControl(i+5)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ if not KitTable:
+ if ClassID == 1:
+ # TODO: check if this is ever reached
+ Kit = GemRB.GetVar("MAGESCHOOL")
+ KitName = SchoolList.GetValue(i+TopIndex, 0)
+ Kit = SchoolList.GetValue (Kit, 3)
+ else:
+ Kit = 0
+ KitName = CommonTables.Classes.GetValue(GemRB.GetVar("Class")-1, 0)
+ else:
+ Kit = KitTable.GetValue (i+TopIndex,0)
+ if ClassID == 1:
+ KitName = SchoolList.GetValue (i+TopIndex, 0)
+ if Kit == 0:
+ KitName = SchoolList.GetValue (0, 0)
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ if Init: #preselection of mage plain kit
+ Button.SetState(IE_GUI_BUTTON_SELECTED)
+ KitSelected = i+TopIndex
+ Init=0
+ if Kit != "*":
+ EnabledButtons.append(Kit-21)
+ else:
+ if Kit:
+ KitName = CommonTables.KitList.GetValue(Kit, 1)
+ else:
+ KitName = CommonTables.Classes.GetValue(GemRB.GetVar("Class")-1, 0)
+ Button.SetText(KitName)
+ if not EnabledButtons or i+TopIndex in EnabledButtons:
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ if Init and i+TopIndex>0:
+ Button.SetState(IE_GUI_BUTTON_SELECTED)
+ KitSelected = i+TopIndex
+ Init=0
+ if Kit == "*":
+ continue
+ if Init and i+TopIndex==0:
+ if EnabledButtons:
+ GemRB.SetVar("ButtonPressed", EnabledButtons[0]) #but leave Init==1
+ else:
+ GemRB.SetVar("ButtonPressed", 0)
+ Button.SetState(IE_GUI_BUTTON_SELECTED)
+ KitSelected = i+TopIndex
+ Init=0
+ if not Init and i+TopIndex == KitSelected: #remark selection state on redraw
+ Button.SetState(IE_GUI_BUTTON_SELECTED)
+ return
+
+def KitPress():
+ global KitSelected
+
+ ButtonPressed=GemRB.GetVar("ButtonPressed")
+ KitSelected = ButtonPressed + TopIndex
+ if not KitTable:
+ if ClassID == 1:
+ # TODO: this seems to be never reached
+ Kit = GemRB.GetVar("MAGESCHOOL")
+ Kit = SchoolList.GetValue (Kit, 3)
+ else:
+ Kit = 0
+ else:
+ Kit = KitTable.GetValue (ButtonPressed+TopIndex, 0)
+ if ClassID == 1:
+ if ButtonPressed + TopIndex == 0:
+ Kit = 0
+ else:
+ Kit = ButtonPressed + TopIndex + 21
+
+ if ClassID == 1 and Kit != 0:
+ GemRB.SetVar("MAGESCHOOL", Kit-21) # hack: -21 to make the generalist 0
+ else:
+ GemRB.SetVar("MAGESCHOOL", 0) # so bards don't get schools
+
+ if Kit == 0:
+ KitDescription = CommonTables.Classes.GetValue(GemRB.GetVar("Class")-1, 1)
+ else:
+ KitDescription = CommonTables.KitList.GetValue(Kit, 3)
+
+ TextAreaControl.SetText(KitDescription)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+
+ GemRB.SetVar("Class Kit", Kit)
+
+ return
+
+def BackPress():
+ GemRB.SetVar("Class Kit", 0) # reverting the value so we are idempotent
+ GemRB.SetVar("MAGESCHOOL", 0)
+ if KitWindow:
+ KitWindow.Unload()
+ GemRB.SetNextScript("GUICG2")
+ return
+
+def NextPress():
+ if KitWindow:
+ KitWindow.Unload()
+
+ #make gnomes always kitted
+ KitIndex = GemRB.GetVar ("Class Kit")
+ MageSchool = GemRB.GetVar ("MAGESCHOOL")
+ if MageSchool and not KitIndex:
+ SchoolTable = GemRB.LoadTable ("magesch")
+ KitIndex = CommonTables.KitList.FindValue (6, SchoolTable.GetValue (MageSchool, 3) )
+
+ #save the kit
+ KitValue = (0x4000 + KitIndex)
+ KitName = CommonTables.KitList.GetValue (KitIndex, 0)
+ GemRB.SetPlayerStat (MyChar, IE_KIT, KitValue)
+
+ GemRB.SetNextScript("CharGen4") #abilities
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG23.py b/gemrb/GUIScripts/bg2/GUICG23.py
new file mode 100644
index 0000000..91e71d3
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG23.py
@@ -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.
+#
+#
+#character generation, biography (GUICG23)
+import GemRB
+
+BioWindow = 0
+EditControl = 0
+
+def OnLoad ():
+ global BioWindow, EditControl
+
+ GemRB.LoadWindowPack ("GUICG", 640, 480)
+ BioWindow = GemRB.LoadWindow (23)
+
+ EditControl = BioWindow.GetControl (3)
+ BIO = GemRB.GetToken("BIO")
+ EditControl = EditControl.ConvertEdit (5)
+ EditControl.SetVarAssoc ("row", 0)
+ EditControl.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ if BIO:
+ EditControl.SetText (BIO)
+ else:
+ EditControl.SetText (15882)
+
+ # done
+ OkButton = BioWindow.GetControl (1)
+ OkButton.SetText (11973)
+
+ ClearButton = BioWindow.GetControl (4)
+ ClearButton.SetText (34881)
+
+ # back
+ CancelButton = BioWindow.GetControl (2)
+ CancelButton.SetText (12896)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ OkButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OkPress)
+ ClearButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClearPress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ BioWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OkPress ():
+ global BioWindow, EditControl
+
+ row = 0
+ line = None
+ BioData = ""
+
+ #there is no way to get the entire TextArea content
+ #this hack retrieves the TextArea content row by row
+ #there is no way to know how much data is in the TextArea
+ while 1:
+ GemRB.SetVar ("row", row)
+ EditControl.SetVarAssoc ("row", row)
+ line = EditControl.QueryText ()
+ if len(line)<=0:
+ break
+ BioData += line+"\n"
+ row += 1
+
+ if BioWindow:
+ BioWindow.Unload ()
+ GemRB.SetNextScript ("CharGen9")
+ GemRB.SetToken ("BIO", BioData)
+ return
+
+def CancelPress ():
+ if BioWindow:
+ BioWindow.Unload ()
+ GemRB.SetNextScript ("CharGen9")
+ return
+
+def ClearPress ():
+ EditControl.SetText ("")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG24.py b/gemrb/GUIScripts/bg2/GUICG24.py
new file mode 100644
index 0000000..5a466a7
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG24.py
@@ -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.
+#
+#
+#character generation, import (GUICG24)
+import GemRB
+
+ImportWindow = 0
+
+def OnLoad():
+ global ImportWindow
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ ImportWindow = GemRB.LoadWindow(24)
+
+ TextAreaControl = ImportWindow.GetControl(0)
+ TextAreaControl.SetText(53605)
+
+ FileButton = ImportWindow.GetControl(1)
+ FileButton.SetText(53604)
+
+ SavedGameButton = ImportWindow.GetControl(2)
+ SavedGameButton.SetText(53602)
+
+ CancelButton = ImportWindow.GetControl(3)
+ CancelButton.SetText(13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ FileButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, FilePress)
+ SavedGameButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, GamePress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ ImportWindow.ShowModal(MODAL_SHADOW_GRAY)
+ return
+
+def FilePress():
+ if ImportWindow:
+ ImportWindow.Unload()
+ GemRB.SetNextScript("ImportFile")
+ return
+
+def GamePress():
+ if ImportWindow:
+ ImportWindow.Unload()
+ GemRB.SetNextScript("ImportGame")
+ return
+
+def CancelPress():
+ if ImportWindow:
+ ImportWindow.Unload()
+ GemRB.SetNextScript("CharGen")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG3.py b/gemrb/GUIScripts/bg2/GUICG3.py
new file mode 100644
index 0000000..bc1886e
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG3.py
@@ -0,0 +1,122 @@
+# 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.
+#
+#
+#character generation, alignment (GUICG3)
+import GemRB
+import GUICommon
+import CommonTables
+from ie_stats import *
+from GUIDefines import *
+
+AlignmentWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+AlignmentTable = 0
+MyChar = 0
+
+def OnLoad():
+ global AlignmentWindow, TextAreaControl, DoneButton
+ global AlignmentTable, MyChar
+
+ MyChar = GemRB.GetVar ("Slot")
+ Kit = GUICommon.GetKitIndex (MyChar)
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ if Kit == 0:
+ KitName = CommonTables.Classes.GetRowName(Class)
+ else:
+ #rowname is just a number, first value row what we need here
+ KitName = CommonTables.KitList.GetValue(Kit, 0)
+
+ AlignmentOk = GemRB.LoadTable("ALIGNMNT")
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ AlignmentTable = GemRB.LoadTable("aligns")
+ AlignmentWindow = GemRB.LoadWindow(3)
+
+ for i in range(9):
+ Button = AlignmentWindow.GetControl(i+2)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetText(AlignmentTable.GetValue(i,0) )
+
+ for i in range(9):
+ Button = AlignmentWindow.GetControl(i+2)
+ if AlignmentOk.GetValue(KitName, AlignmentTable.GetValue(i, 4) ) != 0:
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, AlignmentPress)
+ Button.SetVarAssoc("Alignment", i)
+
+ BackButton = AlignmentWindow.GetControl(13)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = AlignmentWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = AlignmentWindow.GetControl(11)
+ TextAreaControl.SetText(9602)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ AlignmentWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def AlignmentPress():
+ Alignment = GemRB.GetVar("Alignment")
+ TextAreaControl.SetText(AlignmentTable.GetValue(Alignment,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ GemRB.SetVar("Alignment",AlignmentTable.GetValue(Alignment,3) )
+ return
+
+def BackPress():
+ if AlignmentWindow:
+ AlignmentWindow.Unload()
+ GemRB.SetVar("Alignment",-1) #scrapping the alignment value
+ GemRB.SetNextScript("CharGen4")
+ return
+
+def NextPress():
+ if AlignmentWindow:
+ AlignmentWindow.Unload()
+ # save previous stats:
+ # alignment
+ # reputation
+ # alignment abilities
+ Alignment = GemRB.GetVar ("Alignment")
+ AlignmentTable = GemRB.LoadTable ("aligns")
+ GemRB.SetPlayerStat (MyChar, IE_ALIGNMENT, Alignment)
+
+ # use the alignment to apply starting reputation
+ RepTable = GemRB.LoadTable ("repstart")
+ AlignmentAbbrev = AlignmentTable.FindValue (3, Alignment)
+ Rep = RepTable.GetValue (AlignmentAbbrev, 0) * 10
+ GemRB.SetPlayerStat (MyChar, IE_REPUTATION, Rep)
+
+ # set the party rep if this in the main char
+ if MyChar == 1:
+ GemRB.GameSetReputation (Rep)
+
+ # diagnostic output
+ print "CharGen5 output:"
+ print "\tAlignment: ",Alignment
+ print "\tReputation: ",Rep
+
+ GemRB.SetNextScript("CharGen5") #appearance
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG4.py b/gemrb/GUIScripts/bg2/GUICG4.py
new file mode 100644
index 0000000..5b4ab7f
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG4.py
@@ -0,0 +1,355 @@
+# 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.
+#
+#
+#character generation, ability (GUICG4)
+import GemRB
+import GUICommon
+import CommonTables
+from ie_stats import *
+from GUIDefines import *
+from ie_restype import RES_2DA
+
+AbilityWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+AbilityTable = 0
+Abclasrq = 0
+Abclsmod = 0
+Abclasrq = 0
+Abracerq = 0
+PointsLeft = 0
+Minimum = 0
+Maximum = 0
+Add = 0
+KitIndex = 0
+HasStrExtra = 0
+MyChar = 0
+
+def CalcLimits(Abidx):
+ global Minimum, Maximum, Add
+
+ Race = CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (MyChar, IE_RACE) )
+ RaceName = CommonTables.Races.GetRowName(Race)
+
+ Minimum = 3
+ Maximum = 18
+
+ tmp = Abclasrq.GetValue(KitIndex, Abidx)
+ if tmp!=0 and tmp>Minimum:
+ Minimum = tmp
+
+ Race = Abracerq.GetRowIndex(RaceName)
+ tmp = Abracerq.GetValue(Race, Abidx*2)
+ if tmp!=0 and tmp>Minimum:
+ Minimum = tmp
+
+ tmp = Abracerq.GetValue(Race, Abidx*2+1)
+ if tmp!=0 and tmp>Maximum:
+ Maximum = tmp
+
+ Race = Abracead.GetRowIndex(RaceName)
+ if Abclsmod:
+ Add = Abracead.GetValue(Race, Abidx) + Abclsmod.GetValue(KitIndex, Abidx)
+ else:
+ Add = Abracead.GetValue(Race, Abidx)
+ Maximum = Maximum + Add
+ Minimum = Minimum + Add
+ if Minimum<1:
+ Minimum=1
+ if Maximum>25:
+ Maximum=25
+
+ return
+
+def RollPress():
+ global Minimum, Maximum, Add, HasStrExtra, PointsLeft
+
+ GemRB.SetVar("Ability",0)
+ GemRB.SetVar("Ability -1",0)
+ PointsLeft = 0
+ SumLabel = AbilityWindow.GetControl(0x10000002)
+ SumLabel.SetText("0")
+ SumLabel.SetUseRGB(1)
+
+ if HasStrExtra:
+ e = GemRB.Roll(1,100,0)
+ else:
+ e = 0
+ GemRB.SetVar("StrExtra", e)
+ for i in range(6):
+ dice = 3
+ size = 5
+ CalcLimits(i)
+ v = GemRB.Roll(dice, size, Add+3)
+ if v<Minimum:
+ v = Minimum
+ if v>Maximum:
+ v = Maximum
+ GemRB.SetVar("Ability "+str(i), v )
+ Label = AbilityWindow.GetControl(0x10000003+i)
+ if i==0 and v==18 and HasStrExtra:
+ Label.SetText("18/"+str(e) )
+ else:
+ Label.SetText(str(v) )
+ Label.SetUseRGB(1)
+ return
+
+def OnLoad():
+ global AbilityWindow, TextAreaControl, DoneButton
+ global PointsLeft, HasStrExtra
+ global AbilityTable, Abclasrq, Abclsmod, Abracerq, Abracead
+ global KitIndex, Minimum, Maximum, MyChar
+
+ Abracead = GemRB.LoadTable("ABRACEAD")
+ if GemRB.HasResource ("ABCLSMOD", RES_2DA):
+ Abclsmod = GemRB.LoadTable ("ABCLSMOD")
+ Abclasrq = GemRB.LoadTable("ABCLASRQ")
+ Abracerq = GemRB.LoadTable("ABRACERQ")
+
+ MyChar = GemRB.GetVar ("Slot")
+ Kit = GUICommon.GetKitIndex (MyChar)
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ if Kit == 0:
+ KitName = CommonTables.Classes.GetRowName(Class)
+ else:
+ #rowname is just a number, first value row what we need here
+ KitName = CommonTables.KitList.GetValue(Kit, 0)
+
+ #if the class uses the warrior table for saves, then it may have the extra strength
+ if CommonTables.Classes.GetValue(Class, 3)=="SAVEWAR":
+ HasStrExtra=1
+ else:
+ HasStrExtra=0
+
+ KitIndex = Abclasrq.GetRowIndex(KitName)
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ AbilityTable = GemRB.LoadTable("ability")
+ AbilityWindow = GemRB.LoadWindow(4)
+
+ RerollButton = AbilityWindow.GetControl(2)
+ RerollButton.SetText(11982)
+ StoreButton = AbilityWindow.GetControl(37)
+ StoreButton.SetText(17373)
+ RecallButton = AbilityWindow.GetControl(38)
+ RecallButton.SetText(17374)
+
+ BackButton = AbilityWindow.GetControl(36)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = AbilityWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+
+ RollPress()
+ StorePress()
+ for i in range(6):
+ Label = AbilityWindow.GetControl(i+0x10000009)
+ Label.SetEvent(IE_GUI_LABEL_ON_PRESS, eval("OverPress"+str(i)))
+ Button = AbilityWindow.GetControl(i+30)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, JustPress)
+ Button.SetEvent(IE_GUI_MOUSE_LEAVE_BUTTON, EmptyPress)
+ Button.SetVarAssoc("Ability", i)
+
+ Button = AbilityWindow.GetControl(i*2+16)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, LeftPress)
+ Button.SetVarAssoc("Ability", i )
+
+ Button = AbilityWindow.GetControl(i*2+17)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RightPress)
+ Button.SetVarAssoc("Ability", i )
+
+ TextAreaControl = AbilityWindow.GetControl(29)
+ TextAreaControl.SetText(17247)
+
+ StoreButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, StorePress)
+ RecallButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, RecallPress)
+ RerollButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, RollPress)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ AbilityWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+ return
+
+def RightPress():
+ global PointsLeft
+
+ Abidx = GemRB.GetVar("Ability")
+ Ability = GemRB.GetVar("Ability "+str(Abidx) )
+ CalcLimits(Abidx)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(Abidx, 1) )
+ if Ability<=Minimum:
+ return
+ GemRB.SetVar("Ability "+str(Abidx), Ability-1)
+ PointsLeft = PointsLeft + 1
+ GemRB.SetVar("Ability -1",PointsLeft)
+ SumLabel = AbilityWindow.GetControl(0x10000002)
+ SumLabel.SetText(str(PointsLeft) )
+ Label = AbilityWindow.GetControl(0x10000003+Abidx)
+ StrExtra = GemRB.GetVar("StrExtra")
+ if Abidx==0 and Ability==19 and StrExtra:
+ Label.SetText("18/"+str(StrExtra) )
+ else:
+ Label.SetText(str(Ability-1) )
+ return
+
+def JustPress():
+ Abidx = GemRB.GetVar("Ability")
+ Ability = GemRB.GetVar("Ability "+str(Abidx) )
+ CalcLimits(Abidx)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(Abidx, 1) )
+ return
+
+def LeftPress():
+ global PointsLeft
+
+ Abidx = GemRB.GetVar("Ability")
+ Ability = GemRB.GetVar("Ability "+str(Abidx) )
+ CalcLimits(Abidx)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(Abidx, 1) )
+ if PointsLeft == 0:
+ return
+ if Ability>=Maximum: #should be more elaborate
+ return
+ GemRB.SetVar("Ability "+str(Abidx), Ability+1)
+ PointsLeft = PointsLeft - 1
+ GemRB.SetVar("Ability -1",PointsLeft)
+ SumLabel = AbilityWindow.GetControl(0x10000002)
+ SumLabel.SetText(str(PointsLeft) )
+ Label = AbilityWindow.GetControl(0x10000003+Abidx)
+ StrExtra = GemRB.GetVar("StrExtra")
+ if Abidx==0 and Ability==17 and HasStrExtra==1:
+ Label.SetText("18/%02d"%(StrExtra) )
+ else:
+ Label.SetText(str(Ability+1) )
+ return
+
+def EmptyPress():
+ TextAreaControl = AbilityWindow.GetControl(29)
+ TextAreaControl.SetText(17247)
+ return
+
+def StorePress():
+ GemRB.SetVar("StoredStrExtra",GemRB.GetVar("StrExtra") )
+ for i in range(-1,6):
+ GemRB.SetVar("Stored "+str(i),GemRB.GetVar("Ability "+str(i) ) )
+ return
+
+def RecallPress():
+ global PointsLeft
+
+ e=GemRB.GetVar("StoredStrExtra")
+ GemRB.SetVar("StrExtra",e)
+ for i in range(-1,6):
+ v = GemRB.GetVar("Stored "+str(i) )
+ GemRB.SetVar("Ability "+str(i), v)
+ Label = AbilityWindow.GetControl(0x10000003+i)
+ if i==0 and v==18 and HasStrExtra==1:
+ Label.SetText("18/"+str(e) )
+ else:
+ Label.SetText(str(v) )
+
+ PointsLeft = GemRB.GetVar("Ability -1")
+ return
+
+def BackPress():
+ if AbilityWindow:
+ AbilityWindow.Unload()
+ GemRB.SetNextScript("CharGen5")
+ GemRB.SetVar("StrExtra",0)
+ for i in range(-1,6):
+ GemRB.SetVar("Ability "+str(i),0) #scrapping the abilities
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ return
+
+def NextPress():
+ if AbilityWindow:
+ AbilityWindow.Unload()
+ AbilityTable = GemRB.LoadTable ("ability")
+ AbilityCount = AbilityTable.GetRowCount ()
+
+ # print our diagnostic as we loop (so as not to duplicate)
+ for i in range (AbilityCount):
+ StatID = AbilityTable.GetValue (i, 3)
+ StatName = AbilityTable.GetRowName (i)
+ StatValue = GemRB.GetVar ("Ability "+str(i))
+ GemRB.SetPlayerStat (MyChar, StatID, StatValue)
+ print "\t",StatName,":\t", StatValue
+
+ GemRB.SetPlayerStat (MyChar, IE_STREXTRA, GemRB.GetVar ("StrExtra"))
+ print "\tSTREXTRA:\t",GemRB.GetVar ("StrExtra")
+
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ GemRB.SetNextScript("CharGen6")
+ return
+
+def OverPress0():
+ Ability = GemRB.GetVar("Ability 0")
+ CalcLimits(0)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(0, 1) )
+ return
+
+def OverPress1():
+ Ability = GemRB.GetVar("Ability 1")
+ CalcLimits(1)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(1, 1) )
+ return
+
+def OverPress2():
+ Ability = GemRB.GetVar("Ability 2")
+ CalcLimits(2)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(2, 1) )
+ return
+
+def OverPress3():
+ Ability = GemRB.GetVar("Ability 3")
+ CalcLimits(3)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(3, 1) )
+ return
+
+def OverPress4():
+ Ability = GemRB.GetVar("Ability 4")
+ CalcLimits(4)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(4, 1) )
+ return
+
+def OverPress5():
+ Ability = GemRB.GetVar("Ability 5")
+ CalcLimits(5)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(5, 1) )
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG5.py b/gemrb/GUIScripts/bg2/GUICG5.py
new file mode 100644
index 0000000..0388a50
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG5.py
@@ -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.
+#
+#
+#character generation, name (GUICG5)
+import GemRB
+
+NameWindow = 0
+NameField = 0
+DoneButton = 0
+
+def OnLoad():
+ global NameWindow, NameField, DoneButton
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ NameWindow = GemRB.LoadWindow(5)
+
+ BackButton = NameWindow.GetControl(3)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton = NameWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ NameField = NameWindow.GetControl(2)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ NameField.SetEvent(IE_GUI_EDIT_ON_CHANGE, EditChange)
+ NameWindow.SetVisible(WINDOW_VISIBLE)
+ NameField.SetStatus(IE_GUI_CONTROL_FOCUSED)
+ return
+
+def BackPress():
+ if NameWindow:
+ NameWindow.Unload()
+ GemRB.SetNextScript("CharGen8")
+ return
+
+def NextPress():
+ Name = NameField.QueryText()
+ #check length?
+ #seems like a good idea to store it here for the time being
+ GemRB.SetToken("CHARNAME",Name)
+ if NameWindow:
+ NameWindow.Unload()
+ GemRB.SetNextScript("CharGen9")
+ return
+
+def EditChange():
+ Name = NameField.QueryText()
+ if len(Name)==0:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG6.py b/gemrb/GUIScripts/bg2/GUICG6.py
new file mode 100644
index 0000000..7744eaa
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG6.py
@@ -0,0 +1,87 @@
+# 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.
+#
+#
+#character generation, (thief) skills (GUICG6)
+import GemRB
+import LUSkillsSelection
+from GUIDefines import *
+from ie_stats import *
+
+SkillWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+MyChar = 0
+
+def RedrawSkills():
+ PointsLeft = GemRB.GetVar ("SkillPointsLeft")
+ if PointsLeft == 0:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ return
+
+def OnLoad():
+ global SkillWindow, DoneButton, MyChar
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ SkillWindow = GemRB.LoadWindow(6)
+ MyChar = GemRB.GetVar ("Slot")
+
+ Levels = [GemRB.GetPlayerStat (MyChar, IE_LEVEL), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL2), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL3)]
+ LUSkillsSelection.SetupSkillsWindow (MyChar, \
+ LUSkillsSelection.LUSKILLS_TYPE_CHARGEN, SkillWindow, RedrawSkills, [0,0,0], Levels)
+
+ if not GemRB.GetVar ("SkillPointsLeft"): #skipping
+ GemRB.SetNextScript("GUICG9")
+ return
+
+ BackButton = SkillWindow.GetControl(25)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+
+ DoneButton = SkillWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+
+ RedrawSkills ()
+ SkillWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def BackPress():
+ if SkillWindow:
+ SkillWindow.Unload()
+ GemRB.SetNextScript("CharGen6")
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ return
+
+def NextPress():
+ if SkillWindow:
+ SkillWindow.Unload()
+ # save all skills
+
+ LUSkillsSelection.SkillsSave (MyChar)
+
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ GemRB.SetNextScript("GUICG9") #weapon proficiencies
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG7.py b/gemrb/GUIScripts/bg2/GUICG7.py
new file mode 100644
index 0000000..5e5a501
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG7.py
@@ -0,0 +1,65 @@
+# -*-python-*-
+# 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.
+#
+# character generation, mage spells (GUICG7)
+
+import GemRB
+import GUICommon
+import CommonTables
+from ie_stats import *
+from GUIDefines import *
+import LUSpellSelection
+
+def OnLoad():
+ KitTable = GemRB.LoadTable("magesch")
+ Slot = GemRB.GetVar ("Slot")
+ Class = GemRB.GetPlayerStat (Slot, IE_CLASS)
+ TableName = CommonTables.ClassSkills.GetValue(Class, 2)
+
+ # make sure we have a correct table
+ if TableName == "*":
+ GemRB.SetNextScript("GUICG6")
+ return
+ if Class == 19:
+ # sorcerer's need their known not max table
+ TableName = "SPLSRCKN"
+
+ # get our kit index
+ KitIndex = GUICommon.GetKitIndex (Slot)
+ if KitIndex:
+ KitValue = KitTable.GetValue(KitIndex - 21, 3)
+
+ # bards have kits too
+ if KitValue == -1:
+ KitValue = 0x4000 # we only need it for the spells, so this is ok
+ else:
+ KitValue = 0x4000
+
+ # open up the spell selection window
+ # remember, it is pc, table, level, diff, kit, chargen
+ IsMulti = GUICommon.IsMultiClassed (Slot, 1)
+ Level = GemRB.GetPlayerStat (Slot, IE_LEVEL)
+ if IsMulti[0]>1:
+ for i in range (1, IsMulti[0]):
+ if CommonTables.ClassSkills.GetValue (IsMulti[i], 2, 0) != "*":
+ Level = GemRB.GetPlayerStat (Slot, IE_LEVEL2+i-1)
+ break
+
+ LUSpellSelection.OpenSpellsWindow (Slot, TableName, Level, Level, KitValue, 1)
+
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG8.py b/gemrb/GUIScripts/bg2/GUICG8.py
new file mode 100644
index 0000000..eb58808
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG8.py
@@ -0,0 +1,93 @@
+# 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.
+#
+#
+#character generation, race (GUICG2)
+import GemRB
+import CommonTables
+from ie_stats import IE_RACE
+from GUIDefines import *
+
+RaceWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+MyChar = 0
+
+def OnLoad():
+ global RaceWindow, TextAreaControl, DoneButton, MyChar
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ RaceWindow = GemRB.LoadWindow(8)
+
+ MyChar = GemRB.GetVar ("Slot")
+ RaceCount = CommonTables.Races.GetRowCount()
+
+ for i in range(2,RaceCount+2):
+ #hack to stop if the race table has more entries than the gui resource
+ #this needs to be done because the race table has non-selectable entries
+ if not RaceWindow.HasControl(i):
+ RaceCount = i-2
+ break
+ Button = RaceWindow.GetControl(i)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ GemRB.SetVar ("Race", -1)
+ for i in range(2, RaceCount+2):
+ Button = RaceWindow.GetControl(i)
+ Button.SetText(CommonTables.Races.GetValue(i-2,0) )
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RacePress)
+ Button.SetVarAssoc("Race", i-2 )
+
+ BackButton = RaceWindow.GetControl(i+2) #i=8 now (when race count is 7)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ DoneButton = RaceWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ TextAreaControl = RaceWindow.GetControl(12)
+ TextAreaControl.SetText(17237)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ RaceWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def RacePress():
+ Race = GemRB.GetVar("Race")
+ TextAreaControl.SetText(CommonTables.Races.GetValue(Race,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def BackPress():
+ if RaceWindow:
+ RaceWindow.Unload()
+ GemRB.SetNextScript("CharGen2")
+ GemRB.SetVar("Race",0) #scrapping the race value
+ return
+
+def NextPress():
+ if RaceWindow:
+ RaceWindow.Unload()
+
+ Race = GemRB.GetVar ("Race")
+ GemRB.SetPlayerStat (MyChar, IE_RACE, CommonTables.Races.GetValue(Race,3) )
+
+ GemRB.SetNextScript("CharGen3") #class
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICG9.py b/gemrb/GUIScripts/bg2/GUICG9.py
new file mode 100644
index 0000000..3d7c7ff
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICG9.py
@@ -0,0 +1,76 @@
+# 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.
+#
+#
+#character generation, proficiencies (GUICG9)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import LUProfsSelection
+
+SkillWindow = 0
+DoneButton = 0
+MyChar = 0
+
+def RedrawSkills():
+ ProfsPointsLeft = GemRB.GetVar ("ProfsPointsLeft")
+ if not ProfsPointsLeft:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ return
+
+def OnLoad():
+ global SkillWindow, DoneButton, MyChar
+
+ GemRB.LoadWindowPack("GUICG", 640, 480)
+ SkillWindow = GemRB.LoadWindow(9)
+ MyChar = GemRB.GetVar ("Slot")
+ Levels = [GemRB.GetPlayerStat (MyChar, IE_LEVEL), GemRB.GetPlayerStat (MyChar, IE_LEVEL2), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL3)]
+ LUProfsSelection.SetupProfsWindow (MyChar, \
+ LUProfsSelection.LUPROFS_TYPE_CHARGEN, SkillWindow, RedrawSkills, [0,0,0], Levels)
+
+ BackButton = SkillWindow.GetControl(77)
+ BackButton.SetText(15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+
+ DoneButton = SkillWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ SkillWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def BackPress():
+ if SkillWindow:
+ SkillWindow.Unload()
+ GemRB.SetNextScript("CharGen6")
+ #scrap skills
+ return
+
+def NextPress():
+ if SkillWindow:
+ SkillWindow.Unload()
+
+ LUProfsSelection.ProfsSave (MyChar, LUProfsSelection.LUPROFS_TYPE_CHARGEN)
+
+ GemRB.SetNextScript("CharGen7") #appearance
+ return
diff --git a/gemrb/GUIScripts/bg2/GUICommonWindows.py b/gemrb/GUIScripts/bg2/GUICommonWindows.py
new file mode 100644
index 0000000..b8ca28c
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUICommonWindows.py
@@ -0,0 +1,903 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUICommonWindows.py - functions to open common
+# windows in lower part of the screen
+###################################################
+
+import GemRB
+from GUIDefines import *
+from ie_stats import IE_MAXHITPOINTS, IE_STATE_ID, IE_HITPOINTS, STATE_DEAD
+from ie_modal import *
+from ie_action import *
+import GUICommon
+import CommonTables
+import LUCommon
+import InventoryCommon
+
+# needed for all the Open*Window callbacks in the OptionsWindow
+import GUIJRNL
+import GUIMA
+import GUIMG
+import GUIINV
+import GUIOPT
+import GUIPR
+import GUIREC
+
+FRAME_PC_SELECTED = 0
+FRAME_PC_TARGET = 1
+
+PortraitWindow = None
+OptionsWindow = None
+ActionsWindow = None
+DraggedPortrait = None
+
+def SetupMenuWindowControls (Window, Gears, ReturnToGame):
+ """Sets up all of the basic control windows."""
+
+ global OptionsWindow
+
+ OptionsWindow = Window
+ # Return to Game
+ Button = Window.GetControl (0)
+ Button.SetTooltip (16313)
+ Button.SetVarAssoc ("SelectedWindow", 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ReturnToGame)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Map
+ Button = Window.GetControl (1)
+ Button.SetTooltip (16310)
+ Button.SetVarAssoc ("SelectedWindow", 1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIMA.OpenMapWindow)
+
+ # Journal
+ Button = Window.GetControl (2)
+ Button.SetTooltip (16308)
+ Button.SetVarAssoc ("SelectedWindow", 2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIJRNL.OpenJournalWindow)
+
+ # Inventory
+ Button = Window.GetControl (3)
+ Button.SetTooltip (16307)
+ Button.SetVarAssoc ("SelectedWindow", 3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIINV.OpenInventoryWindow)
+
+ # Records
+ Button = Window.GetControl (4)
+ Button.SetTooltip (16306)
+ Button.SetVarAssoc ("SelectedWindow", 4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIREC.OpenRecordsWindow)
+
+ # Mage
+ Button = Window.GetControl (5)
+ Button.SetTooltip (16309)
+ Button.SetVarAssoc ("SelectedWindow", 5)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIMG.OpenMageWindow)
+
+ # Priest
+ Button = Window.GetControl (6)
+ Button.SetTooltip (14930)
+ Button.SetVarAssoc ("SelectedWindow", 6)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIPR.OpenPriestWindow)
+
+ # Options
+ Button = Window.GetControl (7)
+ Button.SetTooltip (16311)
+ Button.SetVarAssoc ("SelectedWindow", 7)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIOPT.OpenOptionsWindow)
+
+ # Multi player team setup?
+ Button = Window.GetControl (8)
+ Button.SetTooltip (13902)
+
+ if Gears:
+ # Pendulum, gears, sun/moon dial (time)
+ # FIXME: display all animations: CPEN, CGEAR, CDIAL
+ Button = Window.GetControl (9)
+ Label = Button.CreateLabelOnButton (0x10000009, "NORMAL", 0)
+
+ Label.SetAnimation ("CPEN")
+ Button.SetAnimation ("CGEAR")
+ Button.SetBAM ("CDIAL", 0, 0)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GUICommon.GearsClicked)
+ GUICommon.SetGamedaysAndHourToken()
+ Button.SetTooltip(16041)
+ rb = 11
+ else:
+ rb = 9
+
+ # Rest
+ Button = Window.GetControl (rb)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.RestPress)
+ Button.SetTooltip (11942)
+
+ if PortraitWindow:
+ UpdatePortraitWindow ()
+
+def MarkMenuButton (WindowIndex):
+ Pressed = WindowIndex.GetControl( GemRB.GetVar ("SelectedWindow") )
+
+ for button in range (9):
+ Button = WindowIndex.GetControl (button)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ if Pressed: # don't draw the selection when returning to the game
+ Pressed.SetState (IE_GUI_BUTTON_SELECTED)
+
+def AIPress ():
+ """Toggles the party AI."""
+
+ Button = PortraitWindow.GetControl (6)
+ AI = GemRB.GetMessageWindowSize () & GS_PARTYAI
+
+ if AI:
+ GemRB.GameSetScreenFlags (GS_PARTYAI, OP_NAND)
+ Button.SetTooltip (15918)
+ GemRB.SetVar ("AI", 0)
+ else:
+ GemRB.GameSetScreenFlags (GS_PARTYAI, OP_OR)
+ Button.SetTooltip (15917)
+ GemRB.SetVar ("AI", GS_PARTYAI)
+ return
+
+def EmptyControls ():
+ global ActionsWindow
+
+ Selected = GemRB.GetSelectedSize()
+ if Selected==1:
+ pc = GemRB.GameGetFirstSelectedActor ()
+ #init spell list
+ GemRB.SpellCast (pc, -1, 0, 1)
+
+ GemRB.SetVar ("ActionLevel", 0)
+ Window = ActionsWindow
+ for i in range (12):
+ Button = Window.GetControl (i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetPicture ("")
+ Button.SetText ("")
+ Button.SetActionIcon (globals(), -1)
+ return
+
+def SelectFormationPreset ():
+ """Choose the default formation."""
+ GemRB.GameSetFormation (GemRB.GetVar ("Value"), GemRB.GetVar ("Formation") )
+ GroupControls ()
+ return
+
+def SetupFormation ():
+ """Opens the formation selection section."""
+ global ActionsWindow
+
+ Window = ActionsWindow
+ for i in range (12):
+ Button = Window.GetControl (i)
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetBAM ("FORM%x"%i,0,0,-1)
+ Button.SetVarAssoc ("Value", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectFormationPreset)
+ return
+
+def GroupControls ():
+ """Sections that control group actions."""
+
+ global ActionsWindow
+
+ GemRB.SetVar ("ActionLevel", 0)
+ Window = ActionsWindow
+ Button = Window.GetControl (0)
+ Button.SetActionIcon (globals(), 7)
+ Button = Window.GetControl (1)
+ Button.SetActionIcon (globals(), 15)
+ Button = Window.GetControl (2)
+ Button.SetActionIcon (globals(), 21)
+ Button = Window.GetControl (3)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (4)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (5)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (6)
+ Button.SetActionIcon (globals(), -1)
+ GemRB.SetVar ("Formation", GemRB.GameGetFormation ())
+ for i in range (5):
+ Button = Window.GetControl (7+i)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ idx = GemRB.GameGetFormation (i)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetBAM ("FORM%x"%idx,0,0,-1)
+ Button.SetVarAssoc ("Formation", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectFormation)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, SetupFormation)
+ str = GemRB.GetString (4935)
+ Button.SetTooltip ("F%d - %s"%(8+i,str) )
+ return
+
+def OpenActionsWindowControls (Window):
+ global ActionsWindow
+
+ ActionsWindow = Window
+ # Gears (time) when options pane is down
+ Button = Window.GetControl (62)
+ Label = Button.CreateLabelOnButton (0x1000003e, "NORMAL", 0)
+
+ # FIXME: display all animations
+ Label.SetAnimation ("CPEN")
+ Button.SetAnimation ("CGEAR")
+ Button.SetBAM ("CDIAL", 0, 0)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GUICommon.GearsClicked)
+ GUICommon.SetGamedaysAndHourToken()
+ Button.SetTooltip(16041)
+ UpdateActionsWindow ()
+ return
+
+def SelectItemAbility():
+ pc = GemRB.GameGetFirstSelectedActor ()
+ slot = GemRB.GetVar ("Slot")
+ ability = GemRB.GetVar ("Ability")
+ GemRB.SetupQuickSlot (pc, 0, slot, ability, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ return
+
+def SetupItemAbilities(pc, slot):
+ Window = ActionsWindow
+
+ slot_item = GemRB.GetSlotItem(pc, slot, 1)
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ Tips = item["Tooltips"]
+
+ for i in range (12):
+ Button = Window.GetControl (i)
+ Button.SetPicture ("")
+ if i<len(Tips):
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetItemIcon (slot_item['ItemResRef'], i+6)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectItemAbility)
+ Button.SetVarAssoc ("Ability", i)
+
+ Button.SetTooltip ("F%d - %s"%(i+1,GemRB.GetString(Tips[i])) )
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ return
+
+def UpdateActionsWindow ():
+ """Redraws the actions section of the window."""
+
+ global ActionsWindow, PortraitWindow, OptionsWindow
+ global level, TopIndex
+
+ if ActionsWindow == -1:
+ return
+
+ if ActionsWindow == None:
+ return
+
+ #fully redraw the side panes to cover the actions window
+ #do this only when there is no 'otherwindow'
+ if GemRB.GetVar ("OtherWindow") == -1:
+ if PortraitWindow:
+ PortraitWindow.Invalidate ()
+ if OptionsWindow:
+ OptionsWindow.Invalidate ()
+
+ Selected = GemRB.GetSelectedSize()
+
+ #setting up the disabled button overlay (using the second border slot)
+ for i in range (12):
+ Button = ActionsWindow.GetControl (i)
+ Button.SetBorder (1, 0, 0, 0, 0, 50,30,10,120, 0, 1)
+ Button.SetFont ("NUMBER")
+ Button.SetText ("")
+
+ if Selected == 0:
+ EmptyControls ()
+ return
+ if Selected > 1:
+ GroupControls ()
+ return
+
+ #we are sure there is only one actor selected
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ level = GemRB.GetVar ("ActionLevel")
+ TopIndex = GemRB.GetVar ("TopIndex")
+ if level == 0:
+ #this is based on class
+ ActionsWindow.SetupControls (globals(), pc, 0, 1)
+ elif level == 1:
+ ActionsWindow.SetupEquipmentIcons(globals(), pc, TopIndex, 0, 1)
+ elif level == 2: #spells
+ GemRB.SetVar ("Type", 3)
+ ActionsWindow.SetupSpellIcons(globals(), pc, 3, TopIndex, 0, 1)
+ elif level == 3: #innates
+ GemRB.SetVar ("Type", 4)
+ ActionsWindow.SetupSpellIcons(globals(), pc, 4, TopIndex, 0, 1)
+ elif level == 4: #quick weapon/item ability selection
+ SetupItemAbilities(pc, GemRB.GetVar("Slot") )
+ elif level == 5: #all known mage spells
+ GemRB.SetVar ("Type", -1)
+ ActionsWindow.SetupSpellIcons(globals(), pc, -1, TopIndex, 0, 1)
+ return
+
+def ActionQWeaponPressed (which):
+ """Selects the given quickslot weapon if possible."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+ qs = GemRB.GetEquippedQuickSlot (pc, 1, 1)
+
+ #38 is the magic slot
+ if ((qs==which) or (qs==38)) and GemRB.GameControlGetTargetMode() != TARGET_MODE_ATTACK:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK, GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
+ else:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_NONE)
+ GemRB.SetEquippedQuickSlot (pc, which, -1, 1)
+
+ ActionsWindow.SetupControls (globals(), pc, 0, 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionQWeapon1Pressed ():
+ ActionQWeaponPressed(0)
+
+def ActionQWeapon2Pressed ():
+ ActionQWeaponPressed(1)
+
+def ActionQWeapon3Pressed ():
+ ActionQWeaponPressed(2)
+
+def ActionQWeapon4Pressed ():
+ ActionQWeaponPressed(3)
+
+#no check needed because the button wouldn't be drawn if illegal
+def ActionLeftPressed ():
+ """Scrolls the actions window left.
+
+ Used primarily for spell selection."""
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ if TopIndex>10:
+ TopIndex -= 10
+ else:
+ TopIndex = 0
+ GemRB.SetVar ("TopIndex", TopIndex)
+ UpdateActionsWindow ()
+ return
+
+#no check needed because the button wouldn't be drawn if illegal
+def ActionRightPressed ():
+ """Scrolls the action window right.
+
+ Used primarily for spell selection."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Type = GemRB.GetVar ("Type")
+ #Type is a bitfield if there is no level given
+ #This is to make sure cleric/mages get all spells listed
+ if Type&128:
+ Max = GemRB.GetKnownSpellsCount(pc, Type&127, -1, 1)
+ else:
+ Max = GemRB.GetMemorizedSpellsCount(pc, Type, -1, 1)
+ TopIndex += 10
+ if TopIndex > Max - 10:
+ if Max>10:
+ TopIndex = Max-10
+ else:
+ TopIndex = 0
+ GemRB.SetVar ("TopIndex", TopIndex)
+ UpdateActionsWindow ()
+ return
+
+def ActionBardSongPressed ():
+ """Toggles the battle song."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_BATTLESONG, 1)
+ GemRB.PlaySound ("act_01")
+ UpdateActionsWindow ()
+ return
+
+def ActionSearchPressed ():
+ """Toggles detect traps."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_DETECTTRAPS, 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionStealthPressed ():
+ """Toggles stealth."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_STEALTH, 1)
+ GemRB.PlaySound ("act_07")
+ UpdateActionsWindow ()
+ return
+
+def ActionTurnPressed ():
+ """Toggles turn undead."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_TURNUNDEAD, 1)
+ GemRB.PlaySound ("act_06")
+ UpdateActionsWindow ()
+ return
+
+def ActionUseItemPressed ():
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionCastPressed ():
+ """Opens the spell choice scrollbar."""
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 2)
+ UpdateActionsWindow ()
+ return
+
+def ActionQItemPressed (action):
+ """Uses the given quick item."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ #quick slot
+ GemRB.UseItem (pc, -2, action, -1, 1)
+ return
+
+def ActionQItem1Pressed ():
+ ActionQItemPressed (ACT_QSLOT1)
+ return
+
+def ActionQItem2Pressed ():
+ ActionQItemPressed (ACT_QSLOT2)
+ return
+
+def ActionQItem3Pressed ():
+ ActionQItemPressed (ACT_QSLOT3)
+ return
+
+def ActionQItem4Pressed ():
+ ActionQItemPressed (ACT_QSLOT4)
+ return
+
+def ActionQItem5Pressed ():
+ ActionQItemPressed (ACT_QSLOT5)
+ return
+
+def ActionQItemRightPressed (action):
+ """Selects the used ability of the quick item."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetVar ("Slot", action)
+ GemRB.SetVar ("ActionLevel", 4)
+ UpdateActionsWindow ()
+ return
+
+def ActionQItem1RightPressed ():
+ ActionQItemRightPressed (19)
+
+def ActionQItem2RightPressed ():
+ ActionQItemRightPressed (20)
+
+def ActionQItem3RightPressed ():
+ ActionQItemRightPressed (21)
+
+def ActionQItem4RightPressed ():
+ ActionQItemRightPressed (22)
+
+def ActionQItem5RightPressed ():
+ ActionQItemRightPressed (23)
+
+def ActionQWeapon1RightPressed ():
+ ActionQItemRightPressed (10)
+
+def ActionQWeapon2RightPressed ():
+ ActionQItemRightPressed (11)
+
+def ActionQWeapon3RightPressed ():
+ ActionQItemRightPressed (12)
+
+def ActionQWeapon4RightPressed ():
+ ActionQItemRightPressed (13)
+
+def ActionInnatePressed ():
+ """Opens the innate spell scrollbar."""
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 3)
+ UpdateActionsWindow ()
+ return
+
+def SpellPressed ():
+ """Prepares a spell to be cast."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
+ Spell = GemRB.GetVar ("Spell")
+ Type = GemRB.GetVar ("Type")
+ if Type==-1:
+ GemRB.SetVar ("ActionLevel", 0)
+ GemRB.SetVar("Type", 0)
+ GemRB.SpellCast (pc, Type, Spell, 1)
+ if GemRB.GetVar ("Type")!=-1:
+ GemRB.SetVar ("ActionLevel", 0)
+ #init spell list
+ GemRB.SpellCast (pc, -1, 0, 1)
+ GemRB.SetVar ("TopIndex", 0)
+ UpdateActionsWindow ()
+ return
+
+def EquipmentPressed ():
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
+ Item = GemRB.GetVar ("Equipment")
+ #equipment index
+ GemRB.UseItem (pc, -1, Item, -1, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ UpdateActionsWindow ()
+ return
+
+SelectionChangeHandler = None
+
+def SetSelectionChangeHandler (handler):
+ """Updates the selection handler."""
+
+ global SelectionChangeHandler
+
+ # Switching from walking to non-walking environment:
+ # set the first selected PC in walking env as a selected
+ # in nonwalking env
+ #if (not SelectionChangeHandler) and handler:
+ if (not SelectionChangeHandler) and handler and (not GUICommon.NextWindowFn):
+ sel = GemRB.GameGetFirstSelectedPC ()
+ if not sel:
+ sel = 1
+ GemRB.GameSelectPCSingle (sel)
+
+ SelectionChangeHandler = handler
+
+ # redraw selection on change main selection | single selection
+ SelectionChanged ()
+ return
+
+def RunSelectionChangeHandler ():
+ if SelectionChangeHandler:
+ SelectionChangeHandler ()
+ return
+
+def OpenPortraitWindow (needcontrols):
+ global PortraitWindow
+
+ PortraitWindow = Window = GemRB.LoadWindow (1)
+
+ if needcontrols:
+ Button=Window.GetControl (8)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MinimizePortraits)
+
+ # AI
+ Button = Window.GetControl (6)
+ #fixing a gui bug, and while we are at it, hacking it to be easier
+ Button.SetSprites ("GUIBTACT", 0, 48, 47, 46, 49)
+ GSFlags = GemRB.GetMessageWindowSize ()&GS_PARTYAI
+
+ GemRB.SetVar ("AI", GSFlags)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, AIPress)
+ Button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ Button.SetVarAssoc ("AI", 1)
+ if GSFlags:
+ Button.SetTooltip (15917)
+ else:
+ Button.SetTooltip (15918)
+
+ #Select All
+ Button = Window.GetControl (7)
+ Button.SetTooltip (10485)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectAllOnPress)
+
+ for i in range (PARTY_SIZE):
+ Button = Window.GetControl (i)
+ Button.SetFont ("STATES2")
+ Button.SetVarAssoc ("PressedPortrait", i+1)
+
+ if (needcontrols):
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, GUIINV.OpenInventoryWindowClick)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, PortraitButtonOnPress)
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitButtonOnPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, PortraitButtonOnShiftPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDropItemToPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT, OnDropPortraitToPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG, PortraitButtonOnDrag)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, PortraitButtonOnMouseEnter)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, PortraitButtonOnMouseLeave)
+
+ Button.SetBorder (FRAME_PC_SELECTED, 1, 1, 2, 2, 0, 255, 0, 255)
+ Button.SetBorder (FRAME_PC_TARGET, 3, 3, 4, 4, 255, 255, 0, 255)
+
+ UpdatePortraitWindow ()
+ SelectionChanged ()
+ return Window
+
+def UpdatePortraitWindow ():
+ """Updates all of the portraits."""
+
+ Window = PortraitWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Inventory = GemRB.GetVar ("Inventory")
+
+ for portid in range (PARTY_SIZE):
+ Button = Window.GetControl (portid)
+ pic = GemRB.GetPlayerPortrait (portid+1, 1)
+ if Inventory and pc != portid+1:
+ pic = None
+
+ if pic and GemRB.GetPlayerStat(portid+1, IE_STATE_ID) & STATE_DEAD:
+ import GUISTORE
+ # dead pcs are hidden in all stores but temples
+ if GUISTORE.StoreWindow and not GUISTORE.StoreHealWindow:
+ pic = None
+
+ if not pic:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetText ("")
+ Button.SetTooltip ("")
+ continue
+
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE| IE_GUI_BUTTON_HORIZONTAL| \
+ IE_GUI_BUTTON_ALIGN_LEFT| IE_GUI_BUTTON_ALIGN_TOP| \
+ IE_GUI_BUTTON_DRAGGABLE|IE_GUI_BUTTON_MULTILINE, OP_SET)
+
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetPicture (pic, "NOPORTSM")
+ GUICommon.SetupDamageInfo (portid+1, Button)
+
+ #add effects on the portrait
+ effects = GemRB.GetPlayerStates (portid+1)
+ states = ""
+ for col in range(len(effects)):
+ states = effects[col:col+1] + states
+ if col % 3 == 2: states = "\n" + states
+ for x in range(3 - (len(effects)/3)):
+ states = "\n" + states
+ states = "\n" + states
+
+ # blank space
+ flag = blank = chr(238)
+
+ # shopping icon
+ if pc==portid+1:
+ if GemRB.GetStore()!=None:
+ flag = chr(155)
+ # talk icon
+ if GemRB.GameGetSelectedPCSingle(1)==portid+1:
+ flag = chr(154)
+
+ if LUCommon.CanLevelUp (portid+1):
+ states = flag+blank+chr(255) + states
+ else:
+ states = flag+blank+blank + states
+ Button.SetText(states)
+ return
+
+def PortraitButtonOnDrag ():
+ global DraggedPortrait
+
+ #they start from 1
+ DraggedPortrait = GemRB.GetVar ("PressedPortrait")
+ GemRB.DragItem (DraggedPortrait, -1, "")
+ return
+
+def PortraitButtonOnPress ():
+ """Selects the portrait individually."""
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if GemRB.GameControlGetTargetMode() != TARGET_MODE_NONE:
+ GemRB.ActOnPC (i)
+ return
+
+ if (not SelectionChangeHandler):
+ if GemRB.GameIsPCSelected (i):
+ GemRB.GameControlSetScreenFlags (SF_CENTERONACTOR, OP_OR)
+ GemRB.GameSelectPC (i, True, SELECT_REPLACE)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def PortraitButtonOnShiftPress ():
+ """Handles selecting multiple portaits with shift."""
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if (not SelectionChangeHandler):
+ sel = GemRB.GameIsPCSelected (i)
+ sel = not sel
+ GemRB.GameSelectPC (i, sel)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def SelectionChanged ():
+ """Ran by the Game class when a PC selection is changed."""
+
+ global PortraitWindow
+
+ if not PortraitWindow:
+ return
+
+ GemRB.SetVar ("ActionLevel", 0)
+ if (not SelectionChangeHandler):
+ UpdateActionsWindow ()
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, GemRB.GameIsPCSelected (i + 1))
+ else:
+ sel = GemRB.GameGetSelectedPCSingle ()
+
+ #update mage school
+ GemRB.SetVar ("MAGESCHOOL", 0)
+ Kit = GUICommon.GetKitIndex (sel)
+ if Kit and CommonTables.KitList.GetValue (Kit, 7) == 1:
+ MageTable = GemRB.LoadTable ("magesch")
+ GemRB.SetVar ("MAGESCHOOL", MageTable.FindValue (3, CommonTables.KitList.GetValue (Kit, 6) ) )
+
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, i + 1 == sel)
+ import CommonWindow
+ CommonWindow.CloseContainerWindow()
+ return
+
+def PortraitButtonOnMouseEnter ():
+ global DraggedPortrait
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ GemRB.GameControlSetLastActor( i )
+ if GemRB.IsDraggingItem()==2:
+ if DraggedPortrait != None:
+ GemRB.SwapPCs (DraggedPortrait, i)
+ GemRB.SetVar ("PressedPortrait", DraggedPortrait)
+ DraggedPortrait = i
+ GemRB.SetTimedEvent (CheckDragging, 1)
+ else:
+ OnDropPortraitToPC()
+ return
+
+ if GemRB.IsDraggingItem ():
+ Button = PortraitWindow.GetControl (i-1)
+ Button.EnableBorder (FRAME_PC_TARGET, 1)
+ return
+
+def OnDropPortraitToPC ():
+ GemRB.SetVar ("PressedPortrait",0)
+ GemRB.DragItem (0, -1, "")
+ DraggedPortrait = None
+ return
+
+def CheckDragging():
+ """Contains portrait dragging in case of mouse out-of-range."""
+
+ global DraggedPortrait
+
+ i = GemRB.GetVar ("PressedPortrait")
+ if not i:
+ GemRB.DragItem (0, -1, "")
+
+ if GemRB.IsDraggingItem()!=2:
+ DraggedPortrait = None
+ return
+
+def PortraitButtonOnMouseLeave ():
+ i = GemRB.GetVar ("PressedPortrait")
+ if not i:
+ return
+
+ Button = PortraitWindow.GetControl (i-1)
+ Button.EnableBorder (FRAME_PC_TARGET, 0)
+ GemRB.SetVar ("PressedPortrait", 0)
+ GemRB.SetTimedEvent (CheckDragging, 1)
+ return
+
+def ActionStopPressed ():
+ for i in GemRB.GetSelectedActors():
+ GemRB.ClearActions (i, 1)
+ return
+
+def ActionTalkPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_TALK,GA_NO_DEAD|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionAttackPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK,GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
+
+def ActionDefendPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_DEFEND,GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionThievingPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_PICK, GA_NO_DEAD|GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def MinimizePortraits():
+ GemRB.GameSetScreenFlags(GS_PORTRAITPANE, OP_OR)
+
+def OpenWaitForDiscWindow ():
+ global DiscWindow
+
+ if DiscWindow:
+ GemRB.HideGUI ()
+ if DiscWindow:
+ DiscWindow.Unload ()
+ GemRB.SetVar ("OtherWindow", -1)
+ # ...LoadWindowPack()
+ EnableAnimatedWindows ()
+ DiscWindow = None
+ GemRB.UnhideGUI ()
+ return
+
+ try:
+ GemRB.HideGUI ()
+ except:
+ pass
+
+ GemRB.LoadWindowPack ("GUIID")
+ DiscWindow = Window = GemRB.LoadWindow (0)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+ label = DiscWindow.GetControl (0)
+
+ disc_num = GemRB.GetVar ("WaitForDisc")
+ #disc_path = GemRB.GetVar ("WaitForDiscPath")
+ disc_path = 'XX:'
+
+ text = GemRB.GetString (31483) + " " + str (disc_num) + " " + GemRB.GetString (31569) + " " + disc_path + "\n" + GemRB.GetString (49152)
+ label.SetText (text)
+ DisableAnimatedWindows ()
+ # 31483 - Please place PS:T disc number
+ # 31568 - Please place the PS:T DVD
+ # 31569 - in drive
+ # 31570 - Wrong disc in drive
+ # 31571 - There is no disc in drive
+ # 31578 - No disc could be found in drive. Please place Disc 1 in drive.
+ # 49152 - To quit the game, press Alt-F4
+
+ try:
+ GemRB.UnhideGUI ()
+ except:
+ DiscWindow.SetVisible (WINDOW_VISIBLE)
+
+def CheckLevelUp(pc):
+ GemRB.SetVar ("CheckLevelUp"+str(pc), LUCommon.CanLevelUp (pc))
diff --git a/gemrb/GUIScripts/bg2/GUIINV.py b/gemrb/GUIScripts/bg2/GUIINV.py
new file mode 100644
index 0000000..633584b
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIINV.py
@@ -0,0 +1,318 @@
+#-*-python-*-
+#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.
+#
+
+#GUIINV.py - scripts to control inventory windows from GUIINV winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import InventoryCommon
+from GUIDefines import *
+from ie_stats import *
+from ie_slots import *
+from ie_spells import *
+
+InventoryWindow = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+def OpenInventoryWindowClick ():
+ tmp = GemRB.GetVar ("PressedPortrait")
+ GemRB.GameSelectPC (tmp, True, SELECT_REPLACE)
+ OpenInventoryWindow ()
+ return
+
+def OpenInventoryWindow ():
+ """Opens the inventory window."""
+
+ import GUICommonWindows
+
+ global InventoryWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenInventoryWindow):
+ if GemRB.IsDraggingItem ()==1:
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #store the item in the inventory before window is closed
+ GemRB.DropDraggedItem (pc, -3)
+ #dropping on ground if cannot store in inventory
+ if GemRB.IsDraggingItem ()==1:
+ GemRB.DropDraggedItem (pc, -2)
+
+ if InventoryWindow:
+ InventoryWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ InventoryWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GemRB.SetVar ("MessageLabel", -1)
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ GUICommonWindows.UpdatePortraitWindow ()
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ #don't go back to multi selection mode when going to the store screen
+ if not GemRB.GetVar ("Inventory"):
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIINV", 640, 480)
+ InventoryWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", InventoryWindow.ID)
+ GemRB.SetVar ("MessageLabel", Window.GetControl (0x1000003f).ID )
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenInventoryWindow)
+ OptionsWindow.SetFrame ()
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ #ground items scrollbar
+ ScrollBar = Window.GetControl (66)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RefreshInventoryWindow)
+
+ #Ground Item
+ for i in range (5):
+ Button = Window.GetControl (i+68)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, InventoryCommon.MouseEnterGround)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, InventoryCommon.MouseLeaveGround)
+ Button.SetVarAssoc ("GroundItemButton", i)
+ Button.SetSprites ("STONSLOT",0,0,2,4,3)
+
+ #major & minor clothing color
+ Button = Window.GetControl (62)
+ Button.SetSprites ("INVBUT",0,0,1,0,0)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.MajorPress)
+ Button.SetTooltip (12007)
+
+ Button = Window.GetControl (63)
+ Button.SetSprites ("INVBUT",0,0,1,0,0)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.MinorPress)
+ Button.SetTooltip (12008)
+
+ #portrait
+ Button = Window.GetControl (50)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnAutoEquip)
+
+ #encumbrance
+ Label = Window.CreateLabel (0x10000043, 5,385,60,20,"NUMBER","0:",IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_TOP)
+ Label = Window.CreateLabel (0x10000044, 5,455,60,20,"NUMBER","0:",IE_FONT_ALIGN_RIGHT|IE_FONT_ALIGN_TOP)
+
+ #armor class
+ Label = Window.GetControl (0x10000038)
+ Label.SetTooltip (17183)
+
+ #hp current
+ Label = Window.GetControl (0x10000039)
+ Label.SetTooltip (17184)
+
+ #hp max
+ Label = Window.GetControl (0x1000003a)
+ Label.SetTooltip (17378)
+
+ #info label, game paused, etc
+ Label = Window.GetControl (0x1000003f)
+ Label.SetText ("")
+
+ SlotCount = GemRB.GetSlotType (-1)["Count"]
+ for slot in range (SlotCount):
+ SlotType = GemRB.GetSlotType (slot+1)
+ if SlotType["ID"]:
+ Button = Window.GetControl (SlotType["ID"])
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, InventoryCommon.MouseEnterSlot)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, InventoryCommon.MouseLeaveSlot)
+ Button.SetVarAssoc ("ItemButton", slot+1)
+ #keeping 2 in the original place, because it is how
+ #the gui resource has it, but setting the other cycles
+ Button.SetSprites ("STONSLOT",0,0,2,4,3)
+
+ GemRB.SetVar ("TopIndex", 0)
+ GUICommonWindows.SetSelectionChangeHandler (UpdateInventoryWindow)
+ UpdateInventoryWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def UpdateInventoryWindow ():
+ """Redraws the inventory window and resets TopIndex."""
+
+ Window = InventoryWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Container = GemRB.GetContainer (pc, 1)
+ ScrollBar = Window.GetControl (66)
+ Count = Container['ItemCount']
+ if Count<1:
+ Count=1
+ ScrollBar.SetVarAssoc ("TopIndex", Count)
+ RefreshInventoryWindow ()
+ #populate inventory slot controls
+ SlotCount = GemRB.GetSlotType (-1)["Count"]
+
+ for i in range (SlotCount):
+ InventoryCommon.UpdateSlot (pc, i)
+ return
+
+def RefreshInventoryWindow ():
+ """Partial redraw without resetting TopIndex."""
+
+ Window = InventoryWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ #name
+ Label = Window.GetControl (0x10000032)
+ Label.SetText (GemRB.GetPlayerName (pc, 0))
+
+ #portrait
+ Button = Window.GetControl (50)
+ Color1 = GemRB.GetPlayerStat (pc, IE_METAL_COLOR)
+ Color2 = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR)
+ Color3 = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR)
+ Color4 = GemRB.GetPlayerStat (pc, IE_SKIN_COLOR)
+ Color5 = GemRB.GetPlayerStat (pc, IE_LEATHER_COLOR)
+ Color6 = GemRB.GetPlayerStat (pc, IE_ARMOR_COLOR)
+ Color7 = GemRB.GetPlayerStat (pc, IE_HAIR_COLOR)
+ Button.SetPLT (GUICommon.GetActorPaperDoll (pc),
+ Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 0)
+
+ anim_id = GemRB.GetPlayerStat (pc, IE_ANIMATION_ID)
+ row = "0x%04X" %anim_id
+ size = CommonTables.Pdolls.GetValue (row, "SIZE")
+
+ #Weapon
+ slot_item = GemRB.GetSlotItem (pc, GemRB.GetEquippedQuickSlot (pc) )
+ if slot_item:
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ if (item['AnimationType'] != ''):
+ Button.SetPLT ("WP" + size + item['AnimationType'] + "INV", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 1)
+
+ #Shield
+ slot_item = GemRB.GetSlotItem (pc, 3)
+ if slot_item:
+ itemname = slot_item["ItemResRef"]
+ item = GemRB.GetItem (itemname)
+ if (item['AnimationType'] != ''):
+ if (GemRB.CanUseItemType (SLOT_WEAPON, itemname)):
+ #off-hand weapon
+ Button.SetPLT ("WP" + size + item['AnimationType'] + "OIN", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 2)
+ else:
+ #shield
+ Button.SetPLT ("WP" + size + item['AnimationType'] + "INV", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 2)
+
+ #Helmet
+ slot_item = GemRB.GetSlotItem (pc, 1)
+ if slot_item:
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ if (item['AnimationType'] != ''):
+ Button.SetPLT ("WP" + size + item['AnimationType'] + "INV", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 3)
+
+ #encumbrance
+ GUICommon.SetEncumbranceLabels ( Window, 0x10000043, 0x10000044, pc)
+
+ #armor class
+ ac = GemRB.GetPlayerStat (pc, IE_ARMORCLASS)
+ #temporary solution, the dexterity bonus should be handled by the core
+ #some ac bonuses are not cummulative with this AC bonus!
+ ac += GemRB.GetAbilityBonus (IE_DEX, 2, GemRB.GetPlayerStat (pc, IE_DEX) )
+ Label = Window.GetControl (0x10000038)
+ Label.SetText (str (ac))
+ Label.SetTooltip (10339)
+
+ #hp current
+ hp = GemRB.GetPlayerStat (pc, IE_HITPOINTS)
+ Label = Window.GetControl (0x10000039)
+ Label.SetText (str (hp))
+ Label.SetTooltip (17184)
+
+ #hp max
+ hpmax = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)
+ Label = Window.GetControl (0x1000003a)
+ Label.SetText (str (hpmax))
+ Label.SetTooltip (17378)
+
+ #party gold
+ Label = Window.GetControl (0x10000040)
+ Label.SetText (str (GemRB.GameGetPartyGold ()))
+
+ #class
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ Label = Window.GetControl (0x10000042)
+ Label.SetText (ClassTitle)
+
+ Button = Window.GetControl (62)
+ Color = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 0, 0, Color)
+
+ Button = Window.GetControl (63)
+ Color = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 0, 0, Color)
+
+ #update ground inventory slots
+ Container = GemRB.GetContainer (pc, 1)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ for i in range (5):
+ Button = Window.GetControl (i+68)
+ if GemRB.IsDraggingItem ()==1:
+ Button.SetState (IE_GUI_BUTTON_SECOND)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDragItemGround)
+ Slot = GemRB.GetContainerItem (pc, i+TopIndex)
+
+ if Slot == None:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.OnDragItemGround)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InventoryCommon.OpenGroundItemInfoWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None) #TODO: implement OpenGroundItemAmountWindow
+
+ GUICommon.UpdateInventorySlot (pc, Button, Slot, "ground")
+
+ #making window visible/shaded depending on the pc's state
+ held = GemRB.GetPlayerStat (pc, IE_HELD) + GemRB.GetPlayerStat (pc, IE_CASTERHOLD)
+ if held or GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+#End of file GUIINV.py
diff --git a/gemrb/GUIScripts/bg2/GUIJRNL.py b/gemrb/GUIScripts/bg2/GUIJRNL.py
new file mode 100644
index 0000000..ab686ce
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIJRNL.py
@@ -0,0 +1,222 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIJRNL.py - scripts to control journal/diary windows from GUIJRNL winpack
+
+import GemRB
+from GUIDefines import *
+import GUICommon
+
+###################################################
+JournalWindow = None
+PortraitWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+global Section
+Section = 1
+Chapter = 0
+Order = 0
+StartTime = 0
+StartYear = 0
+
+###################################################
+def OpenJournalWindow ():
+ import GUICommonWindows
+ global JournalWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+ global StartTime, StartYear
+ global Chapter
+
+ if GUICommon.CloseOtherWindow (OpenJournalWindow):
+
+ if JournalWindow:
+ JournalWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ JournalWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ Table = GemRB.LoadTable("YEARS")
+ StartTime = Table.GetValue("STARTTIME", "VALUE") / 4500
+ StartYear = Table.GetValue("STARTYEAR", "VALUE")
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIJRNL", 640, 480)
+ JournalWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", JournalWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenJournalWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ # prev. chapter
+ Button = JournalWindow.GetControl (3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PrevChapterPress)
+
+ # next chapter
+ Button = JournalWindow.GetControl (4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, NextChapterPress)
+
+ GemRB.SetVar ("Section", Section)
+ # Quests
+ Button = JournalWindow.GetControl (6)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("Section", 1)
+ Button.SetText (45485)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateLogWindow)
+
+ # Quests completed
+ Button = JournalWindow.GetControl (7)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("Section", 2)
+ Button.SetText (45486)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateLogWindow)
+
+ # Journal
+ Button = JournalWindow.GetControl (8)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("Section", 4)
+ Button.SetText (15333)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateLogWindow)
+
+ # User
+ Button = JournalWindow.GetControl (9)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("Section", 0)
+ Button.SetText (45487)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateLogWindow)
+
+ # Order
+ Button = JournalWindow.GetControl (10)
+ Button.SetText (4627)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ToggleOrderWindow)
+
+ # Done
+ #Button = JournalWindow.GetControl (3)
+ #Button.SetText (20636)
+ #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenJournalWindow)
+
+ Chapter = GemRB.GetGameVar("chapter")
+ if Chapter>65535:
+ Chapter=0
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateLogWindow)
+ UpdateLogWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ToggleOrderWindow ():
+ global Order
+
+ if Order:
+ Order = 0
+ else:
+ Order = 1
+ UpdateLogWindow ()
+ return
+
+def UpdateLogWindow ():
+
+ # text area
+ Window = JournalWindow
+
+ Section = GemRB.GetVar("Section")
+ GemRB.SetToken ("CurrentChapter", str(Chapter) )
+ # CurrentChapter
+ Label = JournalWindow.GetControl (0x1000000a)
+ Label.SetText (15873)
+ print "Chapter ", Chapter, "Section ", Section
+
+ Text = Window.GetControl (1)
+
+ Text.Clear ()
+ for i in range (GemRB.GetJournalSize (Chapter, Section)):
+ je = GemRB.GetJournalEntry (Chapter, i, Section)
+
+ if je == None:
+ continue
+ hours = je['GameTime'] / 4500
+ days = int(hours/24)
+ year = str (StartYear + int(days/365))
+ dayandmonth = StartTime + days%365
+ GemRB.SetToken ("GAMEDAYS", str(days) ) #Other IE games use "GAMEDAY"
+ GemRB.SetToken ("HOUR",str(hours%24 ) )
+ GemRB.SetVar ("DAYANDMONTH",dayandmonth)
+ GemRB.SetToken ("YEAR",year)
+
+ # each journal entry consists of the title and description
+ # but the game displays the entry date between the two
+ je2 = GemRB.GetString(je['Text']).split("\n",1)
+ JournalTitle = "[color=d00000]" + je2[0] + "[/color]" + "\n"
+ JournalText = je2[1]
+
+ Text.Append (JournalTitle + GemRB.GetString(15980), 3*i)
+ Text.Append (JournalText, 3*i+1)
+ Text.Append ("", 3*i + 2)
+
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+def PrevChapterPress ():
+ global Chapter
+ if GUICommon.GameIsTOB():
+ firstChapter = 0
+ else:
+ firstChapter = 1
+
+ if Chapter > firstChapter:
+ Chapter = Chapter - 1
+ GemRB.SetToken ("CurrentChapter", str(Chapter) )
+ UpdateLogWindow ()
+ return
+
+###################################################
+def NextChapterPress ():
+ global Chapter
+
+ if Chapter < GemRB.GetGameVar("chapter"):
+ Chapter = Chapter + 1
+ GemRB.SetToken ("CurrentChapter", str(Chapter) )
+ UpdateLogWindow ()
+ return
+
+###################################################
+# End of file GUIJRNL.py
diff --git a/gemrb/GUIScripts/bg2/GUILOAD.py b/gemrb/GUIScripts/bg2/GUILOAD.py
new file mode 100644
index 0000000..12049ee
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUILOAD.py
@@ -0,0 +1,173 @@
+# 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.
+#
+#
+#load window
+import GemRB
+import LoadScreen
+
+LoadWindow = 0
+TextAreaControl = 0
+Games = ()
+ScrollBar = 0
+
+def OnLoad():
+ global LoadWindow, TextAreaControl, Games, ScrollBar
+
+ GemRB.LoadWindowPack("GUILOAD", 640, 480)
+ LoadWindow = GemRB.LoadWindow(0)
+ LoadWindow.SetFrame ()
+ CancelButton=LoadWindow.GetControl(34)
+ CancelButton.SetText(13727)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ GemRB.SetVar("LoadIdx",0)
+
+ for i in range(4):
+ Button = LoadWindow.GetControl(26+i)
+ Button.SetText(15590)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, LoadGamePress)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc("LoadIdx",i)
+
+ Button = LoadWindow.GetControl(30+i)
+ Button.SetText(13957)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc("LoadIdx",i)
+
+ #area previews
+ Button = LoadWindow.GetControl(1+i)
+ Button.SetState(IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ #PC portraits
+ for j in range(PARTY_SIZE):
+ Button = LoadWindow.GetControl(40+i*PARTY_SIZE+j)
+ Button.SetState(IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ ScrollBar=LoadWindow.GetControl(25)
+ ScrollBar.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games=GemRB.GetSaveGames()
+ TopIndex = max (0, len(Games) - 4)
+ GemRB.SetVar ("TopIndex",TopIndex)
+ ScrollBar.SetVarAssoc ("TopIndex", TopIndex+1)
+ ScrollBarPress ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ScrollBarPress():
+ #draw load game portraits
+ Pos = GemRB.GetVar("TopIndex")
+ for i in range(4):
+ ActPos = Pos + i
+
+ Button1 = LoadWindow.GetControl(26+i)
+ Button2 = LoadWindow.GetControl(30+i)
+ if ActPos<len(Games):
+ Button1.SetState(IE_GUI_BUTTON_ENABLED)
+ Button2.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState(IE_GUI_BUTTON_DISABLED)
+ Button2.SetState(IE_GUI_BUTTON_DISABLED)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetName()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl(0x10000008+i)
+ Label.SetText(Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl(0x10000010+i)
+ Label.SetText(Slotname)
+
+ Button=LoadWindow.GetControl(1+i)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture("")
+ for j in range(PARTY_SIZE):
+ Button=LoadWindow.GetControl(40+i*PARTY_SIZE+j)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture("")
+ return
+
+def LoadGamePress():
+ if LoadWindow:
+ LoadWindow.Unload()
+ Pos = GemRB.GetVar("TopIndex")+GemRB.GetVar("LoadIdx")
+ LoadScreen.StartLoadScreen()
+ #loads savegame
+ GemRB.LoadGame(Games[Pos])
+ #performs conversion to ToB
+ if (GemRB.GetVar("oldgame")==0) and GemRB.GetVar("expansion")==1:
+ GemRB.GameSetExpansion()
+
+ #enters game
+ GemRB.EnterGame() #it will close windows, including the loadscreen
+ return
+
+def DeleteGameConfirm():
+ global Games
+
+ TopIndex = GemRB.GetVar("TopIndex")
+ Pos = TopIndex +GemRB.GetVar("LoadIdx")
+ GemRB.DeleteSaveGame(Games[Pos])
+ if TopIndex>0:
+ GemRB.SetVar("TopIndex",TopIndex-1)
+ del Games[Pos]
+ ScrollBar.SetVarAssoc("TopIndex", len(Games))
+ ScrollBarPress()
+ if ConfirmWindow:
+ ConfirmWindow.Unload()
+ LoadWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel():
+ if ConfirmWindow:
+ ConfirmWindow.Unload()
+ LoadWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress():
+ global ConfirmWindow
+
+ LoadWindow.SetVisible(WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow(1)
+ Text=ConfirmWindow.GetControl(0)
+ Text.SetText(15305)
+ DeleteButton=ConfirmWindow.GetControl(1)
+ DeleteButton.SetText(13957)
+ DeleteButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ CancelButton=ConfirmWindow.GetControl(2)
+ CancelButton.SetText(13727)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ ConfirmWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def CancelPress():
+ if LoadWindow:
+ LoadWindow.Unload()
+ GemRB.SetNextScript("Start")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUIMA.py b/gemrb/GUIScripts/bg2/GUIMA.py
new file mode 100644
index 0000000..d4ccd1b
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIMA.py
@@ -0,0 +1,434 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMA.py - scripts to control map windows from the GUIMA and GUIWMAP winpacks
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+import GUICommon
+
+MapWindow = None
+NoteWindow = None
+WorldMapWindow = None
+WorldMapControl = None
+PortraitWindow = None
+OldPortraitWindow = None
+OptionsWindow = None
+OldOptionsWindow = None
+
+def RevealMap ():
+ import GUICommonWindows
+ global MapWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (ShowMap):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+
+ PosX = GemRB.GetVar ("MapControlX")
+ PosY = GemRB.GetVar ("MapControlY")
+
+ GemRB.RevealArea (PosX, PosY, 30, 1)
+ GemRB.GamePause (0,0)
+ return
+
+###################################################
+# for farsight effect
+###################################################
+def ShowMap ():
+ import GUICommonWindows
+ global MapWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (ShowMap):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMAP", 640, 480)
+ MapWindow = Window = GemRB.LoadWindow (2)
+ #this window type blocks the game normally, but map window doesn't
+ GemRB.SetVar ("OtherWindow", MapWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, ShowMap)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ OptionsWindow.SetFrame ()
+
+ # World Map
+ Button = Window.GetControl (1)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Hide or Show mapnotes
+ Button = Window.GetControl (3)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Label = Window.GetControl (0x10000003)
+ Label.SetText ("")
+
+ # Map Control
+ Window.CreateMapControl (2, 0, 0, 0, 0, 0x10000003, "FLAG1")
+ Map = Window.GetControl (2)
+ GemRB.SetVar ("ShowMapNotes",IE_GUI_MAP_REVEAL_MAP)
+ Map.SetVarAssoc ("ShowMapNotes", IE_GUI_MAP_REVEAL_MAP)
+ Map.SetEvent (IE_GUI_MAP_ON_PRESS, RevealMap)
+ Window.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_GRAYED)
+ PortraitWindow.SetVisible (WINDOW_GRAYED)
+ OptionsWindow.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_FRONT)
+ Window.SetVisible (WINDOW_FRONT)
+ Map.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ GemRB.GamePause (0,0)
+ return
+
+###################################################
+def OpenMapWindow ():
+ import GUICommonWindows
+ global MapWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenMapWindow):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMAP", 640, 480)
+ MapWindow = Window = GemRB.LoadWindow (2)
+ #this window type blocks the game normally, but map window doesn't
+ GemRB.SetVar ("OtherWindow", MapWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenMapWindow)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ OptionsWindow.SetFrame ()
+
+ # World Map
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenWorldMapWindowInside)
+
+ # Hide or Show mapnotes
+ Button = Window.GetControl (3)
+ Button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ # Is this an option?
+ GemRB.SetVar ("ShowMapNotes", IE_GUI_MAP_VIEW_NOTES)
+ Button.SetVarAssoc ("ShowMapNotes", IE_GUI_MAP_VIEW_NOTES)
+
+ Label = Window.GetControl (0x10000003)
+ Label.SetText ("")
+
+ # Map Control
+ Window.CreateMapControl (2, 0, 0, 0, 0, 0x10000003, "FLAG1")
+ Map = Window.GetControl (2)
+ Map.SetVarAssoc ("ShowMapNotes", IE_GUI_MAP_VIEW_NOTES)
+ Map.SetEvent (IE_GUI_MAP_ON_RIGHT_PRESS, AddNoteWindow)
+ Map.SetEvent (IE_GUI_MAP_ON_DOUBLE_PRESS, LeftDoublePressMap)
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ Map.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def LeftDoublePressMap ():
+ #close the map on doubleclick
+ OpenMapWindow()
+ return
+
+def CloseNoteWindow ():
+ if NoteWindow:
+ NoteWindow.Unload ()
+ MapWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def RemoveMapNote ():
+ PosX = GemRB.GetVar ("MapControlX")
+ PosY = GemRB.GetVar ("MapControlY")
+ GemRB.SetMapnote (PosX, PosY, 0, "")
+ CloseNoteWindow ()
+ return
+
+def SetMapNote ():
+ PosX = GemRB.GetVar ("MapControlX")
+ PosY = GemRB.GetVar ("MapControlY")
+ Label = NoteWindow.GetControl (1)
+ Text = Label.QueryText ()
+ Color = GemRB.GetVar ("Color")
+ GemRB.SetMapnote (PosX, PosY, Color, Text)
+ CloseNoteWindow ()
+ return
+
+def SetFocusBack ():
+ NoteLabel.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def AddNoteWindow ():
+ global NoteWindow, NoteLabel
+
+ Label = MapWindow.GetControl (0x10000003)
+ Text = Label.QueryText ()
+ NoteWindow = GemRB.LoadWindow (5)
+ NoteLabel = NoteWindow.GetControl (1)
+ NoteLabel.SetText (Text)
+ for i in range(8):
+ Label = NoteWindow.GetControl (4+i)
+ #the .chu is crappy, we have to reset the flags
+ Label.SetSprites ("FLAG1", i,0,1,2,0)
+ Label.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+ Label.SetVarAssoc ("Color", i)
+ Label.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetFocusBack)
+
+ #set
+ Label = NoteWindow.GetControl (0)
+ Label.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetMapNote)
+ Label.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Label.SetText (11973)
+
+ #cancel
+ Label = NoteWindow.GetControl (2)
+ Label.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseNoteWindow)
+ Label.SetText (13727)
+ Label.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ #remove
+ Label = NoteWindow.GetControl (3)
+ Label.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemoveMapNote)
+ Label.SetText (13957)
+
+ NoteWindow.ShowModal (MODAL_SHADOW_GRAY)
+ NoteLabel.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def OpenWorldMapWindowInside ():
+ global MapWindow
+
+ OpenMapWindow () #closes mapwindow
+ MapWindow = -1
+ print "MapWindow=",MapWindow
+ WorldMapWindowCommon (-1)
+ return
+
+def OpenWorldMapWindow ():
+ WorldMapWindowCommon (GemRB.GetVar ("Travel"))
+ return
+
+def MoveToNewArea ():
+ global WorldMapWindow, WorldMapControl
+
+ tmp = WorldMapControl.GetDestinationArea (1)
+ if tmp["Distance"]==-1:
+ print "Invalid target", tmp
+ return
+
+ CloseWorldMapWindow ()
+ GemRB.CreateMovement (tmp["Destination"], tmp["Entrance"], tmp["Direction"])
+ return
+
+def ChangeTooltip ():
+ global WorldMapWindow, WorldMapControl
+ global str
+
+ tmp = WorldMapControl.GetDestinationArea ()
+ if (tmp):
+ str = "%s: %d"%(GemRB.GetString(23084),tmp["Distance"])
+ else:
+ str=""
+
+ WorldMapControl.SetTooltip (str)
+ return
+
+def CloseWorldMapWindow ():
+ global WorldMapWindow, WorldMapControl
+
+ print "CloseWorldMapWindow found Mapwindow = ",MapWindow
+ if MapWindow:
+ # reopen map window
+ if WorldMapWindow:
+ WorldMapWindow.Unload ()
+ WorldMapWindow = None
+ WorldMapControl = None
+ OpenMapWindow ()
+ return
+
+ if WorldMapWindow:
+ WorldMapWindow.Unload ()
+ WorldMapWindow = None
+ WorldMapControl = None
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ return
+
+def WorldMapWindowCommon (Travel):
+ global WorldMapWindow, WorldMapControl
+
+ if WorldMapWindow:
+ CloseWorldMapWindow ()
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIWMAP", 640, 480)
+ WorldMapWindow = Window = GemRB.LoadWindow (0)
+ #saving the original portrait window
+ Window.SetFrame ()
+
+ Window.CreateWorldMapControl (4, 0, 62, 640, 418, Travel, "floattxt")
+ WorldMapControl = Window.GetControl (4)
+ WorldMapControl.SetAnimation ("WMDAG")
+ WorldMapControl.SetEvent (IE_GUI_WORLDMAP_ON_PRESS, MoveToNewArea)
+ WorldMapControl.SetEvent (IE_GUI_MOUSE_ENTER_WORLDMAP, ChangeTooltip)
+
+ #north
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapN)
+
+ #south
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapS)
+
+ #northwest
+ Button = Window.GetControl (8)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapNW)
+
+ #northeast
+ Button = Window.GetControl (9)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapNE)
+
+ #west
+ Button = Window.GetControl (10)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapW)
+
+ #center
+ Button = Window.GetControl (11)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapC)
+
+ #east
+ Button = Window.GetControl (12)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapE)
+
+ #southwest
+ Button = Window.GetControl (13)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapSW)
+
+ #southeast
+ Button = Window.GetControl (14)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapSE)
+
+ # Done
+ Button = Window.GetControl (0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseWorldMapWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ Window.SetVisible (WINDOW_VISIBLE)
+ MapC()
+ return
+
+def MapN():
+ WorldMapControl.AdjustScrolling (0, -10)
+ return
+
+def MapNE():
+ WorldMapControl.AdjustScrolling (10, -10)
+ return
+
+def MapE():
+ WorldMapControl.AdjustScrolling (10, 0)
+ return
+
+def MapSE():
+ WorldMapControl.AdjustScrolling (10, 10)
+ return
+
+def MapS():
+ WorldMapControl.AdjustScrolling (0, 10)
+ return
+
+def MapSW():
+ WorldMapControl.AdjustScrolling (-10, 10)
+ return
+
+def MapW():
+ WorldMapControl.AdjustScrolling (-10, 0)
+ return
+
+def MapNW():
+ WorldMapControl.AdjustScrolling (-10, -10)
+ return
+
+def MapC():
+ WorldMapControl.AdjustScrolling (0, 0)
+ return
+
+###################################################
+# End of file GUIMA.py
diff --git a/gemrb/GUIScripts/bg2/GUIMG.py b/gemrb/GUIScripts/bg2/GUIMG.py
new file mode 100644
index 0000000..4299be0
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIMG.py
@@ -0,0 +1,737 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMG.py - scripts to control mage spells windows from the GUIMG winpack
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import GUICommon
+import CommonTables
+from ie_spells import LS_MEMO
+
+MageWindow = None
+MageSpellInfoWindow = None
+MageSpellLevel = 0
+MageSpellUnmemorizeWindow = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+BookType = None
+OtherWindow = None
+Exclusions = None
+ContCond = None
+ContTarg = None
+SpellType = None
+Level = 1
+
+def OpenMageWindow ():
+ import GUICommonWindows
+ global MageWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenMageWindow):
+ if MageWindow:
+ MageWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MageWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMG", 640, 480)
+
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenMageWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ SetupMageWindow()
+ GUICommonWindows.SetSelectionChangeHandler (SetupMageWindow)
+ return
+
+def SetupMageWindow ():
+ global MageWindow
+ global BookType
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ BookType = 0
+ if (CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS), 8)==2):
+ BookType = 1
+
+ if MageWindow:
+ MageWindow.Unload()
+ MageWindow = None
+
+ if BookType:
+ MageWindow = Window = GemRB.LoadWindow (8)
+ else:
+ MageWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", MageWindow.ID)
+
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MagePrevLevelPress)
+
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageNextLevelPress)
+
+ #unknown usage
+ Button = Window.GetControl (55)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ #setup level buttons
+ for i in range (9):
+ Button = Window.GetControl (56 + i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RefreshMageLevel)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ for i in range (9):
+ Button = Window.GetControl (56 + i)
+ Button.SetVarAssoc ("MageSpellLevel", i)
+
+ # Setup memorized spells buttons
+ if not BookType:
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ Button.SetBorder (0,0,0,0,0,0,0,0,64,0,1)
+ Button.SetSprites ("SPELFRAM",0,0,0,0,0)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Setup book spells buttons
+ for i in range (24):
+ Button = Window.GetControl (27 + i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ UpdateMageWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ #bringing the window front
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def UpdateMageWindow ():
+ global MageMemorizedSpellList, MageKnownSpellList
+
+ MageMemorizedSpellList = []
+ MageKnownSpellList = []
+
+ Window = MageWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+ type = IE_SPELL_TYPE_WIZARD
+ level = MageSpellLevel
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, type, level, 1)
+
+ Label = Window.GetControl (0x10000032)
+ GemRB.SetToken("SPELLLEVEL", str(level+1) )
+ Label.SetText (10345)
+
+ Name = GemRB.GetPlayerName (pc, 0)
+ Label = Window.GetControl (0x10000035)
+ Label.SetText (Name)
+
+ known_cnt = GemRB.GetKnownSpellsCount (pc, type, level)
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+ if not BookType:
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ if i < mem_cnt:
+ ms = GemRB.GetMemorizedSpell (pc, type, level, i)
+ Button.SetSpellIcon (ms['SpellResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if ms['Flags']:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellUnmemorizeWindow)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageUnmemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenMageSpellInfoWindow)
+ spell = GemRB.GetSpell (ms['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ MageMemorizedSpellList.append (ms['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", i)
+ Button.EnableBorder (0, ms['Flags'] == 0)
+ else:
+ if i < max_mem_cnt:
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+ else:
+ label = Window.GetControl (0x10000040)
+ if known_cnt:
+ # we give sorcerers all charges for all the spells, so some extra math is needed
+ label.SetText (GemRB.GetString(61256) + " " + str(mem_cnt/known_cnt) + "/" + str(max_mem_cnt))
+ else:
+ label.SetText ("")
+
+ for i in range (24):
+ Button = Window.GetControl (27 + i)
+ if i < known_cnt:
+ ks = GemRB.GetKnownSpell (pc, type, level, i)
+ Button.SetSpellIcon (ks['SpellResRef'], 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageMemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenMageSpellInfoWindow)
+ spell = GemRB.GetSpell (ks['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ MageKnownSpellList.append (ks['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", 100 + i)
+
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+ if (CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS), 2)=="*") or \
+ GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MagePrevLevelPress ():
+ global MageSpellLevel
+
+ if MageSpellLevel > 0:
+ MageSpellLevel = MageSpellLevel - 1
+ UpdateMageWindow ()
+ return
+
+def MageNextLevelPress ():
+ global MageSpellLevel
+
+ if MageSpellLevel < 8:
+ MageSpellLevel = MageSpellLevel + 1
+ UpdateMageWindow ()
+ return
+
+def RefreshMageLevel ():
+ global MageSpellLevel
+
+ MageSpellLevel = GemRB.GetVar ("MageSpellLevel")
+ UpdateMageWindow ()
+ return
+
+def OpenMageSpellInfoWindow ():
+ global MageSpellInfoWindow
+
+ if MageSpellInfoWindow != None:
+ if MageSpellInfoWindow:
+ MageSpellInfoWindow.Unload ()
+ MageSpellInfoWindow = None
+ return
+
+ MageSpellInfoWindow = Window = GemRB.LoadWindow (3)
+
+ #back
+ Button = Window.GetControl (5)
+ Button.SetText (15416)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellInfoWindow)
+
+ #erase
+ index = GemRB.GetVar ("SpellButton")
+ if GUICommon.HasTOB():
+ Button = Window.GetControl (6)
+ if index < 100:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellRemoveWindow)
+ Button.SetText (63668)
+ if index < 100:
+ ResRef = MageMemorizedSpellList[index]
+ else:
+ ResRef = MageKnownSpellList[index - 100]
+
+ spell = GemRB.GetSpell (ResRef)
+
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (spell['SpellName'])
+
+ Button = Window.GetControl (2)
+ Button.SetSpellIcon (ResRef, 1)
+
+ Text = Window.GetControl (3)
+ Text.SetText (spell['SpellDesc'])
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnMageMemorizeSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ if GemRB.MemorizeSpell (pc, type, level, index):
+ UpdateMageWindow ()
+ return
+
+def CloseMageSpellUnmemorizeWindow ():
+ global MageSpellUnmemorizeWindow
+
+ if MageSpellUnmemorizeWindow:
+ MageSpellUnmemorizeWindow.Unload ()
+ MageSpellUnmemorizeWindow = None
+ return
+
+def OpenMageSpellRemoveWindow ():
+ global MageSpellUnmemorizeWindow
+
+ MageSpellUnmemorizeWindow = Window = GemRB.LoadWindow (101)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (63745)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageRemoveSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseMageSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OpenMageSpellUnmemorizeWindow ():
+ global MageSpellUnmemorizeWindow
+
+ MageSpellUnmemorizeWindow = Window = GemRB.LoadWindow (101)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (11824)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageUnmemorizeSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseMageSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnMageUnmemorizeSpell ():
+ if MageSpellUnmemorizeWindow:
+ CloseMageSpellUnmemorizeWindow()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton")
+
+ if GemRB.UnmemorizeSpell (pc, type, level, index):
+ UpdateMageWindow ()
+ return
+
+def OnMageRemoveSpell ():
+ CloseMageSpellUnmemorizeWindow()
+ OpenMageSpellInfoWindow()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton")-100
+
+ #remove spell from book
+ GemRB.RemoveSpell (pc, type, level, index)
+ UpdateMageWindow ()
+ return
+
+def LoadCondition ():
+ global ContCond, ContTarg
+
+ Table = GemRB.LoadTable ("contcond")
+ CondCount = Table.GetRowCount ()
+ ContCond = [0] * CondCount
+
+ for i in range (CondCount):
+ #get the condition's name and description
+ tuple = (Table.GetValue (i, 0),Table.GetValue (i, 1) )
+ ContCond[i] = tuple
+
+ Table = GemRB.LoadTable ("conttarg")
+ TargCount = Table.GetRowCount ()
+ ContTarg = [0] * TargCount
+
+ for i in range (TargCount):
+ #get the condition's name and description
+ tuple = (Table.GetValue (i, 0),Table.GetValue (i, 1) )
+ ContTarg[i] = tuple
+ return
+
+def OpenSequencerWindow ():
+ global OtherWindow
+ global ContingencyTextArea, TypeButton, OkButton
+ global Level, MaxLevel, Target, Count
+ global pc
+ global ContCond, ContTarg
+ global Spell1, Spell2, Spell3, Source
+
+ if ContCond == None:
+ LoadCondition()
+
+ Level = 1
+ Spell1 = ""
+ Spell2 = ""
+ Spell3 = ""
+ #the target player (who receives the contingency or sequencer)
+ pc = GemRB.GetVar("P0")
+ #maximum spell level
+ MaxLevel = GemRB.GetVar("P1")
+ #target 0 - any, 1 - caster only, 2 - sequencer
+ #spell count 1-3
+ p2 = GemRB.GetVar("P2")
+ Source = GemRB.GetSpellCastOn(pc)
+
+ print "Source: ", Source
+ Target = p2>>16
+ Count = p2&255
+ if Count > 3:
+ Count = 3
+
+ GemRB.LoadWindowPack ("GUIMG", 640, 480)
+
+ #saving the original portrait window
+ OtherWindow = Window = GemRB.LoadWindow (6)
+
+ Title = Window.GetControl (0x0fffffff)
+
+ ContingencyTextArea = Window.GetControl (25)
+
+ if Target == 2:
+ if Count < 3:
+ Title.SetText (55374)
+ ContingencyTextArea.SetText (60420)
+ else:
+ Title.SetText (55375)
+ ContingencyTextArea.SetText (55372)
+ else:
+ if Count < 3:
+ Title.SetText (11941)
+ else:
+ Title.SetText (55376)
+ ContingencyTextArea.SetText (55373)
+
+ CondSelect = Window.GetControl (4)
+ CondLabel = Window.GetControl (0x10000000)
+ TargSelect = Window.GetControl (6)
+ TargLabel = Window.GetControl (0x10000001)
+ TypeButton = Window.GetControl (8)
+
+ #no cleric spells available
+ if (CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS), 0)=="*" and
+ CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS), 1)=="*"):
+ TypeButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if Target == 2:
+ CondSelect.SetPos (-1,-1)
+ CondLabel.SetPos (-1,-1)
+ TargSelect.SetPos (-1,-1)
+ TargLabel.SetPos (-1,-1)
+ sb = Window.GetControl (5)
+ sb.SetPos (-1,-1)
+ sb = Window.GetControl (7)
+ sb.SetPos (-1,-1)
+ else:
+ CondSelect.SetFlags (IE_GUI_TEXTAREA_SELECTABLE, OP_SET)
+ CondSelect.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, ContingencyHelpCondition)
+ CondSelect.SetVarAssoc ("ContCond", 0)
+ for elem in ContCond:
+ CondSelect.Append (elem[0], -1)
+
+ TargSelect.SetFlags (IE_GUI_TEXTAREA_SELECTABLE, OP_SET)
+ TargSelect.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, ContingencyHelpTarget)
+ TargSelect.SetVarAssoc ("ContTarg", 0)
+ for elem in ContTarg:
+ TargSelect.Append (elem[0], -1)
+ #check if target is only self
+ if Target:
+ TargSelect.SetVarAssoc ("ContTarg", 1)
+ break
+
+ GemRB.SetVar ("SpellType", 0)
+ TypeButton.SetVarAssoc ("SpellType", 1)
+ TypeButton.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ TypeButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ContTypePressed)
+
+ Button = Window.GetControl (9)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, LevelIncrease)
+ Button = Window.GetControl (10)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, LevelDecrease)
+
+ OkButton = Window.GetControl (27)
+ OkButton.SetText (11973)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ OkButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = Window.GetControl (29)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ OkButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ContingencyOk)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ContingencyCancel)
+ ContTypePressed ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def UpdateSpellList ():
+ Window = OtherWindow
+
+ if SpellType:
+ Type = IE_SPELL_TYPE_PRIEST
+ else:
+ Type = IE_SPELL_TYPE_WIZARD
+
+ Label = Window.GetControl (0x1000001d)
+ Label.SetText (GemRB.GetString(12137)+str(Level) )
+
+ BuildSpellList(pc, Type, Level-1)
+
+ names = SpellList.keys()
+ names.sort()
+
+ cnt = len(names)
+ j = 0
+ for i in range(11,20):
+ Button = Window.GetControl (i)
+ Button.SetFont ("NUMBER")
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT|IE_GUI_BUTTON_ALIGN_BOTTOM,OP_OR)
+ if j<cnt:
+ Button.SetSpellIcon (names[j], 1)
+ Button.SetText( str(SpellList[names[j]]) )
+ Button.SetVarAssoc("Index", j)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ContingencyHelpSpell)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetSpellIcon("")
+ Button.SetText("")
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ j = j+1
+
+ Button = Window.GetControl (22)
+ Button.SetSpellIcon(Spell1, 1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteSpell1)
+ Button = Window.GetControl (23)
+ Button.SetSpellIcon(Spell2, 1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteSpell2)
+ Button = Window.GetControl (24)
+ Button.SetSpellIcon(Spell3, 1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteSpell3)
+
+ if not Spell1:
+ OkButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ OkButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def ContTypePressed ():
+ global SpellType
+
+ Label = OtherWindow.GetControl (0x10000002)
+ SpellType = GemRB.GetVar ("SpellType")
+
+ if SpellType:
+ Label.SetText (54352)
+ TypeButton.SetText (18039)
+ else:
+ Label.SetText (21836)
+ TypeButton.SetText (7204)
+ UpdateSpellList()
+ return
+
+def ContingencyOk ():
+ global OtherWindow
+
+ #set the storage
+ if Target == 2:
+ GemRB.ApplyEffect (pc, "Sequencer:Store", 0, 0, Spell1, Spell2, Spell3, Source)
+ else:
+ GemRB.ApplyEffect (pc, "CastSpellOnCondition", 0, GemRB.GetVar ("ContCond"), Spell1, Spell2, Spell3, Source)
+ #set the innate
+ GemRB.LearnSpell (pc, Source+"d", LS_MEMO)
+ OtherWindow.Unload()
+ return
+
+def ContingencyCancel ():
+ global OtherWindow
+
+ GemRB.SetPlayerStat (pc, IE_IDENTIFYMODE, 0)
+ OtherWindow.Unload()
+ return
+
+def ContingencyHelpSpell ():
+ global Spell1, Spell2, Spell3
+
+ names = SpellList.keys()
+ names.sort()
+ i = GemRB.GetVar("Index")
+ spell = names[i]
+
+ if Spell1=="" and Count>0:
+ Spell1 = spell
+ elif Spell2=="" and Count>1:
+ Spell2 = spell
+ elif Spell3=="" and Count>2:
+ Spell3 = spell
+
+ spl = GemRB.GetSpell(spell)
+ ContingencyTextArea.SetText (spl["SpellDesc"])
+ UpdateSpellList ()
+ return
+
+def DeleteSpell1 ():
+ global Spell1, Spell2, Spell3
+
+ Spell1 = Spell2
+ Spell2 = Spell3
+ Spell3 = ""
+ ContingencyTextArea.SetText("")
+ UpdateSpellList ()
+ return
+
+def DeleteSpell2 ():
+ global Spell2, Spell3
+
+ Spell2 = Spell3
+ Spell3 = ""
+ ContingencyTextArea.SetText("")
+ UpdateSpellList ()
+ return
+
+def DeleteSpell3 ():
+ global Spell3
+
+ Spell3 = ""
+ ContingencyTextArea.SetText("")
+ UpdateSpellList ()
+ return
+
+def ContingencyHelpCondition ():
+ i = GemRB.GetVar ("ContCond")
+ ContingencyTextArea.SetText (ContCond[i][1])
+ return
+
+def ContingencyHelpTarget ():
+ i = GemRB.GetVar ("ContTarg")
+ ContingencyTextArea.SetText (ContTarg[i][1])
+ return
+
+def LoadExclusions():
+ global Exclusions
+
+ ExclusionTable = GemRB.LoadTable ("contingx")
+ Columns = ExclusionTable.GetColumnCount ()
+ Rows = ExclusionTable.GetRowCount ()
+ Exclusions = []
+ for i in range (Columns):
+ Exclusions.append ([])
+ for j in range (Rows):
+ spell = ExclusionTable.GetValue (j,i,0)
+ if spell[0]=="*":
+ break
+ Exclusions[i].append (spell.lower())
+
+ return
+
+#TODO: build a correct list for sorcerers too!
+def BuildSpellList (pc, type, level):
+ global SpellList
+
+ if not Exclusions:
+ LoadExclusions()
+
+ SpellList = {}
+ dummy = [Spell1,Spell2,Spell3]
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+
+ for i in range(mem_cnt):
+ ms = GemRB.GetMemorizedSpell (pc, type, level, i)
+ if ms["Flags"]:
+ spell = ms["SpellResRef"]
+ if spell in Exclusions[level]:
+ continue
+ if spell in dummy:
+ dummy.remove(spell)
+ continue
+ if not (spell in SpellList):
+ SpellList[spell] = 1
+ else:
+ SpellList[spell] = SpellList[spell]+1
+ return
+
+def LevelIncrease():
+ global Level
+
+ if Level<MaxLevel:
+ Level = Level+1
+ UpdateSpellList()
+ return
+
+def LevelDecrease():
+ global Level
+
+ if Level>1:
+ Level = Level-1
+ UpdateSpellList()
+ return
+
+###################################################
+# End of file GUIMG.py
diff --git a/gemrb/GUIScripts/bg2/GUIMOVIE.py b/gemrb/GUIScripts/bg2/GUIMOVIE.py
new file mode 100644
index 0000000..671e2ce
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIMOVIE.py
@@ -0,0 +1,89 @@
+# 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.
+#
+#
+import GemRB
+import GUICommon
+
+MovieWindow = 0
+TextAreaControl = 0
+MoviesTable = 0
+PlayButton = 0
+
+def OnLoad():
+ global MovieWindow, TextAreaControl, MoviesTable, PlayButton
+
+ GemRB.LoadWindowPack ("GUIMOVIE", 640, 480)
+ MovieWindow = GemRB.LoadWindow (0)
+ MovieWindow.SetFrame ()
+ TextAreaControl = MovieWindow.GetControl (0)
+ PlayButton = MovieWindow.GetControl (2)
+ CreditsButton = MovieWindow.GetControl (3)
+ DoneButton = MovieWindow.GetControl (4)
+ MoviesTable = GemRB.LoadTable ("MOVIDESC")
+ for i in range( MoviesTable.GetRowCount () ):
+ t = MoviesTable.GetRowName (i)
+ if GemRB.GetVar (t)==1:
+ s = MoviesTable.GetValue (i, 0)
+ TextAreaControl.Append (s,-1)
+ TextAreaControl.SetVarAssoc ("MovieIndex",0)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE, OP_NAND)
+ TextAreaControl.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, MoviePress)
+ PlayButton.SetText (17318)
+ CreditsButton.SetText (15591)
+ DoneButton.SetText (11973)
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ PlayButton.SetStatus (IE_GUI_BUTTON_DISABLED)
+ CreditsButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CreditsPress)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePress)
+ MovieWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MoviePress():
+ PlayButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE,OP_SET) # show selection
+ return
+
+def PlayPress():
+ s = GemRB.GetVar ("MovieIndex")
+ for i in range( MoviesTable.GetRowCount () ):
+ t = MoviesTable.GetRowName (i)
+ if GemRB.GetVar (t)==1:
+ if s==0:
+ PlayButton.SetStatus (IE_GUI_BUTTON_DISABLED)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE,OP_NAND) # hide selection
+ s = MoviesTable.GetRowName (i)
+ GemRB.PlayMovie (s, 1)
+ MovieWindow.Invalidate ()
+ return
+ s = s - 1
+ return
+
+def CreditsPress():
+ if MovieWindow:
+ MovieWindow.Unload ()
+ GemRB.SetNextScript ("GUISONGS")
+ return
+
+def DonePress():
+ if MovieWindow:
+ MovieWindow.Unload ()
+ if GUICommon.HasTOB():
+ GemRB.SetNextScript ("Start2")
+ else:
+ GemRB.SetNextScript ("Start")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUIOPT.py b/gemrb/GUIScripts/bg2/GUIOPT.py
new file mode 100644
index 0000000..444925d
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIOPT.py
@@ -0,0 +1,737 @@
+# -*-python-*-
+# 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.
+#
+
+# GUIOPT.py - scripts to control options windows mostly from the GUIOPT winpack:
+# 0 - Main options window (peacock tail)
+# 1 - Video options window
+# 2 - msg win with 1 button
+# 3 - msg win with 2 buttons
+# 4 - msg win with 3 buttons
+# 5 - Audio options window
+# 6 - Gameplay options window
+# 8 - Feedback options window
+# 9 - Autopause options window
+
+###################################################
+import GemRB
+import GUICommon
+import GUISAVE
+from GUIDefines import *
+
+###################################################
+GameOptionsWindow = None
+PortraitWindow = None
+OldPortraitWindow = None
+OptionsWindow = None
+OldOptionsWindow = None
+HelpTextArea = None
+
+LoadMsgWindow = None
+QuitMsgWindow = None
+
+###################################################
+def CloseOptionsWindow ():
+ import GUICommonWindows
+ global GameOptionsWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GameOptionsWindow == None:
+ return
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ GameOptionsWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ return
+
+###################################################
+def OpenOptionsWindow ():
+ """Open main options window"""
+ import GUICommonWindows
+ global GameOptionsWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenOptionsWindow):
+ CloseOptionsWindow()
+ return
+
+ hideflag = GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIOPT", 640, 480)
+ GameOptionsWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", GameOptionsWindow.ID)
+ #saving the original portrait window
+ if OldPortraitWindow == None:
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenOptionsWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ # Return to Game
+ Button = Window.GetControl (11)
+ Button.SetText (10308)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenOptionsWindow)
+
+ # Quit Game
+ Button = Window.GetControl (10)
+ Button.SetText (13731)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenQuitMsgWindow)
+
+ # Load Game
+ Button = Window.GetControl (5)
+ Button.SetText (13729)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenLoadMsgWindow)
+
+ # Save Game
+ Button = Window.GetControl (6)
+ Button.SetText (13730)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSaveMsgWindow)
+
+ # Video Options
+ Button = Window.GetControl (7)
+ Button.SetText (17162)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenVideoOptionsWindow)
+
+ # Audio Options
+ Button = Window.GetControl (8)
+ Button.SetText (17164)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenAudioOptionsWindow)
+
+ # Gameplay Options
+ Button = Window.GetControl (9)
+ Button.SetText (17165)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenGameplayOptionsWindow)
+
+ # game version, e.g. v1.1.0000
+ Label = Window.GetControl (0x1000000b)
+ Label.SetText (GEMRB_VERSION)
+
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+
+def CloseVideoOptionsWindow ():
+ OpenOptionsWindow ()
+ OpenOptionsWindow ()
+
+def OpenVideoOptionsWindow ():
+ """Open video options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (6)
+
+ HelpTextArea = OptHelpText ('VideoOptions', Window, 33, 18038)
+
+ OptDone ('VideoOptions', Window, 21)
+ OptCancel ('VideoOptions', Window, 32)
+
+ OptSlider ('Brightness', Window, 3, 'Brightness Correction', 4)
+ OptSlider ('Contrast', Window, 22, 'Gamma Correction', 1)
+
+ OptRadio ('BPP', Window, 5, 37, 'BitsPerPixel', 16)
+ OptRadio ('BPP', Window, 6, 37, 'BitsPerPixel', 24)
+ OptRadio ('BPP', Window, 7, 37, 'BitsPerPixel', 32)
+
+ OptCheckbox ('FullScreen', Window, 9, 38, 'Full Screen', 1)
+
+ OptCheckbox ('TransShadow', Window, 51, 50, 'Translucent Shadows', 1)
+ OptCheckbox ('SoftMirrBlt', Window, 40, 44, 'SoftMirrorBlt' ,1)
+ OptCheckbox ('SoftTransBlt', Window, 41, 46, 'SoftSrcKeyBlt' ,1)
+ OptCheckbox ('SoftStandBlt', Window, 42, 48, 'SoftBltFast' ,1)
+
+ GameOptionsWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpFullScreen ():
+ HelpTextArea.SetText (18000)
+ GemRB.SetFullScreen (GemRB.GetVar("Full Screen"))
+
+def DisplayHelpBPP ():
+ HelpTextArea.SetText (17205)
+
+def DisplayHelpBrightness ():
+ HelpTextArea.SetText (17203)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction"))
+
+def DisplayHelpContrast ():
+ HelpTextArea.SetText (17204)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction"))
+
+def DisplayHelpSoftMirrBlt ():
+ HelpTextArea.SetText (18004)
+
+def DisplayHelpSoftTransBlt ():
+ HelpTextArea.SetText (18006)
+
+def DisplayHelpSoftStandBlt ():
+ HelpTextArea.SetText (18007)
+
+def DisplayHelpTransShadow ():
+ HelpTextArea.SetText (20620)
+
+
+###################################################
+
+def CloseAudioOptionsWindow ():
+ OpenOptionsWindow ()
+ OpenOptionsWindow ()
+
+def OpenAudioOptionsWindow ():
+ """Open audio options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (7)
+
+ HelpTextArea = OptHelpText ('AudioOptions', Window, 14, 18040)
+
+ OptDone ('AudioOptions', Window, 24)
+ OptCancel ('AudioOptions', Window, 25)
+ OptButton ('CharacterSounds', Window, 13, 17778)
+
+ OptSlider ('AmbientVolume', Window, 1, 'Volume Ambients', 10)
+ OptSlider ('SoundFXVolume', Window, 2, 'Volume SFX', 10)
+ OptSlider ('VoiceVolume', Window, 3, 'Volume Voices', 10)
+ OptSlider ('MusicVolume', Window, 4, 'Volume Music', 10)
+ OptSlider ('MovieVolume', Window, 22, 'Volume Movie', 10)
+
+ OptCheckbox ('CreativeEAX', Window, 26, 28, 'Environmental Audio', 1)
+ GameOptionsWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+
+def DisplayHelpAmbientVolume ():
+ HelpTextArea.SetText (18008)
+ GemRB.UpdateAmbientsVolume ()
+
+def DisplayHelpSoundFXVolume ():
+ HelpTextArea.SetText (18009)
+
+def DisplayHelpVoiceVolume ():
+ HelpTextArea.SetText (18010)
+
+def DisplayHelpMusicVolume ():
+ HelpTextArea.SetText (18011)
+ GemRB.UpdateMusicVolume ()
+
+def DisplayHelpMovieVolume ():
+ HelpTextArea.SetText (18012)
+
+def DisplayHelpCreativeEAX ():
+ HelpTextArea.SetText (18022)
+
+###################################################
+
+def CloseCharacterSoundsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+ OpenGameplayOptionsWindow ()
+ return
+
+def OpenCharacterSoundsWindow ():
+ """Open character sounds window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (12)
+
+ HelpTextArea = OptHelpText ('CharacterSounds', Window, 16, 18041)
+
+ OptDone ('CharacterSounds', Window, 24)
+ OptCancel ('CharacterSounds', Window, 25)
+
+ OptCheckbox ('Subtitles', Window, 5, 20, 'Subtitles', 1)
+ OptCheckbox ('AttackSounds', Window, 6, 18, 'Attack Sounds', 1)
+ OptCheckbox ('Footsteps', Window, 7, 19, 'Footsteps', 1)
+ OptRadio ('CommandSounds', Window, 8, 21, 'Command Sounds Frequency', 1)
+ OptRadio ('CommandSounds', Window, 9, 21, 'Command Sounds Frequency', 2)
+ OptRadio ('CommandSounds', Window, 10, 21, 'Command Sounds Frequency', 3)
+ OptRadio ('SelectionSounds', Window, 58, 57, 'Selection Sounds Frequency', 1)
+ OptRadio ('SelectionSounds', Window, 59, 57, 'Selection Sounds Frequency', 2)
+ OptRadio ('SelectionSounds', Window, 60, 57, 'Selection Sounds Frequency', 3)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def DisplayHelpSubtitles ():
+ HelpTextArea.SetText (18015)
+
+def DisplayHelpAttackSounds ():
+ HelpTextArea.SetText (18013)
+
+def DisplayHelpFootsteps ():
+ HelpTextArea.SetText (18014)
+
+def DisplayHelpCommandSounds ():
+ HelpTextArea.SetText (18016)
+
+def DisplayHelpSelectionSounds ():
+ HelpTextArea.SetText (11352)
+
+###################################################
+
+def CloseGameplayOptionsWindow ():
+ OpenOptionsWindow ()
+ OpenOptionsWindow ()
+
+def OpenGameplayOptionsWindow ():
+ """Open gameplay options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ #gameplayoptions
+ GameOptionsWindow = Window = GemRB.LoadWindow (8)
+
+
+ HelpTextArea = OptHelpText ('GameplayOptions', Window, 40, 18042)
+
+ OptDone ('GameplayOptions', Window, 7)
+ OptCancel ('GameplayOptions', Window, 20)
+
+ OptSlider ('TooltipDelay', Window, 1, 'Tooltips', TOOLTIP_DELAY_FACTOR)
+ OptSlider ('MouseScrollingSpeed', Window, 2, 'Mouse Scroll Speed', 5)
+ OptSlider ('KeyboardScrollingSpeed', Window, 3, 'Keyboard Scroll Speed', 5)
+ OptSlider ('Difficulty', Window, 12, 'Difficulty Level', 0)
+
+ OptCheckbox ('DitherAlways', Window, 14, 25, 'Always Dither', 1)
+ OptCheckbox ('Gore', Window, 19, 27, 'Gore', 1)
+ OptCheckbox ('Infravision', Window, 42, 44, 'Infravision', 1)
+ OptCheckbox ('Weather', Window, 47, 46, 'Weather', 1)
+ if GUICommon.GameIsBG2():
+ OptCheckbox ('RestUntilHealed', Window, 50, 48, 'Heal Party on Rest', 1)
+
+ OptButton ('FeedbackOptions', Window, 5, 17163)
+ OptButton ('AutopauseOptions', Window, 6, 17166)
+ if GUICommon.GameIsBG2():
+ OptButton ('HotkeyOptions', Window, 51, 816)
+
+ GameOptionsWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpTooltipDelay ():
+ HelpTextArea.SetText (18017)
+ GemRB.SetTooltipDelay (GemRB.GetVar ("Tooltips") )
+
+def DisplayHelpMouseScrollingSpeed ():
+ HelpTextArea.SetText (18018)
+ GemRB.SetMouseScrollSpeed (GemRB.GetVar ("Mouse Scroll Speed") )
+
+def DisplayHelpKeyboardScrollingSpeed ():
+ HelpTextArea.SetText (18019)
+
+def DisplayHelpDifficulty ():
+ HelpTextArea.SetText (18020)
+
+def DisplayHelpDitherAlways ():
+ HelpTextArea.SetText (18021)
+
+def DisplayHelpGore ():
+ HelpTextArea.SetText (18023)
+
+def DisplayHelpInfravision ():
+ HelpTextArea.SetText (11797)
+
+def DisplayHelpWeather ():
+ HelpTextArea.SetText (20619)
+
+def DisplayHelpRestUntilHealed ():
+ HelpTextArea.SetText (2242)
+
+###################################################
+
+def CloseFeedbackOptionsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+ OpenGameplayOptionsWindow ()
+
+
+def OpenFeedbackOptionsWindow ():
+ """Open feedback options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ #feedback
+ GameOptionsWindow = Window = GemRB.LoadWindow (9)
+
+ HelpTextArea = OptHelpText ('FeedbackOptions', Window, 28, 18043)
+
+ OptDone ('FeedbackOptions', Window, 26)
+ OptCancel ('FeedbackOptions', Window, 27)
+
+ OptSlider ('MarkerFeedback', Window, 8, 'GUI Feedback Level', 1)
+ OptSlider ('LocatorFeedback', Window, 9, 'Locator Feedback Level', 1)
+
+ OptCheckbox ('ToHitRolls', Window, 10, 32, 'Rolls', 1)
+ OptCheckbox ('CombatInfo', Window, 11, 33, 'Combat Info', 1)
+ OptCheckbox ('Actions', Window, 12, 34, 'Actions', 1)
+ OptCheckbox ('States', Window, 13, 35, 'State Changes', 1)
+ OptCheckbox ('Selection', Window, 14, 36, 'Selection Text', 1)
+ OptCheckbox ('Miscellaneous', Window, 15, 37, 'Miscellaneous Text', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpMarkerFeedback ():
+ HelpTextArea.SetText (18024)
+
+def DisplayHelpLocatorFeedback ():
+ HelpTextArea.SetText (18025)
+
+def DisplayHelpToHitRolls ():
+ HelpTextArea.SetText (18026)
+
+def DisplayHelpCombatInfo ():
+ HelpTextArea.SetText (18027)
+
+def DisplayHelpActions ():
+ HelpTextArea.SetText (18028)
+
+def DisplayHelpStates ():
+ HelpTextArea.SetText (18029)
+
+def DisplayHelpSelection ():
+ HelpTextArea.SetText (18030)
+
+def DisplayHelpMiscellaneous ():
+ HelpTextArea.SetText (18031)
+
+###################################################
+
+def CloseAutopauseOptionsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+ OpenGameplayOptionsWindow ()
+ return
+
+def OpenAutopauseOptionsWindow ():
+ """Open autopause options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (10)
+
+ HelpTextArea = OptHelpText ('AutopauseOptions', Window, 15, 18044)
+
+ OptDone ('AutopauseOptions', Window, 11)
+ OptCancel ('AutopauseOptions', Window, 14)
+
+ OptCheckbox ('CharacterHit', Window, 1, 17, 'Auto Pause State', 1)
+ OptCheckbox ('CharacterInjured', Window, 2, 18, 'Auto Pause State', 2)
+ OptCheckbox ('CharacterDead', Window, 3, 19, 'Auto Pause State', 4)
+ OptCheckbox ('CharacterAttacked', Window, 4, 20, 'Auto Pause State', 8)
+ OptCheckbox ('WeaponUnusable', Window, 5, 21, 'Auto Pause State', 16)
+ OptCheckbox ('TargetGone', Window, 13, 22, 'Auto Pause State', 32)
+ OptCheckbox ('EndOfRound', Window, 25, 24, 'Auto Pause State', 64)
+ if Window.HasControl(26, IE_GUI_BUTTON):
+ OptCheckbox ('EnemySighted', Window, 26, 27, 'Auto Pause State', 128)
+ if GUICommon.GameIsBG2():
+ OptCheckbox ('SpellCast', Window, 34, 30, 'Auto Pause State', 256)
+ OptCheckbox ('TrapFound', Window, 31, 33, 'Auto Pause State', 512)
+ OptCheckbox ('CenterOnActor', Window, 31, 33, 'Auto Pause Center', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpCharacterHit ():
+ HelpTextArea.SetText (18032)
+
+def DisplayHelpCharacterInjured ():
+ HelpTextArea.SetText (18033)
+
+def DisplayHelpCharacterDead ():
+ HelpTextArea.SetText (18034)
+
+def DisplayHelpCharacterAttacked ():
+ HelpTextArea.SetText (18035)
+
+def DisplayHelpWeaponUnusable ():
+ HelpTextArea.SetText (18036)
+
+def DisplayHelpTargetGone ():
+ HelpTextArea.SetText (18037)
+
+def DisplayHelpEndOfRound ():
+ HelpTextArea.SetText (10640)
+
+def DisplayHelpEnemySighted ():
+ HelpTextArea.SetText (23514)
+
+def DisplayHelpSpellCast ():
+ HelpTextArea.SetText (58171)
+
+def DisplayHelpTrapFound ():
+ HelpTextArea.SetText (31872)
+
+def DisplayHelpCenterOnActor ():
+ HelpTextArea.SetText (10571)
+
+###################################################
+
+def OpenSaveMsgWindow ():
+ GemRB.SetVar("QuitAfterSave",0)
+ GUISAVE.OpenSaveWindow ()
+ #save the game without quitting
+ return
+
+###################################################
+
+def OpenLoadMsgWindow ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ return
+
+ LoadMsgWindow = Window = GemRB.LoadWindow (4)
+
+ # Load
+ Button = Window.GetControl (0)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadGamePress)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseLoadMsgWindow)
+
+ # Loading a game will destroy ...
+ Text = Window.GetControl (3)
+ Text.SetText (19531)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseLoadMsgWindow ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ LoadMsgWindow.Unload ()
+ LoadMsgWindow = None
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ GameOptionsWindow.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def LoadGamePress ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ LoadMsgWindow.Unload ()
+ LoadMsgWindow = None
+ GemRB.QuitGame ()
+ OpenOptionsWindow()
+ GemRB.SetNextScript ("GUILOAD")
+ return
+
+#save game AND quit
+def SaveGamePress ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ #we need to set a state: quit after save
+ GemRB.SetVar("QuitAfterSave",1)
+ OpenOptionsWindow()
+ GUISAVE.OpenSaveWindow ()
+ return
+
+def QuitGamePress ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ GemRB.QuitGame ()
+ OpenOptionsWindow()
+ GemRB.SetNextScript ("Start")
+ return
+
+###################################################
+
+def OpenQuitMsgWindow ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ return
+
+ QuitMsgWindow = Window = GemRB.LoadWindow (5)
+
+ # Save
+ Button = Window.GetControl (0)
+ Button.SetText (15589)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SaveGamePress)
+
+ # Quit Game
+ Button = Window.GetControl (1)
+ Button.SetText (15417)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitGamePress)
+
+ # Cancel
+ Button = Window.GetControl (2)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseQuitMsgWindow)
+
+ # Do you wish to save the game ....
+ Text = Window.GetControl (3)
+ Text.SetText (16456)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseQuitMsgWindow ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ GameOptionsWindow.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+#TODO
+def OpenHotkeyOptionsWindow ():
+ return
+
+def CloseHotkeyOptionsWindow ():
+ return
+
+###################################################
+# These functions help to setup controls found
+# in Video, Audio, Gameplay, Feedback and Autopause
+# options windows
+
+# These controls are usually made from an active
+# control (button, slider ...) and a label
+
+
+def OptSlider (name, window, slider_id, variable, value):
+ """Standard slider for option windows"""
+ slider = window.GetControl (slider_id)
+ slider.SetVarAssoc (variable, value)
+ slider.SetEvent (IE_GUI_SLIDER_ON_CHANGE, eval("DisplayHelp" + name))
+ return slider
+
+def OptRadio (name, window, button_id, label_id, variable, value):
+ """Standard radio button for option windows"""
+
+ button = window.GetControl (button_id)
+ button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("DisplayHelp" + name))
+ button.SetVarAssoc (variable, value)
+
+ label = window.GetControl (label_id)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+
+ return button
+
+def OptCheckbox (name, window, button_id, label_id, variable, value):
+ """Standard checkbox for option windows"""
+
+ button = window.GetControl (button_id)
+ button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("DisplayHelp" + name))
+ button.SetVarAssoc (variable, value)
+
+ label = window.GetControl (label_id)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+
+ return button
+
+def OptButton (name, window, button_id, button_strref):
+ """Standard subwindow button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Open%sWindow" %name))
+ button.SetText (button_strref)
+
+def OptDone (name, window, button_id):
+ """Standard `Done' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (11973) # Done
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Close%sWindow" %name))
+ button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+def OptCancel (name, window, button_id):
+ """Standard `Cancel' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (13727) # Cancel
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Close%sWindow" %name))
+ button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+def OptHelpText (name, window, text_id, text_strref):
+ """Standard textarea with context help for option windows"""
+ text = window.GetControl (text_id)
+ text.SetText (text_strref)
+ return text
+
+###################################################
+# End of file GUIOPT.py
diff --git a/gemrb/GUIScripts/bg2/GUIOPT10.py b/gemrb/GUIScripts/bg2/GUIOPT10.py
new file mode 100644
index 0000000..2accde3
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIOPT10.py
@@ -0,0 +1,188 @@
+# 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.
+#
+#
+#autopause
+import GemRB
+
+def OnLoad():
+ global AutoPauseWindow, TextAreaControl
+
+ GemRB.LoadWindowPack("GUIOPT", 640, 480)
+
+ AutoPauseWindow = GemRB.LoadWindow(10)
+ TextAreaControl = AutoPauseWindow.GetControl(15)
+
+ ChHitButton = AutoPauseWindow.GetControl(17)
+ ChHitButtonB = AutoPauseWindow.GetControl(1)
+
+ ChInjured = AutoPauseWindow.GetControl(18)
+ ChInjuredB = AutoPauseWindow.GetControl(2)
+
+ ChDeath = AutoPauseWindow.GetControl(19)
+ ChDeathB = AutoPauseWindow.GetControl(3)
+
+ ChAttacked = AutoPauseWindow.GetControl(20)
+ ChAttackedB = AutoPauseWindow.GetControl(4)
+
+ WeaponUnusable = AutoPauseWindow.GetControl(21)
+ WeaponUnusableB = AutoPauseWindow.GetControl(5)
+
+ TargetDestroyed = AutoPauseWindow.GetControl(22)
+ TargetDestroyedB = AutoPauseWindow.GetControl(13)
+
+ EndOfRound = AutoPauseWindow.GetControl(24)
+ EndOfRoundB = AutoPauseWindow.GetControl(25)
+
+ EnemySighted = AutoPauseWindow.GetControl(27)
+ EnemySightedB = AutoPauseWindow.GetControl(26)
+
+ SpellCast = AutoPauseWindow.GetControl(30)
+ SpellCastB = AutoPauseWindow.GetControl(34)
+
+ TrapFound = AutoPauseWindow.GetControl(33)
+ TrapFoundB = AutoPauseWindow.GetControl(31)
+
+ AutopauseCenter = AutoPauseWindow.GetControl(36)
+ AutopauseCenterB = AutoPauseWindow.GetControl(37)
+
+ OkButton = AutoPauseWindow.GetControl(11)
+ CancelButton = AutoPauseWindow.GetControl(14)
+ TextAreaControl.SetText(18044)
+ OkButton.SetText(11973)
+ CancelButton.SetText(13727)
+
+ ChHitButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ChHitButtonPress)
+ ChHitButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ChHitButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, ChHitButtonPress)
+
+ ChInjured.SetEvent(IE_GUI_BUTTON_ON_PRESS, ChInjuredPress)
+ ChInjuredB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ChInjuredB.SetEvent(IE_GUI_BUTTON_ON_PRESS, ChInjuredPress)
+
+ ChDeath.SetEvent(IE_GUI_BUTTON_ON_PRESS, ChDeathPress)
+ ChDeathB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ChDeathB.SetEvent(IE_GUI_BUTTON_ON_PRESS, ChDeathPress)
+
+ ChAttacked.SetEvent(IE_GUI_BUTTON_ON_PRESS, ChAttackedPress)
+ ChAttackedB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ChAttackedB.SetEvent(IE_GUI_BUTTON_ON_PRESS, ChAttackedPress)
+
+ WeaponUnusable.SetEvent(IE_GUI_BUTTON_ON_PRESS, WeaponUnusablePress)
+ WeaponUnusableB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ WeaponUnusableB.SetEvent(IE_GUI_BUTTON_ON_PRESS, WeaponUnusablePress)
+
+ TargetDestroyed.SetEvent(IE_GUI_BUTTON_ON_PRESS, TargetDestroyedPress)
+ TargetDestroyedB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ TargetDestroyedB.SetEvent(IE_GUI_BUTTON_ON_PRESS, TargetDestroyedPress)
+
+ EndOfRound.SetEvent(IE_GUI_BUTTON_ON_PRESS, EndOfRoundPress)
+ EndOfRoundB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ EndOfRoundB.SetEvent(IE_GUI_BUTTON_ON_PRESS, EndOfRoundPress)
+
+ EnemySighted.SetEvent(IE_GUI_BUTTON_ON_PRESS, EnemySightedPress)
+ EnemySightedB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ EnemySightedB.SetEvent(IE_GUI_BUTTON_ON_PRESS, EnemySightedPress)
+
+ SpellCast.SetEvent(IE_GUI_BUTTON_ON_PRESS, SpellCastPress)
+ SpellCastB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ SpellCastB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SpellCastPress)
+
+ TrapFound.SetEvent(IE_GUI_BUTTON_ON_PRESS, TrapFoundPress)
+ TrapFoundB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ TrapFoundB.SetEvent(IE_GUI_BUTTON_ON_PRESS, TrapFoundPress)
+
+ AutopauseCenter.SetEvent(IE_GUI_BUTTON_ON_PRESS, AutopauseCenterPress)
+ AutopauseCenterB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ AutopauseCenterB.SetEvent(IE_GUI_BUTTON_ON_PRESS, AutopauseCenterPress)
+
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ AutopauseCenterB.SetVarAssoc("Auto Pause Center",1)
+
+ ChHitButtonB.SetVarAssoc("Auto Pause State",1)
+ ChInjuredB.SetVarAssoc("Auto Pause State",2)
+ ChDeathB.SetVarAssoc("Auto Pause State",4)
+ ChAttackedB.SetVarAssoc("Auto Pause State",8)
+ WeaponUnusableB.SetVarAssoc("Auto Pause State",16)
+ TargetDestroyedB.SetVarAssoc("Auto Pause State",32)
+ EndOfRoundB.SetVarAssoc("Auto Pause State",64)
+ EnemySightedB.SetVarAssoc("Auto Pause State",128)
+ SpellCastB.SetVarAssoc("Auto Pause State",256)
+ TrapFoundB.SetVarAssoc("Auto Pause State",512)
+
+ AutoPauseWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ChHitButtonPress():
+ TextAreaControl.SetText(18032)
+ return
+
+def ChInjuredPress():
+ TextAreaControl.SetText(18033)
+ return
+
+def ChDeathPress():
+ TextAreaControl.SetText(18034)
+ return
+
+def ChAttackedPress():
+ TextAreaControl.SetText(18035)
+ return
+
+def WeaponUnusablePress():
+ TextAreaControl.SetText(18036)
+ return
+
+def TargetDestroyedPress():
+ TextAreaControl.SetText(18037)
+ return
+
+def EndOfRoundPress():
+ TextAreaControl.SetText(10640)
+ return
+
+def EnemySightedPress():
+ TextAreaControl.SetText(23514)
+ return
+
+def SpellCastPress():
+ TextAreaControl.SetText(58171)
+ return
+
+def TrapFoundPress():
+ TextAreaControl.SetText(31872)
+ return
+
+def AutopauseCenterPress():
+ TextAreaControl.SetText(10571)
+ return
+
+def OkPress():
+ if AutoPauseWindow:
+ AutoPauseWindow.Unload()
+ GemRB.SetNextScript("GUIOPT8")
+ return
+
+def CancelPress():
+ if AutoPauseWindow:
+ AutoPauseWindow.Unload()
+ GemRB.SetNextScript("GUIOPT8")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUIOPT12.py b/gemrb/GUIScripts/bg2/GUIOPT12.py
new file mode 100644
index 0000000..fcd31ff
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIOPT12.py
@@ -0,0 +1,127 @@
+# 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.
+#
+#
+#character sounds
+import GemRB
+
+SoundWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global SoundWindow, TextAreaControl
+ GemRB.LoadWindowPack("GUIOPT", 640, 480)
+ SoundWindow = GemRB.LoadWindow(12)
+ TextAreaControl = SoundWindow.GetControl(16)
+ SubtitleButton = SoundWindow.GetControl(20)
+ SubtitleButtonB = SoundWindow.GetControl(5)
+ WarCryButton = SoundWindow.GetControl(18)
+ WarCryButtonB = SoundWindow.GetControl(6)
+ StepsButton = SoundWindow.GetControl(19)
+ StepsButtonB = SoundWindow.GetControl(7)
+ ActionButton = SoundWindow.GetControl(21)
+ ActionButtonB1 = SoundWindow.GetControl(8)
+ ActionButtonB2 = SoundWindow.GetControl(9)
+ ActionButtonB3 = SoundWindow.GetControl(10)
+ SelectionButton = SoundWindow.GetControl(57)
+ SelectionButtonB1 = SoundWindow.GetControl(58)
+ SelectionButtonB2 = SoundWindow.GetControl(59)
+ SelectionButtonB3 = SoundWindow.GetControl(60)
+ OkButton = SoundWindow.GetControl(24)
+ CancelButton = SoundWindow.GetControl(25)
+ TextAreaControl.SetText(18041)
+ OkButton.SetText(11973)
+ CancelButton.SetText(13727)
+ SubtitleButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SubtitlePress)
+ SubtitleButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SubtitlePress)
+ SubtitleButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ SubtitleButtonB.SetVarAssoc("Subtitles",1)
+
+ WarCryButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, WarCryPress)
+ WarCryButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, WarCryPress)
+ WarCryButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ WarCryButtonB.SetVarAssoc("Attack Sounds",1)
+
+ StepsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, StepsPress)
+ StepsButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, StepsPress)
+ StepsButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ StepsButtonB.SetVarAssoc("Footsteps",1)
+
+ ActionButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ActionPress)
+ ActionButtonB1.SetEvent(IE_GUI_BUTTON_ON_PRESS, ActionPress)
+ ActionButtonB1.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ ActionButtonB1.SetVarAssoc("Command Sounds Frequency",1)
+
+ ActionButtonB2.SetEvent(IE_GUI_BUTTON_ON_PRESS, ActionPress)
+ ActionButtonB2.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ ActionButtonB2.SetVarAssoc("Command Sounds Frequency",2)
+
+ ActionButtonB3.SetEvent(IE_GUI_BUTTON_ON_PRESS, ActionPress)
+ ActionButtonB3.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ ActionButtonB3.SetVarAssoc("Command Sounds Frequency",3)
+
+ SelectionButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionPress)
+ SelectionButtonB1.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionPress)
+ SelectionButtonB1.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ SelectionButtonB1.SetVarAssoc("Selection Sounds Frequency",1)
+
+ SelectionButtonB2.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionPress)
+ SelectionButtonB2.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ SelectionButtonB2.SetVarAssoc("Selection Sounds Frequency",2)
+
+ SelectionButtonB3.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionPress)
+ SelectionButtonB3.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ SelectionButtonB3.SetVarAssoc("Selection Sounds Frequency",3)
+
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ SoundWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def SubtitlePress():
+ TextAreaControl.SetText(18015)
+ return
+
+def WarCryPress():
+ TextAreaControl.SetText(18013)
+ return
+
+def StepsPress():
+ TextAreaControl.SetText(18014)
+ return
+
+def ActionPress():
+ TextAreaControl.SetText(18016)
+ return
+
+def SelectionPress():
+ TextAreaControl.SetText(11352)
+ return
+
+def OkPress():
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("GUIOPT7")
+ return
+
+def CancelPress():
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("GUIOPT7")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUIOPT6.py b/gemrb/GUIScripts/bg2/GUIOPT6.py
new file mode 100644
index 0000000..fcd090d
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIOPT6.py
@@ -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.
+#
+#
+#graphics options
+import GemRB
+
+GraphicsWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global GraphicsWindow, TextAreaControl
+ GemRB.LoadWindowPack("GUIOPT", 640, 480)
+ GraphicsWindow = GemRB.LoadWindow(6)
+ TextAreaControl = GraphicsWindow.GetControl(33)
+ BrightnessButton = GraphicsWindow.GetControl(35)
+ BrightnessSlider = GraphicsWindow.GetControl(3)
+ BrightnessSlider.SetVarAssoc("Brightness Correction",4)
+
+ ContrastButton = GraphicsWindow.GetControl(36)
+ ContrastSlider = GraphicsWindow.GetControl(22)
+ ContrastSlider.SetVarAssoc("Gamma Correction",1)
+
+ BppButton = GraphicsWindow.GetControl(37)
+ BppButtonB1 = GraphicsWindow.GetControl(5)
+ BppButtonB2 = GraphicsWindow.GetControl(6)
+ BppButtonB3 = GraphicsWindow.GetControl(7)
+ BppButtonB1.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ BppButtonB2.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ BppButtonB3.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ BppButtonB1.SetVarAssoc("BitsPerPixel",16)
+ BppButtonB2.SetVarAssoc("BitsPerPixel",24)
+ BppButtonB3.SetVarAssoc("BitsPerPixel",32)
+
+ FullScreenButton = GraphicsWindow.GetControl(38)
+ FullScreenButtonB = GraphicsWindow.GetControl(9)
+ FullScreenButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ FullScreenButtonB.SetVarAssoc("Full Screen",1)
+
+ SoftMirrBltButton = GraphicsWindow.GetControl(44)
+ SoftMirrBltButtonB = GraphicsWindow.GetControl(40)
+ SoftMirrBltButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ SoftMirrBltButtonB.SetVarAssoc("SoftMirrorBlt",1)
+
+ SoftTransBltButton = GraphicsWindow.GetControl(46)
+ SoftTransBltButtonB = GraphicsWindow.GetControl(41)
+ SoftTransBltButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ SoftTransBltButtonB.SetVarAssoc("SoftSrcKeyBlt",1)
+
+ SoftStandBltButton = GraphicsWindow.GetControl(48)
+ SoftStandBltButtonB = GraphicsWindow.GetControl(42)
+ SoftStandBltButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ SoftStandBltButtonB.SetVarAssoc("SoftBltFast",1)
+
+ TransShadowButton = GraphicsWindow.GetControl(50)
+ TransShadowButtonB = GraphicsWindow.GetControl(51)
+ TransShadowButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ TransShadowButtonB.SetVarAssoc("Translucent Shadows",1)
+
+ OkButton = GraphicsWindow.GetControl(21)
+ CancelButton = GraphicsWindow.GetControl(32)
+ TextAreaControl.SetText(18038)
+ OkButton.SetText(11973)
+ CancelButton.SetText(13727)
+ BrightnessButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BrightnessPress)
+ BrightnessSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, BrightnessPress)
+ ContrastButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ContrastPress)
+ ContrastSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, ContrastPress)
+ BppButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BppPress)
+ BppButtonB1.SetEvent(IE_GUI_BUTTON_ON_PRESS, BppPress)
+ BppButtonB2.SetEvent(IE_GUI_BUTTON_ON_PRESS, BppPress)
+ BppButtonB3.SetEvent(IE_GUI_BUTTON_ON_PRESS, BppPress)
+ FullScreenButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, FullScreenPress)
+ FullScreenButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, FullScreenPress)
+ TransShadowButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, TransShadowPress)
+ TransShadowButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, TransShadowPress)
+ SoftMirrBltButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftMirrBltPress)
+ SoftMirrBltButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftMirrBltPress)
+ SoftTransBltButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftTransBltPress)
+ SoftTransBltButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftTransBltPress)
+ SoftStandBltButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftStandBltPress)
+ SoftStandBltButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftStandBltPress)
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ GraphicsWindow.ShowModal()
+ return
+
+def BrightnessPress():
+ TextAreaControl.SetText(17203)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction"))
+ return
+
+def ContrastPress():
+ TextAreaControl.SetText(17204)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction"))
+ return
+
+def BppPress():
+ TextAreaControl.SetText(17205)
+ return
+
+def FullScreenPress():
+ TextAreaControl.SetText(18000)
+ GemRB.SetFullScreen (GemRB.GetVar("Full Screen"))
+ return
+
+def TransShadowPress():
+ TextAreaControl.SetText(20620)
+ return
+
+def SoftMirrBltPress():
+ TextAreaControl.SetText(18004)
+ return
+
+def SoftTransBltPress():
+ TextAreaControl.SetText(18006)
+ return
+
+def SoftStandBltPress():
+ TextAreaControl.SetText(18007)
+ return
+
+def OkPress():
+ if GraphicsWindow:
+ GraphicsWindow.Unload()
+ GemRB.SetNextScript("StartOpt")
+ return
+
+def CancelPress():
+ if GraphicsWindow:
+ GraphicsWindow.Unload()
+ GemRB.SetNextScript("StartOpt")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUIOPT7.py b/gemrb/GUIScripts/bg2/GUIOPT7.py
new file mode 100644
index 0000000..93a887a
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIOPT7.py
@@ -0,0 +1,125 @@
+# 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.
+#
+#
+#sound options
+import GemRB
+
+SoundWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global SoundWindow, TextAreaControl
+
+ GemRB.LoadWindowPack("GUIOPT", 640, 480)
+ SoundWindow = GemRB.LoadWindow(7)
+ TextAreaControl = SoundWindow.GetControl(14)
+ AmbientButton = SoundWindow.GetControl(16)
+ AmbientSlider = SoundWindow.GetControl(1)
+ SoundEffectsButton = SoundWindow.GetControl(17)
+ SoundEffectsSlider = SoundWindow.GetControl(2)
+ DialogueButton = SoundWindow.GetControl(18)
+ DialogueSlider = SoundWindow.GetControl(3)
+ MusicButton = SoundWindow.GetControl(19)
+ MusicSlider = SoundWindow.GetControl(4)
+ MoviesButton = SoundWindow.GetControl(20)
+ MoviesSlider = SoundWindow.GetControl(22)
+ EnvironmentalButton = SoundWindow.GetControl(28)
+ EnvironmentalButtonB = SoundWindow.GetControl(26)
+ CharacterSoundButton = SoundWindow.GetControl(13)
+ OkButton = SoundWindow.GetControl(24)
+ CancelButton = SoundWindow.GetControl(25)
+ TextAreaControl.SetText(18040)
+ CharacterSoundButton.SetText(17778)
+ OkButton.SetText(11973)
+ CancelButton.SetText(13727)
+ AmbientButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, AmbientPress)
+ AmbientSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, AmbientPress)
+ AmbientSlider.SetVarAssoc("Volume Ambients",10)
+
+ SoundEffectsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoundEffectsPress)
+ SoundEffectsSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, SoundEffectsPress)
+ SoundEffectsSlider.SetVarAssoc("Volume SFX",10)
+
+ DialogueButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DialoguePress)
+ DialogueSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, DialoguePress)
+ DialogueSlider.SetVarAssoc("Volume Voices",10)
+
+ MusicButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MusicPress)
+ MusicSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, MusicPress)
+ MusicSlider.SetVarAssoc("Volume Music",10)
+
+ MoviesButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MoviesPress)
+ MoviesSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, MoviesPress)
+ MoviesSlider.SetVarAssoc("Volume Movie",10)
+
+ EnvironmentalButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, EnvironmentalPress)
+ EnvironmentalButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, EnvironmentalPress)
+ EnvironmentalButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ EnvironmentalButtonB.SetVarAssoc("Environmental Audio",1)
+
+ CharacterSoundButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharacterSoundPress)
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ SoundWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def AmbientPress():
+ TextAreaControl.SetText(18008)
+ GemRB.UpdateAmbientsVolume ()
+ return
+
+def SoundEffectsPress():
+ TextAreaControl.SetText(18009)
+ return
+
+def DialoguePress():
+ TextAreaControl.SetText(18010)
+ return
+
+def MusicPress():
+ TextAreaControl.SetText(18011)
+ GemRB.UpdateMusicVolume ()
+ return
+
+def MoviesPress():
+ TextAreaControl.SetText(18012)
+ return
+
+def EnvironmentalPress():
+ TextAreaControl.SetText(18022)
+ return
+
+def CharacterSoundPress():
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("GUIOPT12")
+ return
+
+def OkPress():
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("StartOpt")
+ return
+
+def CancelPress():
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("StartOpt")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUIOPT8.py b/gemrb/GUIScripts/bg2/GUIOPT8.py
new file mode 100644
index 0000000..106fc53
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIOPT8.py
@@ -0,0 +1,176 @@
+# 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.
+#
+#
+#gameplay
+import GemRB
+
+GamePlayWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global GamePlayWindow, TextAreaControl
+ GemRB.LoadWindowPack("GUIOPT", 640, 480)
+ GamePlayWindow = GemRB.LoadWindow(8)
+ TextAreaControl = GamePlayWindow.GetControl(40)
+ DelayButton = GamePlayWindow.GetControl(21)
+ DelaySlider = GamePlayWindow.GetControl(1)
+ MouseSpdButton = GamePlayWindow.GetControl(22)
+ MouseSpdSlider = GamePlayWindow.GetControl(2)
+ KeySpdButton = GamePlayWindow.GetControl(23)
+ KeySpdSlider = GamePlayWindow.GetControl(3)
+ DifficultyButton = GamePlayWindow.GetControl(24)
+ DifficultySlider = GamePlayWindow.GetControl(12)
+ BloodButton = GamePlayWindow.GetControl(27)
+ BloodButtonB = GamePlayWindow.GetControl(19)
+ DitherButton = GamePlayWindow.GetControl(25)
+ DitherButtonB = GamePlayWindow.GetControl(14)
+ InfravisionButton = GamePlayWindow.GetControl(44)
+ InfravisionButtonB = GamePlayWindow.GetControl(42)
+ WeatherButton = GamePlayWindow.GetControl(46)
+ WeatherButtonB = GamePlayWindow.GetControl(47)
+ HealButton = GamePlayWindow.GetControl(48)
+ HealButtonB = GamePlayWindow.GetControl(50)
+ HotKeyButton = GamePlayWindow.GetControl(51)
+ FeedbackButton = GamePlayWindow.GetControl(5)
+ AutoPauseButton = GamePlayWindow.GetControl(6)
+ OkButton = GamePlayWindow.GetControl(7)
+ CancelButton = GamePlayWindow.GetControl(20)
+ TextAreaControl.SetText(18042)
+ OkButton.SetText(11973)
+ CancelButton.SetText(13727)
+ HotKeyButton.SetText(816)
+ FeedbackButton.SetText(17163)
+ AutoPauseButton.SetText(17166)
+ DelayButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DelayPress)
+ DelaySlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, DelayPress)
+ DelaySlider.SetVarAssoc("Tooltips",0)
+
+ KeySpdButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, KeySpdPress)
+ KeySpdSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, KeySpdPress)
+ KeySpdSlider.SetVarAssoc("Keyboard Scroll Speed",0)
+
+ MouseSpdButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MouseSpdPress)
+ MouseSpdSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, MouseSpdPress)
+ MouseSpdSlider.SetVarAssoc("Mouse Scroll Speed",0)
+
+ DifficultyButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DifficultyPress)
+ DifficultySlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, DifficultyPress)
+ DifficultySlider.SetVarAssoc("Difficulty Level",0)
+
+ BloodButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BloodPress)
+ BloodButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, BloodPress)
+ BloodButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ BloodButtonB.SetVarAssoc("Gore",1)
+
+ DitherButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DitherPress)
+ DitherButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, DitherPress)
+ DitherButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ DitherButtonB.SetVarAssoc("Always Dither",1)
+
+ InfravisionButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, InfravisionPress)
+ InfravisionButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, InfravisionPress)
+ InfravisionButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ InfravisionButtonB.SetVarAssoc("Infravision",1)
+
+ WeatherButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, WeatherPress)
+ WeatherButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, WeatherPress)
+ WeatherButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ WeatherButtonB.SetVarAssoc("Weather",1)
+
+ HealButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, HealPress)
+ HealButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, HealPress)
+ HealButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ HealButtonB.SetVarAssoc("Heal Party on Rest",1)
+
+ HotKeyButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, HotKeyPress)
+ FeedbackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, FeedbackPress)
+ AutoPauseButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, AutoPausePress)
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ GamePlayWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def DelayPress():
+ TextAreaControl.SetText(18017)
+ return
+
+def KeySpdPress():
+ TextAreaControl.SetText(18019)
+ return
+
+def MouseSpdPress():
+ TextAreaControl.SetText(18018)
+ return
+
+def DifficultyPress():
+ TextAreaControl.SetText(18020)
+ return
+
+def BloodPress():
+ TextAreaControl.SetText(18023)
+ return
+
+def DitherPress():
+ TextAreaControl.SetText(18021)
+ return
+
+def InfravisionPress():
+ TextAreaControl.SetText(11797)
+ return
+
+def WeatherPress():
+ TextAreaControl.SetText(20619)
+ return
+
+def HealPress():
+ TextAreaControl.SetText(2242)
+ return
+
+def HotKeyPress():
+ #TextAreaControl.SetText(18016)
+ return
+
+def FeedbackPress():
+ GamePlayWindow.SetVisible(WINDOW_INVISIBLE)
+ if GamePlayWindow:
+ GamePlayWindow.Unload()
+ GemRB.SetNextScript("GUIOPT9")
+ return
+
+def AutoPausePress():
+ GamePlayWindow.SetVisible(WINDOW_INVISIBLE)
+ if GamePlayWindow:
+ GamePlayWindow.Unload()
+ GemRB.SetNextScript("GUIOPT10")
+ return
+
+def OkPress():
+ GamePlayWindow.SetVisible(WINDOW_INVISIBLE)
+ if GamePlayWindow:
+ GamePlayWindow.Unload()
+ GemRB.SetNextScript("StartOpt")
+ return
+
+def CancelPress():
+ GamePlayWindow.SetVisible(WINDOW_INVISIBLE)
+ if GamePlayWindow:
+ GamePlayWindow.Unload()
+ GemRB.SetNextScript("StartOpt")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUIOPT9.py b/gemrb/GUIScripts/bg2/GUIOPT9.py
new file mode 100644
index 0000000..ba5b750
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIOPT9.py
@@ -0,0 +1,176 @@
+# 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.
+#
+#
+#feedback
+import GemRB
+
+FeedbackWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global FeedbackWindow, TextAreaControl
+
+ GemRB.LoadWindowPack("GUIOPT", 640, 480)
+ FeedbackWindow = GemRB.LoadWindow(9)
+
+ MarkerSlider = FeedbackWindow.GetControl(30)
+ MarkerSliderS = FeedbackWindow.GetControl(8)
+
+ LocatorSlider = FeedbackWindow.GetControl(31)
+ LocatorSliderS = FeedbackWindow.GetControl(9)
+
+ THac0Rolls = FeedbackWindow.GetControl(32)
+ THac0RollsB = FeedbackWindow.GetControl(10)
+
+ CombatInfo = FeedbackWindow.GetControl(33)
+ CombatInfoB = FeedbackWindow.GetControl(11)
+
+ Actions = FeedbackWindow.GetControl(34)
+ ActionsB = FeedbackWindow.GetControl(12)
+
+ StateChanges = FeedbackWindow.GetControl(35)
+ StateChangesB = FeedbackWindow.GetControl(13)
+
+ SelectionText = FeedbackWindow.GetControl(36)
+ SelectionTextB = FeedbackWindow.GetControl(14)
+
+ Miscellaneous = FeedbackWindow.GetControl(37)
+ MiscellaneousB = FeedbackWindow.GetControl(15)
+ OkButton = FeedbackWindow.GetControl(26)
+ CancelButton = FeedbackWindow.GetControl(27)
+ TextAreaControl = FeedbackWindow.GetControl(28)
+
+ TextAreaControl.SetText(18043)
+ OkButton.SetText(11973)
+ CancelButton.SetText(13727)
+
+ MarkerSlider.SetEvent(IE_GUI_BUTTON_ON_PRESS, MarkerSliderPress)
+ MarkerSliderS.SetEvent(IE_GUI_SLIDER_ON_CHANGE, MarkerSliderPress)
+ MarkerSliderS.SetVarAssoc("GUI Feedback Level",1)
+
+ LocatorSlider.SetEvent(IE_GUI_BUTTON_ON_PRESS, LocatorSliderPress)
+ LocatorSliderS.SetEvent(IE_GUI_SLIDER_ON_CHANGE, LocatorSliderPress)
+ LocatorSliderS.SetVarAssoc("Locator Feedback Level",1)
+
+ THac0Rolls.SetEvent(IE_GUI_BUTTON_ON_PRESS, THac0RollsPress)
+ THac0RollsB.SetEvent(IE_GUI_BUTTON_ON_PRESS, THac0RollsBPress)
+ THac0RollsB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ THac0RollsB.SetVarAssoc("Rolls",1)
+
+ CombatInfo.SetEvent(IE_GUI_BUTTON_ON_PRESS, CombatInfoPress)
+ CombatInfoB.SetEvent(IE_GUI_BUTTON_ON_PRESS, CombatInfoBPress)
+ CombatInfoB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ CombatInfoB.SetVarAssoc("Combat Info",1)
+
+ Actions.SetEvent(IE_GUI_BUTTON_ON_PRESS, ActionsPress)
+ ActionsB.SetEvent(IE_GUI_BUTTON_ON_PRESS, ActionsBPress)
+ ActionsB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ActionsB.SetVarAssoc("Actions",1)
+
+ StateChanges.SetEvent(IE_GUI_BUTTON_ON_PRESS, StateChangesPress)
+ StateChangesB.SetEvent(IE_GUI_BUTTON_ON_PRESS, StateChangesBPress)
+ StateChangesB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ StateChangesB.SetVarAssoc("State Changes",1)
+
+ SelectionText.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionTextPress)
+ SelectionTextB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionTextBPress)
+ SelectionTextB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ SelectionTextB.SetVarAssoc("Selection Text",1)
+
+ Miscellaneous.SetEvent(IE_GUI_BUTTON_ON_PRESS, MiscellaneousPress)
+ MiscellaneousB.SetEvent(IE_GUI_BUTTON_ON_PRESS, MiscellaneousBPress)
+ MiscellaneousB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ MiscellaneousB.SetVarAssoc("Miscellaneous Text",1)
+
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ FeedbackWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def MarkerSliderPress():
+ TextAreaControl.SetText(18024)
+ return
+
+def LocatorSliderPress():
+ TextAreaControl.SetText(18025)
+ return
+
+def THac0RollsPress():
+ TextAreaControl.SetText(18026)
+ return
+
+def THac0RollsBPress():
+ #TODO: TextAreaControl.SetText()
+ return
+
+def CombatInfoPress():
+ TextAreaControl.SetText(18027)
+ return
+
+def CombatInfoBPress():
+ #TODO: TextAreaControl.SetText()
+ return
+
+def ActionsPress():
+ TextAreaControl.SetText(18028)
+ return
+
+def ActionsBPress():
+ #TODO: TextAreaControl.SetText(18028)
+ return
+
+
+def StateChangesPress():
+ TextAreaControl.SetText(18029)
+ return
+
+def StateChangesBPress():
+ #TODO: TextAreaControl.SetText(18029)
+ return
+
+def SelectionTextPress():
+ TextAreaControl.SetText(18030)
+ return
+
+def SelectionTextBPress():
+ #TODO: TextAreaControl.SetText(18030)
+ return
+
+def MiscellaneousPress():
+ TextAreaControl.SetText(18031)
+ return
+
+def MiscellaneousBPress():
+ #TODO: TextAreaControl.SetText(18031)
+ return
+
+
+def OkPress():
+ if FeedbackWindow:
+ FeedbackWindow.Unload()
+ GemRB.SetNextScript("GUIOPT8")
+ return
+
+def CancelPress():
+ if FeedbackWindow:
+ FeedbackWindow.Unload()
+ GemRB.SetNextScript("GUIOPT8")
+ return
+
diff --git a/gemrb/GUIScripts/bg2/GUIPR.py b/gemrb/GUIScripts/bg2/GUIPR.py
new file mode 100644
index 0000000..cbf2748
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIPR.py
@@ -0,0 +1,356 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIPR.py - scripts to control the priest spells windows from the GUIPR winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+from GUIDefines import *
+from ie_stats import *
+from ie_action import ACT_CAST
+
+PriestWindow = None
+PriestSpellInfoWindow = None
+PriestSpellLevel = 0
+PriestSpellUnmemorizeWindow = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+def OpenPriestWindow ():
+ import GUICommonWindows
+ global PriestWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenPriestWindow):
+ if PriestWindow:
+ PriestWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ PriestWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIPR", 640, 480)
+ PriestWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", PriestWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenPriestWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestPrevLevelPress)
+
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestNextLevelPress)
+
+ #setup level buttons
+ for i in range (7):
+ Button = Window.GetControl (55 + i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RefreshPriestLevel)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ for i in range (7):
+ Button = Window.GetControl (55 + i)
+ Button.SetVarAssoc ("PriestSpellLevel", i)
+
+ # Setup memorized spells buttons
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ Button.SetBorder (0,0,0,0,0,0,0,0,64,0,1)
+ Button.SetSprites ("SPELFRAM",0,0,0,0,0)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Setup book spells buttons
+ for i in range (24):
+ Button = Window.GetControl (27 + i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdatePriestWindow)
+ UpdatePriestWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ #bringing window front
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def UpdatePriestWindow ():
+ global PriestMemorizedSpellList, PriestKnownSpellList
+
+ PriestMemorizedSpellList = []
+ PriestKnownSpellList = []
+
+ Window = PriestWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+ type = IE_SPELL_TYPE_PRIEST
+ level = PriestSpellLevel
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, type, level)
+
+ Label = Window.GetControl (0x10000032)
+ GemRB.SetToken("SPELLLEVEL", str(level+1) )
+ Label.SetText (10345)
+
+ Name = GemRB.GetPlayerName (pc, 0)
+ Label = Window.GetControl (0x10000035)
+ Label.SetText (Name)
+
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ if i < mem_cnt:
+ ms = GemRB.GetMemorizedSpell (pc, type, level, i)
+ Button.SetSpellIcon (ms['SpellResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if ms['Flags']:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellUnmemorizeWindow)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestUnmemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenPriestSpellInfoWindow)
+ spell = GemRB.GetSpell (ms['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ PriestMemorizedSpellList.append (ms['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", i)
+ Button.EnableBorder (0, ms['Flags'] == 0)
+ else:
+ if i < max_mem_cnt:
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+
+ known_cnt = GemRB.GetKnownSpellsCount (pc, type, level)
+ for i in range (24):
+ Button = Window.GetControl (27 + i)
+ if i < known_cnt:
+ ks = GemRB.GetKnownSpell (pc, type, level, i)
+ Button.SetSpellIcon (ks['SpellResRef'], 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestMemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenPriestSpellInfoWindow)
+ spell = GemRB.GetSpell (ks['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ PriestKnownSpellList.append (ks['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", 100 + i)
+
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ DivineCaster = CommonTables.ClassSkills.GetValue (Class, 1)
+ if DivineCaster == "*":
+ # also check the DRUIDSPELL column
+ DivineCaster = CommonTables.ClassSkills.GetValue (Class, 0)
+ CantCast = DivineCaster == "*" or GemRB.GetPlayerStat(pc, IE_DISABLEDBUTTON)&(1<<ACT_CAST)
+ if CantCast or GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def PriestPrevLevelPress ():
+ global PriestSpellLevel
+
+ if PriestSpellLevel > 0:
+ PriestSpellLevel = PriestSpellLevel - 1
+ UpdatePriestWindow ()
+ return
+
+def PriestNextLevelPress ():
+ global PriestSpellLevel
+
+ if PriestSpellLevel < 6:
+ PriestSpellLevel = PriestSpellLevel + 1
+ UpdatePriestWindow ()
+ return
+
+def RefreshPriestLevel ():
+ global PriestSpellLevel
+
+ PriestSpellLevel = GemRB.GetVar ("PriestSpellLevel")
+ UpdatePriestWindow ()
+ return
+
+def OpenPriestSpellInfoWindow ():
+ global PriestSpellInfoWindow
+
+ if PriestSpellInfoWindow != None:
+ if PriestSpellInfoWindow:
+ PriestSpellInfoWindow.Unload ()
+ PriestSpellInfoWindow = None
+ return
+
+ PriestSpellInfoWindow = Window = GemRB.LoadWindow (3)
+
+ #back
+ Button = Window.GetControl (5)
+ Button.SetText (15416)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellInfoWindow)
+
+ index = GemRB.GetVar ("SpellButton")
+ if index < 100:
+ ResRef = PriestMemorizedSpellList[index]
+ else:
+ ResRef = PriestKnownSpellList[index - 100]
+
+ spell = GemRB.GetSpell (ResRef)
+
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (spell['SpellName'])
+
+ Button = Window.GetControl (2)
+ Button.SetSpellIcon (ResRef, 1)
+
+ Text = Window.GetControl (3)
+ Text.SetText (spell['SpellDesc'])
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnPriestMemorizeSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ if GemRB.MemorizeSpell (pc, type, level, index):
+ UpdatePriestWindow ()
+ return
+
+def OpenPriestSpellRemoveWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ PriestSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (11824)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestRemoveSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClosePriestSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ClosePriestSpellUnmemorizeWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ if PriestSpellUnmemorizeWindow:
+ PriestSpellUnmemorizeWindow.Unload ()
+ PriestSpellUnmemorizeWindow = None
+ return
+
+def OpenPriestSpellUnmemorizeWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ PriestSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (11824)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestUnmemorizeSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClosePriestSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnPriestUnmemorizeSpell ():
+ if PriestSpellUnmemorizeWindow:
+ ClosePriestSpellUnmemorizeWindow ()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton")
+
+ if GemRB.UnmemorizeSpell (pc, type, level, index):
+ UpdatePriestWindow ()
+ return
+
+def OnPriestRemoveSpell ():
+ ClosePriestSpellUnmemorizeWindow()
+ OpenPriestSpellInfoWindow()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ #remove spell from book
+ GemRB.RemoveSpell (pc, type, level, index)
+ UpdatePriestWindow ()
+ return
+
+###################################################
+# End of file GUIPR.py
diff --git a/gemrb/GUIScripts/bg2/GUIREC.py b/gemrb/GUIScripts/bg2/GUIREC.py
new file mode 100644
index 0000000..c13fdc9
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIREC.py
@@ -0,0 +1,1567 @@
+# -*-python-*-
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2003-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.
+#
+
+
+# GUIREC.py - scripts to control stats/records windows from the GUIREC winpack
+
+###################################################
+import GemRB
+import GUICommon
+import CommonTables
+import LUCommon
+import GUIWORLD
+import DualClass
+import LevelUp
+from GUIDefines import *
+from ie_stats import *
+from ie_restype import *
+
+###################################################
+RecordsWindow = None
+InformationWindow = None
+BiographyWindow = None
+PortraitWindow = None
+OptionsWindow = None
+CustomizeWindow = None
+SubCustomizeWindow = None
+SubSubCustomizeWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+ExportWindow = None
+KitInfoWindow = None
+ExportDoneButton = None
+ExportFileName = ""
+PortraitsTable = None
+ScriptsTable = None
+ColorTable = None
+ColorIndex = None
+ScriptTextArea = None
+OldVoiceSet = None
+
+# the available sounds
+SoundSequence = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', \
+ 'm', 's', 't', 'u', 'v', '_', 'x', 'y', 'z', '0', '1', '2', \
+ '3', '4', '5', '6', '7', '8', '9']
+SoundIndex = 0
+
+###################################################
+def OpenRecordsWindow ():
+ import GUICommonWindows
+
+ global RecordsWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenRecordsWindow):
+ if InformationWindow: OpenInformationWindow ()
+
+ if RecordsWindow:
+ RecordsWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ RecordsWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIREC", 640, 480)
+ RecordsWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", RecordsWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenRecordsWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ # dual class
+ Button = Window.GetControl (0)
+ Button.SetText (7174)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DualClass.DualClassWindow)
+
+ # levelup
+ Button = Window.GetControl (37)
+ Button.SetText (7175)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LevelUp.OpenLevelUpWindow)
+
+ # information
+ Button = Window.GetControl (1)
+ Button.SetText (11946)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenInformationWindow)
+
+ # reform party
+ Button = Window.GetControl (51)
+ Button.SetText (16559)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenRecReformPartyWindow)
+
+ # customize
+ Button = Window.GetControl (50)
+ Button.SetText (10645)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenCustomizeWindow)
+
+ # export
+ Button = Window.GetControl (36)
+ Button.SetText (13956)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenExportWindow)
+
+ # kit info
+ Button = Window.GetControl (52)
+ Button.SetText (61265)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenKitInfoWindow)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateRecordsWindow)
+ UpdateRecordsWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+#original returns to game before continuing...
+def OpenRecReformPartyWindow ():
+ OpenRecordsWindow ()
+ GemRB.SetTimedEvent (GUIWORLD.OpenReformPartyWindow, 1)
+ return
+
+def UpdateRecordsWindow ():
+ global stats_overview, alignment_help
+
+ Window = RecordsWindow
+ if not RecordsWindow:
+ print "SelectionChange handler points to non existing window\n"
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ #update mage school
+ GemRB.SetVar ("MAGESCHOOL", 0)
+ Kit = GUICommon.GetKitIndex (pc)
+ if Kit and CommonTables.KitList.GetValue (Kit, 7) == 1:
+ MageTable = GemRB.LoadTable ("magesch")
+ GemRB.SetVar ("MAGESCHOOL", MageTable.FindValue (3, CommonTables.KitList.GetValue (Kit, 6) ) )
+
+ # exportable
+ Button = Window.GetControl (36)
+ if GemRB.GetPlayerStat (pc, IE_MC_FLAGS)&MC_EXPORTABLE:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # dual-classable
+ Button = Window.GetControl (0)
+ if GUICommon.CanDualClass (pc):
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # levelup
+ Button = Window.GetControl (37)
+ if LUCommon.CanLevelUp (pc):
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # name
+ Label = Window.GetControl (0x1000000e)
+ Label.SetText (GemRB.GetPlayerName (pc, 0))
+
+ # portrait
+ Button = Window.GetControl (2)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetPicture (GemRB.GetPlayerPortrait (pc,0), "NOPORTMD")
+
+ # armorclass
+ Label = Window.GetControl (0x10000028)
+ ac = GemRB.GetPlayerStat (pc, IE_ARMORCLASS)
+ #This is a temporary solution, the core engine should set the stat correctly!
+ ac += GemRB.GetAbilityBonus (IE_DEX, 2, GemRB.GetPlayerStat (pc, IE_DEX) )
+ Label.SetText (str (ac))
+ Label.SetTooltip (17183)
+
+ # hp now
+ Label = Window.GetControl (0x10000029)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_HITPOINTS)))
+ Label.SetTooltip (17184)
+
+ # hp max
+ Label = Window.GetControl (0x1000002a)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)))
+ Label.SetTooltip (17378)
+
+ # stats
+
+ sstr = GemRB.GetPlayerStat (pc, IE_STR)
+ cstr = GetStatColor (pc, IE_STR)
+ sstrx = GemRB.GetPlayerStat (pc, IE_STREXTRA)
+
+ if sstrx > 0 and sstr==18:
+ sstr = "%d/%02d" %(sstr, sstrx % 100)
+ else:
+ sstr = str (sstr)
+ sint = str (GemRB.GetPlayerStat (pc, IE_INT))
+ cint = GetStatColor (pc, IE_INT)
+ swis = str (GemRB.GetPlayerStat (pc, IE_WIS))
+ cwis = GetStatColor (pc, IE_WIS)
+ sdex = str (GemRB.GetPlayerStat (pc, IE_DEX))
+ cdex = GetStatColor (pc, IE_DEX)
+ scon = str (GemRB.GetPlayerStat (pc, IE_CON))
+ ccon = GetStatColor (pc, IE_CON)
+ schr = str (GemRB.GetPlayerStat (pc, IE_CHR))
+ cchr = GetStatColor (pc, IE_CHR)
+
+ Label = Window.GetControl (0x1000002f)
+ Label.SetText (sstr)
+ Label.SetTextColor (cstr[0], cstr[1], cstr[2])
+
+ Label = Window.GetControl (0x10000009)
+ Label.SetText (sdex)
+ Label.SetTextColor (cdex[0], cdex[1], cdex[2])
+
+ Label = Window.GetControl (0x1000000a)
+ Label.SetText (scon)
+ Label.SetTextColor (ccon[0], ccon[1], ccon[2])
+
+ Label = Window.GetControl (0x1000000b)
+ Label.SetText (sint)
+ Label.SetTextColor (cint[0], cint[1], cint[2])
+
+ Label = Window.GetControl (0x1000000c)
+ Label.SetText (swis)
+ Label.SetTextColor (cwis[0], cwis[1], cwis[2])
+
+ Label = Window.GetControl (0x1000000d)
+ Label.SetText (schr)
+ Label.SetTextColor (cchr[0], cchr[1], cchr[2])
+
+ # class
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ Label = Window.GetControl (0x10000030)
+ Label.SetText (ClassTitle)
+
+ # race
+ text = CommonTables.Races.GetValue (CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (pc, IE_RACE)) , 0)
+
+ Label = Window.GetControl (0x1000000f)
+ Label.SetText (text)
+
+ Table = GemRB.LoadTable ("aligns")
+
+ text = Table.GetValue (Table.FindValue ( 3, GemRB.GetPlayerStat (pc, IE_ALIGNMENT) ), 0)
+ Label = Window.GetControl (0x10000010)
+ Label.SetText (text)
+
+ Label = Window.GetControl (0x10000011)
+ if GemRB.GetPlayerStat (pc, IE_SEX) == 1:
+ Label.SetText (7198)
+ else:
+ Label.SetText (7199)
+
+ # help, info textarea
+ stats_overview = GetStatOverview (pc)
+ Text = Window.GetControl (45)
+ Text.SetText (stats_overview)
+ #making window visible/shaded depending on the pc's state
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def GetStatColor (pc, stat):
+ a = GemRB.GetPlayerStat (pc, stat)
+ b = GemRB.GetPlayerStat (pc, stat, 1)
+ if a==b:
+ return (255,255,255)
+ if a<b:
+ return (255,255,0)
+ return (0,255,0)
+
+# GemRB.GetPlayerStat wrapper that only returns nonnegative values
+def GSNN (pc, stat):
+ val = GemRB.GetPlayerStat (pc, stat)
+ if val >= 0:
+ return val
+ else:
+ return 0
+
+# LevelDiff is used only from the level up code and holds the level
+# difference for each class
+def GetStatOverview (pc, LevelDiff=[0,0,0]):
+ StateTable = GemRB.LoadTable ("statdesc")
+ str_None = GemRB.GetString (61560)
+
+ GS = lambda s, pc=pc: GemRB.GetPlayerStat (pc, s)
+ GB = lambda s, pc=pc: GemRB.GetPlayerStat (pc, s, 1)
+ GA = lambda s, col, pc=pc: GemRB.GetAbilityBonus (s, col, GS (s) )
+
+ stats = []
+ # class levels
+ # 16480 <CLASS>: Level <LEVEL>
+ # Experience: <EXPERIENCE>
+ # Next Level: <NEXTLEVEL>
+
+ # collecting tokens for stat overview
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ GemRB.SetToken ("CLASS", ClassTitle)
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ Class = CommonTables.Classes.GetRowName (Class)
+ Dual = GUICommon.IsDualClassed (pc, 1)
+ Multi = GUICommon.IsMultiClassed (pc, 1)
+ XP = GemRB.GetPlayerStat (pc, IE_XP)
+ LevelDrain = GS (IE_LEVELDRAIN)
+
+ if GS (IE_STATE_ID) & STATE_DEAD:
+ stats.append ( (11829,1,'c') ) # DEAD
+ stats.append (None)
+
+ if Multi[0] > 1: # we're multiclassed
+ print "\tMulticlassed"
+ Levels = [GemRB.GetPlayerStat (pc, IE_LEVEL), GemRB.GetPlayerStat (pc, IE_LEVEL2), GemRB.GetPlayerStat (pc, IE_LEVEL3)]
+
+ stats.append ( (19721,1,'c') )
+ stats.append (None)
+ for i in range (Multi[0]):
+ ClassIndex = CommonTables.Classes.FindValue (5, Multi[i+1])
+ ClassTitle = GemRB.GetString (CommonTables.Classes.GetValue (ClassIndex, 2))
+ GemRB.SetToken ("CLASS", ClassTitle)
+ Class = CommonTables.Classes.GetRowName (ClassIndex)
+ GemRB.SetToken ("LEVEL", str (Levels[i]+LevelDiff[i]-int(LevelDrain/Multi[0])) )
+ GemRB.SetToken ("EXPERIENCE", str (XP/Multi[0]) )
+ if LevelDrain:
+ stats.append ( (GemRB.GetString (19720),1,'d') )
+ stats.append ( (GemRB.GetString (57435),1,'d') ) # LEVEL DRAINED
+ else:
+ GemRB.SetToken ("NEXTLEVEL", LUCommon.GetNextLevelExp (Levels[i]+LevelDiff[i], Class) )
+ stats.append ( (GemRB.GetString (16480),"",'d') )
+ stats.append (None)
+ print "\t\tClass (Level):",Class,"(",Levels[i],")"
+
+ elif Dual[0] > 0: # dual classed; first show the new class
+ print "\tDual classed"
+ stats.append ( (19722,1,'c') )
+ stats.append (None)
+
+ Levels = [GemRB.GetPlayerStat (pc, IE_LEVEL), GemRB.GetPlayerStat (pc, IE_LEVEL2), GemRB.GetPlayerStat (pc, IE_LEVEL3)]
+
+ # the levels are stored in the class order (eg. FIGHTER_MAGE)
+ # the current active class does not matter!
+ if GUICommon.IsDualSwap (pc):
+ Levels = [Levels[1], Levels[0], Levels[2]]
+
+ Levels[0] += LevelDiff[0]
+
+ ClassTitle = GemRB.GetString (CommonTables.Classes.GetValue (Dual[2], 2))
+ GemRB.SetToken ("CLASS", ClassTitle)
+ GemRB.SetToken ("LEVEL", str (Levels[0]-LevelDrain))
+ Class = CommonTables.Classes.GetRowName (Dual[2])
+ XP2 = GemRB.GetPlayerStat (pc, IE_XP)
+ GemRB.SetToken ("EXPERIENCE", str (XP2) )
+ if LevelDrain:
+ stats.append ( (GemRB.GetString (19720),1,'d') )
+ stats.append ( (GemRB.GetString (57435),1,'d') ) # LEVEL DRAINED
+ else:
+ GemRB.SetToken ("NEXTLEVEL", LUCommon.GetNextLevelExp (Levels[0], Class) )
+ stats.append ( (GemRB.GetString (16480),"",'d') )
+ stats.append (None)
+
+ # the first class (shown second)
+ if Dual[0] == 1:
+ ClassTitle = GemRB.GetString (CommonTables.KitList.GetValue (Dual[1], 2))
+ elif Dual[0] == 2:
+ ClassTitle = GemRB.GetString (CommonTables.Classes.GetValue (Dual[1], 2))
+ GemRB.SetToken ("CLASS", ClassTitle)
+ GemRB.SetToken ("LEVEL", str (Levels[1]) )
+
+ # the xp table contains only classes, so we have to determine the base class for kits
+ if Dual[0] == 2:
+ BaseClass = CommonTables.Classes.GetRowName (Dual[1])
+ else:
+ BaseClass = GUICommon.GetKitIndex (pc)
+ BaseClass = CommonTables.KitList.GetValue (BaseClass, 7)
+ BaseClass = CommonTables.Classes.FindValue (5, BaseClass)
+ BaseClass = CommonTables.Classes.GetRowName (BaseClass)
+ # the first class' XP is discarded and set to the minimum level
+ # requirement, so if you don't dual class right after a levelup,
+ # the game would eat some of your XP
+ XP1 = CommonTables.NextLevel.GetValue (BaseClass, str (Levels[1]))
+ GemRB.SetToken ("EXPERIENCE", str (XP1) )
+
+ # inactive until the new class SURPASSES the former
+ if Levels[0] <= Levels[1]:
+ # inactive
+ stats.append ( (19719,1,'c') )
+ else:
+ stats.append ( (19720,1,'c') )
+ stats.append (None)
+ else: # single classed
+ print "\tSingle classed"
+ Level = GemRB.GetPlayerStat (pc, IE_LEVEL) + LevelDiff[0]
+ GemRB.SetToken ("LEVEL", str (Level-LevelDrain))
+ GemRB.SetToken ("EXPERIENCE", str (XP) )
+ if LevelDrain:
+ stats.append ( (19720,1,'c') )
+ stats.append ( (57435,1,'c') ) # LEVEL DRAINED
+ else:
+ GemRB.SetToken ("NEXTLEVEL", LUCommon.GetNextLevelExp (Level, Class) )
+ stats.append ( (16480,1,'c') )
+ stats.append (None)
+ print "\t\tClass (Level):",Class,"(",Level,")"
+
+ # check to see if we have a level diff anywhere
+ if sum (LevelDiff) == 0:
+ effects = GemRB.GetPlayerStates (pc)
+ if len (effects):
+ for c in effects:
+ tmp = StateTable.GetValue (ord(c)-66, 0)
+ stats.append ( (tmp,c,'a') )
+ stats.append (None)
+
+ #proficiencies
+ stats.append ( (8442,1,'c') )
+
+ stats.append ( (61932, GS (IE_TOHIT), '0') )
+ if (GemRB.IsDualWielding(pc)):
+ stats.append ( (56911, GemRB.GetCombatDetails(pc, 0)["ToHit"], '0') )
+ stats.append ( (56910, GemRB.GetCombatDetails(pc, 1)["ToHit"], '0') )
+ else:
+ stats.append ( (9457, GemRB.GetCombatDetails(pc, 0)["ToHit"], '0') )
+ tmp = GS (IE_NUMBEROFATTACKS)
+ if (tmp&1):
+ tmp2 = str (tmp/2) + chr (188)
+ else:
+ tmp2 = str (tmp/2)
+ stats.append ( (9458, tmp2, '') )
+ stats.append ( (9459, GSNN (pc, IE_LORE), '0') )
+
+ #reputation
+ reptxt = GetReputation (GemRB.GameGetReputation ()/10)
+ stats.append ( (9465, reptxt, '') )
+ stats.append ( (9460, GSNN (pc, IE_LOCKPICKING), '') )
+ stats.append ( (9462, GSNN (pc, IE_TRAPS), '') )
+ stats.append ( (9463, GSNN (pc, IE_PICKPOCKET), '') )
+ stats.append ( (9461, GSNN (pc, IE_STEALTH), '') )
+ HatedRace = GS (IE_HATEDRACE)
+ if HatedRace:
+ HateTable = GemRB.LoadTable ("haterace")
+ Racist = HateTable.FindValue (1, HatedRace)
+ if Racist != -1:
+ HatedRace = HateTable.GetValue (Racist, 0)
+ stats.append ( (15982, GemRB.GetString (HatedRace), '') )
+
+ stats.append ( (34120, GSNN (pc, IE_HIDEINSHADOWS), '') )
+ stats.append ( (34121, GSNN (pc, IE_DETECTILLUSIONS), '') )
+ stats.append ( (34122, GSNN (pc, IE_SETTRAPS), '') )
+ stats.append ( (12128, GS (IE_BACKSTABDAMAGEMULTIPLIER), 'x') )
+ stats.append ( (12126, GS (IE_TURNUNDEADLEVEL), '') )
+
+ #this hack only displays LOH if we know the spell
+ #TODO: the core should just not set LOH if the paladin can't learn it
+ if (GUICommon.HasSpell (pc, IE_SPELL_TYPE_INNATE, 0, "SPCL211") >= 0):
+ stats.append ( (12127, GS (IE_LAYONHANDSAMOUNT), '') )
+ #script
+ aiscript = GemRB.GetPlayerScript (pc )
+ stats.append ( (2078, aiscript, '') )
+ stats.append (None)
+
+ # 17379 Saving throws
+ stats.append (17379)
+ # 17380 Paralyze/Poison/Death
+ stats.append ( (17380, IE_SAVEVSDEATH, 's') )
+ # 17381 Rod/Staff/Wand
+ stats.append ( (17381, IE_SAVEVSWANDS, 's') )
+ # 17382 Petrify/Polymorph
+ stats.append ( (17382, IE_SAVEVSPOLY, 's') )
+ # 17383 Breath weapon
+ stats.append ( (17383, IE_SAVEVSBREATH, 's') )
+ # 17384 Spells
+ stats.append ( (17384, IE_SAVEVSSPELL, 's') )
+ stats.append (None)
+
+ # 9466 Weapon proficiencies
+ stats.append (9466)
+ table = GemRB.LoadTable ("weapprof")
+ RowCount = table.GetRowCount ()
+ # the first 7 profs are foobared
+ for i in range (8,RowCount):
+ text = table.GetValue (i, 1)
+ stat = table.GetValue (i, 0)
+ stats.append ( (text, GS (stat)&0x07, '+') )
+ stats.append (None)
+
+ # 11766 AC bonuses
+ stats.append (11766)
+ # 11770 AC vs. Crushing
+ stats.append ((11770, GS (IE_ACCRUSHINGMOD), 'p'))
+ # 11767 AC vs. Missile
+ stats.append ((11767, GS (IE_ACMISSILEMOD), 'p'))
+ # 11769 AC vs. Piercing
+ stats.append ((11769, GS (IE_ACPIERCINGMOD), 'p'))
+ # 11768 AC vs. Slashing
+ stats.append ((11768, GS (IE_ACSLASHINGMOD), 'p'))
+ stats.append (None)
+
+ # 10315 Ability bonuses
+ stats.append (10315)
+ value = GemRB.GetPlayerStat (pc, IE_STR)
+ ex = GemRB.GetPlayerStat (pc, IE_STREXTRA)
+ # 10332 to hit
+ stats.append ( (10332, GemRB.GetAbilityBonus (IE_STR,0,value,ex), 'p') )
+ # 10336 damage
+ stats.append ( (10336, GemRB.GetAbilityBonus (IE_STR,1,value,ex), 'p') )
+ # 10337 open doors (bend bars lift gates)
+ stats.append ( (10337, GemRB.GetAbilityBonus (IE_STR,2,value,ex), '0') )
+ # 10338 weight allowance
+ stats.append ( (10338, GemRB.GetAbilityBonus (IE_STR,3,value,ex), '0') )
+ # 10339 AC
+ stats.append ( (10339, GA (IE_DEX,2), '0') )
+ # 10340 Missile adjustment
+ stats.append ( (10340, GA (IE_DEX,1), 'p') )
+ # 10341 Reaction adjustment
+ stats.append ( (10341, GA (IE_DEX,0), 'p') )
+ # 10342 CON HP Bonus/Level
+ stats.append ( (10342, GA (IE_CON,0), 'p') )
+ # 10343 Chance To Learn spell
+ if GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_WIZARD, 0, 0)>0:
+ stats.append ( (10343, GA (IE_INT,0), '%' ) )
+ # 10347 Reaction
+ stats.append ( (10347, GA (IE_REPUTATION,0), '0') )
+ stats.append (None)
+
+ #Bonus spells
+ if GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, 0, 0)>0:
+ stats.append (10344)
+ for level in range (7):
+ GemRB.SetToken ("SPELLLEVEL", str (level+1) )
+ #get the bonus spell count
+ base = GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, level, 0)
+ if base:
+ count = GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, level)
+ stats.append ( (GemRB.GetString (10345), count-base, 'r') )
+ stats.append (None)
+
+ # 32204 Resistances
+ stats.append (32204)
+ # 32213 Normal Fire
+ stats.append ((32213, GS (IE_RESISTFIRE), '%'))
+ # 32222 Magic Fire
+ stats.append ((32222, GS (IE_RESISTMAGICFIRE), '%'))
+ # 32214 Normal Cold
+ stats.append ((32214, GS (IE_RESISTCOLD), '%'))
+ # 32223 Magic Cold
+ stats.append ((32223, GS (IE_RESISTMAGICCOLD), '%'))
+ # 32220 Electricity
+ stats.append ((32220, GS (IE_RESISTELECTRICITY), '%'))
+ # 32221 Acid
+ stats.append ((32221, GS (IE_RESISTACID), '%'))
+ # Magic
+ stats.append ((62146, GS (IE_RESISTMAGIC), '%'))
+ # Magic Damage
+ stats.append ((32233, GS (IE_MAGICDAMAGERESISTANCE), '%'))
+ # Missile
+ stats.append ((11767, GS (IE_RESISTMISSILE), '%'))
+ # Slashing
+ stats.append ((11768, GS (IE_RESISTSLASHING), '%'))
+ # Piercing
+ stats.append ((11769, GS (IE_RESISTPIERCING), '%'))
+ # Crushing
+ stats.append ((11770, GS (IE_RESISTCRUSHING), '%'))
+ # Poison
+ stats.append ((14017, GS (IE_RESISTPOISON), '%'))
+ stats.append (None)
+
+ #Weapon Style bonuses
+ stats.append (32131)
+ wstyle = GemRB.GetCombatDetails (pc, 0)["Style"] # equipped weapon style + 1000 * proficiency level
+ profcount = wstyle / 1000
+ if profcount:
+ wstyletables = { IE_PROFICIENCY2WEAPON:"wstwowpn", IE_PROFICIENCY2HANDED:"wstwohnd", IE_PROFICIENCYSINGLEWEAPON:"wssingle", IE_PROFICIENCYSWORDANDSHIELD:"wsshield" }
+ bonusrefs = { "THAC0BONUSRIGHT":56911, "THAC0BONUSLEFT":56910, "DAMAGEBONUS":10336, "CRITICALHITBONUS":32140, "PHYSICALSPEED":32141, "AC":10339, "ACVSMISSLE":10340 }
+ WStyleTable = GemRB.LoadTable (wstyletables[wstyle%1000])
+ for col in range(WStyleTable.GetColumnCount()):
+ value = WStyleTable.GetValue (profcount, col)
+ stats.append ((bonusrefs[WStyleTable.GetColumnName(col)], value, ''))
+ stats.append (None)
+
+ res = []
+ lines = 0
+ for s in stats:
+ try:
+ strref, val, type = s
+ if val == 0 and type != '0':
+ continue
+ if type == '+': #pluses
+ res.append ("[capital=0]"+GemRB.GetString (strref) + ' '+ '+' * val)
+ elif type == 'p': #a plus prefix if positive
+ if val > 0:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ' +' + str (val) )
+ else:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ' ' + str (val) )
+ elif type == 'r': #a plus prefix if positive, strref is an already resolved string
+ if val > 0:
+ res.append ("[capital=0]" + strref + ' +' + str (val) )
+ else:
+ res.append ("[capital=0]" + strref + ' ' + str (val) )
+ elif type == 's': #both base and (modified) stat, but only if they differ
+ base = GB (val)
+ stat = GS (val)
+ base_str = "[capital=0]" + GemRB.GetString (strref) + ': ' + str(stat)
+ if base == stat:
+ res.append (base_str)
+ else:
+ res.append (base_str + " (" + str(stat-base) + ")")
+ elif type == 'x': #x character before value
+ res.append ("[capital=0]"+GemRB.GetString (strref)+': x' + str (val) )
+ elif type == 'a': #value (portrait icon) + string
+ res.append ("[capital=2]"+val+" "+GemRB.GetString (strref) )
+ elif type == 'b': #strref is an already resolved string
+ res.append ("[capital=0]"+strref+": "+str (val) )
+ elif type == 'c': #normal string
+ res.append ("[capital=0]"+GemRB.GetString (strref) )
+ elif type == 'd': #strref is an already resolved string
+ res.append ("[capital=0]"+strref)
+ elif type == '0': #normal value
+ res.append (GemRB.GetString (strref) + ': ' + str (val) )
+ else: #normal value + type character, for example percent sign
+ res.append (GemRB.GetString (strref) + ': ' + str (val) + type)
+ lines = 1
+ except:
+ if s != None:
+ res.append ("[capital=0]"+ GemRB.GetString (s) )
+ lines = 0
+ else:
+ if not lines:
+ res.append (str_None)
+ res.append ("")
+ lines = 0
+
+ return "\n".join (res)
+
+
+def GetReputation (repvalue):
+ table = GemRB.LoadTable ("reptxt")
+ if repvalue>20:
+ repvalue=20
+ txt = GemRB.GetString (table.GetValue (repvalue, 0) )
+ return txt+" ("+str (repvalue)+")"
+
+
+def OpenInformationWindow ():
+ global InformationWindow
+
+ if InformationWindow != None:
+ if BiographyWindow:
+ OpenBiographyWindow ()
+
+ if InformationWindow:
+ InformationWindow.Unload ()
+ OptionsWindow.SetVisible (WINDOW_FRONT)
+ RecordsWindow.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_FRONT)
+ InformationWindow = None
+
+ return
+
+ InformationWindow = Window = GemRB.LoadWindow (4)
+
+ # Biography
+ Button = Window.GetControl (26)
+ Button.SetText (18003)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyWindow)
+
+ # Done
+ Button = Window.GetControl (24)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenInformationWindow)
+
+ TotalPartyExp = 0
+ ChapterPartyExp = 0
+ TotalCount = 0
+ ChapterCount = 0
+ for i in range (1, GemRB.GetPartySize () + 1):
+ stat = GemRB.GetPCStats(i)
+ TotalPartyExp = TotalPartyExp + stat['KillsTotalXP']
+ ChapterPartyExp = ChapterPartyExp + stat['KillsChapterXP']
+ TotalCount = TotalCount + stat['KillsTotalCount']
+ ChapterCount = ChapterCount + stat['KillsChapterCount']
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+ stat = GemRB.GetPCStats (pc)
+
+ Label = Window.GetControl (0x10000000)
+ Label.SetText (GemRB.GetPlayerName (pc, 1))
+ # class
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ Label = Window.GetControl (0x10000018)
+ Label.SetText (ClassTitle)
+
+ #most powerful vanquished
+ Label = Window.GetControl (0x10000005)
+ #we need getstring, so -1 will translate to empty string
+ Label.SetText (GemRB.GetString (stat['BestKilledName']))
+
+ # NOTE: currentTime is in seconds, joinTime is in seconds * 15
+ # (script updates???). In each case, there are 60 seconds
+ # in a minute, 24 hours in a day, but ONLY 5 minutes in an hour!!
+ # Hence currentTime (and joinTime after div by 15) has
+ # 7200 secs a day (60 * 5 * 24)
+ currentTime = GemRB.GetGameTime ()
+ joinTime = stat['JoinDate'] - stat['AwayTime']
+
+ party_time = currentTime - (joinTime / 15)
+ print "CurrentTime",currentTime
+ days = party_time / 7200
+ hours = (party_time % 7200) / 300
+
+ GemRB.SetToken ('GAMEDAY', str (days))
+ GemRB.SetToken ('HOUR', str (hours))
+ Label = Window.GetControl (0x10000006)
+ #actually it is 16043 <DURATION>, but duration is translated to
+ #16041, hopefully this won't cause problem with international version
+ Label.SetText (16041)
+
+ #favourite spell
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (stat['FavouriteSpell'])
+
+ #favourite weapon
+ Label = Window.GetControl (0x10000008)
+ #actually it is 10479 <WEAPONNAME>, but weaponname is translated to
+ #the real weapon name (which we should set using SetToken)
+ #there are other strings like bow+wname/xbow+wname/sling+wname
+ #are they used?
+ Label.SetText (stat['FavouriteWeapon'])
+
+ #total party xp
+ Label = Window.GetControl (0x10000013)
+ if TotalPartyExp != 0:
+ PartyExp = int ((stat['KillsTotalXP'] * 100) / TotalPartyExp)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ #chapter party xp
+ Label = Window.GetControl (0x1000000f)
+ if ChapterPartyExp != 0:
+ PartyExp = int ((stat['KillsChapterXP'] * 100) / ChapterPartyExp)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ #total xp
+ Label = Window.GetControl (0x10000014)
+ if TotalCount != 0:
+ PartyExp = int ((stat['KillsTotalCount'] * 100) / TotalCount)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ #chapter xp
+ Label = Window.GetControl (0x10000010)
+ if ChapterCount != 0:
+ PartyExp = int ((stat['KillsChapterCount'] * 100) / ChapterCount)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ Label = Window.GetControl (0x10000011)
+ Label.SetText (str (stat['KillsChapterXP']))
+ Label = Window.GetControl (0x10000015)
+ Label.SetText (str (stat['KillsTotalXP']))
+
+ #count of kills in chapter/game
+ Label = Window.GetControl (0x10000012)
+ Label.SetText (str (stat['KillsChapterCount']))
+ Label = Window.GetControl (0x10000016)
+ Label.SetText (str (stat['KillsTotalCount']))
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OpenBiographyWindow ():
+ global BiographyWindow
+
+ if BiographyWindow != None:
+ if BiographyWindow:
+ BiographyWindow.Unload ()
+ BiographyWindow = None
+ InformationWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+ BiographyWindow = Window = GemRB.LoadWindow (12)
+
+ TextArea = Window.GetControl (0)
+ pc = GemRB.GameGetSelectedPCSingle ()
+ TextArea.SetText (GemRB.GetPlayerString (pc, 74) )
+
+ # Done
+ Button = Window.GetControl (2)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyWindow)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+ return
+
+def OpenExportWindow ():
+ global ExportWindow, NameField, ExportDoneButton
+
+ ExportWindow = GemRB.LoadWindow (13)
+
+ TextArea = ExportWindow.GetControl (2)
+ TextArea.SetText (10962)
+
+ TextArea = ExportWindow.GetControl (0)
+ TextArea.GetCharacters ()
+
+ ExportDoneButton = ExportWindow.GetControl (4)
+ ExportDoneButton.SetText (11973)
+ ExportDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = ExportWindow.GetControl (5)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ NameField = ExportWindow.GetControl (6)
+
+ ExportDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExportDonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExportCancelPress)
+ NameField.SetEvent (IE_GUI_EDIT_ON_CHANGE, ExportEditChanged)
+ ExportWindow.ShowModal (MODAL_SHADOW_GRAY)
+ NameField.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def ExportDonePress():
+ if ExportWindow:
+ ExportWindow.Unload()
+ #save file under name from EditControl
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SaveCharacter (pc, ExportFileName)
+ return
+
+def ExportCancelPress():
+ if ExportWindow:
+ ExportWindow.Unload()
+ return
+
+def ExportEditChanged():
+ global ExportFileName
+
+ ExportFileName = NameField.QueryText ()
+ if ExportFileName == "":
+ ExportDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ ExportDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def OpenKitInfoWindow ():
+ global KitInfoWindow
+
+ KitInfoWindow = GemRB.LoadWindow (24)
+
+ #back button (setting first, to be less error prone)
+ DoneButton = KitInfoWindow.GetControl (2)
+ DoneButton.SetText (11973)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, KitDonePress)
+
+ #kit or class description
+ TextArea = KitInfoWindow.GetControl (0)
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ Multi = CommonTables.Classes.GetValue (ClassIndex, 4)
+ Dual = GUICommon.IsDualClassed (pc, 1)
+
+ if Multi and Dual[0] == 0: # true multi class
+ text = CommonTables.Classes.GetValue (ClassIndex, 1)
+ TextArea.SetText (text)
+ KitInfoWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+ KitIndex = GUICommon.GetKitIndex (pc)
+
+ if Dual[0]: # dual class
+ # first (previous) kit or class of the dual class
+ if Dual[0] == 1:
+ text = CommonTables.KitList.GetValue (Dual[1], 3)
+ elif Dual[0] == 2:
+ text = CommonTables.Classes.GetValue (Dual[1], 1)
+
+ TextArea.SetText (text)
+ TextArea.Append ("\n\n")
+ text = CommonTables.Classes.GetValue (Dual[2], 1)
+
+ else: # ordinary class or kit
+ if KitIndex:
+ text = CommonTables.KitList.GetValue (KitIndex, 3)
+ else:
+ text = CommonTables.Classes.GetValue (ClassIndex, 1)
+
+ TextArea.Append (text)
+
+ KitInfoWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def KitDonePress():
+ if KitInfoWindow:
+ KitInfoWindow.Unload()
+ return
+
+def OpenCustomizeWindow ():
+ global CustomizeWindow
+ global PortraitsTable, ScriptsTable, ColorTable
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ if GemRB.GetPlayerStat (pc, IE_MC_FLAGS)&MC_EXPORTABLE:
+ Exportable = 1
+ else:
+ Exportable = 0
+
+ PortraitsTable = GemRB.LoadTable ("PICTURES")
+ ScriptsTable = GemRB.LoadTable ("SCRPDESC")
+ ColorTable = GemRB.LoadTable ("CLOWNCOL")
+ CustomizeWindow = GemRB.LoadWindow (17)
+
+ AppearanceButton = CustomizeWindow.GetControl (0)
+ AppearanceButton.SetText (11961)
+ if not Exportable:
+ AppearanceButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ SoundButton = CustomizeWindow.GetControl (1)
+ SoundButton.SetText (10647)
+ if not Exportable:
+ SoundButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ColorButton = CustomizeWindow.GetControl (2)
+ ColorButton.SetText (10646)
+ if not Exportable:
+ ColorButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ScriptButton = CustomizeWindow.GetControl (3)
+ ScriptButton.SetText (17111)
+
+ BiographyButton = CustomizeWindow.GetControl (9)
+ BiographyButton.SetText (18003)
+ if not Exportable:
+ BiographyButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ TextArea = CustomizeWindow.GetControl (5)
+ TextArea.SetText (11327)
+
+ DoneButton = CustomizeWindow.GetControl (7)
+ DoneButton.SetText (11973)
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ CancelButton = CustomizeWindow.GetControl (8);
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ AppearanceButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenAppearanceWindow)
+ SoundButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSoundWindow)
+ ColorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenColorWindow)
+ ScriptButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenScriptWindow)
+ BiographyButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyEditWindow)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomizeDonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomizeCancelPress)
+
+ CustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CustomizeDonePress ():
+ global CustomizeWindow
+
+ if CustomizeWindow:
+ CustomizeWindow.Unload ()
+ CustomizeWindow = None
+
+ UpdateRecordsWindow ()
+ return
+
+def CustomizeCancelPress ():
+ global CustomizeWindow
+
+ if CustomizeWindow:
+ CustomizeWindow.Unload ()
+ CustomizeWindow = None
+
+ UpdateRecordsWindow ()
+ return
+
+def OpenAppearanceWindow ():
+ global SubCustomizeWindow
+ global PortraitButton
+ global Gender, LastPortrait
+
+ SubCustomizeWindow = GemRB.LoadWindow (18)
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Gender = GemRB.GetPlayerStat (pc, IE_SEX, 1)
+ PortraitName = GemRB.GetPlayerPortrait (pc, 0)
+ LastPortrait = PortraitsTable.GetRowIndex (PortraitName[0:len(PortraitName)-1])
+
+ PortraitButton = SubCustomizeWindow.GetControl (0)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ LeftButton = SubCustomizeWindow.GetControl (1)
+ RightButton = SubCustomizeWindow.GetControl (2)
+
+ DoneButton = SubCustomizeWindow.GetControl (3)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (4)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ CustomPortraitButton = SubCustomizeWindow.GetControl (5)
+ CustomPortraitButton.SetText (17545)
+
+ LeftButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitLeftPress)
+ RightButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitRightPress)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePortraitWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ CustomPortraitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenCustomPortraitWindow)
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+ while True:
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ UpdatePortrait ()
+ break
+ LastPortrait = LastPortrait + 1
+
+ return
+
+def DonePortraitWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Name = PortraitsTable.GetRowName (LastPortrait)
+ GemRB.FillPlayerInfo (pc, Name + "M", Name + "S")
+ CloseSubCustomizeWindow ()
+ return
+
+def PortraitRightPress():
+ global LastPortrait
+
+ while True:
+ LastPortrait = LastPortrait + 1
+ if LastPortrait >= PortraitsTable.GetRowCount ():
+ LastPortrait = 0
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ UpdatePortrait ()
+ return
+
+ return
+
+def PortraitLeftPress():
+ global LastPortrait
+
+ while True:
+ LastPortrait = LastPortrait - 1
+ if LastPortrait < 0:
+ LastPortrait = PortraitsTable.GetRowCount ()-1
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ UpdatePortrait ()
+ return
+
+ return
+
+def UpdatePortrait ():
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"L"
+ PortraitButton.SetPicture (PortraitName, "NOPORTLG")
+ return
+
+def OpenCustomPortraitWindow ():
+ global SubSubCustomizeWindow
+ global PortraitList1, PortraitList2
+ global RowCount1, RowCount2
+
+ SubSubCustomizeWindow = GemRB.LoadWindow (19)
+
+ SmallPortraitButton = SubSubCustomizeWindow.GetControl (1)
+ SmallPortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+
+ LargePortraitButton = SubSubCustomizeWindow.GetControl (0)
+ LargePortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+
+ DoneButton = SubSubCustomizeWindow.GetControl (10)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = SubSubCustomizeWindow.GetControl (11)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Portrait List Large
+ PortraitList1 = SubSubCustomizeWindow.GetControl (2)
+ RowCount1 = PortraitList1.GetPortraits (0)
+ PortraitList1.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, LargeCustomPortrait)
+ GemRB.SetVar ("Row1", RowCount1)
+ PortraitList1.SetVarAssoc ("Row1",RowCount1)
+
+ # Portrait List Small
+ PortraitList2 = SubSubCustomizeWindow.GetControl (3)
+ RowCount2 = PortraitList2.GetPortraits (1)
+ PortraitList2.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, SmallCustomPortrait)
+ GemRB.SetVar ("Row2", RowCount2)
+ PortraitList2.SetVarAssoc ("Row2",RowCount2)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePortraitCustomizeWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubSubCustomizeWindow)
+
+ SubSubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DonePortraitCustomizeWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.FillPlayerInfo (pc, PortraitList1.QueryText () , PortraitList2.QueryText ())
+ CloseSubSubCustomizeWindow ()
+ #closing the generic portraits, because we just set a custom one
+ CloseSubCustomizeWindow ()
+ return
+
+def LargeCustomPortrait ():
+ Window = SubSubCustomizeWindow
+
+ Portrait = PortraitList1.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row1") == RowCount1:
+ return
+
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (10)
+ if Portrait=="":
+ Portrait = "NOPORTMD"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList2.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (0)
+ Button.SetPicture (Portrait, "NOPORTMD")
+ return
+
+def SmallCustomPortrait ():
+ Window = SubSubCustomizeWindow
+
+ Portrait = PortraitList2.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row2") == RowCount2:
+ return
+
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (10)
+ if Portrait=="":
+ Portrait = "NOPORTSM"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList1.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (1)
+ Button.SetPicture (Portrait, "NOPORTSM")
+ return
+
+def OpenSoundWindow ():
+ global SubCustomizeWindow
+ global VoiceList
+ global Gender
+ global OldVoiceSet
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ OldVoiceSet = GemRB.GetPlayerSound (pc)
+ SubCustomizeWindow = GemRB.LoadWindow (20)
+
+ VoiceList = SubCustomizeWindow.GetControl (5)
+ VoiceList.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ Gender = GemRB.GetPlayerStat (pc, IE_SEX, 1)
+
+ VoiceList.SetVarAssoc ("Selected", 0)
+ RowCount=VoiceList.GetCharSounds()
+ VoiceList.SelectText (OldVoiceSet)
+
+ PlayButton = SubCustomizeWindow.GetControl (7)
+ PlayButton.SetText (17318)
+
+ TextArea = SubCustomizeWindow.GetControl (8)
+ TextArea.SetText (11315)
+
+ DoneButton = SubCustomizeWindow.GetControl (10)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (11)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlaySoundPressed)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneSoundWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSoundWindow)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseSoundWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerSound (pc, OldVoiceSet)
+ CloseSubCustomizeWindow ()
+ return
+
+def DoneSoundWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ CharSound = VoiceList.QueryText ()
+ GemRB.SetPlayerSound (pc, CharSound)
+
+ CloseSubCustomizeWindow ()
+ return
+
+def PlaySoundPressed():
+ global CharSoundWindow, SoundIndex, SoundSequence
+
+ CharSound = VoiceList.QueryText ()
+ tmp = SoundIndex
+ while (not GemRB.HasResource (CharSound + SoundSequence[SoundIndex], RES_WAV)):
+ NextSound()
+ if SoundIndex == tmp:
+ break
+ else:
+ NextSound()
+ GemRB.PlaySound (CharSound + SoundSequence[SoundIndex], 0, 0, 5)
+ return
+
+def NextSound():
+ global SoundIndex, SoundSequence
+ SoundIndex += 1
+ if SoundIndex >= len(SoundSequence):
+ SoundIndex = 0
+ return
+
+def OpenColorWindow ():
+ global SubCustomizeWindow
+ global PortraitWindow
+ global PortraitButton
+ global HairButton, SkinButton, MajorButton, MinorButton
+ global HairColor, SkinColor, MajorColor, MinorColor
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ MinorColor = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR)
+ MajorColor = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR)
+ SkinColor = GemRB.GetPlayerStat (pc, IE_SKIN_COLOR)
+ HairColor = GemRB.GetPlayerStat (pc, IE_HAIR_COLOR)
+ SubCustomizeWindow = GemRB.LoadWindow (21)
+
+ PortraitButton = SubCustomizeWindow.GetControl (0)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ HairButton = SubCustomizeWindow.GetControl (3)
+ SkinButton = SubCustomizeWindow.GetControl (4)
+ MajorButton = SubCustomizeWindow.GetControl (5)
+ MinorButton = SubCustomizeWindow.GetControl (6)
+
+ DoneButton = SubCustomizeWindow.GetControl (12)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (13)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ HairButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetHairColor)
+ SkinButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetSkinColor)
+ MajorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetMajorColor)
+ MinorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetMinorColor)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneColorWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ UpdatePaperDoll ()
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DoneColorWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerStat (pc, IE_MINOR_COLOR, MinorColor)
+ GemRB.SetPlayerStat (pc, IE_MAJOR_COLOR, MajorColor)
+ GemRB.SetPlayerStat (pc, IE_SKIN_COLOR, SkinColor)
+ GemRB.SetPlayerStat (pc, IE_HAIR_COLOR, HairColor)
+ CloseSubCustomizeWindow ()
+ return
+
+def UpdatePaperDoll ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Color1 = GemRB.GetPlayerStat (pc, IE_METAL_COLOR)
+ MinorButton.SetBAM ("COLGRAD", 0, 0, MinorColor&0xff)
+ MajorButton.SetBAM ("COLGRAD", 0, 0, MajorColor&0xff)
+ SkinButton.SetBAM ("COLGRAD", 0, 0, SkinColor&0xff)
+ Color5 = GemRB.GetPlayerStat (pc, IE_LEATHER_COLOR)
+ Color6 = GemRB.GetPlayerStat (pc, IE_ARMOR_COLOR)
+ HairButton.SetBAM ("COLGRAD", 0, 0, HairColor&0xff)
+ PortraitButton.SetPLT (GUICommon.GetActorPaperDoll (pc),
+ Color1, MinorColor, MajorColor, SkinColor, Color5, Color6, HairColor, 0, 0)
+ return
+
+def SetHairColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 0
+ PickedColor = HairColor
+ OpenColorPicker ()
+ return
+
+def SetSkinColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 1
+ PickedColor = SkinColor
+ OpenColorPicker ()
+ return
+
+def SetMinorColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 2
+ PickedColor = MinorColor
+ OpenColorPicker ()
+ return
+
+def SetMajorColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 3
+ PickedColor = MajorColor
+ OpenColorPicker ()
+ return
+
+def OpenColorPicker ():
+ global SubSubCustomizeWindow
+ #global Selected
+
+ SubSubCustomizeWindow = GemRB.LoadWindow (22)
+
+ GemRB.SetVar ("Selected",-1)
+ for i in range (1,35):
+ Button = SubSubCustomizeWindow.GetControl (i)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ #Selected = -1
+ for i in range (34):
+ MyColor = ColorTable.GetValue (ColorIndex, i)
+ if MyColor == "*":
+ break
+ Button = SubSubCustomizeWindow.GetControl (i+1)
+ Button.SetBAM("COLGRAD", 2, 0, MyColor)
+ if PickedColor == MyColor:
+ GemRB.SetVar ("Selected",i)
+ #Selected = i
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetVarAssoc("Selected",i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePress)
+
+ SubSubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DonePress():
+ global HairColor, SkinColor, MajorColor, MinorColor
+ global PickedColor
+
+ CloseSubSubCustomizeWindow ()
+ PickedColor=ColorTable.GetValue (ColorIndex, GemRB.GetVar ("Selected"))
+ if ColorIndex==0:
+ HairColor=PickedColor
+ UpdatePaperDoll ()
+ return
+ if ColorIndex==1:
+ SkinColor=PickedColor
+ UpdatePaperDoll ()
+ return
+ if ColorIndex==2:
+ MinorColor=PickedColor
+ UpdatePaperDoll ()
+ return
+
+ MajorColor=PickedColor
+ UpdatePaperDoll ()
+ return
+
+def OpenScriptWindow ():
+ global SubCustomizeWindow
+ global ScriptTextArea, SelectedTextArea
+
+ SubCustomizeWindow = GemRB.LoadWindow (11)
+
+ ScriptTextArea = SubCustomizeWindow.GetControl (2)
+ ScriptTextArea.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ FillScriptList ()
+ pc = GemRB.GameGetSelectedPCSingle ()
+ script = GemRB.GetPlayerScript (pc)
+ scriptindex = ScriptsTable.GetRowIndex (script)
+ GemRB.SetVar ("Selected", scriptindex)
+ ScriptTextArea.SetVarAssoc ("Selected", scriptindex)
+
+ SelectedTextArea = SubCustomizeWindow.GetControl (4)
+ UpdateScriptSelection ()
+
+ DoneButton = SubCustomizeWindow.GetControl (5)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (6)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneScriptWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ ScriptTextArea.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, UpdateScriptSelection)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def FillScriptList ():
+ ScriptTextArea.Clear ()
+ row = ScriptsTable.GetRowCount ()
+ for i in range (row):
+ GemRB.SetToken ("script", ScriptsTable.GetRowName (i) )
+ title = ScriptsTable.GetValue (i,0)
+ if title!=-1:
+ desc = ScriptsTable.GetValue (i,1)
+ txt = GemRB.GetString (title)
+
+ if (desc!=-1):
+ txt += GemRB.GetString (desc)
+
+ ScriptTextArea.Append (txt+"\n", -1)
+
+ else:
+ ScriptTextArea.Append (ScriptsTable.GetRowName (i)+"\n" ,-1)
+
+ return
+
+def DoneScriptWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ script = ScriptsTable.GetRowName (GemRB.GetVar ("Selected") )
+ GemRB.SetPlayerScript (pc, script)
+ CloseSubCustomizeWindow ()
+ return
+
+def UpdateScriptSelection():
+ text = ScriptTextArea.QueryText ()
+ SelectedTextArea.SetText (text)
+ return
+
+def OpenBiographyEditWindow ():
+ global SubCustomizeWindow
+ global BioStrRef
+ global TextArea
+
+ Changed = 0
+ pc = GemRB.GameGetSelectedPCSingle ()
+ BioStrRef = GemRB.GetPlayerString (pc, 74)
+ if BioStrRef != 33347:
+ Changed = 1
+
+ SubCustomizeWindow = GemRB.LoadWindow (23)
+
+ ClearButton = SubCustomizeWindow.GetControl (5)
+ ClearButton.SetText (34881)
+
+ DoneButton = SubCustomizeWindow.GetControl (1)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ RevertButton = SubCustomizeWindow.GetControl (3)
+ RevertButton.SetText (2240)
+ if not Changed:
+ RevertButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = SubCustomizeWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ TextArea = SubCustomizeWindow.GetControl (4)
+ TextArea.SetBufferLength (65535)
+ TextArea.SetText (BioStrRef)
+
+ ClearButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClearBiography)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneBiographyWindow)
+ RevertButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RevertBiography)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ClearBiography():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ BioStrRef = 62015+pc
+ #GemRB.CreateString (BioStrRef, "")
+ TextArea.SetText ("")
+ return
+
+def DoneBiographyWindow ():
+ global BioStrRef
+
+ #TODO set bio
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #pc is 1 based
+ BioStrRef = 62015+pc
+ GemRB.CreateString (BioStrRef, TextArea.QueryText())
+ GemRB.SetPlayerString (pc, 74, BioStrRef)
+ CloseSubCustomizeWindow ()
+ return
+
+def RevertBiography():
+ global BioStrRef
+
+ BioStrRef = 33347
+ TextArea.SetText (33347)
+ return
+
+def CloseSubCustomizeWindow ():
+ global SubCustomizeWindow
+
+ if SubCustomizeWindow:
+ SubCustomizeWindow.Unload ()
+ SubCustomizeWindow = None
+ return
+
+def CloseSubSubCustomizeWindow ():
+ global SubSubCustomizeWindow
+
+ if SubSubCustomizeWindow:
+ SubSubCustomizeWindow.Unload ()
+ SubSubCustomizeWindow = None
+ return
+
+###################################################
+# End of file GUIREC.py
diff --git a/gemrb/GUIScripts/bg2/GUISAVE.py b/gemrb/GUIScripts/bg2/GUISAVE.py
new file mode 100644
index 0000000..5f3d0f3
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUISAVE.py
@@ -0,0 +1,281 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUISAVE.py - Save window
+
+###################################################
+
+import GemRB
+import GUICommon
+import LoadScreen
+from GUIDefines import *
+
+SaveWindow = 0
+ConfirmWindow = 0
+NameField = 0
+SaveButton = 0
+TextAreaControl = 0
+Games = ()
+ScrollBar = 0
+str_chapter = (48007, 48006, 16205, 16206, 16207, 16208, 16209, 71020, 71021, 71022)
+
+def OpenSaveWindow ():
+ global SaveWindow, TextAreaControl, Games, ScrollBar
+
+ if GUICommon.CloseOtherWindow (OpenSaveWindow):
+ CloseSaveWindow ()
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUISAVE", 640, 480)
+ Window = SaveWindow = GemRB.LoadWindow (0)
+ Window.SetFrame ()
+ CancelButton=Window.GetControl (34)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSaveWindow)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ GemRB.SetVar ("LoadIdx",0)
+
+ for i in range(4):
+ Button = Window.GetControl (26+i)
+ Button.SetText (15588)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SavePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ Button = Window.GetControl (30+i)
+ Button.SetText (13957)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ #area previews
+ Button = Window.GetControl (1+i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ #PC portraits
+ for j in range(PARTY_SIZE):
+ Button = Window.GetControl (40+i*PARTY_SIZE+j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ ScrollBar=Window.GetControl (25)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games=GemRB.GetSaveGames ()
+ TopIndex = max (0, len(Games) - 4 + 1) #one more for the 'new game'
+ GemRB.SetVar ("TopIndex",TopIndex)
+ ScrollBar.SetVarAssoc ("TopIndex", TopIndex+1)
+ ScrollBar.SetDefaultScrollBar ()
+ ScrollBarPress ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ScrollBarPress():
+ Window = SaveWindow
+
+ #draw load game portraits
+ Pos = GemRB.GetVar ("TopIndex")
+ for i in range(4):
+ ActPos = Pos + i
+
+ Button1 = Window.GetControl (26+i)
+ Button2 = Window.GetControl (30+i)
+ if ActPos<=len(Games):
+ Button1.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetName()
+ Button2.SetState (IE_GUI_BUTTON_ENABLED)
+ elif ActPos == len(Games):
+ Slotname = 15304
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Slotname = ""
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Label = Window.GetControl (0x10000008+i)
+ Label.SetText (Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = Window.GetControl (0x10000010+i)
+ Label.SetText (Slotname)
+
+ Button=Window.GetControl (1+i)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture("")
+ for j in range(PARTY_SIZE):
+ Button=Window.GetControl (40+i*PARTY_SIZE+j)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture("")
+ return
+
+def AbortedSaveGame():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ConfirmedSaveGame():
+ global ConfirmWindow
+
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ Label = ConfirmWindow.GetControl (3)
+ Slotname = Label.QueryText ()
+ LoadScreen.StartLoadScreen()
+ if Pos < len(Games):
+ GemRB.SaveGame(Games[Pos], Slotname)
+ else:
+ GemRB.SaveGame(None, Slotname)
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ OpenSaveWindow() # close window
+ return
+
+def SavePress():
+ global ConfirmWindow, NameField, SaveButton
+
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ ConfirmWindow = GemRB.LoadWindow (1)
+
+ #slot name
+ if Pos<len(Games):
+ Slotname = Games[Pos].GetName();
+ save_strref = 15306
+ else:
+ Slotname = ""
+ save_strref = 15588
+ NameField = ConfirmWindow.GetControl (3)
+ NameField.SetText (Slotname)
+ NameField.SetEvent (IE_GUI_EDIT_ON_CHANGE, EditChange)
+
+ #game hours (should be generated from game)
+ if Pos<len(Games):
+ Chapter = GemRB.GetGameVar ("CHAPTER") & 0x7fffffff
+ Slotname = GemRB.GetString(str_chapter[Chapter-1]) + " " + Games[Pos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = ConfirmWindow.GetControl (0x10000004)
+ Label.SetText (Slotname)
+
+ #areapreview
+ Button=ConfirmWindow.GetControl (0)
+ if Pos<len(Games):
+ Button.SetSprite2D(Games[Pos].GetPreview())
+ else:
+ Button.SetPicture("")
+
+ #portraits
+ for j in range(PARTY_SIZE):
+ Button=ConfirmWindow.GetControl (40+j)
+ if Pos<len(Games):
+ Button.SetSprite2D(Games[Pos].GetPortrait(j))
+ else:
+ Button.SetPicture("")
+
+ #save
+ SaveButton=ConfirmWindow.GetControl (7)
+ SaveButton.SetText (save_strref)
+ SaveButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ConfirmedSaveGame)
+ SaveButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ #SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+ if Slotname == "":
+ SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ #cancel
+ CancelButton=ConfirmWindow.GetControl (8)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbortedSaveGame)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ NameField.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def EditChange():
+ Name = NameField.QueryText ()
+ if len(Name)==0:
+ SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ SaveButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def DeleteGameConfirm():
+ global Games
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Pos = TopIndex +GemRB.GetVar ("LoadIdx")
+ GemRB.DeleteSaveGame(Games[Pos])
+ del Games[Pos]
+ if TopIndex>0:
+ GemRB.SetVar ("TopIndex",TopIndex-1)
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress()
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress():
+ global ConfirmWindow
+
+ SaveWindow.SetVisible (WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow (2)
+ Text=ConfirmWindow.GetControl (0)
+ Text.SetText (15305)
+ DeleteButton=ConfirmWindow.GetControl (1)
+ DeleteButton.SetText (13957)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ CancelButton=ConfirmWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CloseSaveWindow ():
+ if SaveWindow:
+ SaveWindow.Unload ()
+ if GemRB.GetVar ("QuitAfterSave"):
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("Start")
+ return
+
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE) #enabling the game control screen
+ GemRB.UnhideGUI () #enabling the other windows
+ return
diff --git a/gemrb/GUIScripts/bg2/GUISONGS.py b/gemrb/GUIScripts/bg2/GUISONGS.py
new file mode 100644
index 0000000..e75841d
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUISONGS.py
@@ -0,0 +1,69 @@
+# 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.
+#
+#
+#instead of credits, you can listen the songs of the game :)
+import GemRB
+import GUICommon
+
+MovieWindow = 0
+TextAreaControl = 0
+MoviesTable = 0
+
+def OnLoad():
+ global MovieWindow, TextAreaControl, MoviesTable
+
+ GemRB.LoadWindowPack("GUIMOVIE", 640, 480)
+ MovieWindow = GemRB.LoadWindow(0)
+ MovieWindow.SetFrame ()
+ TextAreaControl = MovieWindow.GetControl(0)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ PlayButton = MovieWindow.GetControl(2)
+ CreditsButton = MovieWindow.GetControl(3)
+ DoneButton = MovieWindow.GetControl(4)
+ MoviesTable = GemRB.LoadTable("SONGLIST")
+ for i in range(0, MoviesTable.GetRowCount() ):
+ s = MoviesTable.GetValue(i, 0)
+ TextAreaControl.Append(s,-1)
+ TextAreaControl.SetVarAssoc("MovieIndex",0)
+ PlayButton.SetText(17318)
+ CreditsButton.SetText(15591)
+ DoneButton.SetText(11973)
+ PlayButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ CreditsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CreditsPress)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+ MovieWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def PlayPress():
+ s = GemRB.GetVar("MovieIndex")
+ t = MoviesTable.GetValue(s, 1)
+ GemRB.LoadMusicPL(t,1)
+ return
+
+def CreditsPress():
+ GemRB.PlayMovie("endcrdit", 1)
+ return
+
+def DonePress():
+ if MovieWindow:
+ MovieWindow.Unload()
+ if GUICommon.HasTOB():
+ GemRB.SetNextScript ("Start2")
+ else:
+ GemRB.SetNextScript ("Start")
+ return
diff --git a/gemrb/GUIScripts/bg2/GUIWORLD.py b/gemrb/GUIScripts/bg2/GUIWORLD.py
new file mode 100644
index 0000000..e9f207b
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/GUIWORLD.py
@@ -0,0 +1,335 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIW.py - scripts to control some windows from the GUIWORLD winpack
+# except of Actions, Portrait, Options and Dialog windows
+#################################################################
+
+import GemRB
+from GUIDefines import *
+import GUICommon
+import GUICommonWindows
+import GUIClasses
+from ie_stats import *
+import MessageWindow
+import CommonWindow
+
+FRAME_PC_SELECTED = 0
+FRAME_PC_TARGET = 1
+
+EmptyWindow = None
+ContinueWindow = None
+ReformPartyWindow = None
+OldActionsWindow = None
+OldMessageWindow = None
+
+removable_pcs = []
+
+def DialogStarted ():
+ global ContinueWindow, OldActionsWindow
+
+ # try to force-close anything which is open
+ GUICommon.CloseOtherWindow(None)
+ CommonWindow.CloseContainerWindow()
+
+ # we need GUI for dialogs
+ GemRB.UnhideGUI()
+
+ # opening control size to maximum, enabling dialog window
+ GemRB.GameSetScreenFlags(GS_HIDEGUI, OP_NAND)
+ GemRB.GameSetScreenFlags(GS_DIALOG, OP_OR)
+
+ if GUICommonWindows.PortraitWindow:
+ GUICommonWindows.UpdatePortraitWindow ()
+
+ # we want this to happen before we start fiddling with the GUI
+ MessageWindow.UpdateControlStatus()
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ContinueWindow = Window = GemRB.LoadWindow (9)
+
+ GUICommonWindows.EmptyControls()
+ OldActionsWindow = GUICommonWindows.ActionsWindow
+ #GUICommonWindows.ActionsWindow = None
+ OldActionsWindow.SetVisible(WINDOW_INVISIBLE)
+ GemRB.SetVar ("ActionsWindow", -1)
+
+def DialogEnded ():
+ global ContinueWindow, OldActionsWindow
+
+ # TODO: why is this being called at game start?!
+ if not ContinueWindow:
+ return
+
+ #GUICommonWindows.ActionsWindow = OldActionsWindow
+ OldActionsWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.SetVar ("ActionsWindow", OldActionsWindow.ID)
+ GUICommonWindows.UpdateActionsWindow()
+
+ ContinueWindow.Unload ()
+ ContinueWindow = None
+ OldActionsWindow = None
+
+ if GUICommonWindows.PortraitWindow:
+ GUICommonWindows.UpdatePortraitWindow ()
+
+def CloseContinueWindow ():
+ # don't close the actual window now to avoid flickering: we might still want it open
+ GemRB.SetVar ("DialogChoose", GemRB.GetVar ("DialogOption"))
+
+def NextDialogState ():
+ if not ContinueWindow:
+ return
+
+ ContinueWindow.SetVisible(WINDOW_INVISIBLE)
+ OldActionsWindow.SetVisible(WINDOW_VISIBLE)
+
+ MessageWindow.TMessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenEndMessageWindow ():
+ ContinueWindow.SetVisible(WINDOW_VISIBLE)
+ OldActionsWindow.SetVisible(WINDOW_INVISIBLE)
+ Button = ContinueWindow.GetControl (0)
+ Button.SetText (9371)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenContinueMessageWindow ():
+ ContinueWindow.SetVisible(WINDOW_VISIBLE)
+ OldActionsWindow.SetVisible(WINDOW_INVISIBLE)
+ #continue
+ Button = ContinueWindow.GetControl (0)
+ Button.SetText (9372)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def UpdateReformWindow ():
+ Window = ReformPartyWindow
+
+ select = GemRB.GetVar ("Selected")
+
+ need_to_drop = GemRB.GetPartySize ()-PARTY_SIZE
+ if need_to_drop<0:
+ need_to_drop = 0
+
+ #excess player number
+ Label = Window.GetControl (0x1000000f)
+ Label.SetText (str(need_to_drop) )
+
+ #done
+ Button = Window.GetControl (8)
+ if need_to_drop:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ #remove
+ Button = Window.GetControl (15)
+ if select:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ for i in range (PARTY_SIZE+1):
+ Button = Window.GetControl (i)
+ if i+1 not in removable_pcs:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ continue
+
+ for i in removable_pcs:
+ Button = Window.GetControl (removable_pcs.index(i))
+ Button.EnableBorder (FRAME_PC_SELECTED, select == i )
+ pic = GemRB.GetPlayerPortrait (i, 1)
+ if not pic:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ continue
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ALIGN_BOTTOM|IE_GUI_BUTTON_ALIGN_LEFT, OP_SET)
+ Button.SetPicture (pic, "NOPORTSM")
+ GUICommonWindows.UpdatePortraitWindow ()
+ return
+
+def RemovePlayer ():
+ global ReformPartyWindow
+
+ hideflag = GemRB.HideGUI ()
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+ ReformPartyWindow = Window = GemRB.LoadWindow (25)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+
+ #are you sure
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (17518)
+
+ #confirm
+ Button = Window.GetControl (1)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemovePlayerConfirm)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ #cancel
+ Button = Window.GetControl (2)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemovePlayerCancel)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.SetVar ("OtherWindow", Window.ID)
+ GemRB.SetVar ("ActionsWindow", -1)
+ if hideflag:
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def RemovePlayerConfirm ():
+ slot = GemRB.GetVar ("Selected")
+ GemRB.LeaveParty (slot, 2)
+ OpenReformPartyWindow ()
+ return
+
+def RemovePlayerCancel ():
+ #Once for getting rid of the confirmation window
+ OpenReformPartyWindow ()
+ #and once for reopening the reform party window
+ OpenReformPartyWindow ()
+ return
+
+def OpenReformPartyWindow ():
+ global ReformPartyWindow, OldActionsWindow, OldMessageWindow
+ global removable_pcs
+
+ GemRB.SetVar ("Selected", 0)
+ hideflag = GemRB.HideGUI ()
+
+ if ReformPartyWindow:
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+
+ GemRB.SetVar ("ActionsWindow", OldActionsWindow.ID)
+ GemRB.SetVar ("MessageWindow", OldMessageWindow.ID)
+ GemRB.SetVar ("OtherWindow", -1)
+
+ OldActionsWindow = None
+ OldMessageWindow = None
+ ReformPartyWindow = None
+ if hideflag:
+ GemRB.UnhideGUI ()
+ #re-enabling party size control
+ GemRB.GameSetPartySize (PARTY_SIZE)
+ GUICommonWindows.UpdatePortraitWindow()
+ return
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ReformPartyWindow = Window = GemRB.LoadWindow (24)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+
+ # skip exportable party members (usually only the protagonist)
+ removable_pcs = []
+ for i in range (1, GemRB.GetPartySize()+1):
+ if not GemRB.GetPlayerStat (i, IE_MC_FLAGS)&MC_EXPORTABLE:
+ removable_pcs.append(i)
+
+ #PC portraits
+ for j in range (PARTY_SIZE+1):
+ Button = Window.GetControl (j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+ Button.SetBorder (FRAME_PC_SELECTED, 1, 1, 2, 2, 0, 255, 0, 255)
+ if j < len(removable_pcs):
+ Button.SetVarAssoc ("Selected", removable_pcs[j])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateReformWindow)
+
+ # Remove
+ Button = Window.GetControl (15)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemovePlayer)
+
+ # Done
+ Button = Window.GetControl (8)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenReformPartyWindow)
+
+ OldActionsWindow = GUIClasses.GWindow( GemRB.GetVar ("ActionsWindow") )
+ OldMessageWindow = GUIClasses.GWindow( GemRB.GetVar ("MessageWindow") )
+ GemRB.SetVar ("ActionsWindow", -1)
+ GemRB.SetVar ("MessageWindow", -1)
+
+ # if nobody can be removed, just close the window
+ if not removable_pcs:
+ OpenReformPartyWindow ()
+ if hideflag:
+ GemRB.UnhideGUI ()
+ return
+
+ UpdateReformWindow ()
+ if hideflag:
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DeathWindow ():
+ GemRB.HideGUI ()
+ GemRB.SetTimedEvent (DeathWindowEnd, 10)
+ return
+
+def DeathWindowEnd ():
+ #playing death movie before continuing
+ GemRB.PlayMovie ("deathand",1)
+ GemRB.GamePause (1,1)
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ Window = GemRB.LoadWindow (17)
+
+ #reason for death
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (16498)
+
+ #load
+ Button = Window.GetControl (1)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadPress)
+
+ #quit
+ Button = Window.GetControl (2)
+ Button.SetText (15417)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitPress)
+
+ GemRB.HideGUI ()
+ GemRB.SetVar ("MessageWindow", -1)
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def QuitPress():
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("Start")
+ return
+
+def LoadPress():
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("GUILOAD")
+ return
+
diff --git a/gemrb/GUIScripts/bg2/ImportFile.py b/gemrb/GUIScripts/bg2/ImportFile.py
new file mode 100644
index 0000000..ed4d579
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/ImportFile.py
@@ -0,0 +1,76 @@
+# 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.
+#
+#
+#character generation, import (GUICG24)
+import GemRB
+
+#import from a character sheet
+ImportWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global ImportWindow, TextAreaControl
+
+ GemRB.LoadWindowPack("GUICG",640,480)
+ ImportWindow = GemRB.LoadWindow(20)
+
+ TextAreaControl = ImportWindow.GetControl(4)
+ TextAreaControl.SetText(10963)
+
+ TextAreaControl = ImportWindow.GetControl(2)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ TextAreaControl.GetCharacters()
+
+ DoneButton = ImportWindow.GetControl(0)
+ DoneButton.SetText(2610)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = ImportWindow.GetControl(1)
+ CancelButton.SetText(15416)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ TextAreaControl.SetEvent(IE_GUI_TEXTAREA_ON_CHANGE, SelectPress)
+ ImportWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def SelectPress():
+ DoneButton = ImportWindow.GetControl(0)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def DonePress():
+ FileName = TextAreaControl.QueryText()
+ Slot = GemRB.GetVar("Slot")
+ GemRB.CreatePlayer(FileName, Slot| 0x8000, 1, 11) # 11 = force bg2
+ if ImportWindow:
+ ImportWindow.Unload()
+ # the medium portrait isn't available, so we copy the original hack
+ MediumPortrait = GemRB.GetPlayerPortrait (Slot, 1)[0:-1] + "M"
+ GemRB.SetToken("SmallPortrait", GemRB.GetPlayerPortrait (Slot, 1) )
+ GemRB.SetToken("LargePortrait", MediumPortrait )
+ GemRB.SetNextScript("CharGen7")
+ return
+
+def CancelPress():
+ if ImportWindow:
+ ImportWindow.Unload()
+ GemRB.SetNextScript(GemRB.GetToken("NextScript"))
+ return
diff --git a/gemrb/GUIScripts/bg2/ImportGame.py b/gemrb/GUIScripts/bg2/ImportGame.py
new file mode 100644
index 0000000..ff0b942
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/ImportGame.py
@@ -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.
+#
+#
+#character generation, import (GUICG24)
+import GemRB
+
+#import from a game
+ImportWindow = 0
+
+def OnLoad():
+ global ImportWindow
+
+ GemRB.LoadWindowPack("GUICG",640,480)
+ ImportWindow = GemRB.LoadWindow(20)
+
+ TextAreaControl = ImportWindow.GetControl(4)
+ TextAreaControl.SetText(53774)
+
+ TextAreaControl = ImportWindow.GetControl(2)
+#Fill TextArea Control with character sheets, make textarea a listbox
+
+ DoneButton = ImportWindow.GetControl(0)
+ DoneButton.SetText(2610)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = ImportWindow.GetControl(1)
+ CancelButton.SetText(15416)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, Done1Press)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ ImportWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def Done1Press():
+ DoneButton = ImportWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, Done2Press)
+ return
+
+def Done2Press():
+ if ImportWindow:
+ ImportWindow.Unload()
+ GemRB.SetNextScript("Start")
+ return
+
+def CancelPress():
+ if ImportWindow:
+ ImportWindow.Unload()
+ GemRB.SetNextScript("CharGen")
+ return
diff --git a/gemrb/GUIScripts/bg2/LUHLASelection.py b/gemrb/GUIScripts/bg2/LUHLASelection.py
new file mode 100644
index 0000000..5b335f1
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/LUHLASelection.py
@@ -0,0 +1,444 @@
+# -*-python-*-
+# 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.
+#
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import GUICommon
+import CommonTables
+
+# HLA selection
+HLAWindow = 0 # << HLA selection window
+HLAAbilities = [] # << all learnable HLA abilities
+HLANewAbilities = [] # << selected HLA abilites
+HLADoneButton = 0 # << done button
+HLATextArea = 0 # << HLA ability description area
+HLACount = 0 # << number of HLA selections left
+pc = 0 # << the pc
+NumClasses = 0 # << number of classes
+Classes = [] # << classes (ids)
+Level = [] # << levels for each class
+EnhanceGUI = 0 # << toggle for scrollbar and 25th hla slot
+
+def OpenHLAWindow (actor, numclasses, classes, levels):
+ """Opens the HLA selection window."""
+
+ global HLAWindow, HLADoneButton, HLATextArea, HLACount, NumClasses, pc, Classes, Level
+ global EnhanceGUI
+
+ #enhance GUI?
+ if (GemRB.GetVar("GUIEnhancements")):
+ EnhanceGUI = 1
+
+ # save our variables
+ pc = actor
+ NumClasses = numclasses
+ Classes = classes
+ Level = levels
+ HLACount = GemRB.GetVar ("HLACount")
+
+ # we use the same window as sorcerer spell selection
+ HLAWindow = GemRB.LoadWindow (8)
+
+ # get all our HLAs (stored in HLAAbilities)
+ GetHLAs ()
+
+ # change the title to ABILITIES
+ TitleLabel = HLAWindow.GetControl (0x10000017)
+ TitleLabel.SetText (63818)
+
+ # create the done button
+ HLADoneButton = HLAWindow.GetControl (28)
+ HLADoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ HLADoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, HLADonePress)
+ HLADoneButton.SetText(11973)
+ HLADoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # setup our text area
+ HLATextArea = HLAWindow.GetControl(26)
+
+ print "Number of HLAs:",len (HLAAbilities)
+
+ # create a scrollbar if need-be
+ if ( len (HLAAbilities) >= 25 ) and EnhanceGUI:
+ # setup extra 25th HLA slot:
+ HLAWindow.CreateButton (24, 231, 345, 42, 42)
+ if ( len (HLAAbilities) > 25):
+ # setup our scroll index
+ GemRB.SetVar("HLATopIndex", 0)
+ # setup scrollbar
+ HLAWindow.CreateScrollBar (1000, 290,142, 16,252)
+ ScrollBar = HLAWindow.GetControl (1000)
+ ScrollBar.SetSprites ("GUISCRCW", 0, 0,1,2,3,5,4)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, HLAShowAbilities)
+ #with enhanced GUI we have 5 rows of 5 abilities (the last one is 'the extra slot')
+ ScrollBar.SetVarAssoc ("HLATopIndex", GUICommon.ceildiv ( ( len (HLAAbilities)-25 ) , 5 ) + 1 )
+ ScrollBar.SetDefaultScrollBar ()
+
+ # draw our HLAs and show the window
+ HLAShowAbilities ()
+ HLAWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+ return
+
+def HLADonePress ():
+ """Saves the new HLAs and closes the HLA selection window."""
+
+ # save all of our HLAs
+ for i in range (len (HLANewAbilities)):
+ # see if we're going to learn this ability
+ if HLANewAbilities[i] == 0:
+ continue
+
+ # figure out the ability type
+ HLARef = HLAAbilities[i][0]
+ HLAType = HLARef[5:7]
+ if HLAType == "PR":
+ HLAType = IE_SPELL_TYPE_PRIEST
+ HLALevel = int(HLARef[7])-1
+ elif HLAType == "WI":
+ HLAType = IE_SPELL_TYPE_WIZARD
+ HLALevel = int(HLARef[7])-1
+ else:
+ HLAType = IE_SPELL_TYPE_INNATE
+ HLALevel = 0
+
+ # do we need to apply or learn it?
+ if HLARef[:2] == "AP":
+ GemRB.ApplySpell(pc, HLARef[3:])
+ elif HLARef[:2] == "GA":
+ # make sure it isn't already learned
+ SpellIndex = GUICommon.HasSpell (pc, HLAType, HLALevel, HLARef[3:])
+ if SpellIndex < 0: # gotta learn it
+ GemRB.LearnSpell (pc, HLARef[3:], 8)
+ else: # memorize it again
+ GemRB.MemorizeSpell (pc, HLAType, HLALevel, SpellIndex)
+
+ #save the number of this HLA memorized
+ #TODO: check param2 (0 seems to work ok)
+ GemRB.ApplyEffect(pc, "HLA", HLAAbilities[i][2], 0, HLARef[3:])
+
+ # close the window
+ if HLAWindow:
+ HLAWindow.Unload ()
+
+ # so redraw skills knows we're done
+ GemRB.SetVar ("HLACount", 0)
+
+ return
+
+def HLAShowAbilities ():
+ """Updates the HLA selections window.
+
+ Called whenever an HLA is pressed."""
+
+ j = ( GemRB.GetVar("HLATopIndex") + 1 ) * 5 - 5
+
+ # we have a grid of 24 abilites
+ for i in range (24+EnhanceGUI):
+ # ensure we can learn this many abilites
+ if len (HLAAbilities) < 25 and i == 24: #break if we don't need extra 25th button
+ break
+ SpellButton = HLAWindow.GetControl (i)
+ if i + j >= len (HLAAbilities):
+ SpellButton.SetState (IE_GUI_BUTTON_DISABLED)
+ SpellButton.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ continue
+ else:
+ SpellButton.SetState(IE_GUI_BUTTON_ENABLED)
+ SpellButton.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+
+ # fill in the button with the spell data
+ HLARef = HLAAbilities[i+j][0][3:]
+ if not HLARef:
+ continue
+ Spell = GemRB.GetSpell (HLARef)
+ SpellButton.SetTooltip(Spell['SpellName'])
+ SpellButton.SetSpellIcon(HLARef, 1)
+ SpellButton.SetVarAssoc("ButtonPressed", i)
+ SpellButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, HLASelectPress)
+ SpellButton.SetSprites("GUIBTBUT", 0,0,1,2,3)
+ SpellButton.SetFlags(IE_GUI_BUTTON_PICTURE, OP_OR)
+
+ # don't allow the selection of an un-learnable ability
+ if HLAAbilities[i+j][1] == 0:
+ SpellButton.SetState(IE_GUI_BUTTON_LOCKED)
+ # shade red
+ SpellButton.SetBorder (0, 0,0, 0,0, 200,0,0,100, 1,1)
+ else:
+ SpellButton.SetState (IE_GUI_BUTTON_ENABLED)
+ # unset any borders on this button or an un-learnable from last level
+ # will still shade red even though it is clickable
+ SpellButton.SetBorder (0, 0,0, 0,0, 0,0,0,0, 0,0)
+
+ # show which spells are selected
+ HLAShowSelectedAbilities ()
+
+ GemRB.SetToken("number", str(HLACount))
+ HLATextArea.SetText(63817)
+
+ # show the points left
+ PointsLeftLabel = HLAWindow.GetControl (0x10000018)
+ PointsLeftLabel.SetText (str (HLACount))
+
+ return
+
+def HLASelectPress ():
+ """Toggles the HLA and displays a description string."""
+
+ global HLACount, HLAAbilities, HLANewAbilities
+
+ # get our variables
+ j = ( GemRB.GetVar("HLATopIndex") + 1 ) * 5 - 5
+ i = GemRB.GetVar ("ButtonPressed") + j
+
+ # get the spell that's been pushed
+ Spell = GemRB.GetSpell (HLAAbilities[i][0][3:])
+ HLATextArea.SetText (Spell["SpellDesc"])
+
+ # make sure we can learn the spell
+ if HLAAbilities[i][1]:
+ if HLANewAbilities[i]: # already picked -- unselecting
+ # make we aren't the pre-req to another spell that is selected
+ for j in range (len (HLAAbilities)):
+ if (HLAAbilities[j][3] == HLAAbilities[i][0]) and (HLANewAbilities[j]):
+ HLAShowSelectedAbilities () # so our pre-req is still highlighted
+ return
+
+ HLACount += 1
+ HLANewAbilities[i] = 0
+ HLAAbilities[i][2] -= 1 # internal counter
+ HLADoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else: # selecting
+ # we don't have any picks left
+ if HLACount == 0:
+ HLAMarkButton (i, 0)
+ return
+
+ # select the spell and change the done state if need be
+ HLACount -= 1
+ HLANewAbilities[i] = 1
+ HLAAbilities[i][2] += 1 # increment internal counter
+ if HLACount == 0:
+ HLADoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # recheck internal exclusions and prereqs
+ HLARecheckPrereqs (i)
+
+ # show selected spells
+ HLAShowAbilities ()
+ HLAShowSelectedAbilities ()
+ HLATextArea.SetText (Spell["SpellDesc"])
+
+ # show the points left
+ PointsLeftLabel = HLAWindow.GetControl (0x10000018)
+ PointsLeftLabel.SetText (str (HLACount))
+ return
+
+def HLAShowSelectedAbilities ():
+ """Marks all of the selected abilities."""
+
+ j = ( GemRB.GetVar("HLATopIndex") + 1 ) * 5 - 5
+
+ # mark all of the abilities picked thus far
+ for i in range (24+EnhanceGUI):
+ if i + j >= len (HLANewAbilities): # make sure we don't call unavailable indexes
+ break
+ if HLANewAbilities[i+j]:
+ HLAMarkButton (i+j, 1)
+ else:
+ HLAMarkButton (i+j, 0)
+
+ return
+
+def HLAMarkButton (i, select):
+ """Enables, disables, or highlights the given button.
+
+ If select is true, the button is highlighted."""
+
+ j = ( GemRB.GetVar("HLATopIndex") + 1 ) * 5 - 5
+
+ if select:
+ type = IE_GUI_BUTTON_SELECTED
+ else:
+ if HLAAbilities[i][1]:
+ type = IE_GUI_BUTTON_ENABLED
+ else: # can't learn
+ type = IE_GUI_BUTTON_LOCKED
+
+ # we have to use the index on the actual grid
+ SpellButton = HLAWindow.GetControl(i-j)
+ SpellButton.SetState(type)
+ return
+
+def GetHLAs ():
+ """Updates HLAAbilites with all the choosable class HLAs.
+
+ HLAAbilities[x][0] is the given HLAs spell reference.
+ HLAAbilities[x][1] is true if the HLAs prerequisites have been met."""
+
+ global HLAAbilities, HLANewAbilities, HLACount
+
+ # get some needed values
+ Kit = GUICommon.GetKitIndex (pc)
+ IsDual = GUICommon.IsDualClassed (pc, 0)
+ IsDual = IsDual[0] > 0
+ MaxHLACount = 0
+
+ # reset the abilities
+ HLAAbilities = []
+ HLANewAbilities = []
+
+ # the HLA table lookup table
+ HLAAbbrTable = GemRB.LoadTable ("luabbr")
+
+ # get all the HLAs for each class
+ for i in range (NumClasses):
+ ClassIndex = CommonTables.Classes.FindValue (5, Classes[i])
+ ClassName = CommonTables.Classes.GetRowName (ClassIndex)
+ CurrentLevel = Level[i]
+
+ if Kit != 0 and NumClasses == 1 and not IsDual: # kitted single-class
+ KitName = CommonTables.KitList.GetValue (Kit, 0)
+ HLAClassTable = "lu" + HLAAbbrTable.GetValue (KitName, "ABBREV")
+ ClassName = KitName
+ else: # everyone else
+ HLAClassTable = "lu" + HLAAbbrTable.GetValue (ClassName, "ABBREV")
+
+ # actually load the table
+ HLAClassTable = GemRB.LoadTable (HLAClassTable)
+ print "HLA Class/Kit:",ClassName
+
+ # save all our HLAs from this class
+ for j in range (HLAClassTable.GetRowCount ()):
+ HLARef = HLAClassTable.GetValue (j, 0, 0)
+ print "\tHLA",j,":",HLARef
+
+ # make sure we have an ability here
+ if HLARef == "*":
+ print "\t\tEnd of HLAs"
+ break
+
+ # [ref to hla, memorizable?, num memorized, pre-req ref, excluded ref]
+ SaveArray = [\
+ HLARef,\
+ 0,\
+ GemRB.CountEffects (pc, "HLA", -1, -1, HLARef[3:]),\
+ HLAClassTable.GetValue (j, 6, 0),\
+ HLAClassTable.GetValue (j, 7, 0)]
+
+ # make sure we fall within the min and max paramaters
+ if HLAClassTable.GetValue (j, 3) > CurrentLevel or HLAClassTable.GetValue (j, 4) < CurrentLevel:
+ print "\t\tNot within parameters"
+ HLAAbilities.append(SaveArray)
+ continue
+
+ # see if we're alignment restricted (we never get them)
+ HLAAlign = HLAClassTable.GetValue (j, 8, 0)
+ if HLAAlign == "ALL_EVIL" and GemRB.GetPlayerStat (pc, IE_ALIGNMENT) < 6:
+ # don't even save this one because we can never get it
+ print "\t\tNeeds ALL_EVIL"
+ continue
+ elif HLAAlign == "ALL_GOOD" and GemRB.GetPlayerStat (pc, IE_ALIGNMENT) > 2:
+ # ditto
+ print "\t\tNeeds ALL_GOOD"
+ continue
+
+ # make sure we haven't already surpassed the number of time memorizable
+ HLANumAllowed = HLAClassTable.GetValue (j, 5)
+ print "\t\tHLA count:",SaveArray[2]
+ if SaveArray[2] >= HLANumAllowed:
+ print "\t\tOnly allowed to learn",HLANumAllowed,"times"
+ HLAAbilities.append(SaveArray)
+ continue
+
+ # make sure we haven't learned an HLA that excludes this one
+ HLAMemorized = GemRB.CountEffects (pc, "HLA", -1, -1, SaveArray[4][3:])
+ print "\t\tHLAExcluded count:",HLAMemorized
+ if (SaveArray[4] != "*") and (HLAMemorized > 0):
+ print "\t\tExcluded by:",SaveArray[4]
+ HLAAbilities.append(SaveArray)
+ continue
+
+ # we meet the prereqs so we can learn the HLA
+ HLAMemorized = GemRB.CountEffects (pc, "HLA", -1, -1, SaveArray[3][3:])
+ print "\t\tHLAPre count:",HLAMemorized
+ if (SaveArray[3] == "*") or (HLAMemorized > 0):
+ print "\t\tWe can learn it!"
+ MaxHLACount += 1
+ SaveArray[1] = 1
+ HLAAbilities.append (SaveArray)
+ continue
+
+ # we didn't meet prereqs :(
+ print "\t\tNeed pre-req:",SaveArray[3]
+ HLAAbilities.append (SaveArray)
+
+ # create an array to store our abilities as they are selected
+ HLANewAbilities = [0]*len (HLAAbilities)
+
+ #make sure we don't get stuck with HLAs we can't apply
+ if MaxHLACount < HLACount:
+ HLACount = MaxHLACount
+ GemRB.SetVar ("HLACount", HLACount)
+
+ return
+
+def HLARecheckPrereqs (index):
+ """Rechecks the HLA prequisites on the index on the fly."""
+
+ # the numer of times memorized
+ Ref = HLAAbilities[index][0]
+ Memorized = HLAAbilities[index][2]
+
+ # check for new exclusions and pre-reqs
+ for i in range (len (HLAAbilities)):
+ # we don't need to check the index
+ # this also fixes the assassination bug (it is excluded by itself)
+ if i == index:
+ continue
+ # check for exclusions first
+ if HLAAbilities[i][4] == Ref:
+ if Memorized > 0: # can't learn it
+ HLAAbilities[i][1] = 0
+ else: # can, if it meets pre-reqs
+ if HLAAbilities[i][3] != "*": # check prereqs
+ for j in range (len (HLAAblities)): # search for the prereq ref
+ if (HLAAbilities[j][0] == HLAAbilities[i][3]) and (HLAAbilities[j][2] > 0): # can learn
+ HLAAbilities[i][1] = 1
+ break
+ else: # no prereqs
+ HLAAbilities[i][1] = 1
+
+ # check for prereqs
+ if HLAAbilities[i][3] == Ref:
+ if Memorized > 0: # can learn if not excluded
+ if HLAAbilities[i][4] != "*": # check for exclusions
+ for j in range (len (HLAAbilities)): # search for the exclusion ref
+ if (HLAAbilities[j][0] == HLAAbilities[i][4]) and (HLAAbilities[j][2] <= 0): # can learn
+ HLAAbilities[i][1] = 1
+ break
+ else: # no exlusions
+ HLAAbilities[i][1] = 1
+ else: # prereqs not met
+ HLAAbilities[i][1] = 0
+
+ return
+
+
diff --git a/gemrb/GUIScripts/bg2/LoadScreen.py b/gemrb/GUIScripts/bg2/LoadScreen.py
new file mode 100644
index 0000000..97aac5a
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/LoadScreen.py
@@ -0,0 +1,66 @@
+# -*-python-*-
+# 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.
+#
+
+# LoadScreen.py - display Loading screen
+
+###################################################
+
+import GemRB
+import GUICommon
+from GUIDefines import *
+
+LoadScreen = None
+hide = None
+
+def SetLoadScreen ():
+ return
+
+def StartLoadScreen ():
+ global LoadScreen
+
+ GemRB.LoadWindowPack ("guils", 640, 480)
+ LoadScreen = GemRB.LoadWindow (0)
+ LoadScreen.SetFrame ()
+ Middle = LoadScreen.GetControl (3)
+ LoadPic = GemRB.GetGameString (STR_LOADMOS)
+ if LoadPic == "":
+ #the addition of 1 is not an error, bg2 loadpic resrefs are GTRSK002-GTRSK006
+ LoadPic = "GTRSK00"+str(GemRB.Roll(1,5,1) )
+ Middle.SetMOS (LoadPic)
+ Progress = 0
+ GemRB.SetVar ("Progress", Progress)
+ if GUICommon.HasTOB():
+ Table = GemRB.LoadTable ("loadh25")
+ else:
+ Table = GemRB.LoadTable ("loadhint")
+ tmp = Table.GetRowCount ()
+ tmp = GemRB.Roll (1,tmp,0)
+ HintStr = Table.GetValue (tmp, 0)
+ TextArea = LoadScreen.GetControl (2)
+ TextArea.SetText (HintStr)
+ Bar = LoadScreen.GetControl (0)
+ Bar.SetVarAssoc ("Progress", Progress)
+ Bar.SetEvent (IE_GUI_PROGRESS_END_REACHED, EndLoadScreen)
+ LoadScreen.SetVisible (WINDOW_VISIBLE)
+
+def EndLoadScreen ():
+ LoadScreen.SetVisible (WINDOW_VISIBLE)
+ LoadScreen.Unload()
+ return
+
diff --git a/gemrb/GUIScripts/bg2/Makefile.am b/gemrb/GUIScripts/bg2/Makefile.am
new file mode 100644
index 0000000..d68de2c
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/Makefile.am
@@ -0,0 +1,4 @@
+bg2script_DATA = *.py
+bg2scriptdir = $(moddir)/GUIScripts/bg2/
+EXTRA_DIST = *.py
+MOSTLYCLEANFILES = *.pyc
diff --git a/gemrb/GUIScripts/bg2/MessageWindow.py b/gemrb/GUIScripts/bg2/MessageWindow.py
new file mode 100644
index 0000000..b91da9c
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/MessageWindow.py
@@ -0,0 +1,239 @@
+# -*-python-*-
+# 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.
+#
+
+# MessageWindow.py - scripts and GUI for the main (walk) window
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+import CommonWindow
+import GUIClasses
+import CommonTables
+from GUIDefines import *
+from CharGenEnd import GiveEquipment
+
+MessageWindow = 0
+PortraitWindow = 0
+OptionsWindow = 0
+ExpandButton = 0
+ContractButton = 0
+TMessageTA = 0 # for dialog code
+
+def OnLoad():
+ global PortraitWindow, OptionsWindow
+
+ GemRB.GameSetPartySize(PARTY_SIZE)
+ GemRB.GameSetProtagonistMode(1)
+ GemRB.LoadWindowPack(GUICommon.GetWindowPack())
+
+ GUICommonWindows.PortraitWindow = None
+ GUICommonWindows.ActionsWindow = None
+ GUICommonWindows.OptionsWindow = None
+
+ OptionsWindow = GemRB.LoadWindow(0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 1, None)
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow(1)
+
+ Button=OptionsWindow.GetControl(10)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, MinimizeOptions)
+ Button=PortraitWindow.GetControl(8)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GUICommonWindows.MinimizePortraits)
+
+ ActionsWindow = GemRB.LoadWindow(3)
+ GUICommonWindows.OpenActionsWindowControls (ActionsWindow)
+ Button=ActionsWindow.GetControl(60)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, MaximizeOptions)
+ Button=ActionsWindow.GetControl(61)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, MaximizePortraits)
+
+ GemRB.SetVar("PortraitWindow", PortraitWindow.ID)
+ GemRB.SetVar("ActionsWindow", ActionsWindow.ID)
+ GemRB.SetVar("OptionsWindow", OptionsWindow.ID)
+ GemRB.SetVar("TopWindow", -1)
+ GemRB.SetVar("OtherWindow", -1)
+ GemRB.SetVar("FloatWindow", -1)
+ GemRB.SetVar("PortraitPosition", 2) #Right
+ GemRB.SetVar("ActionsPosition", 4) #BottomAdded
+ GemRB.SetVar("OptionsPosition", 0) #Left
+ GemRB.SetVar("MessagePosition", 4) #BottomAdded
+ GemRB.SetVar("OtherPosition", 5) #Inactivating
+ GemRB.SetVar("TopPosition", 5) #Inactivating
+
+ UpdateControlStatus()
+
+def MinimizeOptions():
+ GemRB.GameSetScreenFlags(GS_OPTIONPANE, OP_OR)
+
+def MaximizeOptions():
+ GemRB.GameSetScreenFlags(GS_OPTIONPANE, OP_NAND)
+
+# MinimizePortraits is in GUICommonWindows for dependency reasons
+
+def MaximizePortraits():
+ GemRB.GameSetScreenFlags(GS_PORTRAITPANE, OP_NAND)
+
+def TogglePartyAI():
+ GemRB.GameSetScreenFlags(GS_PARTYAI, OP_XOR)
+
+def UpdateControlStatus():
+ global MessageWindow, ExpandButton, ContractButton, TMessageTA
+
+ TMessageWindow = 0
+ TMessageTA = 0
+ GSFlags = GemRB.GetMessageWindowSize()
+ Expand = GSFlags&GS_DIALOGMASK
+ Override = GSFlags&GS_DIALOG
+ GSFlags = GSFlags-Expand
+
+ #a dialogue is running, setting messagewindow size to maximum
+ if Override:
+ Expand = GS_LARGEDIALOG
+
+ MessageWindow = GemRB.GetVar("MessageWindow")
+
+ GemRB.LoadWindowPack(GUICommon.GetWindowPack())
+
+ if Expand == GS_MEDIUMDIALOG:
+ TMessageWindow = GemRB.LoadWindow(12)
+ TMessageTA = TMessageWindow.GetControl(1)
+ ExpandButton = TMessageWindow.GetControl(0)
+ ExpandButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnIncreaseSize)
+ ContractButton = TMessageWindow.GetControl(3)
+ ContractButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnDecreaseSize)
+
+ elif Expand == GS_LARGEDIALOG:
+ TMessageWindow = GemRB.LoadWindow(7)
+ TMessageTA = TMessageWindow.GetControl(1)
+ ContractButton = TMessageWindow.GetControl(0)
+ ContractButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnDecreaseSize)
+ else:
+ TMessageWindow = GemRB.LoadWindow(4)
+ TMessageTA = TMessageWindow.GetControl(3)
+ ExpandButton = TMessageWindow.GetControl(2)
+ ExpandButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnIncreaseSize)
+
+ TMessageTA.SetFlags(IE_GUI_TEXTAREA_AUTOSCROLL|IE_GUI_TEXTAREA_SPEAKER)
+ TMessageTA.SetHistory(100)
+ hideflag = GemRB.HideGUI()
+ MessageTA = GUIClasses.GTextArea(MessageWindow,GemRB.GetVar("MessageTextArea"))
+ if MessageWindow>0 and MessageWindow!=TMessageWindow.ID:
+ MessageTA.MoveText(TMessageTA)
+ GUIClasses.GWindow(MessageWindow).Unload()
+
+ GemRB.SetVar("MessageWindow", TMessageWindow.ID)
+ GemRB.SetVar("MessageTextArea", TMessageTA.ID)
+ if Override:
+ TMessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ else:
+ GUICommon.GameControl.SetStatus(IE_GUI_CONTROL_FOCUSED)
+
+ if GSFlags & GS_OPTIONPANE:
+ GemRB.SetVar("OptionsWindow", -1)
+ else:
+ GemRB.SetVar("OptionsWindow", OptionsWindow.ID)
+
+ if GSFlags & GS_PORTRAITPANE:
+ GemRB.SetVar("PortraitWindow", -1)
+ else:
+ GemRB.SetVar("PortraitWindow", PortraitWindow.ID)
+
+ if hideflag:
+ GemRB.UnhideGUI()
+ return
+
+def RemoveYoshimo( idx):
+ GemRB.DisplayString(72046, 0xF5F596)
+ #WARNING:multiple strings are executed in reverse order
+ GemRB.ExecuteString('ApplySpellRES("destself",myself)', idx)
+ GemRB.ExecuteString('GivePartyAllEquipment()', idx)
+ return
+
+def RemoveImoen( idx):
+ GemRB.DisplayString(72047, 0xF5F596)
+ GemRB.ExecuteString('ApplySpellRES("destself",myself)', idx)
+ GemRB.ExecuteString('GivePartyAllEquipment()', idx)
+ return
+
+def FixEdwin( idx):
+ GemRB.ApplySpell(idx, "SPIN661")
+ return
+
+def FixAnomen( idx):
+ #lawful neutral
+ if (GemRB.GetPlayerStat(idx, IE_ALIGNMENT) == 0x12):
+ GemRB.ApplySpell(idx, "SPIN678")
+ return
+
+#do all the stuff not done yet
+def FixProtagonist( idx):
+
+ Class = GemRB.GetPlayerStat (idx, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ ClassName = CommonTables.Classes.GetRowName (ClassIndex)
+ KitIndex = GUICommon.GetKitIndex (idx)
+ GiveEquipment(idx, ClassName, KitIndex)
+ return
+
+#upgrade savegame to next version
+def GameExpansion():
+
+ version = GemRB.GameGetExpansion()
+ if version<3:
+ GemRB.GameSetReputation(100)
+
+ if not GUICommon.HasTOB():
+ return
+
+ if GemRB.GetVar("oldgame"):
+ #upgrade SoA to ToB/SoA
+ if GemRB.GameSetExpansion(4):
+ GemRB.AddNewArea("xnewarea")
+ return
+
+ if not GemRB.GameSetExpansion(5):
+ return
+
+ #upgrade to ToB only
+ GemRB.SetMasterScript("BALDUR25","WORLDM25")
+ GemRB.SetGlobal("INTOB","GLOBAL",1)
+ GemRB.SetGlobal("HADELLESIMEDREAM1","GLOBAL", 1)
+ GemRB.SetGlobal("HADELLESIMEDREAM2","GLOBAL", 1)
+ GemRB.SetGlobal("HADIMOENDREAM1","GLOBAL", 1)
+ GemRB.SetGlobal("HADSLAYERDREAM","GLOBAL", 1)
+ GemRB.SetGlobal("HADJONDREAM1","GLOBAL", 1)
+ GemRB.SetGlobal("HADJONDREAM2","GLOBAL", 1)
+ idx = GemRB.GetPartySize()
+
+ while idx:
+ name = GemRB.GetPlayerName(idx, 2) #scripting name
+ if name == "yoshimo":
+ RemoveYoshimo(idx)
+ elif name == "imoen":
+ RemoveImoen(idx)
+ elif name == "edwin":
+ FixEdwin(idx)
+ elif name == "anomen":
+ FixAnomen(idx)
+ elif name == "none":
+ FixProtagonist(idx)
+ GemRB.GameSelectPC (idx, True, SELECT_REPLACE)
+ idx=idx-1
+ return
diff --git a/gemrb/GUIScripts/bg2/QuitGame.py b/gemrb/GUIScripts/bg2/QuitGame.py
new file mode 100644
index 0000000..0c3e95d
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/QuitGame.py
@@ -0,0 +1,29 @@
+# -*-python-*-
+# 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.
+#
+
+# QuitGame.py - display EndGame sequence
+
+###################################################
+
+import GemRB
+
+def OnLoad ():
+ GemRB.HideGUI ()
+ GemRB.QuitGame ()
+ GemRB.SetNextScript("Start")
diff --git a/gemrb/GUIScripts/bg2/Start.py b/gemrb/GUIScripts/bg2/Start.py
new file mode 100644
index 0000000..5f29358
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/Start.py
@@ -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.
+#
+#
+#ToB start window, precedes the SoA window
+import GemRB
+import GUICommon
+
+StartWindow = 0
+
+def OnLoad():
+ global StartWindow, skip_videos
+
+ GemRB.EnableCheatKeys(1)
+ skip_videos = GemRB.GetVar ("SkipIntroVideos")
+ if not skip_videos and not GemRB.GetVar ("SeenIntroVideos"):
+ GemRB.PlayMovie ("BISLOGO", 1)
+ GemRB.PlayMovie ("BWDRAGON", 1)
+ GemRB.PlayMovie ("WOTC", 1)
+ # don't replay the intros on subsequent reentries
+ GemRB.SetVar ("SeenIntroVideos", 1)
+
+ # Find proper window border for higher resolutions
+ screen_width = GemRB.GetSystemVariable (SV_WIDTH)
+ screen_height = GemRB.GetSystemVariable (SV_HEIGHT)
+ if screen_width == 800:
+ GemRB.LoadWindowFrame("STON08L", "STON08R", "STON08T", "STON08B")
+ elif screen_width == 1024:
+ GemRB.LoadWindowFrame("STON10L", "STON10R", "STON10T", "STON10B")
+
+ #if not detected tob, we go right to the main menu
+ if not GUICommon.HasTOB():
+ GemRB.SetMasterScript("BALDUR","WORLDMAP")
+ GemRB.SetVar("oldgame",1)
+ GemRB.SetNextScript("Start2")
+ return
+
+ GemRB.LoadWindowPack("START", 640, 480)
+ StartWindow = GemRB.LoadWindow(7)
+ StartWindow.SetFrame ()
+ StartWindow.CreateLabel(0x0fff0000, 0,0,640,30, "REALMS", "", 1)
+ Label=StartWindow.GetControl(0x0fff0000)
+ Label.SetText(GEMRB_VERSION)
+
+ TextArea = StartWindow.GetControl(0)
+ TextArea.SetText(73245)
+ TextArea = StartWindow.GetControl(1)
+ TextArea.SetText(73246)
+ SoAButton = StartWindow.GetControl(2)
+ SoAButton.SetText(73247)
+ ToBButton = StartWindow.GetControl(3)
+ ToBButton.SetText(73248)
+ ExitButton = StartWindow.GetControl(4)
+ ExitButton.SetText(13731)
+ ExitButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ SoAButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoAPress)
+ ToBButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ToBPress)
+ ExitButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitPress)
+ StartWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.LoadMusicPL("Cred.mus")
+ return
+
+def SoAPress():
+ if StartWindow:
+ StartWindow.Unload()
+ GemRB.SetMasterScript("BALDUR","WORLDMAP")
+ GemRB.SetVar("oldgame",1)
+ GemRB.SetNextScript("Start2")
+ return
+
+def ToBPress():
+ GemRB.SetMasterScript("BALDUR25","WORLDM25")
+ GemRB.SetVar("oldgame",0)
+ if StartWindow:
+ StartWindow.Unload()
+ GemRB.SetNextScript("Start2")
+ return
+
+def ExitPress():
+ GemRB.Quit()
+ return
diff --git a/gemrb/GUIScripts/bg2/Start2.py b/gemrb/GUIScripts/bg2/Start2.py
new file mode 100644
index 0000000..bfc6385
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/Start2.py
@@ -0,0 +1,331 @@
+# 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 is essentially Start.py from the SoA game, except for a very small change
+import GemRB
+import GUICommon
+
+StartWindow = 0
+TutorialWindow = 0
+QuitWindow = 0
+ExitButton = 0
+SinglePlayerButton = 0
+OptionsButton = 0
+MultiPlayerButton = 0
+MoviesButton = 0
+BackButton = 0
+
+def OnLoad():
+ global StartWindow, TutorialWindow, QuitWindow
+ global ExitButton, OptionsButton, MultiPlayerButton, MoviesButton, SinglePlayerButton, BackButton
+ global SinglePlayerButton
+
+ skip_videos = GemRB.GetVar ("SkipIntroVideos")
+
+ GemRB.LoadWindowPack("START", 640, 480)
+#tutorial subwindow
+ if not GUICommon.GameIsBG2Demo():
+ TutorialWindow = GemRB.LoadWindow (5)
+ TextAreaControl = TutorialWindow.GetControl (1)
+ CancelButton = TutorialWindow.GetControl (11)
+ PlayButton = TutorialWindow.GetControl (10)
+ TextAreaControl.SetText (44200)
+ CancelButton.SetText (13727)
+ PlayButton.SetText (33093)
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelTut)
+ PlayButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+#quit subwindow
+ QuitWindow = GemRB.LoadWindow (3)
+ QuitTextArea = QuitWindow.GetControl (0)
+ CancelButton = QuitWindow.GetControl (2)
+ ConfirmButton = QuitWindow.GetControl (1)
+ QuitTextArea.SetText (19532)
+ CancelButton.SetText (13727)
+ ConfirmButton.SetText (15417)
+ ConfirmButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitConfirmed)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitCancelled)
+ ConfirmButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+#main window
+ StartWindow = GemRB.LoadWindow (0)
+ StartWindow.SetFrame ()
+ #this is the ToB specific part of Start.py
+ if GemRB.GetVar("oldgame")==1:
+ if GUICommon.HasTOB():
+ StartWindow.SetPicture("STARTOLD")
+ if not skip_videos:
+ GemRB.PlayMovie ("INTRO15F", 1)
+ else:
+ if not skip_videos:
+ GemRB.PlayMovie ("INTRO", 1)
+
+ #end ToB specific part
+ SinglePlayerButton = StartWindow.GetControl (0)
+ ExitButton = StartWindow.GetControl (3)
+ OptionsButton = StartWindow.GetControl (4)
+ MultiPlayerButton = StartWindow.GetControl (1)
+ MoviesButton = StartWindow.GetControl (2)
+ BackButton = StartWindow.GetControl (5)
+ StartWindow.CreateLabel(0x0fff0000, 0,450,640,30, "REALMS", "", 1)
+ Label=StartWindow.GetControl (0x0fff0000)
+ Label.SetText (GEMRB_VERSION)
+ if GUICommon.HasTOB():
+ BackButton.SetState (IE_GUI_BUTTON_ENABLED)
+ BackButton.SetText (15416)
+ else:
+ BackButton.SetState (IE_GUI_BUTTON_DISABLED)
+ BackButton.SetText ("")
+ SinglePlayerButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ExitButton.SetState (IE_GUI_BUTTON_ENABLED)
+ OptionsButton.SetState (IE_GUI_BUTTON_ENABLED)
+ if GUICommon.GameIsBG2Demo():
+ MultiPlayerButton.SetState (IE_GUI_BUTTON_DISABLED)
+ MoviesButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ MultiPlayerButton.SetState (IE_GUI_BUTTON_ENABLED)
+ MultiPlayerButton.SetText (15414)
+ MultiPlayerButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MultiPlayerPress)
+
+ MoviesButton.SetState (IE_GUI_BUTTON_ENABLED)
+ MoviesButton.SetText (15415)
+ MoviesButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MoviesPress)
+ SinglePlayerButton.SetText (15413)
+ ExitButton.SetText (15417)
+ OptionsButton.SetText (13905)
+ SinglePlayerButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SinglePlayerPress)
+ ExitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitPress)
+ OptionsButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OptionsPress)
+ BackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, Restart)
+ ExitButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ QuitWindow.SetVisible (WINDOW_INVISIBLE)
+ if not GUICommon.GameIsBG2Demo():
+ TutorialWindow.SetVisible (WINDOW_INVISIBLE)
+ StartWindow.SetVisible (WINDOW_VISIBLE)
+ MusicTable = GemRB.LoadTable ("songlist")
+ # the table has useless rownames, so we can't search for BG2Theme
+ theme = MusicTable.GetValue ("33", "RESOURCE")
+ GemRB.LoadMusicPL (theme, 1)
+ return
+
+def SinglePlayerPress():
+
+ SinglePlayerButton.SetText (13728)
+ SinglePlayerButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, NewSingle)
+
+ MultiPlayerButton.SetText (13729)
+ MultiPlayerButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadSingle)
+ MultiPlayerButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ if not GUICommon.GameIsBG2Demo():
+ if GemRB.GetVar("oldgame")==1:
+ MoviesButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, Tutorial)
+ MoviesButton.SetText (33093)
+ else:
+ MoviesButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ImportGame)
+ MoviesButton.SetText (71175)
+
+ ExitButton.SetText (15416)
+ ExitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BackToMain)
+
+ OptionsButton.SetText ("")
+ OptionsButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ BackButton.SetText ("")
+ BackButton.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def MultiPlayerPress():
+
+ OptionsButton.SetText ("")
+ SinglePlayerButton.SetText (20642)
+ ExitButton.SetText (15416)
+ MultiPlayerButton.SetText ("")
+ MoviesButton.SetText (11825)
+ MultiPlayerButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ SinglePlayerButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ConnectPress)
+ MoviesButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PregenPress)
+ ExitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BackToMain)
+ MultiPlayerButton.SetState (IE_GUI_BUTTON_DISABLED)
+ OptionsButton.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def ConnectPress():
+#well...
+ #GemRB.SetVar("PlayMode",2)
+ return
+
+def PregenPress():
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ if TutorialWindow:
+ TutorialWindow.Unload()
+ #do not start game after chargen
+ GemRB.SetVar("PlayMode",-1) #will allow export
+ GemRB.SetVar("Slot",0)
+ GemRB.LoadGame(None)
+ GemRB.SetNextScript ("CharGen")
+ return
+
+def LoadSingle():
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ if TutorialWindow:
+ TutorialWindow.Unload()
+ if GemRB.GetVar ("oldgame") == 0:
+ GemRB.SetVar ("PlayMode", 2)
+ GemRB.SetVar ("SaveDir", 1)
+ else:
+ GemRB.SetVar ("PlayMode", 0)
+ GemRB.SetVar ("SaveDir", 0)
+
+ GemRB.SetNextScript ("GUILOAD")
+ return
+
+def NewSingle():
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ if TutorialWindow:
+ TutorialWindow.Unload()
+ if GemRB.GetVar ("oldgame") == 0:
+ GemRB.SetVar ("PlayMode", 2)
+ GemRB.SetVar ("SaveDir", 1)
+ else:
+ GemRB.SetVar ("PlayMode", 0)
+ GemRB.SetVar ("SaveDir", 0)
+ GemRB.SetVar("Slot",1)
+ GemRB.LoadGame(None)
+ GemRB.SetNextScript ("CharGen")
+ return
+
+def ImportGame():
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ if TutorialWindow:
+ TutorialWindow.Unload()
+ #now this is tricky, we need to load old games, but set up the expansion
+ GemRB.SetVar ("PlayMode", 0)
+ GemRB.SetVar ("SaveDir", 0)
+ GemRB.SetNextScript ("GUILOAD")
+ return
+
+def Tutorial():
+ StartWindow.SetVisible (WINDOW_INVISIBLE)
+ TutorialWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def PlayPress():
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ if TutorialWindow:
+ TutorialWindow.Unload()
+ GemRB.SetVar("PlayMode",1) #tutorial
+ GemRB.SetVar("SaveDir",0)
+ GemRB.SetVar("Slot",1)
+ GemRB.LoadGame(None)
+ GemRB.SetNextScript ("CharGen")
+ return
+
+def CancelTut():
+ TutorialWindow.SetVisible (WINDOW_INVISIBLE)
+ StartWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ExitPress():
+ StartWindow.SetVisible (WINDOW_INVISIBLE)
+ QuitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ExitConfirmed():
+ GemRB.Quit()
+ return
+
+def OptionsPress():
+#apparently the order is important
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ if TutorialWindow:
+ TutorialWindow.Unload()
+ GemRB.SetNextScript ("StartOpt")
+ return
+
+def MoviesPress():
+#apparently the order is important
+ if StartWindow:
+ StartWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ if TutorialWindow:
+ TutorialWindow.Unload()
+ GemRB.SetNextScript ("GUIMOVIE")
+ return
+
+def ExitCancelled():
+ QuitWindow.SetVisible (WINDOW_INVISIBLE)
+ StartWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def BackToMain():
+ SinglePlayerButton.SetState (IE_GUI_BUTTON_ENABLED)
+ OptionsButton.SetState (IE_GUI_BUTTON_ENABLED)
+ MultiPlayerButton.SetState (IE_GUI_BUTTON_ENABLED)
+ BackButton.SetState (IE_GUI_BUTTON_ENABLED)
+ SinglePlayerButton.SetText (15413)
+ ExitButton.SetText (15417)
+ OptionsButton.SetText (13905)
+ if GUICommon.GameIsBG2Demo():
+ MoviesButton.SetText ("")
+ BackButton.SetText ("")
+ BackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ BackButton.SetState (IE_GUI_BUTTON_DISABLED)
+ MultiPlayerButton.SetText ("")
+ MultiPlayerButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ MultiPlayerButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ MultiPlayerButton.SetText (15414)
+ MultiPlayerButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MultiPlayerPress)
+ MoviesButton.SetText (15415)
+ MoviesButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MoviesPress)
+ BackButton.SetText (15416)
+
+ SinglePlayerButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SinglePlayerPress)
+ ExitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitPress)
+ OptionsButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OptionsPress)
+ QuitWindow.SetVisible (WINDOW_INVISIBLE)
+ StartWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def Restart():
+ StartWindow.Unload()
+ QuitWindow.Unload()
+ GemRB.SetNextScript ("Start")
+ return
+
diff --git a/gemrb/GUIScripts/bg2/StartOpt.py b/gemrb/GUIScripts/bg2/StartOpt.py
new file mode 100644
index 0000000..966dfa8
--- /dev/null
+++ b/gemrb/GUIScripts/bg2/StartOpt.py
@@ -0,0 +1,70 @@
+# 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.
+#
+#
+import GemRB
+
+OptionsWindow = 0
+
+def OnLoad():
+ global OptionsWindow
+ GemRB.LoadWindowPack("GUIOPT", 640, 480)
+ OptionsWindow = GemRB.LoadWindow(13)
+ OptionsWindow.SetFrame ()
+ SoundButton = OptionsWindow.GetControl(8)
+ GameButton = OptionsWindow.GetControl(9)
+ GraphicButton = OptionsWindow.GetControl(7)
+ BackButton = OptionsWindow.GetControl(11)
+ SoundButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ GameButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ GraphicButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ BackButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ SoundButton.SetText(17164)
+ GameButton.SetText(17165)
+ GraphicButton.SetText(17162)
+ BackButton.SetText(10308)
+ SoundButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoundPress)
+ GameButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, GamePress)
+ GraphicButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, GraphicPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ OptionsWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def SoundPress():
+ if OptionsWindow:
+ OptionsWindow.Unload()
+ GemRB.SetNextScript("GUIOPT7")
+ return
+
+def GamePress():
+ if OptionsWindow:
+ OptionsWindow.Unload()
+ GemRB.SetNextScript("GUIOPT8")
+ return
+
+def GraphicPress():
+ if OptionsWindow:
+ OptionsWindow.Unload()
+ GemRB.SetNextScript("GUIOPT6")
+ return
+
+def BackPress():
+ if OptionsWindow:
+ OptionsWindow.Unload()
+ GemRB.SetNextScript("Start")
+ return
diff --git a/gemrb/GUIScripts/ie_action.py b/gemrb/GUIScripts/ie_action.py
new file mode 100644
index 0000000..f7d047d
--- /dev/null
+++ b/gemrb/GUIScripts/ie_action.py
@@ -0,0 +1,53 @@
+# -*-python-*-
+# 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.
+#
+
+#Note: keep this file in sync with PCStatStructs.h
+ACT_STEALTH=0
+ACT_THIEVING=1
+ACT_CAST=2
+ACT_QSPELL1=3
+ACT_QSPELL2=4
+ACT_QSPELL3=5
+ACT_TURN=6
+ACT_TALK=7
+ACT_USE=8
+ACT_QSLOT1=9
+ACT_QSLOT4=10 #heh
+ACT_QSLOT2=11
+ACT_QSLOT3=12
+ACT_QSLOT5=31 #
+ACT_INNATE=13
+ACT_DEFEND=14
+ACT_ATTACK=15
+ACT_WEAPON1=16
+ACT_WEAPON2=17
+ACT_WEAPON3=18
+ACT_WEAPON4=19
+ACT_BARDSONG=20
+ACT_STOP=21
+ACT_SEARCH=22
+ACT_SHAPE=23
+ACT_TAMING=24
+ACT_SKILLS=25
+ACT_WILDERNESS=26
+#gui navigation (scrolling button rows left or right)
+ACT_LEFT=32
+ACT_RIGHT=33
+
+ACT_NONE=100
diff --git a/gemrb/GUIScripts/ie_modal.py b/gemrb/GUIScripts/ie_modal.py
new file mode 100644
index 0000000..9eca0b0
--- /dev/null
+++ b/gemrb/GUIScripts/ie_modal.py
@@ -0,0 +1,24 @@
+# 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.
+#
+#
+#modal states
+MS_NONE = 0
+MS_BATTLESONG = 1
+MS_DETECTTRAPS = 2
+MS_STEALTH = 3
+MS_TURNUNDEAD = 4
diff --git a/gemrb/GUIScripts/ie_restype.py b/gemrb/GUIScripts/ie_restype.py
new file mode 100644
index 0000000..1eb50ae
--- /dev/null
+++ b/gemrb/GUIScripts/ie_restype.py
@@ -0,0 +1,67 @@
+# -*-python-*-
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2006 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.
+#
+
+# ie_restype.py - definitions of engine resource types
+
+# !!! NOTE: Keep this file synchronized with gemrb/includes/SClassID.h !!!
+
+RES_2DA = 0x000003F4
+RES_ACM = 0x00010000
+RES_ARE = 0x000003F2
+RES_BAM = 0x000003E8
+RES_BCS = 0x000003EF
+RES_BS = 0x100003EF
+RES_BIF = 0x00020000
+RES_BMP = 0x00000001
+RES_PNG = 0x00000003
+RES_CHR = 0x000003FA
+RES_CHU = 0x000003EA
+RES_CRE = 0x000003F1
+RES_DLG = 0x000003F3
+RES_EFF = 0x000003F8
+RES_GAM = 0x000003F5
+RES_IDS = 0x000003F0
+RES_INI = 0x00000802
+RES_ITM = 0x000003ED
+RES_KEY = 0x00030000
+RES_MOS = 0x000003EC
+RES_MUS = 0x00040000
+RES_MVE = 0x00000002
+RES_PLT = 0x00000006
+RES_PRO = 0x000003FD
+RES_SAV = 0x00050000
+RES_SPL = 0x000003EE
+RES_SRC = 0x00000803
+RES_STO = 0x000003F6
+RES_TIS = 0x000003EB
+RES_TLK = 0x00060000
+RES_TOH = 0x00070000
+RES_TOT = 0x00080000
+RES_VAR = 0x00090000
+RES_VVC = 0x000003FB
+RES_WAV = 0x00000004
+RES_WED = 0x000003E9
+RES_WFX = 0x00000005
+RES_WMP = 0x000003F7
+RES_VIDEO = 0x000B0000
+RES_AUDIO = 0x000C0000
+RES_SCRIPT = 0x000D0000
+RES_GUI_SCRIPT = 0x000E0000
+RES_COMPRESSION = 0x000F0000
+RES_FX = 0x00100000
diff --git a/gemrb/GUIScripts/ie_slots.py b/gemrb/GUIScripts/ie_slots.py
new file mode 100644
index 0000000..8b0c868
--- /dev/null
+++ b/gemrb/GUIScripts/ie_slots.py
@@ -0,0 +1,51 @@
+# -*-python-*-
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2006 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.
+#
+
+# ie_slots.py - definitions of slottypes
+
+# !!! NOTE: Keep this file synchronized with gemrb/plugins/Core/Inventory.h !!!
+
+SLOT_HELM = 1
+SLOT_ARMOUR = 2
+SLOT_SHIELD = 4
+SLOT_GLOVE = 8
+SLOT_RING = 16
+SLOT_AMULET = 32
+SLOT_BELT = 64
+SLOT_BOOT = 128
+SLOT_WEAPON = 256
+SLOT_QUIVER = 512
+SLOT_CLOAK = 1024
+SLOT_ITEM = 2048 #quick item
+SLOT_SCROLL = 4096
+SLOT_BAG = 8192
+SLOT_POTION = 16384
+SLOT_INVENTORY = 32768
+SLOT_ANY = 32767 #any except inventory
+SLOT_ALL = 65535 #all including inventory
+
+TYPE_NORMAL = 0 #inventory
+TYPE_ARMOR = 1 #normal armor
+TYPE_FIST = 2 #fist weapon
+TYPE_MAGIC = 3 #magic weapon
+TYPE_WEAPON = 4 #normal weapon
+TYPE_QUIVER = 5 #projectile slots
+TYPE_OFFHAND = 6 #offhand (shield/weapon)
+TYPE_HELMET = 7 #critical hit protection
+# End of file ie_slots.py
diff --git a/gemrb/GUIScripts/ie_spells.py b/gemrb/GUIScripts/ie_spells.py
new file mode 100644
index 0000000..21ee9e1
--- /dev/null
+++ b/gemrb/GUIScripts/ie_spells.py
@@ -0,0 +1,17 @@
+# !!! Keep these synchronized with Spellbook.h !!!
+
+# LearnSpell flags
+LS_ADDXP = 1 #give xp for learning it
+LS_LEARN = 2 #give message when learned it
+LS_STATS = 4 #check stats (alignment, etc)
+LS_MEMO = 8 #memorize it instantly (add innate)
+LS_NOXP = 16 #don't give xp; has precedence over LS_ADDXP
+
+# LearnSpell return values
+LSR_OK = 0
+LSR_KNOWN = 1 #already knows
+LSR_INVALID = 2 #invalid resref
+LSR_FAILED = 3 #failed stat roll
+LSR_STAT = 4 #insufficient stat (can't learn the spell due to low stat)
+LSR_LEVEL = 5 #insufficient level (low mage, etc level)
+LSR_FULL = 6 #can't learn more spells of this level (due to level)
diff --git a/gemrb/GUIScripts/ie_stats.py b/gemrb/GUIScripts/ie_stats.py
new file mode 100644
index 0000000..f4e04e9
--- /dev/null
+++ b/gemrb/GUIScripts/ie_stats.py
@@ -0,0 +1,368 @@
+# -*-python-*-
+# 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.
+#
+
+# ie_stats.py - definitions of creature stats codes
+
+# !!! NOTE: Keep this file synchronized with gemrb/includes/ie_stats.h !!!
+
+# EA values
+INANIMATE = 1
+PC = 2
+FAMILIAR = 3
+ALLY = 4
+CONTROLLED = 5
+#charmed is 7 inside the engine???
+CHARMED = 7
+GOODBUTRED = 28
+GOODBUTBLUE = 29
+GOODCUTOFF = 30
+NOTGOOD = 31
+ANYTHING = 126
+NEUTRAL = 128
+NOTEVIL = 199
+EVILCUTOFF = 200
+EVILBUTGREEN = 201
+EVILBUTBLUE = 202
+CHARMEDPC = 254
+ENEMY = 255
+
+# state bits (IE_STATE)
+STATE_PANIC = 4
+STATE_HELPLESS = 1 + 32
+STATE_PETRIFIED = 8 + 64 + 128
+STATE_DEAD = 2048
+STATE_POISONED = 0x4000
+
+MC_WAS_FIGHTER = 0x0008
+MC_WAS_MAGE = 0x0010
+MC_WAS_CLERIC = 0x0020
+MC_WAS_THIEF = 0x0040
+MC_WAS_DRUID = 0x0080
+MC_WAS_RANGER = 0x0100
+MC_WAS_ANY_CLASS = MC_WAS_FIGHTER|MC_WAS_MAGE|MC_WAS_CLERIC|MC_WAS_THIEF|MC_WAS_DRUID|MC_WAS_RANGER
+
+MC_FALLEN_PALADIN = 0x200
+MC_FALLEN_RANGER = 0x400
+MC_EXPORTABLE = 0x800
+
+MC_PLOT_CRITICAL = 0x2000
+MC_BEENINPARTY = 0x8000
+MC_HIDDEN = 0x10000
+
+# stats
+IE_HITPOINTS = 0
+IE_MAXHITPOINTS = 1
+IE_ARMORCLASS = 2
+IE_ACCRUSHINGMOD = 3
+IE_ACMISSILEMOD = 4
+IE_ACPIERCINGMOD = 5
+IE_ACSLASHINGMOD = 6
+IE_TOHIT = 7
+IE_NUMBEROFATTACKS = 8
+IE_SAVEVSDEATH = 9
+IE_SAVEVSWANDS = 10
+IE_SAVEVSPOLY = 11
+IE_SAVEVSBREATH = 12
+IE_SAVEVSSPELL = 13
+IE_SAVEFORTITUDE = 9
+IE_SAVEREFLEX = 10
+IE_SAVEWILL = 11
+IE_RESISTFIRE = 14
+IE_RESISTCOLD = 15
+IE_RESISTELECTRICITY = 16
+IE_RESISTACID = 17
+IE_RESISTMAGIC = 18
+IE_RESISTMAGICFIRE = 19
+IE_RESISTMAGICCOLD = 20
+IE_RESISTSLASHING = 21
+IE_RESISTCRUSHING = 22
+IE_RESISTPIERCING = 23
+IE_RESISTMISSILE = 24
+IE_LORE = 25
+IE_LOCKPICKING = 26
+IE_STEALTH = 27
+IE_TRAPS = 28
+IE_PICKPOCKET = 29
+IE_FATIGUE = 30
+IE_INTOXICATION = 31
+IE_LUCK = 32
+IE_TRACKING = 33
+IE_LEVEL = 34
+IE_LEVELFIGHTER = 34 # pst, iwd2
+IE_SEX = 35
+IE_STR = 36
+IE_STREXTRA = 37
+IE_INT = 38
+IE_WIS = 39
+IE_DEX = 40
+IE_CON = 41
+IE_CHR = 42
+IE_XPVALUE = 43
+IE_XP = 44
+IE_GOLD = 45
+IE_MORALEBREAK = 46
+IE_MORALERECOVERYTIME = 47
+IE_REPUTATION = 48
+IE_HATEDRACE = 49
+IE_DAMAGEBONUS = 50
+IE_SPELLFAILUREMAGE = 51
+IE_SPELLFAILUREPRIEST = 52
+IE_SPELLDURATIONMODMAGE = 53
+IE_SPELLDURATIONMODPRIEST = 54
+IE_TURNUNDEADLEVEL = 55
+IE_BACKSTABDAMAGEMULTIPLIER = 56
+IE_LAYONHANDSAMOUNT = 57
+IE_HELD = 58
+IE_POLYMORPHED = 59
+IE_TRANSLUCENT = 60
+IE_IDENTIFYMODE = 61
+IE_ENTANGLE = 62
+IE_SANCTUARY = 63
+IE_MINORGLOBE = 64
+IE_SHIELDGLOBE = 65
+IE_GREASE = 66
+IE_WEB = 67
+IE_LEVEL2 = 68
+IE_LEVELMAGE = 68 # pst, iwd2
+IE_LEVEL3 = 69
+IE_LEVELTHIEF = 69 # pst, iwd2
+IE_CASTERHOLD = 70
+IE_ENCUMBRANCE = 71
+IE_MISSILEHITBONUS = 72
+IE_MAGICDAMAGERESISTANCE = 73
+IE_RESISTPOISON = 74
+IE_DONOTJUMP = 75
+IE_AURACLEANSING = 76
+IE_MENTALSPEED = 77
+IE_PHYSICALSPEED = 78
+IE_CASTINGLEVELBONUSMAGE = 79
+IE_CASTINGLEVELBONUSCLERIC = 80
+IE_SEEINVISIBLE = 81
+IE_IGNOREDIALOGPAUSE = 82
+IE_MINHITPOINTS = 83
+IE_HITBONUSRIGHT = 84
+IE_HITBONUSLEFT = 85
+IE_DAMAGEBONUSRIGHT = 86
+IE_DAMAGEBONUSLEFT = 87
+IE_STONESKINS = 88
+IE_FEAT_BOW = 89
+IE_FEAT_CROSSBOW = 90
+IE_FEAT_SLING = 91
+IE_FEAT_AXE = 92
+IE_FEAT_MACE = 93
+IE_FEAT_FLAIL = 94
+IE_FEAT_POLEARM = 95
+IE_FEAT_HAMMER = 96
+IE_FEAT_STAFF = 97
+IE_FEAT_GREAT_SWORD = 98
+IE_FEAT_LARGE_SWORD = 99
+IE_FEAT_SMALL_SWORD = 100
+IE_FEAT_TOUGHNESS = 101
+IE_FEAT_ARMORED_ARCANA = 102
+IE_FEAT_CLEAVE = 103
+IE_FEAT_ARMOUR = 104
+IE_FEAT_ENCHANTMENT = 105
+IE_FEAT_EVOCATION = 106
+IE_FEAT_NECROMANCY = 107
+IE_FEAT_TRANSMUTATION = 108
+IE_FEAT_SPELL_PENETRATION = 109
+IE_FEAT_EXTRA_RAGE = 110
+IE_FEAT_EXTRA_SHAPE = 111
+IE_FEAT_EXTRA_SMITING = 112
+IE_FEAT_EXTRA_TURNING = 113
+IE_FEAT_BASTARDSWORD = 114
+IE_ALCHEMY = 115
+IE_ANIMALS = 116
+IE_BLUFF = 117
+IE_CONCENTRATION = 118
+IE_DIPLOMACY = 119
+IE_INTIMIDATE = 120
+IE_SEARCH = 121
+IE_SPELLCRAFT = 122
+IE_MAGICDEVICE = 123
+IE_PROFICIENCYBASTARDSWORD = 89
+IE_PROFICIENCYLONGSWORD = 90
+IE_PROFICIENCYSHORTSWORD = 91
+IE_PROFICIENCYAXE = 92
+IE_PROFICIENCYTWOHANDEDSWORD = 93
+IE_PROFICIENCYKATANA = 94
+IE_PROFICIENCYSCIMITARWAKISASHININJATO = 95
+IE_PROFICIENCYDAGGER = 96
+IE_PROFICIENCYWARHAMMER = 97
+IE_PROFICIENCYSPEAR = 98
+IE_PROFICIENCYHALBERD = 99
+IE_PROFICIENCYFLAILMORNINGSTAR = 100
+IE_PROFICIENCYMACE = 101
+IE_PROFICIENCYQUARTERSTAFF = 102
+IE_PROFICIENCYCROSSBOW = 103
+IE_PROFICIENCYLONGBOW = 104
+IE_PROFICIENCYSHORTBOW = 105
+IE_PROFICIENCYDART = 106
+IE_PROFICIENCYSLING = 107
+IE_PROFICIENCYBLACKJACK = 108
+IE_PROFICIENCYGUN = 109
+IE_PROFICIENCYMARTIALARTS = 110
+IE_PROFICIENCY2HANDED = 111
+IE_PROFICIENCYSWORDANDSHIELD = 112
+IE_PROFICIENCYSINGLEWEAPON = 113
+IE_PROFICIENCY2WEAPON = 114
+IE_EXTRAPROFICIENCY1 = 115
+IE_EXTRAPROFICIENCY2 = 116
+IE_EXTRAPROFICIENCY3 = 117
+IE_EXTRAPROFICIENCY4 = 118
+IE_EXTRAPROFICIENCY5 = 119
+IE_EXTRAPROFICIENCY6 = 120
+IE_EXTRAPROFICIENCY7 = 121
+IE_EXTRAPROFICIENCY8 = 122
+IE_EXTRAPROFICIENCY9 = 123
+IE_EXTRAPROFICIENCY10 = 124
+IE_EXTRAPROFICIENCY11 = 125
+IE_EXTRAPROFICIENCY12 = 126
+IE_EXTRAPROFICIENCY13 = 127
+IE_EXTRAPROFICIENCY14 = 128
+IE_EXTRAPROFICIENCY15 = 129
+IE_EXTRAPROFICIENCY16 = 130
+IE_EXTRAPROFICIENCY17 = 131
+IE_FEATS1 = 131
+IE_EXTRAPROFICIENCY18 = 132
+IE_FEATS2 = 132
+IE_EXTRAPROFICIENCY19 = 133
+IE_FEATS3 = 133
+IE_EXTRAPROFICIENCY20 = 134
+IE_FREESLOTS = 134 #not an error (PST)
+IE_HIDEINSHADOWS = 135
+IE_DETECTILLUSIONS = 136
+IE_SETTRAPS = 137
+IE_PUPPETMASTERID = 138
+IE_PUPPETMASTERTYPE = 139
+IE_PUPPETTYPE = 140
+IE_PUPPETID = 141
+IE_CHECKFORBERSERK = 142
+IE_BERSERKSTAGE1 = 143
+IE_BERSERKSTAGE2 = 144
+IE_DAMAGELUCK = 145
+IE_CRITICALHITBONUS = 146
+IE_VISUALRANGE = 147
+IE_EXPLORE = 148
+IE_THRULLCHARM = 149
+IE_SUMMONDISABLE = 150
+IE_HITBONUS = 151
+IE_KIT = 152
+IE_FORCESURGE = 153
+IE_SURGEMOD = 154
+IE_IMPROVEDHASTE = 155
+IE_SCRIPTINGSTATE1 = 156
+IE_SCRIPTINGSTATE2 = 157
+IE_SCRIPTINGSTATE3 = 158
+IE_SCRIPTINGSTATE4 = 159
+IE_SCRIPTINGSTATE5 = 160
+IE_SCRIPTINGSTATE6 = 161
+IE_SCRIPTINGSTATE7 = 162
+IE_SCRIPTINGSTATE8 = 163
+IE_SCRIPTINGSTATE9 = 164
+IE_SCRIPTINGSTATE10 = 165
+IE_MELEETOHIT = 166
+IE_MELEEDAMAGE = 167
+IE_MISSILEDAMAGE = 168
+IE_NOCIRCLE = 169
+IE_FISTHIT = 170
+IE_FISTDAMAGE = 171
+IE_TITLE1 = 172
+IE_TITLE2 = 173
+IE_DISABLEOVERLAY = 174
+IE_DISABLEBACKSTAB = 175
+#176-182 overwritten by us
+IE_XP_MAGE = 176 # In PST this stores secondary level exp
+IE_XP_THIEF = 177 # In PST this stores tertiary level exp
+IE_DIALOGRANGE = 178 # distance for dialogue
+IE_MOVEMENTRATE = 179
+IE_MORALE = 180
+IE_BOUNCE = 181
+IE_MIRRORIMAGES = 182
+#these are original
+IE_ENABLEOFFSCREENAI = 183
+IE_EXISTANCEDELAY = 184
+IE_ATTACKNUMBERDOUBLE = 185
+IE_DISABLECHUNKING = 186
+IE_NOTURNABLE = 187
+#188 was summondisable2 in original
+IE_STONESKINSGOLEM = 199
+IE_LEVELDRAIN = 200
+IE_AVATARREMOVAL = 201
+#202 is unused
+# GemRB Specific Defines
+IE_IMMUNITY = 203
+IE_DISABLEDBUTTON = 204
+IE_ANIMATION_ID = 205
+IE_STATE_ID = 206
+IE_EXTSTATE_ID = 207
+IE_METAL_COLOR = 208
+IE_MINOR_COLOR = 209
+IE_MAJOR_COLOR = 210
+IE_SKIN_COLOR = 211
+IE_LEATHER_COLOR = 212
+IE_ARMOR_COLOR = 213
+IE_HAIR_COLOR = 214
+IE_MC_FLAGS = 215
+IE_CLASSLEVELSUM = 216
+IE_ALIGNMENT = 217
+IE_UNSELECTABLE = 218
+IE_ARMOR_TYPE = 219
+IE_TEAM = 220
+IE_FACTION = 221
+IE_SUBRACE = 222
+IE_SPECIES = 223 #pst specific
+IE_HATEDRACE2 = 224
+IE_HATEDRACE3 = 225
+IE_HATEDRACE4 = 226
+IE_HATEDRACE5 = 227
+IE_HATEDRACE6 = 228
+IE_HATEDRACE7 = 229
+IE_HATEDRACE8 = 230
+# These are in original PST, IWD, IWD2, but not as stats
+IE_RACE = 231
+IE_CLASS = 232
+IE_GENERAL = 233
+IE_EA = 234
+IE_SPECIFIC = 235
+IE_SAVEDXPOS = 236
+IE_SAVEDYPOS = 237
+IE_SAVEDFACE = 238
+#239 user defined stat
+IE_LEVELBARBARIAN = 240
+IE_LEVELBARD = 241
+IE_LEVELCLERIC = 242
+IE_LEVELDRUID = 243
+IE_LEVELMONK = 244
+IE_LEVELPALADIN = 245
+IE_LEVELRANGER = 246
+IE_LEVELSORCEROR = 247
+#248 IE_LEVELCLASS12
+#249 IE_LEVELCLASS13
+#the remaining six stats are spell states
+IE_SPLSTATE_ID1 = 250
+
+#these stats exist only in PC's (but we access only PCs anyway)
+IE_EXPERTISE = 0x1003
+IE_POWERATTACK = 0x1004
+IE_ARTERIAL_STRIKE = 0x1005
+IE_HAMSTRING = 0x1006
+IE_RAPID_SHOT = 0x1007
+
+# End of file ie_stats.py
diff --git a/gemrb/GUIScripts/include.py b/gemrb/GUIScripts/include.py
new file mode 100644
index 0000000..adb76e6
--- /dev/null
+++ b/gemrb/GUIScripts/include.py
@@ -0,0 +1,2 @@
+# this file is executed at gemrb startup, for the Console
+from ie_stats import *
diff --git a/gemrb/GUIScripts/iwd/Autodetect.py b/gemrb/GUIScripts/iwd/Autodetect.py
new file mode 100644
index 0000000..a05d41a
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/Autodetect.py
@@ -0,0 +1,45 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2010 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.
+#
+
+import GemRB
+from ie_restype import *
+from AutodetectCommon import CheckFiles
+
+files = (
+ ("START", "CHU", RES_CHU),
+ ("STARTPOS", "2DA", RES_2DA),
+ ("STARTARE", "2DA", RES_2DA),
+
+ ("EXPTABLE", "2DA", RES_2DA),
+ ("MUSIC", "2DA", RES_2DA),
+)
+
+files_how = (
+ ("CHMB1A1", "BAM", RES_BAM),
+ ("TRACKING", "2DA", RES_2DA),
+)
+
+if CheckFiles(files):
+ if CheckFiles(files_how):
+ GemRB.AddGameTypeHint ("how", 95)
+ else:
+ GemRB.AddGameTypeHint ("iwd", 90)
+
+
diff --git a/gemrb/GUIScripts/iwd/CMakeLists.txt b/gemrb/GUIScripts/iwd/CMakeLists.txt
new file mode 100644
index 0000000..afd062e
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/CMakeLists.txt
@@ -0,0 +1,3 @@
+FILE( GLOB FILES_TO_INSTALL *.py )
+
+INSTALL( FILES ${FILES_TO_INSTALL} DESTINATION ${DATA_DIR}/GUIScripts/iwd )
diff --git a/gemrb/GUIScripts/iwd/CharGen.py b/gemrb/GUIScripts/iwd/CharGen.py
new file mode 100644
index 0000000..eee6add
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/CharGen.py
@@ -0,0 +1,2761 @@
+# -*-python-*-
+# 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.
+#
+
+
+#Character Generation
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+from ie_spells import LS_MEMO
+import GUICommon
+import CommonTables
+import LUSkillsSelection
+
+CharGenWindow = 0
+CharGenState = 0
+TextArea = 0
+PortraitButton = 0
+AcceptButton = 0
+
+GenderButton = 0
+GenderWindow = 0
+GenderTextArea = 0
+GenderDoneButton = 0
+
+Portrait = 0
+PortraitsTable = 0
+PortraitPortraitButton = 0
+
+RaceButton = 0
+RaceWindow = 0
+RaceTextArea = 0
+RaceDoneButton = 0
+
+ClassButton = 0
+ClassWindow = 0
+ClassTextArea = 0
+ClassDoneButton = 0
+
+ClassMultiWindow = 0
+ClassMultiTextArea = 0
+ClassMultiDoneButton = 0
+
+KitTable = 0
+KitWindow = 0
+KitTextArea = 0
+KitDoneButton = 0
+
+AlignmentButton = 0
+AlignmentWindow = 0
+AlignmentTable = 0
+AlignmentTextArea = 0
+AlignmentDoneButton = 0
+
+AbilitiesButton = 0
+AbilitiesWindow = 0
+AbilitiesTable = 0
+AbilitiesRaceAddTable = 0
+AbilitiesRaceReqTable = 0
+AbilitiesClassReqTable = 0
+AbilitiesMinimum = 0
+AbilitiesMaximum = 0
+AbilitiesModifier = 0
+AbilitiesTextArea = 0
+AbilitiesRecallButton = 0
+AbilitiesDoneButton = 0
+
+SkillsButton = 0
+SkillsWindow = 0
+SkillsTable = 0
+SkillsTextArea = 0
+SkillsDoneButton = 0
+SkillsPointsLeft = 0
+SkillsState = 0
+
+RacialEnemyButton = 0
+RacialEnemyWindow = 0
+RacialEnemyTable = 0
+RacialEnemyTextArea = 0
+RacialEnemyDoneButton = 0
+
+ProficienciesWindow = 0
+ProficienciesTable = 0
+ProfsMaxTable = 0
+ProficienciesTextArea = 0
+ProficienciesDoneButton = 0
+ProficienciesPointsLeft = 0
+
+MageSpellsWindow = 0
+MageSpellsTextArea = 0
+MageSpellsDoneButton = 0
+MageSpellsSelectPointsLeft = 0
+
+MageMemorizeWindow = 0
+MageMemorizeTextArea = 0
+MageMemorizeDoneButton = 0
+MageMemorizePointsLeft = 0
+
+PriestMemorizeWindow = 0
+PriestMemorizeTextArea = 0
+PriestMemorizeDoneButton = 0
+PriestMemorizePointsLeft = 0
+
+AppearanceButton = 0
+AppearanceWindow = 0
+AppearanceTable = 0
+AppearanceAvatarButton = 0
+AppearanceHairButton = 0
+AppearanceSkinButton = 0
+AppearanceMajorButton = 0
+AppearanceMinorButton = 0
+HairColor = 0
+SkinColor = 0
+MajorColor = 0
+MinorColor = 0
+
+CharSoundWindow = 0
+CharSoundTable = 0
+CharSoundStrings = 0
+
+BiographyButton = 0
+BiographyWindow = 0
+BiographyField = 0
+
+NameButton = 0
+NameWindow = 0
+NameField = 0
+NameDoneButton = 0
+
+SoundIndex = 0
+VerbalConstants = None
+HasStrExtra = 0
+MyChar = 0
+
+def OnLoad():
+ global CharGenWindow, CharGenState, TextArea, PortraitButton, AcceptButton
+ global GenderButton, RaceButton, ClassButton, AlignmentButton
+ global AbilitiesButton, SkillsButton, AppearanceButton, BiographyButton, NameButton
+ global KitTable, ProficienciesTable, AlignmentTable, RacialEnemyTable
+ global AbilitiesTable, SkillsTable, PortraitsTable
+ global MyChar
+
+ KitTable = GemRB.LoadTable ("magesch")
+ ProficienciesTable = GemRB.LoadTable ("weapprof")
+ AlignmentTable = GemRB.LoadTable ("aligns")
+ RacialEnemyTable = GemRB.LoadTable ("haterace")
+ AbilitiesTable = GemRB.LoadTable ("ability")
+ SkillsTable = GemRB.LoadTable ("skills")
+ PortraitsTable = GemRB.LoadTable ("pictures")
+ GemRB.LoadWindowPack ("GUICG", 640, 480)
+ CharGenWindow = GemRB.LoadWindow (0)
+ CharGenWindow.SetFrame ()
+ CharGenState = 0
+ MyChar = GemRB.GetVar ("Slot")
+
+ GenderButton = CharGenWindow.GetControl (0)
+ GenderButton.SetState (IE_GUI_BUTTON_ENABLED)
+ GenderButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ GenderButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, GenderPress)
+ GenderButton.SetText (11956)
+
+ RaceButton = CharGenWindow.GetControl (1)
+ RaceButton.SetState (IE_GUI_BUTTON_DISABLED)
+ RaceButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RacePress)
+ RaceButton.SetText (11957)
+
+ ClassButton = CharGenWindow.GetControl (2)
+ ClassButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ClassButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClassPress)
+ ClassButton.SetText (11959)
+
+ AlignmentButton = CharGenWindow.GetControl (3)
+ AlignmentButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AlignmentButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AlignmentPress)
+ AlignmentButton.SetText (11958)
+
+ AbilitiesButton = CharGenWindow.GetControl (4)
+ AbilitiesButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AbilitiesButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesPress)
+ AbilitiesButton.SetText (11960)
+
+ SkillsButton = CharGenWindow.GetControl (5)
+ SkillsButton.SetState (IE_GUI_BUTTON_DISABLED)
+ SkillsButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SkillsPress)
+ SkillsButton.SetText (11983)
+
+ AppearanceButton = CharGenWindow.GetControl (6)
+ AppearanceButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AppearanceButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AppearancePress)
+ AppearanceButton.SetText (11961)
+
+ BiographyButton = CharGenWindow.GetControl (16)
+ BiographyButton.SetState (IE_GUI_BUTTON_DISABLED)
+ BiographyButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BiographyPress)
+ BiographyButton.SetText (18003)
+
+ NameButton = CharGenWindow.GetControl (7)
+ NameButton.SetState (IE_GUI_BUTTON_DISABLED)
+ NameButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, NamePress)
+ NameButton.SetText (11963)
+
+ BackButton = CharGenWindow.GetControl (11)
+ BackButton.SetState (IE_GUI_BUTTON_ENABLED)
+ BackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BackPress)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PortraitButton = CharGenWindow.GetControl (12)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ ImportButton = CharGenWindow.GetControl (13)
+ ImportButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ImportButton.SetText (13955)
+ ImportButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ImportPress)
+
+ CancelButton = CharGenWindow.GetControl (15)
+ CancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+
+ AcceptButton = CharGenWindow.GetControl (8)
+ AcceptButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AcceptButton.SetText (11962)
+ AcceptButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AcceptPress)
+
+ TextArea = CharGenWindow.GetControl (9)
+ TextArea.SetText (16575)
+
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def BackPress():
+ global CharGenWindow, CharGenState, SkillsState
+ global GenderButton, RaceButton, ClassButton, AlignmentButton, AbilitiesButton, SkillsButton, AppearanceButton, BiographyButton, NameButton
+
+ if CharGenState > 0:
+ CharGenState = CharGenState - 1
+ else:
+ CancelPress()
+ return
+
+ if CharGenState > 6:
+ CharGenState = 6
+ GemRB.SetToken ("CHARNAME","")
+
+ if CharGenState == 0:
+ RaceButton.SetState (IE_GUI_BUTTON_DISABLED)
+ RaceButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ GenderButton.SetState (IE_GUI_BUTTON_ENABLED)
+ GenderButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ elif CharGenState == 1:
+ ClassButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ClassButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ RaceButton.SetState (IE_GUI_BUTTON_ENABLED)
+ RaceButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ elif CharGenState == 2:
+ AlignmentButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AlignmentButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ ClassButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ClassButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ elif CharGenState == 3:
+ AbilitiesButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AbilitiesButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ AlignmentButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AlignmentButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ elif CharGenState == 4:
+ SkillsButton.SetState (IE_GUI_BUTTON_DISABLED)
+ SkillsButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ AbilitiesButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AbilitiesButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ elif CharGenState == 5:
+ AppearanceButton.SetState (IE_GUI_BUTTON_DISABLED)
+ SkillsButton.SetState (IE_GUI_BUTTON_ENABLED)
+ SkillsButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ SkillsState = 0
+ elif CharGenState == 6:
+ NameButton.SetState (IE_GUI_BUTTON_DISABLED)
+ BiographyButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AppearanceButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ AcceptButton.SetState (IE_GUI_BUTTON_DISABLED)
+ SetCharacterDescription()
+ return
+
+def CancelPress():
+ global CharGenWindow
+
+ if CharGenWindow:
+ CharGenWindow.Unload ()
+ GemRB.CreatePlayer ("", MyChar | 0x8000 )
+ GemRB.SetNextScript ("PartyFormation")
+ return
+
+def AcceptPress():
+ #mage spells
+ Kit = GemRB.GetPlayerStat (MyChar, IE_KIT)
+ KitIndex = KitTable.FindValue (3, Kit)
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ t = GemRB.GetPlayerStat (MyChar, IE_ALIGNMENT)
+ TableName = CommonTables.ClassSkills.GetValue (Class, 2, 0)
+ if TableName != "*":
+ #todo: set up ALL spell levels not just level 1
+ GUICommon.SetupSpellLevels (MyChar, TableName, IE_SPELL_TYPE_WIZARD, 1)
+ Learnable = GUICommon.GetLearnableMageSpells (KitIndex, t, 1)
+ SpellBook = GemRB.GetVar ("MageSpellBook")
+ MemoBook = GemRB.GetVar ("MageMemorized")
+ j=1
+ for i in range (len(Learnable) ):
+ if SpellBook & j:
+ if MemoBook & j:
+ memorize = 8
+ else:
+ memorize = 0
+ GemRB.LearnSpell (MyChar, Learnable[i], memorize)
+ j=j<<1
+
+ #priest spells
+ TableName = CommonTables.ClassSkills.GetValue (Class, 1, 0)
+ if TableName != "*":
+ if TableName == "MXSPLPRS" or TableName == "MXSPLPAL":
+ ClassFlag = 0x8000
+ elif TableName == "MXSPLDRU":
+ #there is no separate druid table, falling back to priest
+ TableName = "MXSPLPRS"
+ ClassFlag = 0x4000
+ elif TableName == "MXSPLRAN":
+ ClassFlag = 0x4000
+ else:
+ ClassFlag = 0
+ #todo: set up ALL spell levels not just level 1
+ GUICommon.SetupSpellLevels (MyChar, TableName, IE_SPELL_TYPE_PRIEST, 1)
+ Learnable = GUICommon.GetLearnablePriestSpells (ClassFlag, t, 1)
+ for i in range (len(Learnable) ):
+ GemRB.LearnSpell (MyChar, Learnable[i], 0)
+
+ # ranger tracking is a hardcoded innate
+ if GUICommon.HasHOW():
+ if CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat (MyChar, IE_CLASS), 0) != "*":
+ GemRB.LearnSpell (MyChar, "spin139", LS_MEMO)
+
+ # save all the skills
+ LUSkillsSelection.SkillsSave (MyChar)
+
+ TmpTable = GemRB.LoadTable ("repstart")
+ t = AlignmentTable.FindValue (3, t)
+ t = TmpTable.GetValue (t, 0) * 10
+ GemRB.SetPlayerStat (MyChar, IE_REPUTATION, t)
+ # set the party rep if this in the main char
+ if MyChar == 1:
+ GemRB.GameSetReputation (t)
+
+ print "Reputation", t
+ TmpTable = GemRB.LoadTable ("strtgold")
+ a = TmpTable.GetValue (Class, 1) #number of dice
+ b = TmpTable.GetValue (Class, 0) #size
+ c = TmpTable.GetValue (Class, 2) #adjustment
+ d = TmpTable.GetValue (Class, 3) #external multiplier
+ e = TmpTable.GetValue (Class, 4) #level bonus rate
+ t = GemRB.GetPlayerStat (MyChar, IE_LEVEL) #FIXME: calculate multiclass average
+ if t>1:
+ e=e*(t-1)
+ else:
+ e=0
+ t = GemRB.Roll (a,b,c)*d+e
+ GemRB.SetPlayerStat (MyChar, IE_GOLD, t)
+ GemRB.SetPlayerStat (MyChar, IE_EA, 2 )
+ #GemRB.SetPlayerStat (MyChar, IE_HATEDRACE, GemRB.GetVar ("HatedRace") )
+ #Str = GemRB.GetVar ("Ability1")
+ #GemRB.SetPlayerStat (MyChar, IE_STR, Str)
+ #if Str == 18:
+ # GemRB.SetPlayerStat (MyChar, IE_STREXTRA, GemRB.GetVar ("StrExtra"))
+ #else:
+ # GemRB.SetPlayerStat (MyChar, IE_STREXTRA, 0)
+
+ #GemRB.SetPlayerStat (MyChar, IE_DEX, GemRB.GetVar ("Ability2"))
+ #GemRB.SetPlayerStat (MyChar, IE_CON, GemRB.GetVar ("Ability3"))
+ #GemRB.SetPlayerStat (MyChar, IE_INT, GemRB.GetVar ("Ability4"))
+ #GemRB.SetPlayerStat (MyChar, IE_WIS, GemRB.GetVar ("Ability5"))
+ #GemRB.SetPlayerStat (MyChar, IE_CHR, GemRB.GetVar ("Ability6"))
+
+ GemRB.SetPlayerName (MyChar, GemRB.GetToken ("CHARNAME"), 0)
+ GemRB.SetToken ("CHARNAME","")
+ GemRB.SetPlayerStat (MyChar, IE_XP, CommonTables.ClassSkills.GetValue (Class, 3) )
+
+ GUICommon.SetColorStat (MyChar, IE_SKIN_COLOR, GemRB.GetVar ("SkinColor") )
+ GUICommon.SetColorStat (MyChar, IE_HAIR_COLOR, GemRB.GetVar ("HairColor") )
+ GUICommon.SetColorStat (MyChar, IE_MAJOR_COLOR, GemRB.GetVar ("MajorColor") )
+ GUICommon.SetColorStat (MyChar, IE_MINOR_COLOR, GemRB.GetVar ("MinorColor") )
+ GUICommon.SetColorStat (MyChar, IE_METAL_COLOR, 0x1B )
+ GUICommon.SetColorStat (MyChar, IE_LEATHER_COLOR, 0x16 )
+ GUICommon.SetColorStat (MyChar, IE_ARMOR_COLOR, 0x17 )
+
+ #does all the rest
+ LargePortrait = GemRB.GetToken ("LargePortrait")
+ SmallPortrait = GemRB.GetToken ("SmallPortrait")
+ GemRB.FillPlayerInfo (MyChar, LargePortrait, SmallPortrait)
+
+ #10 is a weapon slot (see slottype.2da row 10)
+ GemRB.CreateItem (MyChar, "staf01", 10, 1, 0, 0)
+ GemRB.SetEquippedQuickSlot (MyChar, 0)
+
+ if CharGenWindow:
+ CharGenWindow.Unload ()
+ GemRB.SetNextScript ("PartyFormation")
+ return
+
+def SetCharacterDescription():
+ global CharGenWindow, TextArea, CharGenState, ClassFlag
+ global MyChar
+
+ TextArea.Clear()
+ if CharGenState > 7:
+ TextArea.Append (1047)
+ TextArea.Append (": ")
+ TextArea.Append (GemRB.GetToken ("CHARNAME"))
+ TextArea.Append ("", -1)
+ if CharGenState > 0:
+ TextArea.Append (12135)
+ TextArea.Append (": ")
+ if GemRB.GetPlayerStat (MyChar, IE_SEX) == 1:
+ TextArea.Append (1050)
+ else:
+ TextArea.Append (1051)
+ if CharGenState > 2:
+ Class = CommonTables.Classes.FindValue (5, GemRB.GetPlayerStat (MyChar, IE_CLASS) )
+ TextArea.Append (12136, -1)
+ TextArea.Append (": ")
+ #this is only mage school in iwd
+ Kit = GemRB.GetPlayerStat (MyChar, IE_KIT)
+ KitIndex = KitTable.FindValue (3, Kit)
+ if KitIndex <= 0:
+ ClassTitle = CommonTables.Classes.GetValue (Class, 2)
+ else:
+ ClassTitle = KitTable.GetValue (KitIndex, 2)
+ TextArea.Append (ClassTitle)
+
+ if CharGenState > 1:
+ TextArea.Append (1048, -1)
+ TextArea.Append (": ")
+ Race = GemRB.GetPlayerStat (MyChar, IE_RACE)
+ Race = CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (MyChar, IE_RACE) )
+ TextArea.Append (CommonTables.Races.GetValue (Race, 2) )
+ if CharGenState > 3:
+ TextArea.Append (1049, -1)
+ TextArea.Append (": ")
+ Alignment = AlignmentTable.FindValue (3, GemRB.GetPlayerStat(MyChar, IE_ALIGNMENT) )
+ TextArea.Append (AlignmentTable.GetValue (Alignment, 2) )
+ if CharGenState > 4:
+ strextra = GemRB.GetPlayerStat (MyChar, IE_STREXTRA)
+ TextArea.Append ("", -1)
+ for i in range (6):
+ TextArea.Append (AbilitiesTable.GetValue (i, 2), -1)
+ TextArea.Append (": " )
+ StatID = AbilitiesTable.GetValue (i, 3)
+ stat = GemRB.GetPlayerStat (MyChar, StatID)
+ if (i == 0) and HasStrExtra and (stat==18):
+ TextArea.Append (str(stat) + "/" + str(strextra) )
+ else:
+ TextArea.Append (str(stat) )
+ if CharGenState > 5:
+ ClassName = CommonTables.Classes.GetRowName (Class)
+ Row = CommonTables.Classes.GetValue (Class, 5)
+ IsRanger = CommonTables.ClassSkills.GetValue (Row, 0)
+ IsArcane = CommonTables.ClassSkills.GetValue (Row, 1)
+ IsMage = CommonTables.ClassSkills.GetValue (Row, 2)
+ IsBard = CommonTables.ClassSkills.GetValue (Row, 4)
+ IsThief = CommonTables.ClassSkills.GetValue (Row, 5)
+
+ if IsThief!="*":
+ TextArea.Append ("", -1)
+ TextArea.Append (8442, -1)
+ for i in range (4):
+ TextArea.Append (SkillsTable.GetValue (i+2, 2), -1)
+ StatID = SkillsTable.GetValue (i+2, 3)
+ TextArea.Append (": " )
+ TextArea.Append (str(GemRB.GetPlayerStat (MyChar, StatID)) )
+ TextArea.Append ("%" )
+ elif IsRanger!="*":
+ TextArea.Append ("", -1)
+ TextArea.Append (8442, -1)
+ for i in range (4):
+ StatID = SkillsTable.GetValue (i+2, 3)
+ Stat = GemRB.GetPlayerStat (MyChar, StatID)
+ if Stat>0:
+ TextArea.Append (SkillsTable.GetValue (i+2, 2), -1)
+ TextArea.Append (": " )
+ TextArea.Append (str(Stat) )
+ TextArea.Append ("%" )
+ TextArea.Append ("", -1)
+ TextArea.Append (15982, -1)
+ TextArea.Append (": " )
+ RacialEnemy = GemRB.GetVar ("RacialEnemyIndex") + GemRB.GetVar ("RacialEnemy") - 1
+ TextArea.Append (RacialEnemyTable.GetValue (RacialEnemy, 3) )
+ elif IsBard!="*":
+ TextArea.Append ("", -1)
+ TextArea.Append (8442, -1)
+ for i in range (4):
+ StatID = SkillsTable.GetValue (i+2, 3)
+ Stat = GemRB.GetPlayerStat (MyChar, StatID)
+ if Stat>0:
+ TextArea.Append (SkillsTable.GetValue (i+2, 2), -1)
+ TextArea.Append (": " )
+ TextArea.Append (str(Stat) )
+ TextArea.Append ("%" )
+
+ TextArea.Append ("", -1)
+ TextArea.Append (9466, -1)
+ for i in range (15):
+ StatID = ProficienciesTable.GetValue (i, 0)
+ ProficiencyValue = GemRB.GetPlayerStat (MyChar, StatID )
+ if ProficiencyValue > 0:
+ TextArea.Append (ProficienciesTable.GetValue (i, 3), -1)
+ TextArea.Append (" ")
+ j = 0
+ while j < ProficiencyValue:
+ TextArea.Append ("+")
+ j = j + 1
+
+ if IsMage !="*":
+ TextArea.Append ("", -1)
+ TextArea.Append (11027, -1)
+ TextArea.Append (": " )
+ t = GemRB.GetPlayerStat (MyChar, IE_ALIGNMENT)
+ Learnable = GUICommon.GetLearnableMageSpells (GemRB.GetPlayerStat (MyChar, IE_KIT), t,1)
+ MageSpellBook = GemRB.GetVar ("MageSpellBook")
+ MageMemorized = GemRB.GetVar ("MageMemorized")
+ for i in range (len(Learnable)):
+ if (1 << i) & MageSpellBook:
+ Spell = GemRB.GetSpell (Learnable[i])
+ TextArea.Append (Spell["SpellName"], -1)
+ if (1 << i) & MageMemorized:
+ TextArea.Append (" +")
+ TextArea.Append (" ")
+
+ if IsArcane!="*":
+ TextArea.Append ("", -1)
+ TextArea.Append (11028, -1)
+ TextArea.Append (": " )
+ t = GemRB.GetPlayerStat (MyChar, IE_ALIGNMENT)
+ if IsArcane == "MXSPLPRS" or IsArcane == "MXSPLPAL":
+ ClassFlag = 0x4000
+ elif IsArcane == "MXSPLDRU" or IsArcane == "MXSPLRAN":
+ ClassFlag = 0x8000
+ else:
+ ClassFlag = 0
+
+ Learnable = GUICommon.GetLearnablePriestSpells( ClassFlag, t, 1)
+ PriestMemorized = GemRB.GetVar ("PriestMemorized")
+ for i in range (len(Learnable)):
+ if (1 << i) & PriestMemorized:
+ Spell = GemRB.GetSpell (Learnable[i])
+ TextArea.Append (Spell["SpellName"], -1)
+ TextArea.Append (" +")
+ return
+
+
+# Gender Selection
+
+def GenderPress():
+ global CharGenWindow, GenderWindow, GenderDoneButton, GenderTextArea
+ global MyChar
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ GenderWindow = GemRB.LoadWindow (1)
+ GemRB.SetVar ("Gender", 0)
+ GemRB.CreatePlayer ("charbase", MyChar | 0x8000 )
+
+ MaleButton = GenderWindow.GetControl (2)
+ MaleButton.SetState (IE_GUI_BUTTON_ENABLED)
+ MaleButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ MaleButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MalePress)
+
+ FemaleButton = GenderWindow.GetControl (3)
+ FemaleButton.SetState (IE_GUI_BUTTON_ENABLED)
+ FemaleButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ FemaleButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, FemalePress)
+
+ MaleButton.SetVarAssoc ("Gender", 1)
+ FemaleButton.SetVarAssoc ("Gender", 2)
+
+ GenderTextArea = GenderWindow.GetControl (5)
+ GenderTextArea.SetText (17236)
+
+ GenderDoneButton = GenderWindow.GetControl (0)
+ GenderDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ GenderDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, GenderDonePress)
+ GenderDoneButton.SetText (11973)
+ GenderDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ GenderCancelButton = GenderWindow.GetControl (6)
+ GenderCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ GenderCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, GenderCancelPress)
+ GenderCancelButton.SetText (13727)
+ GenderCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GenderWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MalePress():
+ global GenderWindow, GenderDoneButton, GenderTextArea
+
+ GenderTextArea.SetText (13083)
+ GenderDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def FemalePress():
+ global GenderWindow, GenderDoneButton, GenderTextArea
+
+ GenderTextArea.SetText (13084)
+ GenderDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def GenderDonePress():
+ global CharGenWindow, GenderWindow
+ global MyChar
+
+ if GenderWindow:
+ GenderWindow.Unload ()
+ Gender = GemRB.GetVar ("Gender")
+ GemRB.SetPlayerStat (MyChar, IE_SEX, Gender)
+
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ PortraitSelect()
+ return
+
+def GenderCancelPress():
+ global CharGenWindow, GenderWindow
+ global MyChar
+
+ GemRB.SetVar ("Gender", 0)
+ GemRB.SetPlayerStat (MyChar, IE_SEX, 0)
+ if GenderWindow:
+ GenderWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def PortraitSelect():
+ global CharGenWindow, PortraitWindow, Portrait, PortraitPortraitButton
+ global MyChar
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ PortraitWindow = GemRB.LoadWindow (11)
+
+ # this is not the correct one, but I don't know which is
+ Portrait = 0
+
+ PortraitPortraitButton = PortraitWindow.GetControl (1)
+ PortraitPortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+
+ PortraitLeftButton = PortraitWindow.GetControl (2)
+ PortraitLeftButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitLeftButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CGPortraitLeftPress)
+ PortraitLeftButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ PortraitRightButton = PortraitWindow.GetControl (3)
+ PortraitRightButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitRightButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CGPortraitRightPress)
+ PortraitRightButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ PortraitCustomButton = PortraitWindow.GetControl (6)
+ PortraitCustomButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitCustomButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitCustomPress)
+ PortraitCustomButton.SetText (17545)
+
+ PortraitDoneButton = PortraitWindow.GetControl (0)
+ PortraitDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CGPortraitDonePress)
+ PortraitDoneButton.SetText (11973)
+ PortraitDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ PortraitCancelButton = PortraitWindow.GetControl (5)
+ PortraitCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CGPortraitCancelPress)
+ PortraitCancelButton.SetText (13727)
+ PortraitCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ while PortraitsTable.GetValue (Portrait, 0) != GemRB.GetPlayerStat (MyChar, IE_SEX):
+ Portrait = Portrait + 1
+ PortraitPortraitButton.SetPicture (PortraitsTable.GetRowName (Portrait) + "G")
+
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CGPortraitLeftPress():
+ global PortraitWindow, Portrait, PortraitPortraitButton
+ global MyChar
+
+ while True:
+ Portrait = Portrait - 1
+ if Portrait < 0:
+ Portrait = PortraitsTable.GetRowCount () - 1
+ if PortraitsTable.GetValue (Portrait, 0) == GemRB.GetPlayerStat(MyChar, IE_SEX):
+ PortraitPortraitButton.SetPicture (PortraitsTable.GetRowName (Portrait) + "G")
+ return
+
+def CGPortraitRightPress():
+ global PortraitWindow, Portrait, PortraitPortraitButton
+ global MyChar
+
+ while True:
+ Portrait = Portrait + 1
+ if Portrait == PortraitsTable.GetRowCount():
+ Portrait = 0
+ if PortraitsTable.GetValue (Portrait, 0) == GemRB.GetPlayerStat(MyChar, IE_SEX):
+ PortraitPortraitButton.SetPicture (PortraitsTable.GetRowName (Portrait) + "G")
+ return
+
+def CustomDone():
+ global CharGenWindow, PortraitWindow
+ global PortraitButton, GenderButton, RaceButton
+ global CharGenState, Portrait
+
+ Window = CustomWindow
+
+ PortraitName = PortraitList2.QueryText ()
+ GemRB.SetToken ("SmallPortrait", PortraitName)
+ PortraitName = PortraitList1.QueryText ()
+ GemRB.SetToken ("LargePortrait", PortraitName)
+ if Window:
+ Window.Unload ()
+
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ PortraitButton.SetPicture(PortraitName)
+ GenderButton.SetState (IE_GUI_BUTTON_DISABLED)
+ GenderButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ RaceButton.SetState (IE_GUI_BUTTON_ENABLED)
+ RaceButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CharGenState = 1
+ Portrait = -1
+ SetCharacterDescription()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CustomAbort():
+ if CustomWindow:
+ CustomWindow.Unload ()
+ return
+
+def CGLargeCustomPortrait():
+ Window = CustomWindow
+
+ Portrait = PortraitList1.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row1") == RowCount1:
+ return
+
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (6)
+ if Portrait=="":
+ Portrait = "NOPORTMD"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList2.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (0)
+ Button.SetPicture (Portrait, "NOPORTMD")
+ return
+
+def CGSmallCustomPortrait():
+ Window = CustomWindow
+
+ Portrait = PortraitList2.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row2") == RowCount2:
+ return
+
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (6)
+ if Portrait=="":
+ Portrait = "NOPORTSM"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList1.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (1)
+ Button.SetPicture (Portrait, "NOPORTSM")
+ return
+
+def PortraitCustomPress():
+ global PortraitList1, PortraitList2
+ global RowCount1, RowCount2
+ global CustomWindow
+
+ CustomWindow = Window = GemRB.LoadWindow (18)
+ PortraitList1 = Window.GetControl (2)
+ RowCount1 = PortraitList1.GetPortraits (0)
+ PortraitList1.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, CGLargeCustomPortrait)
+ GemRB.SetVar ("Row1", RowCount1)
+ PortraitList1.SetVarAssoc ("Row1",RowCount1)
+
+ PortraitList2 = Window.GetControl (4)
+ RowCount2 = PortraitList2.GetPortraits (1)
+ PortraitList2.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, CGSmallCustomPortrait)
+ GemRB.SetVar ("Row2", RowCount2)
+ PortraitList2.SetVarAssoc ("Row2",RowCount2)
+
+ Button = Window.GetControl (6)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomDone)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Button = Window.GetControl (7)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomAbort)
+
+ Button = Window.GetControl (0)
+ PortraitName = PortraitsTable.GetRowName (Portrait)+"L"
+ Button.SetPicture (PortraitName, "NOPORTMD")
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Button = Window.GetControl (1)
+ PortraitName = PortraitsTable.GetRowName (Portrait)+"S"
+ Button.SetPicture (PortraitName, "NOPORTSM")
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Window.ShowModal (MODAL_SHADOW_NONE)
+ return
+
+def CGPortraitDonePress():
+ global CharGenWindow, PortraitWindow, PortraitButton, GenderButton, RaceButton
+ global CharGenState, Portrait
+
+ PortraitName = PortraitsTable.GetRowName (Portrait )
+ GemRB.SetToken ("SmallPortrait", PortraitName+"S")
+ GemRB.SetToken ("LargePortrait", PortraitName+"L")
+ PortraitButton.SetPicture(PortraitsTable.GetRowName (Portrait) + "L")
+ GenderButton.SetState (IE_GUI_BUTTON_DISABLED)
+ GenderButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ RaceButton.SetState (IE_GUI_BUTTON_ENABLED)
+ RaceButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CharGenState = 1
+ SetCharacterDescription()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ return
+
+def CGPortraitCancelPress():
+ global CharGenWindow, PortraitWindow
+
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Race Selection
+
+def RacePress():
+ global CharGenWindow, RaceWindow, RaceDoneButton, RaceTextArea
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ RaceWindow = GemRB.LoadWindow (8)
+ GemRB.SetVar ("Race", 0)
+
+ for i in range (2, 8):
+ RaceSelectButton = RaceWindow.GetControl (i)
+ RaceSelectButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ for i in range (2, 8):
+ RaceSelectButton = RaceWindow.GetControl (i)
+ RaceSelectButton.SetState (IE_GUI_BUTTON_ENABLED)
+ RaceSelectButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RaceSelectPress)
+ RaceSelectButton.SetText (CommonTables.Races.GetValue (i - 2, 0))
+ RaceSelectButton.SetVarAssoc ("Race", i - 1)
+
+ RaceTextArea = RaceWindow.GetControl (8)
+ RaceTextArea.SetText (17237)
+
+ RaceDoneButton = RaceWindow.GetControl (0)
+ RaceDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ RaceDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RaceDonePress)
+ RaceDoneButton.SetText (11973)
+ RaceDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ RaceCancelButton = RaceWindow.GetControl (10)
+ RaceCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ RaceCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RaceCancelPress)
+ RaceCancelButton.SetText (13727)
+ RaceCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ RaceWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def RaceSelectPress():
+ global RaceWindow, RaceDoneButton, RaceTextArea
+
+ Race = GemRB.GetVar ("Race") - 1
+ RaceTextArea.SetText (CommonTables.Races.GetValue (Race, 1) )
+ RaceDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def RaceDonePress():
+ global CharGenWindow, CharGenState, RaceWindow, RaceButton, ClassButton
+
+ if RaceWindow:
+ RaceWindow.Unload ()
+ RaceButton.SetState (IE_GUI_BUTTON_DISABLED)
+ RaceButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ ClassButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ClassButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CharGenState = 2
+
+ Race = GemRB.GetVar ("Race")-1
+ Race = CommonTables.Races.GetValue (Race, 3)
+ GemRB.SetPlayerStat (MyChar, IE_RACE, Race)
+ SetCharacterDescription()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def RaceCancelPress():
+ global CharGenWindow, RaceWindow
+
+ if RaceWindow:
+ RaceWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Class Selection
+
+def ClassPress():
+ global CharGenWindow, ClassWindow, ClassTextArea, ClassDoneButton
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ ClassWindow = GemRB.LoadWindow (2)
+ ClassCount = CommonTables.Classes.GetRowCount ()
+ RaceRow = CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (MyChar, IE_RACE) )
+ RaceName = CommonTables.Races.GetRowName (RaceRow)
+ GemRB.SetVar ("Class", 0)
+ GemRB.SetVar ("Class Kit", 0)
+ GemRB.SetVar ("MAGESCHOOL", 0)
+
+ for i in range (2, 10):
+ ClassSelectButton = ClassWindow.GetControl (i)
+ ClassSelectButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+
+ HasMulti = 0
+ j = 2
+ for i in range (ClassCount):
+ Allowed = CommonTables.Classes.GetValue (CommonTables.Classes.GetRowName (i), RaceName)
+ if CommonTables.Classes.GetValue (i, 4):
+ if Allowed != 0:
+ HasMulti = 1
+ else:
+ ClassSelectButton = ClassWindow.GetControl (j)
+ j = j + 1
+ if Allowed > 0:
+ ClassSelectButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ ClassSelectButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ClassSelectButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClassSelectPress)
+ ClassSelectButton.SetText (CommonTables.Classes.GetValue (i, 0) )
+ ClassSelectButton.SetVarAssoc ("Class", i + 1)
+
+ ClassMultiButton = ClassWindow.GetControl (10)
+ if HasMulti == 0:
+ ClassMultiButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ ClassMultiButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ClassMultiButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClassMultiPress)
+ ClassMultiButton.SetText (11993)
+
+ KitButton = ClassWindow.GetControl (11)
+ #only the mage class has schools
+ Allowed = CommonTables.Classes.GetValue ("MAGE", RaceName)
+ if Allowed:
+ KitButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ KitButton.SetState (IE_GUI_BUTTON_DISABLED)
+ KitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, KitPress)
+ KitButton.SetText (11994)
+
+ ClassTextArea = ClassWindow.GetControl (13)
+ ClassTextArea.SetText (17242)
+
+ ClassDoneButton = ClassWindow.GetControl (0)
+ ClassDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ClassDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClassDonePress)
+ ClassDoneButton.SetText (11973)
+ ClassDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ ClassCancelButton = ClassWindow.GetControl (14)
+ ClassCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ClassCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClassCancelPress)
+ ClassCancelButton.SetText (13727)
+ ClassCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ClassWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ClassSelectPress():
+ global ClassWindow, ClassTextArea, ClassDoneButton
+
+ Class = GemRB.GetVar ("Class") - 1
+ ClassTextArea.SetText (CommonTables.Classes.GetValue (Class, 1) )
+ ClassDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def ClassMultiPress():
+ global ClassWindow, ClassMultiWindow, ClassMultiTextArea, ClassMultiDoneButton
+
+ ClassWindow.SetVisible (WINDOW_INVISIBLE)
+ ClassMultiWindow = GemRB.LoadWindow (10)
+ ClassCount = CommonTables.Classes.GetRowCount ()
+ RaceRow = CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (MyChar, IE_RACE) )
+ RaceName = CommonTables.Races.GetRowName (RaceRow)
+
+ print "Multi racename:", RaceName
+ for i in range (2, 10):
+ ClassMultiSelectButton = ClassMultiWindow.GetControl (i)
+ ClassMultiSelectButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+
+ j = 2
+ for i in range (ClassCount):
+ ClassName = CommonTables.Classes.GetRowName (i)
+ if (CommonTables.Classes.GetValue (ClassName, "MULTI") > 0):
+ ClassMultiSelectButton = ClassMultiWindow.GetControl (j)
+ j = j + 1
+ if (CommonTables.Classes.GetValue (ClassName, RaceName) > 0):
+ ClassMultiSelectButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ ClassMultiSelectButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ClassMultiSelectButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClassMultiSelectPress)
+ ClassMultiSelectButton.SetText (CommonTables.Classes.GetValue (i, 0) )
+ ClassMultiSelectButton.SetVarAssoc ("Class", i + 1)
+
+ ClassMultiTextArea = ClassMultiWindow.GetControl (12)
+ ClassMultiTextArea.SetText (17244)
+
+ ClassMultiDoneButton = ClassMultiWindow.GetControl (0)
+ ClassMultiDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ClassMultiDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClassMultiDonePress)
+ ClassMultiDoneButton.SetText (11973)
+ ClassMultiDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ ClassMultiCancelButton = ClassMultiWindow.GetControl (14)
+ ClassMultiCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ClassMultiCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClassMultiCancelPress)
+ ClassMultiCancelButton.SetText (13727)
+ ClassMultiCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ClassMultiWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ClassMultiSelectPress():
+ global ClassMultiWindow, ClassMultiTextArea, ClassMultiDoneButton
+
+ Class = GemRB.GetVar ("Class") - 1
+ ClassMultiTextArea.SetText (CommonTables.Classes.GetValue (Class, 1) )
+ ClassMultiDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def ClassMultiDonePress():
+ global ClassMultiWindow
+
+ if ClassMultiWindow:
+ ClassMultiWindow.Unload ()
+ ClassDonePress()
+ return
+
+def ClassMultiCancelPress():
+ global ClassWindow, ClassMultiWindow
+
+ if ClassMultiWindow:
+ ClassMultiWindow.Unload ()
+ ClassWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def KitPress():
+ global ClassWindow, KitWindow, KitTextArea, KitDoneButton
+
+ ClassWindow.SetVisible (WINDOW_INVISIBLE)
+ KitWindow = GemRB.LoadWindow (12)
+
+ #only mage class (1) has schools. It is the sixth button
+ GemRB.SetVar ("Class", 6)
+ GemRB.SetVar ("Class Kit",0)
+ GemRB.SetVar ("MAGESCHOOL",0)
+
+ for i in range (8):
+ Button = KitWindow.GetControl (i+2)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetText (KitTable.GetValue (i+1, 0) )
+ Button.SetVarAssoc ("MAGESCHOOL", i+1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, KitSelectPress)
+
+ KitTextArea = KitWindow.GetControl (11)
+ KitTextArea.SetText (17245)
+
+ KitDoneButton = KitWindow.GetControl (0)
+ KitDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ KitDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, KitDonePress)
+ KitDoneButton.SetText (11973)
+ KitDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ KitCancelButton = KitWindow.GetControl (12)
+ KitCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ KitCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, KitCancelPress)
+ KitCancelButton.SetText (13727)
+ KitCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ KitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def KitSelectPress():
+ global KitWindow, KitTextArea
+
+ Kit = GemRB.GetVar ("MAGESCHOOL")
+ KitTextArea.SetText (KitTable.GetValue (Kit, 1))
+ KitDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def KitDonePress():
+ global KitWindow
+
+ if KitWindow:
+ KitWindow.Unload ()
+ ClassDonePress()
+ return
+
+def KitCancelPress():
+ global ClassWindow, KitWindow
+
+ if KitWindow:
+ KitWindow.Unload ()
+ ClassWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ClassDonePress():
+ global CharGenWindow, CharGenState, ClassWindow, ClassButton, AlignmentButton
+ global MyChar
+
+ if ClassWindow:
+ ClassWindow.Unload ()
+ ClassButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ClassButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ AlignmentButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AlignmentButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ ClassIndex = GemRB.GetVar ("Class")-1
+ Class = CommonTables.Classes.GetValue (ClassIndex, 5)
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, Class)
+
+ Kit = KitTable.GetValue (GemRB.GetVar ("MAGESCHOOL"), 3 )
+ if (Kit == -1 ):
+ Kit = 0x4000
+
+ GemRB.SetPlayerStat (MyChar, IE_KIT, Kit)
+
+ CharGenState = 3
+ SetCharacterDescription()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ClassCancelPress():
+ global CharGenWindow, ClassWindow
+
+ if ClassWindow:
+ ClassWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Alignment Selection
+
+def AlignmentPress():
+ global CharGenWindow, AlignmentWindow, AlignmentTextArea, AlignmentDoneButton
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ AlignmentWindow = GemRB.LoadWindow (3)
+ ClassAlignmentTable = GemRB.LoadTable ("alignmnt")
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ ClassName = CommonTables.Classes.GetRowName (ClassIndex)
+ GemRB.SetVar ("Alignment", 0)
+
+ for i in range (2, 11):
+ AlignmentSelectButton = AlignmentWindow.GetControl (i)
+ AlignmentSelectButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ for i in range (9):
+ AlignmentSelectButton = AlignmentWindow.GetControl (i + 2)
+ if ClassAlignmentTable.GetValue (ClassName, AlignmentTable.GetValue(i, 4)) == 0:
+ AlignmentSelectButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ AlignmentSelectButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AlignmentSelectButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AlignmentSelectPress)
+ AlignmentSelectButton.SetText (AlignmentTable.GetValue (i, 0) )
+ AlignmentSelectButton.SetVarAssoc ("Alignment", i + 1)
+
+ AlignmentTextArea = AlignmentWindow.GetControl (11)
+ AlignmentTextArea.SetText (9602)
+
+ AlignmentDoneButton = AlignmentWindow.GetControl (0)
+ AlignmentDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AlignmentDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AlignmentDonePress)
+ AlignmentDoneButton.SetText (11973)
+ AlignmentDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ AlignmentCancelButton = AlignmentWindow.GetControl (13)
+ AlignmentCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AlignmentCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AlignmentCancelPress)
+ AlignmentCancelButton.SetText (13727)
+ AlignmentCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ AlignmentWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def AlignmentSelectPress():
+ global AlignmentWindow, AlignmentTextArea, AlignmentDoneButton
+
+ Alignment = GemRB.GetVar ("Alignment") - 1
+ AlignmentTextArea.SetText (AlignmentTable.GetValue (Alignment, 1))
+ AlignmentDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def AlignmentDonePress():
+ global CharGenWindow, CharGenState, AlignmentWindow, AlignmentButton, AbilitiesButton
+ global MyChar
+
+ if AlignmentWindow:
+ AlignmentWindow.Unload ()
+ AlignmentButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AlignmentButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ AbilitiesButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AbilitiesButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ Alignment = AlignmentTable.GetValue (GemRB.GetVar ("Alignment")-1, 3)
+ GemRB.SetPlayerStat (MyChar, IE_ALIGNMENT, Alignment )
+
+ CharGenState = 4
+ SetCharacterDescription()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def AlignmentCancelPress():
+ global CharGenWindow, AlignmentWindow
+
+ if AlignmentWindow:
+ AlignmentWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Abilities Selection
+
+def AbilitiesPress():
+ global CharGenWindow, AbilitiesWindow
+ global AbilitiesTextArea, AbilitiesRecallButton, AbilitiesDoneButton
+ global AbilitiesRaceAddTable, AbilitiesRaceReqTable, AbilitiesClassReqTable
+ global HasStrExtra
+
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ AbilitiesWindow = GemRB.LoadWindow (4)
+ AbilitiesRaceAddTable = GemRB.LoadTable ("ABRACEAD")
+ AbilitiesRaceReqTable = GemRB.LoadTable ("ABRACERQ")
+ AbilitiesClassReqTable = GemRB.LoadTable ("ABCLASRQ")
+
+ PointsLeftLabel = AbilitiesWindow.GetControl (0x10000002)
+ PointsLeftLabel.SetUseRGB (1)
+
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ HasStrExtra = CommonTables.Classes.GetValue (Class, 3)=="SAVEWAR"
+
+ for i in range (6):
+ AbilitiesLabelButton = AbilitiesWindow.GetControl (30 + i)
+ AbilitiesLabelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AbilitiesLabelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesLabelPress)
+ AbilitiesLabelButton.SetVarAssoc ("AbilityIndex", i + 1)
+
+ AbilitiesPlusButton = AbilitiesWindow.GetControl (16 + i * 2)
+ AbilitiesPlusButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AbilitiesPlusButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesPlusPress)
+ AbilitiesPlusButton.SetVarAssoc ("AbilityIndex", i + 1)
+
+ AbilitiesMinusButton = AbilitiesWindow.GetControl (17 + i * 2)
+ AbilitiesMinusButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AbilitiesMinusButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesMinusPress)
+ AbilitiesMinusButton.SetVarAssoc ("AbilityIndex", i + 1)
+
+ AbilityLabel = AbilitiesWindow.GetControl (0x10000003 + i)
+ AbilityLabel.SetUseRGB (1)
+
+ AbilitiesStoreButton = AbilitiesWindow.GetControl (37)
+ AbilitiesStoreButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AbilitiesStoreButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesStorePress)
+ AbilitiesStoreButton.SetText (17373)
+
+ AbilitiesRecallButton = AbilitiesWindow.GetControl (38)
+ AbilitiesRecallButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AbilitiesRecallButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesRecallPress)
+ AbilitiesRecallButton.SetText (17374)
+
+ AbilitiesRerollButton = AbilitiesWindow.GetControl (2)
+ AbilitiesRerollButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AbilitiesRerollButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesRerollPress)
+ AbilitiesRerollButton.SetText (11982)
+
+ AbilitiesTextArea = AbilitiesWindow.GetControl (29)
+ AbilitiesTextArea.SetText (17247)
+
+ AbilitiesDoneButton = AbilitiesWindow.GetControl (0)
+ AbilitiesDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AbilitiesDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesDonePress)
+ AbilitiesDoneButton.SetText (11973)
+ AbilitiesDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ AbilitiesCancelButton = AbilitiesWindow.GetControl (36)
+ AbilitiesCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AbilitiesCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbilitiesCancelPress)
+ AbilitiesCancelButton.SetText (13727)
+ AbilitiesCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ AbilitiesRerollPress()
+
+ AbilitiesWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def AbilitiesCalcLimits(Index):
+ global AbilitiesRaceReqTable, AbilitiesRaceAddTable, AbilitiesClassReqTable
+ global AbilitiesMinimum, AbilitiesMaximum, AbilitiesModifier
+
+ RaceName = CommonTables.Races.GetRowName (GemRB.GetPlayerStat (MyChar, IE_RACE) - 1)
+ Race = AbilitiesRaceReqTable.GetRowIndex (RaceName)
+ AbilitiesMinimum = AbilitiesRaceReqTable.GetValue (Race, Index * 2)
+ AbilitiesMaximum = AbilitiesRaceReqTable.GetValue (Race, Index * 2 + 1)
+ AbilitiesModifier = AbilitiesRaceAddTable.GetValue (Race, Index)
+
+ Class = CommonTables.Classes.FindValue (5, GemRB.GetPlayerStat (MyChar, IE_CLASS) )
+ ClassName = CommonTables.Classes.GetRowName (Class)
+ ClassIndex = AbilitiesClassReqTable.GetRowIndex (ClassName)
+ Min = AbilitiesClassReqTable.GetValue (ClassIndex, Index)
+ if Min > 0 and AbilitiesMinimum < Min:
+ AbilitiesMinimum = Min
+
+ AbilitiesMinimum = AbilitiesMinimum + AbilitiesModifier
+ AbilitiesMaximum = AbilitiesMaximum + AbilitiesModifier
+ return
+
+def AbilitiesLabelPress():
+ global AbilitiesWindow, AbilitiesTextArea
+
+ AbilityIndex = GemRB.GetVar ("AbilityIndex") - 1
+ AbilitiesCalcLimits(AbilityIndex)
+ GemRB.SetToken ("MINIMUM", str(AbilitiesMinimum) )
+ GemRB.SetToken ("MAXIMUM", str(AbilitiesMaximum) )
+ AbilitiesTextArea.SetText (AbilitiesTable.GetValue (AbilityIndex, 1) )
+ return
+
+def AbilitiesPlusPress():
+ global AbilitiesWindow, AbilitiesTextArea
+ global AbilitiesMinimum, AbilitiesMaximum
+
+ Abidx = GemRB.GetVar ("AbilityIndex") - 1
+ AbilitiesCalcLimits(Abidx)
+ GemRB.SetToken ("MINIMUM", str(AbilitiesMinimum) )
+ GemRB.SetToken ("MAXIMUM", str(AbilitiesMaximum) )
+ AbilitiesTextArea.SetText (AbilitiesTable.GetValue (Abidx, 1) )
+ PointsLeft = GemRB.GetVar ("Ability0")
+ Ability = GemRB.GetVar ("Ability" + str(Abidx + 1) )
+ if PointsLeft > 0 and Ability < AbilitiesMaximum:
+ PointsLeft = PointsLeft - 1
+ GemRB.SetVar ("Ability0", PointsLeft)
+ PointsLeftLabel = AbilitiesWindow.GetControl (0x10000002)
+ PointsLeftLabel.SetText (str(PointsLeft) )
+ Ability = Ability + 1
+ GemRB.SetVar ("Ability" + str(Abidx + 1), Ability)
+ Label = AbilitiesWindow.GetControl (0x10000003 + Abidx)
+ StrExtra = GemRB.GetVar("StrExtra")
+ if Abidx==0 and Ability==18 and HasStrExtra:
+ Label.SetText("18/"+str(StrExtra) )
+ else:
+ Label.SetText(str(Ability) )
+
+ if PointsLeft == 0:
+ AbilitiesDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def AbilitiesMinusPress():
+ global AbilitiesWindow, AbilitiesTextArea
+ global AbilitiesMinimum, AbilitiesMaximum
+
+ Abidx = GemRB.GetVar ("AbilityIndex") - 1
+ AbilitiesCalcLimits(Abidx)
+ GemRB.SetToken ("MINIMUM", str(AbilitiesMinimum) )
+ GemRB.SetToken ("MAXIMUM", str(AbilitiesMaximum) )
+ AbilitiesTextArea.SetText (AbilitiesTable.GetValue (Abidx, 1) )
+ PointsLeft = GemRB.GetVar ("Ability0")
+ Ability = GemRB.GetVar ("Ability" + str(Abidx + 1) )
+ if Ability > AbilitiesMinimum:
+ Ability = Ability - 1
+ GemRB.SetVar ("Ability" + str(Abidx + 1), Ability)
+ Label = AbilitiesWindow.GetControl (0x10000003 + Abidx)
+ StrExtra = GemRB.GetVar("StrExtra")
+ if Abidx==0 and Ability==18 and HasStrExtra:
+ Label.SetText("18/"+str(StrExtra) )
+ else:
+ Label.SetText(str(Ability) )
+
+ PointsLeft = PointsLeft + 1
+ GemRB.SetVar ("Ability0", PointsLeft)
+ PointsLeftLabel = AbilitiesWindow.GetControl (0x10000002)
+ PointsLeftLabel.SetText (str(PointsLeft) )
+ AbilitiesDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def AbilitiesStorePress():
+ global AbilitiesWindow, AbilitiesRecallButton
+
+ GemRB.SetVar("StoredStrExtra", GemRB.GetVar ("StrExtra") )
+ for i in range (7):
+ GemRB.SetVar ("Stored" + str(i), GemRB.GetVar ("Ability" + str(i)))
+ AbilitiesRecallButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def AbilitiesRecallPress():
+ global AbilitiesWindow
+
+ AbilitiesWindow.Invalidate ()
+ e=GemRB.GetVar("StoredStrExtra")
+ GemRB.SetVar("StrExtra",e)
+ for i in range (7):
+ v = GemRB.GetVar ("Stored" + str(i))
+ GemRB.SetVar ("Ability" + str(i), v)
+ Label = AbilitiesWindow.GetControl (0x10000002 + i)
+ if i==0 and v==18 and HasStrExtra==1:
+ Label.SetText("18/"+str(e) )
+ else:
+ Label.SetText(str(v) )
+
+ PointsLeft = GemRB.GetVar("Ability0")
+ if PointsLeft == 0:
+ AbilitiesDoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ AbilitiesDoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ return
+
+def AbilitiesRerollPress():
+ global AbilitiesWindow, AbilitiesMinimum, AbilitiesMaximum, AbilitiesModifier
+
+ AbilitiesWindow.Invalidate ()
+ GemRB.SetVar ("Ability0", 0)
+ PointsLeftLabel = AbilitiesWindow.GetControl (0x10000002)
+ PointsLeftLabel.SetText ("0")
+ Dices = 3
+ Sides = 5
+
+ #roll strextra even when the current stat is not 18
+ if HasStrExtra:
+ e = GemRB.Roll (1,100,0)
+ else:
+ e = 0
+ GemRB.SetVar("StrExtra", e)
+ for i in range (6):
+ AbilitiesCalcLimits(i)
+ Value = GemRB.Roll (Dices, Sides, AbilitiesModifier+3)
+ if Value < AbilitiesMinimum:
+ Value = AbilitiesMinimum
+ if Value > AbilitiesMaximum:
+ Value = AbilitiesMaximum
+ GemRB.SetVar ("Ability" + str(i + 1), Value)
+ Label = AbilitiesWindow.GetControl (0x10000003 + i)
+
+ if i==0 and HasStrExtra and Value==18:
+ Label.SetText("18/"+str(e) )
+ else:
+ Label.SetText(str(Value) )
+
+ AbilitiesDoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def AbilitiesDonePress():
+ global CharGenWindow, CharGenState, AbilitiesWindow, AbilitiesButton, SkillsButton, SkillsState
+
+ if AbilitiesWindow:
+ AbilitiesWindow.Unload ()
+ AbilitiesButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AbilitiesButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ SkillsButton.SetState (IE_GUI_BUTTON_ENABLED)
+ SkillsButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ Str = GemRB.GetVar ("Ability1")
+ GemRB.SetPlayerStat (MyChar, IE_STR, Str)
+ if Str == 18:
+ GemRB.SetPlayerStat (MyChar, IE_STREXTRA, GemRB.GetVar ("StrExtra"))
+ else:
+ GemRB.SetPlayerStat (MyChar, IE_STREXTRA, 0)
+
+ GemRB.SetPlayerStat (MyChar, IE_DEX, GemRB.GetVar ("Ability2"))
+ GemRB.SetPlayerStat (MyChar, IE_CON, GemRB.GetVar ("Ability3"))
+ GemRB.SetPlayerStat (MyChar, IE_INT, GemRB.GetVar ("Ability4"))
+ GemRB.SetPlayerStat (MyChar, IE_WIS, GemRB.GetVar ("Ability5"))
+ GemRB.SetPlayerStat (MyChar, IE_CHR, GemRB.GetVar ("Ability6"))
+
+ CharGenState = 5
+ SkillsState = 0
+ SetCharacterDescription()
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def AbilitiesCancelPress():
+ global CharGenWindow, AbilitiesWindow
+
+ if AbilitiesWindow:
+ AbilitiesWindow.Unload ()
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Skills Selection
+
+def SkillsPress():
+ global CharGenWindow, AppearanceButton
+ global SkillsState, SkillsButton, CharGenState, ClassFlag
+
+ Level = 1
+ SpellLevel = 1
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ IsRanger = CommonTables.ClassSkills.GetValue (Class, 0)
+ IsArcane = CommonTables.ClassSkills.GetValue (Class, 1)
+ IsMage = CommonTables.ClassSkills.GetValue (Class, 2)
+ IsBard = CommonTables.ClassSkills.GetValue (Class, 4)
+ IsThief = CommonTables.ClassSkills.GetValue (Class, 5)
+
+ if SkillsState == 0:
+ GemRB.SetVar ("HatedRace", 0)
+ RaceName = CommonTables.Races.GetRowName (GemRB.GetPlayerStat (MyChar, IE_RACE) - 1)
+ if IsThief!="*":
+ SkillsSelect()
+ elif IsRanger!="*":
+ SkillRaceTable = GemRB.LoadTable ("SKILLRAC")
+ SkillDexterityTable = GemRB.LoadTable ("SKILLDEX")
+ Dexterity = str(GemRB.GetPlayerStat (MyChar, IE_DEX) )
+ Skill = SkillRaceTable.GetValue (RaceName, "STEALTH")
+ Skill = Skill + SkillDexterityTable.GetValue(Dexterity, "STEALTH")
+ Skill = Skill + GemRB.LoadTable("SKILLRNG").GetValue(str(Level), "STEALTH")
+ GemRB.SetPlayerStat (MyChar, IE_STEALTH, Skill)
+ RacialEnemySelect()
+ elif IsBard!="*":
+ SkillRaceTable = GemRB.LoadTable ("SKILLRAC")
+ SkillDexterityTable = GemRB.LoadTable ("SKILLDEX")
+ Dexterity = str(GemRB.GetPlayerStat (MyChar, IE_DEX) )
+ Skill = SkillRaceTable.GetValue (RaceName, "PICK_POCKETS")
+ Skill = Skill + SkillDexterityTable.GetValue(Dexterity, "PICK_POCKETS")
+ Skill = Skill + GemRB.LoadTable(IsBard).GetValue(str(Level), "PICK_POCKETS")
+ GemRB.SetPlayerStat (MyChar, IE_PICKPOCKET, Skill)
+ SkillsState = 1
+ else:
+ SkillsState = 1
+
+ if SkillsState == 1:
+ ProficienciesSelect()
+
+ if SkillsState == 2:
+ if IsMage!="*":
+ MageSpellsSelect(IsMage, Level, SpellLevel)
+ else:
+ SkillsState = 3
+
+ if SkillsState == 3:
+ if IsMage!="*":
+ MageSpellsMemorize(IsMage, Level, SpellLevel)
+ else:
+ SkillsState = 4
+
+ if SkillsState == 4:
+ if IsArcane=="MXSPLPRS" or IsArcane =="MXSPLPAL":
+ ClassFlag = 0x4000
+ PriestSpellsMemorize(IsArcane, Level, SpellLevel)
+ elif IsArcane=="MXSPLDRU" or IsArcane =="MXSPLRAN":
+ #no separate spell progression
+ if IsArcane == "MXSPLDRU":
+ IsArcane = "MXSPLPRS"
+ ClassFlag = 0x8000
+ PriestSpellsMemorize(IsArcane, Level, SpellLevel)
+ else:
+ SkillsState = 5
+
+ if SkillsState == 5:
+ SkillsButton.SetState (IE_GUI_BUTTON_DISABLED)
+ SkillsButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ AppearanceButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AppearanceButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ Race = GemRB.GetVar ("HatedRace")
+ GemRB.SetPlayerStat (MyChar, IE_HATEDRACE, Race)
+
+ ProfCount = ProficienciesTable.GetRowCount ()
+ for i in range(ProfCount):
+ StatID = ProficienciesTable.GetValue (i, 0)
+ Value = GemRB.GetVar ("Proficiency"+str(i) )
+ GemRB.SetPlayerStat (MyChar, StatID, Value )
+
+ CharGenState = 6
+ SetCharacterDescription()
+ return
+
+def SkillsSelect():
+ global CharGenWindow, SkillsWindow, SkillsTextArea, SkillsDoneButton, SkillsPointsLeft
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ SkillsWindow = GemRB.LoadWindow (6)
+ RaceName = CommonTables.Races.GetRowName (GemRB.GetPlayerStat (MyChar, IE_RACE) - 1)
+ Dexterity = str(GetPlayerStat (MyChar, IE_DEX) )
+ SkillRaceTable = GemRB.LoadTable ("SKILLRAC")
+ SkillDexterityTable = GemRB.LoadTable ("SKILLDEX")
+
+ Levels = [GemRB.GetPlayerStat (MyChar, IE_LEVEL), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL2), \
+ GemRB.GetPlayerStat (MyChar, IE_LEVEL3)]
+
+ LUSkillsSelection.SetupSkillsWindow (MyChar, \
+ LUSkillsSelection.LUSKILLS_TYPE_CHARGEN, SkillsWindow, RedrawSkills, [0,0,0], Levels, 0, False)
+
+ SkillsPointsLeft = GemRB.GetVar ("SkillPointsLeft")
+ if SkillsPointsLeft<=0:
+ SkillsDonePress()
+ return
+
+ SkillsDoneButton = SkillsWindow.GetControl (0)
+ SkillsDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ SkillsDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SkillsDonePress)
+ SkillsDoneButton.SetText (11973)
+ SkillsDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ SkillsCancelButton = SkillsWindow.GetControl (25)
+ SkillsCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ SkillsCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SkillsCancelPress)
+ SkillsCancelButton.SetText (13727)
+ SkillsCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+
+ RedrawSkills()
+ SkillsWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def RedrawSkills():
+ PointsLeft = GemRB.GetVar ("SkillPointsLeft")
+ if PointsLeft == 0:
+ SkillsDoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ SkillsDoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ return
+
+def SkillsDonePress():
+ global CharGenWindow, SkillsWindow, SkillsState
+
+ if SkillsWindow:
+ SkillsWindow.Unload ()
+ SkillsState = 1
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ SkillsPress()
+ return
+
+def SkillsCancelPress():
+ global CharGenWindow, SkillsWindow, SkillsState
+
+ if SkillsWindow:
+ SkillsWindow.Unload ()
+ SkillsState = 0
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Racial Enemy Selection
+
+def RacialEnemySelect():
+ global CharGenWindow, RacialEnemyWindow, RacialEnemyTextArea, RacialEnemyDoneButton
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ RacialEnemyWindow = GemRB.LoadWindow (15)
+ RacialEnemyCount = RacialEnemyTable.GetRowCount ()
+
+ for i in range (2, 8):
+ RacialEnemySelectButton = RacialEnemyWindow.GetControl (i)
+ RacialEnemySelectButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ for i in range (2, 8):
+ RacialEnemySelectButton = RacialEnemyWindow.GetControl (i)
+ RacialEnemySelectButton.SetState (IE_GUI_BUTTON_ENABLED)
+ RacialEnemySelectButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RacialEnemySelectPress)
+ RacialEnemySelectButton.SetVarAssoc ("RacialEnemy", i - 1)
+
+ GemRB.SetVar ("RacialEnemyIndex", 0)
+ GemRB.SetVar ("HatedRace", 0)
+ RacialEnemyScrollBar = RacialEnemyWindow.GetControl (1)
+ RacialEnemyScrollBar.SetVarAssoc ("RacialEnemyIndex", RacialEnemyCount - 5)
+ RacialEnemyScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, DisplayRacialEnemies)
+
+ RacialEnemyTextArea = RacialEnemyWindow.GetControl (8)
+ RacialEnemyTextArea.SetText (17256)
+
+ RacialEnemyDoneButton = RacialEnemyWindow.GetControl (11)
+ RacialEnemyDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ RacialEnemyDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RacialEnemyDonePress)
+ RacialEnemyDoneButton.SetText (11973)
+ RacialEnemyDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ RacialEnemyCancelButton = RacialEnemyWindow.GetControl (10)
+ RacialEnemyCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ RacialEnemyCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RacialEnemyCancelPress)
+ RacialEnemyCancelButton.SetText (13727)
+ RacialEnemyCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DisplayRacialEnemies()
+ RacialEnemyWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DisplayRacialEnemies():
+ global RacialEnemyWindow
+
+ RacialEnemyIndex = GemRB.GetVar ("RacialEnemyIndex")
+ for i in range (2, 8):
+ RacialEnemySelectButton = RacialEnemyWindow.GetControl (i)
+ RacialEnemySelectButton.SetText (RacialEnemyTable.GetValue (RacialEnemyIndex + i - 2, 0))
+ return
+
+def RacialEnemySelectPress():
+ global RacialEnemyWindow, RacialEnemyDoneButton, RacialEnemyTextArea
+
+ RacialEnemy = GemRB.GetVar ("RacialEnemyIndex") + GemRB.GetVar ("RacialEnemy") - 1
+ RacialEnemyTextArea.SetText (RacialEnemyTable.GetValue (RacialEnemy, 2) )
+ GemRB.SetVar ("HatedRace", RacialEnemyTable.GetValue (RacialEnemy, 1) )
+ RacialEnemyDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def RacialEnemyDonePress():
+ global CharGenWindow, RacialEnemyWindow, SkillsState
+
+ if RacialEnemyWindow:
+ RacialEnemyWindow.Unload ()
+
+ SkillsState = 1
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ SkillsPress()
+ return
+
+def RacialEnemyCancelPress():
+ global CharGenWindow, RacialEnemyWindow, SkillsState
+
+ if RacialEnemyWindow:
+ RacialEnemyWindow.Unload ()
+ SkillsState = 0
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+
+# Weapon Proficiencies Selection
+
+def ProficienciesSelect():
+ global CharGenWindow, ProficienciesWindow, ProficienciesTextArea
+ global ProficienciesPointsLeft, ProficienciesDoneButton, ProfsMaxTable
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ ProficienciesWindow = GemRB.LoadWindow (9)
+ ProfsTable = GemRB.LoadTable ("profs")
+ ProfsMaxTable = GemRB.LoadTable ("profsmax")
+ ClassWeaponsTable = GemRB.LoadTable ("clasweap")
+
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ ClassName = CommonTables.Classes.GetRowName (ClassIndex)
+ Class = ProfsTable.GetRowIndex (ClassName)
+ ProficienciesPointsLeft = ProfsTable.GetValue (Class, 0)
+ PointsLeftLabel = ProficienciesWindow.GetControl (0x10000009)
+ PointsLeftLabel.SetUseRGB (1)
+ PointsLeftLabel.SetText (str(ProficienciesPointsLeft))
+
+ for i in range (8):
+ ProficienciesLabel = ProficienciesWindow.GetControl (69 + i)
+ ProficienciesLabel.SetState (IE_GUI_BUTTON_ENABLED)
+ ProficienciesLabel.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProficienciesLabelPress)
+ ProficienciesLabel.SetVarAssoc ("ProficienciesIndex", i + 1)
+
+ for j in range (5):
+ ProficienciesMark = ProficienciesWindow.GetControl (27 + i * 5 + j)
+ ProficienciesMark.SetSprites("GUIPFC", 0, 0, 0, 0, 0)
+ ProficienciesMark.SetState (IE_GUI_BUTTON_DISABLED)
+ ProficienciesMark.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+
+ Allowed = ClassWeaponsTable.GetValue (ClassName, ProficienciesTable.GetRowName (i))
+
+ ProficienciesPlusButton = ProficienciesWindow.GetControl (11 + i * 2)
+ if Allowed == 0:
+ ProficienciesPlusButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ProficienciesPlusButton.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ else:
+ ProficienciesPlusButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ProficienciesPlusButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProficienciesPlusPress)
+ ProficienciesPlusButton.SetVarAssoc ("ProficienciesIndex", i + 1)
+
+ ProficienciesMinusButton = ProficienciesWindow.GetControl (12 + i * 2)
+ if Allowed == 0:
+ ProficienciesMinusButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ProficienciesMinusButton.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ else:
+ ProficienciesMinusButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ProficienciesMinusButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProficienciesMinusPress)
+ ProficienciesMinusButton.SetVarAssoc ("ProficienciesIndex", i + 1)
+
+ for i in range (7):
+ ProficienciesLabel = ProficienciesWindow.GetControl (85 + i)
+ ProficienciesLabel.SetState (IE_GUI_BUTTON_ENABLED)
+ ProficienciesLabel.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProficienciesLabelPress)
+ ProficienciesLabel.SetVarAssoc ("ProficienciesIndex", i + 9)
+
+ for j in range (5):
+ ProficienciesMark = ProficienciesWindow.GetControl (92 + i * 5 + j)
+ ProficienciesMark.SetSprites("GUIPFC", 0, 0, 0, 0, 0)
+ ProficienciesMark.SetState (IE_GUI_BUTTON_DISABLED)
+ ProficienciesMark.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+
+ Allowed = ClassWeaponsTable.GetValue (ClassName, ProficienciesTable.GetRowName (i + 8))
+
+ ProficienciesPlusButton = ProficienciesWindow.GetControl (127 + i * 2)
+ if Allowed == 0:
+ ProficienciesPlusButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ProficienciesPlusButton.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ else:
+ ProficienciesPlusButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ProficienciesPlusButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProficienciesPlusPress)
+ ProficienciesPlusButton.SetVarAssoc ("ProficienciesIndex", i + 9)
+
+ ProficienciesMinusButton = ProficienciesWindow.GetControl (128 + i * 2)
+ if Allowed == 0:
+ ProficienciesMinusButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ProficienciesMinusButton.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ else:
+ ProficienciesMinusButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ProficienciesMinusButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProficienciesMinusPress)
+ ProficienciesMinusButton.SetVarAssoc ("ProficienciesIndex", i + 9)
+
+ for i in range (15):
+ GemRB.SetVar ("Proficiency" + str(i), 0)
+
+ GemRB.SetToken ("number", str(ProficienciesPointsLeft) )
+ ProficienciesTextArea = ProficienciesWindow.GetControl (68)
+ ProficienciesTextArea.SetText (9588)
+
+ ProficienciesDoneButton = ProficienciesWindow.GetControl (0)
+ ProficienciesDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ProficienciesDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProficienciesDonePress)
+ ProficienciesDoneButton.SetText (11973)
+ ProficienciesDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ ProficienciesCancelButton = ProficienciesWindow.GetControl (77)
+ ProficienciesCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ProficienciesCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProficienciesCancelPress)
+ ProficienciesCancelButton.SetText (13727)
+ ProficienciesCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ProficienciesWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ProficienciesLabelPress():
+ global ProficienciesWindow, ProficienciesTextArea
+
+ ProficienciesIndex = GemRB.GetVar ("ProficienciesIndex") - 1
+ ProficienciesTextArea.SetText (ProficienciesTable.GetValue (ProficienciesIndex, 2) )
+ return
+
+def ProficienciesPlusPress():
+ global ProficienciesWindow, ProficienciesTextArea
+ global ProficienciesPointsLeft, ProfsMaxTable
+
+ ProficienciesIndex = GemRB.GetVar ("ProficienciesIndex") - 1
+ ProficienciesValue = GemRB.GetVar ("Proficiency" + str(ProficienciesIndex) )
+ Class = GemRB.GetPlayerStat (MyChar, IE_CLASS)
+ ClassIndex = CommonTables.Classes.FindValue (5, Class)
+ ClassName = CommonTables.Classes.GetRowName (ClassIndex)
+ Class = ProfsMaxTable.GetRowIndex (ClassName)
+ if ProficienciesPointsLeft > 0 and ProficienciesValue < ProfsMaxTable.GetValue (Class, 0):
+ ProficienciesPointsLeft = ProficienciesPointsLeft - 1
+ PointsLeftLabel = ProficienciesWindow.GetControl (0x10000009)
+ PointsLeftLabel.SetText (str(ProficienciesPointsLeft))
+ if ProficienciesPointsLeft == 0:
+ ProficienciesDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ ProficienciesValue = ProficienciesValue + 1
+ GemRB.SetVar ("Proficiency" + str(ProficienciesIndex), ProficienciesValue)
+ if ProficienciesIndex < 8:
+ ControlID = 26 + ProficienciesIndex * 5 + ProficienciesValue
+ else:
+ ControlID = 51 + ProficienciesIndex * 5 + ProficienciesValue
+ ProficienciesMark = ProficienciesWindow.GetControl (ControlID)
+ ProficienciesMark.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+
+ ProficienciesTextArea.SetText (ProficienciesTable.GetValue (ProficienciesIndex, 2) )
+ return
+
+def ProficienciesMinusPress():
+ global ProficienciesWindow, ProficienciesTextArea, ProficienciesPointsLeft
+
+ ProficienciesIndex = GemRB.GetVar ("ProficienciesIndex") - 1
+ ProficienciesValue = GemRB.GetVar ("Proficiency" + str(ProficienciesIndex) )
+ if ProficienciesValue > 0:
+ ProficienciesValue = ProficienciesValue - 1
+ GemRB.SetVar ("Proficiency" + str(ProficienciesIndex), ProficienciesValue)
+ if ProficienciesIndex < 8:
+ ControlID = 27 + ProficienciesIndex * 5 + ProficienciesValue
+ else:
+ ControlID = 52 + ProficienciesIndex * 5 + ProficienciesValue
+ ProficienciesMark = ProficienciesWindow.GetControl (ControlID)
+ ProficienciesMark.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+
+ ProficienciesPointsLeft = ProficienciesPointsLeft + 1
+ PointsLeftLabel = ProficienciesWindow.GetControl (0x10000009)
+ PointsLeftLabel.SetText (str(ProficienciesPointsLeft))
+ ProficienciesDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ProficienciesTextArea.SetText (ProficienciesTable.GetValue (ProficienciesIndex, 2) )
+ return
+
+def ProficienciesDonePress():
+ global CharGenWindow, ProficienciesWindow, SkillsState
+
+ if ProficienciesWindow:
+ ProficienciesWindow.Unload ()
+ SkillsState = 2
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ SkillsPress()
+ return
+
+def ProficienciesCancelPress():
+ global CharGenWindow, ProficienciesWindow, SkillsState
+
+ if ProficienciesWindow:
+ ProficienciesWindow.Unload ()
+ SkillsState = 0
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Spells Selection
+
+def MageSpellsSelect(SpellTable, Level, SpellLevel):
+ global CharGenWindow, MageSpellsWindow, MageSpellsTextArea, MageSpellsDoneButton, MageSpellsSelectPointsLeft, Learnable
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ MageSpellsWindow = GemRB.LoadWindow (7)
+ #kit (school), alignment, level
+ k = GemRB.GetPlayerStat (MyChar, IE_KIT)
+ t = GemRB.GetPlayerStat (MyChar, IE_ALIGNMENT)
+ Learnable = GUICommon.GetLearnableMageSpells(k, t, SpellLevel)
+ GemRB.SetVar ("MageSpellBook", 0)
+ GemRB.SetVar ("SpellMask", 0)
+
+ if len(Learnable)<1:
+ MageSpellsDonePress()
+ return
+
+ if k>0:
+ MageSpellsSelectPointsLeft = 3
+ else:
+ MageSpellsSelectPointsLeft = 2
+ PointsLeftLabel = MageSpellsWindow.GetControl (0x1000001b)
+ PointsLeftLabel.SetUseRGB (1)
+ PointsLeftLabel.SetText (str(MageSpellsSelectPointsLeft))
+
+ for i in range (24):
+ SpellButton = MageSpellsWindow.GetControl (i + 2)
+ SpellButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ if i < len(Learnable):
+ Spell = GemRB.GetSpell (Learnable[i])
+ SpellButton.SetSpellIcon(Learnable[i], 1)
+ SpellButton.SetState (IE_GUI_BUTTON_ENABLED)
+ SpellButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageSpellsSelectPress)
+ SpellButton.SetVarAssoc ("SpellMask", 1 << i)
+ SpellButton.SetTooltip(Spell["SpellName"])
+ else:
+ SpellButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ GemRB.SetToken ("number", str(MageSpellsSelectPointsLeft))
+ MageSpellsTextArea = MageSpellsWindow.GetControl (27)
+ MageSpellsTextArea.SetText (17250)
+
+ MageSpellsDoneButton = MageSpellsWindow.GetControl (0)
+ MageSpellsDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ MageSpellsDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageSpellsDonePress)
+ MageSpellsDoneButton.SetText (11973)
+ MageSpellsDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ MageSpellsCancelButton = MageSpellsWindow.GetControl (29)
+ MageSpellsCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ MageSpellsCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageSpellsCancelPress)
+ MageSpellsCancelButton.SetText (13727)
+ MageSpellsCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ MageSpellsWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MageSpellsSelectPress():
+ global MageSpellsWindow, MageSpellsTextArea, MageSpellsDoneButton, MageSpellsSelectPointsLeft, Learnable
+
+ MageSpellBook = GemRB.GetVar ("MageSpellBook")
+ SpellMask = GemRB.GetVar ("SpellMask")
+
+ #getting the bit index
+ Spell = abs(MageSpellBook - SpellMask)
+ i = -1
+ while (Spell > 0):
+ i = i + 1
+ Spell = Spell >> 1
+
+ Spell = GemRB.GetSpell (Learnable[i])
+ MageSpellsTextArea.SetText (Spell["SpellDesc"])
+
+ if SpellMask < MageSpellBook:
+ MageSpellsSelectPointsLeft = MageSpellsSelectPointsLeft + 1
+ else:
+ if MageSpellsSelectPointsLeft==0:
+ SpellMask = MageSpellBook
+ GemRB.SetVar ("SpellMask", SpellMask)
+ else:
+ MageSpellsSelectPointsLeft = MageSpellsSelectPointsLeft - 1
+
+ if MageSpellsSelectPointsLeft == 0:
+ MageSpellsDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ MageSpellsDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ for i in range (len(Learnable)):
+ SpellButton = MageSpellsWindow.GetControl (i + 2)
+ if ((1 << i) & SpellMask) == 0:
+ SpellButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ PointsLeftLabel = MageSpellsWindow.GetControl (0x1000001b)
+ PointsLeftLabel.SetText (str(MageSpellsSelectPointsLeft))
+ GemRB.SetVar ("MageSpellBook", SpellMask)
+ return
+
+def MageSpellsDonePress():
+ global CharGenWindow, MageSpellsWindow, SkillsState
+
+ if MageSpellsWindow:
+ MageSpellsWindow.Unload ()
+ SkillsState = 3
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ SkillsPress()
+ return
+
+def MageSpellsCancelPress():
+ global CharGenWindow, MageSpellsWindow, SkillsState
+
+ if MageSpellsWindow:
+ MageSpellsWindow.Unload ()
+ SkillsState = 0
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+
+# Mage Spells Memorize
+
+def MageSpellsMemorize(SpellTable, Level, SpellLevel):
+ global CharGenWindow, MageMemorizeWindow, MageMemorizeTextArea, MageMemorizeDoneButton, MageMemorizePointsLeft
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ MageMemorizeWindow = GemRB.LoadWindow (16)
+ MaxSpellsMageTable = GemRB.LoadTable (SpellTable)
+ MageSpellBook = GemRB.GetVar ("MageSpellBook")
+ GemRB.SetVar ("MageMemorized", 0)
+ GemRB.SetVar ("SpellMask", 0)
+
+ MageMemorizePointsLeft = MaxSpellsMageTable.GetValue (str(Level), str(SpellLevel) )
+ if MageMemorizePointsLeft<1 or len(Learnable)<1:
+ MageMemorizeDonePress()
+ return
+
+ PointsLeftLabel = MageMemorizeWindow.GetControl (0x1000001b)
+ PointsLeftLabel.SetUseRGB (1)
+ PointsLeftLabel.SetText (str(MageMemorizePointsLeft))
+
+ j = 0
+ for i in range (12):
+ SpellButton = MageMemorizeWindow.GetControl (i + 2)
+ SpellButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ while (j < len(Learnable)) and (((1 << j) & MageSpellBook) == 0):
+ j = j + 1
+ if j < len(Learnable):
+ Spell = GemRB.GetSpell (Learnable[j])
+ SpellButton.SetTooltip(Spell["SpellName"])
+ SpellButton.SetSpellIcon(Learnable[j], 1)
+ SpellButton.SetState (IE_GUI_BUTTON_ENABLED)
+ SpellButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageMemorizeSelectPress)
+ SpellButton.SetVarAssoc ("SpellMask", 1 << j)
+ j = j + 1
+ else:
+ SpellButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ GemRB.SetToken ("number", str(MageMemorizePointsLeft))
+ MageMemorizeTextArea = MageMemorizeWindow.GetControl (27)
+ MageMemorizeTextArea.SetText (17253)
+
+ MageMemorizeDoneButton = MageMemorizeWindow.GetControl (0)
+ MageMemorizeDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ MageMemorizeDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageMemorizeDonePress)
+ MageMemorizeDoneButton.SetText (11973)
+ MageMemorizeDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ MageMemorizeCancelButton = MageMemorizeWindow.GetControl (29)
+ MageMemorizeCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ MageMemorizeCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageMemorizeCancelPress)
+ MageMemorizeCancelButton.SetText (13727)
+ MageMemorizeCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ MageMemorizeWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MageMemorizeSelectPress():
+ global MageMemorizeWindow, MageMemorizeTextArea, MageMemorizeDoneButton, MageMemorizePointsLeft, Learnable
+
+ MageSpellBook = GemRB.GetVar ("MageSpellBook")
+ MageMemorized = GemRB.GetVar ("MageMemorized")
+ SpellMask = GemRB.GetVar ("SpellMask")
+
+ Spell = abs(MageMemorized - SpellMask)
+ i = -1
+ while (Spell > 0):
+ i = i + 1
+ Spell = Spell >> 1
+
+ Spell = GemRB.GetSpell (Learnable[i])
+ MageMemorizeTextArea.SetText (Spell["SpellDesc"])
+
+ if SpellMask < MageMemorized:
+ MageMemorizePointsLeft = MageMemorizePointsLeft + 1
+ j = 0
+ for i in range (12):
+ SpellButton = MageMemorizeWindow.GetControl (i + 2)
+ while (j < len(Learnable) ) and (((1 << j) & MageSpellBook) == 0):
+ j = j + 1
+ if j < len(Learnable):
+ SpellButton.SetState (IE_GUI_BUTTON_ENABLED)
+ j = j + 1
+ MageMemorizeDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ MageMemorizePointsLeft = MageMemorizePointsLeft - 1
+ if MageMemorizePointsLeft == 0:
+ j = 0
+ for i in range (12):
+ SpellButton = MageMemorizeWindow.GetControl (i + 2)
+ while (j < len(Learnable) ) and (((1 << j) & MageSpellBook) == 0):
+ j = j + 1
+ if j < len(Learnable):
+ if ((1 << j) & SpellMask) == 0:
+ SpellButton.SetState (IE_GUI_BUTTON_DISABLED)
+ j = j + 1
+ MageMemorizeDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ PointsLeftLabel = MageMemorizeWindow.GetControl (0x1000001b)
+ PointsLeftLabel.SetText (str(MageMemorizePointsLeft))
+ GemRB.SetVar ("MageMemorized", SpellMask)
+ return
+
+def MageMemorizeDonePress():
+ global CharGenWindow, MageMemorizeWindow, SkillsState
+
+ if MageMemorizeWindow:
+ MageMemorizeWindow.Unload ()
+ SkillsState = 4
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ SkillsPress()
+ return
+
+def MageMemorizeCancelPress():
+ global CharGenWindow, MageMemorizeWindow, SkillsState
+
+ if MageMemorizeWindow:
+ MageMemorizeWindow.Unload ()
+ SkillsState = 0
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Priest Spells Memorize
+
+def PriestSpellsMemorize(SpellTable, Level, SpellLevel):
+ global CharGenWindow, PriestMemorizeWindow, Learnable, ClassFlag
+ global PriestMemorizeTextArea, PriestMemorizeDoneButton, PriestMemorizePointsLeft
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ PriestMemorizeWindow = GemRB.LoadWindow (17)
+ t = AlignmentTable.GetValue ( GemRB.GetVar ("Alignment")-1, 3)
+ Learnable = GUICommon.GetLearnablePriestSpells( ClassFlag, t, SpellLevel)
+
+ MaxSpellsPriestTable = GemRB.LoadTable (SpellTable)
+ GemRB.SetVar ("PriestMemorized", 0)
+ GemRB.SetVar ("SpellMask", 0)
+
+ PriestMemorizePointsLeft = MaxSpellsPriestTable.GetValue (str(Level), str(SpellLevel) )
+ if PriestMemorizePointsLeft<1 or len(Learnable)<1:
+ PriestMemorizeDonePress()
+ return
+
+ PointsLeftLabel = PriestMemorizeWindow.GetControl (0x1000001b)
+ PointsLeftLabel.SetUseRGB (1)
+ PointsLeftLabel.SetText (str(PriestMemorizePointsLeft))
+
+ for i in range (12):
+ SpellButton = PriestMemorizeWindow.GetControl (i + 2)
+ SpellButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ if i < len(Learnable):
+ Spell = GemRB.GetSpell (Learnable[i])
+ SpellButton.SetTooltip(Spell["SpellName"])
+ SpellButton.SetSpellIcon(Learnable[i], 1)
+ SpellButton.SetState (IE_GUI_BUTTON_ENABLED)
+ SpellButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestMemorizeSelectPress)
+ SpellButton.SetVarAssoc ("SpellMask", 1 << i)
+ else:
+ SpellButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ GemRB.SetToken ("number", str(PriestMemorizePointsLeft))
+ PriestMemorizeTextArea = PriestMemorizeWindow.GetControl (27)
+ PriestMemorizeTextArea.SetText (17253)
+
+ PriestMemorizeDoneButton = PriestMemorizeWindow.GetControl (0)
+ PriestMemorizeDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ PriestMemorizeDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestMemorizeDonePress)
+ PriestMemorizeDoneButton.SetText (11973)
+ PriestMemorizeDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ PriestMemorizeCancelButton = PriestMemorizeWindow.GetControl (29)
+ PriestMemorizeCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PriestMemorizeCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestMemorizeCancelPress)
+ PriestMemorizeCancelButton.SetText (13727)
+ PriestMemorizeCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PriestMemorizeWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def PriestMemorizeSelectPress():
+ global PriestMemorizeWindow, Learnable, PriestMemorizeTextArea, PriestMemorizeDoneButton, PriestMemorizePointsLeft
+
+ PriestMemorized = GemRB.GetVar ("PriestMemorized")
+ SpellMask = GemRB.GetVar ("SpellMask")
+ Spell = abs(PriestMemorized - SpellMask)
+
+ i = -1
+ while (Spell > 0):
+ i = i + 1
+ Spell = Spell >> 1
+
+ Spell=GemRB.GetSpell (Learnable[i])
+ PriestMemorizeTextArea.SetText (Spell["SpellDesc"])
+
+ if SpellMask < PriestMemorized:
+ PriestMemorizePointsLeft = PriestMemorizePointsLeft + 1
+ for i in range (len(Learnable)):
+ SpellButton = PriestMemorizeWindow.GetControl (i + 2)
+ if (((1 << i) & SpellMask) == 0):
+ SpellButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PriestMemorizeDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ PriestMemorizePointsLeft = PriestMemorizePointsLeft - 1
+ if PriestMemorizePointsLeft == 0:
+ for i in range (len(Learnable)):
+ SpellButton = PriestMemorizeWindow.GetControl (i + 2)
+ if ((1 << i) & SpellMask) == 0:
+ SpellButton.SetState (IE_GUI_BUTTON_DISABLED)
+ PriestMemorizeDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ PointsLeftLabel = PriestMemorizeWindow.GetControl (0x1000001b)
+ PointsLeftLabel.SetText (str(PriestMemorizePointsLeft))
+ GemRB.SetVar ("PriestMemorized", SpellMask)
+ return
+
+def PriestMemorizeDonePress():
+ global CharGenWindow, PriestMemorizeWindow, SkillsState
+
+ if PriestMemorizeWindow:
+ PriestMemorizeWindow.Unload ()
+ SkillsState = 5
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ SkillsPress()
+ return
+
+def PriestMemorizeCancelPress():
+ global CharGenWindow, PriestMemorizeWindow, SkillsState
+
+ if PriestMemorizeWindow:
+ PriestMemorizeWindow.Unload ()
+ SkillsState = 0
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Appearance Selection
+
+def AppearancePress():
+ global CharGenWindow, AppearanceWindow, AppearanceTable
+ global Portrait, AppearanceAvatarButton, PortraitName
+ global AppearanceHairButton, AppearanceSkinButton
+ global AppearanceMajorButton, AppearanceMinorButton
+ global HairColor, SkinColor, MajorColor, MinorColor
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ AppearanceWindow = GemRB.LoadWindow (13)
+ AppearanceTable = GemRB.LoadTable ("PORTCOLR")
+
+ if Portrait<0:
+ PortraitIndex = 0
+ else:
+ PortraitName = PortraitsTable.GetRowName (Portrait)
+ PortraitIndex = AppearanceTable.GetRowIndex (PortraitName + "L")
+
+ HairColor = AppearanceTable.GetValue (PortraitIndex, 1)
+ GemRB.SetVar ("HairColor", HairColor)
+ SkinColor = AppearanceTable.GetValue (PortraitIndex, 0)
+ GemRB.SetVar ("SkinColor", SkinColor)
+ MajorColor = AppearanceTable.GetValue (PortraitIndex, 2)
+ GemRB.SetVar ("MajorColor", MajorColor)
+ MinorColor = AppearanceTable.GetValue (PortraitIndex, 3)
+ GemRB.SetVar ("MinorColor", MinorColor)
+
+ AppearanceAvatarButton = AppearanceWindow.GetControl (1)
+ AppearanceAvatarButton.SetState (IE_GUI_BUTTON_LOCKED)
+ AppearanceAvatarButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED, OP_OR)
+ DrawAvatar()
+
+ AppearanceHairButton = AppearanceWindow.GetControl (2)
+ AppearanceHairButton.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ AppearanceHairButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AppearanceHairButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AppearanceHairPress)
+ AppearanceHairButton.SetBAM ("COLGRAD", 0, 0, HairColor)
+
+ AppearanceSkinButton = AppearanceWindow.GetControl (3)
+ AppearanceSkinButton.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ AppearanceSkinButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AppearanceSkinButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AppearanceSkinPress)
+ AppearanceSkinButton.SetBAM ("COLGRAD", 0, 0, SkinColor)
+
+ AppearanceMajorButton = AppearanceWindow.GetControl (4)
+ AppearanceMajorButton.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ AppearanceMajorButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AppearanceMajorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AppearanceMajorPress)
+ AppearanceMajorButton.SetBAM ("COLGRAD", 0, 0, MajorColor)
+
+ AppearanceMinorButton = AppearanceWindow.GetControl (5)
+ AppearanceMinorButton.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ AppearanceMinorButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AppearanceMinorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AppearanceMinorPress)
+ AppearanceMinorButton.SetBAM ("COLGRAD", 0, 0, MinorColor)
+
+ AppearanceDoneButton = AppearanceWindow.GetControl (0)
+ AppearanceDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AppearanceDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AppearanceDonePress)
+ AppearanceDoneButton.SetText (11973)
+ AppearanceDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ AppearanceCancelButton = AppearanceWindow.GetControl (13)
+ AppearanceCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AppearanceCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AppearanceCancelPress)
+ AppearanceCancelButton.SetText (13727)
+ AppearanceCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ AppearanceWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DrawAvatar():
+ global AppearanceAvatarButton
+ global MyChar
+
+ AvatarID = 0x6000
+ table = GemRB.LoadTable ("avprefr")
+ lookup = CommonTables.Races.FindValue (3, GemRB.GetPlayerStat(MyChar, IE_RACE))
+ lookup = CommonTables.Races.GetRowName (lookup)
+ AvatarID = AvatarID+table.GetValue (lookup, "RACE")
+ table = GemRB.LoadTable ("avprefc")
+ lookup = CommonTables.Classes.FindValue (5, GemRB.GetPlayerStat(MyChar, IE_CLASS))
+ lookup = CommonTables.Classes.GetRowName (lookup)
+ AvatarID = AvatarID+table.GetValue (lookup, "PREFIX")
+ table = GemRB.LoadTable ("avprefg")
+ AvatarID = AvatarID+table.GetValue (GemRB.GetPlayerStat(MyChar,IE_SEX),0)
+
+ AvatarRef = CommonTables.Pdolls.GetValue (hex(AvatarID), "LEVEL1")
+ AppearanceAvatarButton.SetPLT(AvatarRef, 0, MinorColor, MajorColor, SkinColor, 0, 0, HairColor, 0)
+
+ return
+
+def AppearanceHairPress():
+ GemRB.SetVar ("ColorType", 0)
+ AppearanceColorChoice (GemRB.GetVar ("HairColor"))
+ return
+
+def AppearanceSkinPress():
+ GemRB.SetVar ("ColorType", 1)
+ AppearanceColorChoice (GemRB.GetVar ("SkinColor"))
+ return
+
+def AppearanceMajorPress():
+ GemRB.SetVar ("ColorType", 2)
+ AppearanceColorChoice (GemRB.GetVar ("MajorColor"))
+ return
+
+def AppearanceMinorPress():
+ GemRB.SetVar ("ColorType", 3)
+ AppearanceColorChoice (GemRB.GetVar ("MinorColor"))
+ return
+
+def AppearanceColorChoice (CurrentColor):
+ global AppearanceWindow, AppearanceColorWindow
+
+ AppearanceWindow.SetVisible (WINDOW_INVISIBLE)
+ AppearanceColorWindow = GemRB.LoadWindow (14)
+ AppearanceColorTable = GemRB.LoadTable ("clowncol")
+ ColorType = GemRB.GetVar ("ColorType")
+ GemRB.SetVar ("SelectedColor", CurrentColor)
+
+ for i in range (34):
+ ColorButton = AppearanceColorWindow.GetControl (i)
+ ColorButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ColorButton.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+
+ for i in range (34):
+ Color = AppearanceColorTable.GetValue (ColorType, i)
+ if Color != "*":
+ ColorButton = AppearanceColorWindow.GetControl (i)
+ ColorButton.SetBAM ("COLGRAD", 2, 0, Color)
+ ColorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AppearanceColorSelected)
+ ColorButton.SetVarAssoc ("SelectedColor", Color)
+
+ AppearanceColorWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def AppearanceColorSelected():
+ global HairColor, SkinColor, MajorColor, MinorColor
+ global AppearanceWindow, AppearanceColorWindow
+ global AppearanceHairButton, AppearanceSkinButton
+ global AppearanceMajorButton, AppearanceMinorButton
+
+ if AppearanceColorWindow:
+ AppearanceColorWindow.Unload ()
+ ColorType = GemRB.GetVar ("ColorType")
+ if ColorType == 0:
+ HairColor = GemRB.GetVar ("SelectedColor")
+ GemRB.SetVar ("HairColor", HairColor)
+ AppearanceHairButton.SetBAM ("COLGRAD", 0, 0, HairColor)
+ elif ColorType == 1:
+ SkinColor = GemRB.GetVar ("SelectedColor")
+ GemRB.SetVar ("SkinColor", SkinColor)
+ AppearanceSkinButton.SetBAM ("COLGRAD", 0, 0, SkinColor)
+ elif ColorType == 2:
+ MajorColor = GemRB.GetVar ("SelectedColor")
+ GemRB.SetVar ("MajorColor", MajorColor)
+ AppearanceMajorButton.SetBAM ("COLGRAD", 0, 0, MajorColor)
+ elif ColorType == 3:
+ MinorColor = GemRB.GetVar ("SelectedColor")
+ GemRB.SetVar ("MinorColor", MinorColor)
+ AppearanceMinorButton.SetBAM ("COLGRAD", 0, 0, MinorColor)
+ DrawAvatar()
+ AppearanceWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def AppearanceDonePress():
+ global CharGenWindow, AppearanceWindow
+
+ if AppearanceWindow:
+ AppearanceWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ CharSoundSelect()
+ return
+
+def AppearanceCancelPress():
+ global CharGenWindow, AppearanceWindow
+
+ if AppearanceWindow:
+ AppearanceWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CharSoundSelect():
+ global CharGenWindow, CharSoundWindow, CharSoundTable, CharSoundStrings
+ global CharSoundVoiceList, VerbalConstants
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ CharSoundWindow = GemRB.LoadWindow (19)
+ CharSoundTable = GemRB.LoadTable ("CHARSND")
+ CharSoundStrings = GemRB.LoadTable ("CHARSTR")
+
+ VerbalConstants = [CharSoundTable.GetRowName(i) for i in range(CharSoundTable.GetRowCount())]
+ CharSoundVoiceList = CharSoundWindow.GetControl (45)
+ CharSoundVoiceList.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ RowCount=CharSoundVoiceList.GetCharSounds()
+
+ CharSoundPlayButton = CharSoundWindow.GetControl (47)
+ CharSoundPlayButton.SetState (IE_GUI_BUTTON_ENABLED)
+ CharSoundPlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CharSoundPlayPress)
+ CharSoundPlayButton.SetText (17318)
+
+ CharSoundTextArea = CharSoundWindow.GetControl (50)
+ CharSoundTextArea.SetText (11315)
+
+ CharSoundDoneButton = CharSoundWindow.GetControl (0)
+ CharSoundDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ CharSoundDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CharSoundDonePress)
+ CharSoundDoneButton.SetText (11973)
+ CharSoundDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CharSoundCancelButton = CharSoundWindow.GetControl (10)
+ CharSoundCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ CharSoundCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CharSoundCancelPress)
+ CharSoundCancelButton.SetText (13727)
+ CharSoundCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ CharSoundWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CharSoundPlayPress():
+ global CharGenWindow, CharSoundWindow, CharSoundTable, CharSoundStrings
+ global CharSoundVoiceList, SoundIndex, VerbalConstants
+
+ row = CharSoundVoiceList.QueryText ()
+
+ GemRB.SetPlayerSound (MyChar, row)
+
+ #play sound as sound slot
+ GemRB.VerbalConstant (MyChar, int(VerbalConstants[SoundIndex]))
+
+ SoundIndex += 1
+ if SoundIndex >= len(VerbalConstants):
+ SoundIndex = 0
+ return
+
+def CharSoundDonePress():
+ global CharGenWindow, CharSoundWindow, AppearanceButton, BiographyButton, NameButton, CharGenState
+
+ if CharSoundWindow:
+ CharSoundWindow.Unload ()
+ AppearanceButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AppearanceButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ BiographyButton.SetState (IE_GUI_BUTTON_ENABLED)
+ NameButton.SetState (IE_GUI_BUTTON_ENABLED)
+ NameButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CharGenState = 7
+ SetCharacterDescription()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CharSoundCancelPress():
+ global CharGenWindow, CharSoundWindow
+
+ if CharSoundWindow:
+ CharSoundWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Biography Selection
+
+def BiographyPress():
+ global CharGenWindow, BiographyWindow, BiographyField
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ BiographyWindow = GemRB.LoadWindow (51)
+
+ BiographyField = BiographyWindow.GetControl (4)
+ BiographyField.SetText (19423)
+
+ BiographyClearButton = BiographyWindow.GetControl (5)
+ BiographyClearButton.SetState (IE_GUI_BUTTON_ENABLED)
+ BiographyClearButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BiographyClearPress)
+ BiographyClearButton.SetText (18622)
+
+ BiographyCancelButton = BiographyWindow.GetControl (2)
+ BiographyCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ BiographyCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BiographyCancelPress)
+ BiographyCancelButton.SetText (13727)
+ BiographyCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ BiographyDoneButton = BiographyWindow.GetControl (1)
+ BiographyDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ BiographyDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BiographyDonePress)
+ BiographyDoneButton.SetText (11973)
+ BiographyDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ BiographyWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def BiographyClearPress():
+ global BiographyWindow, BiographyField
+
+ BiographyField.SetText ("")
+ return
+
+def BiographyCancelPress():
+ global CharGenWindow, BiographyWindow
+
+ if BiographyWindow:
+ BiographyWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def BiographyDonePress():
+ global CharGenWindow, BiographyWindow, BiographyField
+
+ GemRB.SetToken ("Biography", BiographyField.QueryText () )
+ if BiographyWindow:
+ BiographyWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Name Selection
+
+def NamePress():
+ global CharGenWindow, NameWindow, NameDoneButton, NameField
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ NameWindow = GemRB.LoadWindow (5)
+
+ NameDoneButton = NameWindow.GetControl (0)
+ NameDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ NameDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, NameDonePress)
+ NameDoneButton.SetText (11973)
+ NameDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ NameCancelButton = NameWindow.GetControl (3)
+ NameCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ NameCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, NameCancelPress)
+ NameCancelButton.SetText (13727)
+ NameCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ NameField = NameWindow.GetControl (2)
+ NameField.SetEvent (IE_GUI_EDIT_ON_CHANGE, NameEditChange)
+ NameField.SetText (GemRB.GetToken ("CHARNAME") )
+ NameField.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+ NameWindow.SetVisible (WINDOW_VISIBLE)
+ NameEditChange()
+ return
+
+def NameEditChange():
+ global NameField
+
+ if NameField.QueryText () == "":
+ NameDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ NameDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def NameDonePress():
+ global CharGenWindow, CharGenState, NameWindow, NameField, AcceptButton
+
+ GemRB.SetToken ("CHARNAME", NameField.QueryText () )
+ if NameWindow:
+ NameWindow.Unload ()
+ CharGenState = 8
+ AcceptButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AcceptButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ SetCharacterDescription()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def NameCancelPress():
+ global CharGenWindow, NameWindow
+
+ GemRB.SetToken ("CHARNAME", "")
+ if NameWindow:
+ NameWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+# Import Character
+
+def ImportPress():
+ global CharGenWindow, ImportWindow
+ global CharImportList
+
+ CharGenWindow.SetVisible (WINDOW_INVISIBLE)
+ ImportWindow = GemRB.LoadWindow (20)
+
+ TextAreaControl = ImportWindow.GetControl(4)
+ TextAreaControl.SetText(10963)
+
+ GemRB.SetVar ("Selected", 0)
+ CharImportList = ImportWindow.GetControl(2)
+ CharImportList.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ CharImportList.SetVarAssoc ("Selected",0)
+ CharImportList.GetCharacters()
+
+ ImportDoneButton = ImportWindow.GetControl (0)
+ ImportDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ImportDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ImportDonePress)
+ ImportDoneButton.SetText (11973)
+ ImportDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ ImportCancelButton = ImportWindow.GetControl (1)
+ ImportCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ ImportCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ImportCancelPress)
+ ImportCancelButton.SetText (13727)
+ ImportCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ImportWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ImportDonePress():
+ global CharGenWindow, ImportWindow, CharImportList
+ global CharGenState, SkillsState, Portrait
+
+ # Import the character from the chosen name
+ GemRB.CreatePlayer (CharImportList.QueryText(), MyChar|0x8000, 1)
+
+ GemRB.SetToken ("CHARNAME", GemRB.GetPlayerName (MyChar) )
+ GemRB.SetToken ("SmallPortrait", GemRB.GetPlayerPortrait (MyChar, 1) )
+ PortraitName = GemRB.GetPlayerPortrait (MyChar, 0)
+ GemRB.SetToken ("LargePortrait", PortraitName )
+ PortraitButton.SetPicture (PortraitName)
+ Portrait = -1
+
+ CharGenState = 7
+ SkillsState = 5
+ SetCharacterDescription ()
+ GenderButton.SetState (IE_GUI_BUTTON_DISABLED)
+ GenderButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ RaceButton.SetState (IE_GUI_BUTTON_DISABLED)
+ RaceButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ ClassButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ClassButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ AlignmentButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AlignmentButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ AbilitiesButton.SetState (IE_GUI_BUTTON_DISABLED)
+ AbilitiesButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ SkillsButton.SetState (IE_GUI_BUTTON_DISABLED)
+ SkillsButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_NAND)
+ AppearanceButton.SetState (IE_GUI_BUTTON_ENABLED)
+ AppearanceButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ BiographyButton.SetState (IE_GUI_BUTTON_ENABLED)
+ BiographyButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ NameButton.SetState (IE_GUI_BUTTON_ENABLED)
+ NameButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ if ImportWindow:
+ ImportWindow.Unload ()
+ return
+
+def ImportCancelPress():
+ global CharGenWindow, ImportWindow
+
+ if ImportWindow:
+ ImportWindow.Unload ()
+ CharGenWindow.SetVisible (WINDOW_VISIBLE)
+ return
diff --git a/gemrb/GUIScripts/iwd/GUICommonWindows.py b/gemrb/GUIScripts/iwd/GUICommonWindows.py
new file mode 100644
index 0000000..0cb7e0f
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUICommonWindows.py
@@ -0,0 +1,843 @@
+# -*-python-*-
+# 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.
+#
+
+# GUICommonWindows.py - functions to open common
+# windows in lower part of the screen
+###################################################
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+from ie_modal import *
+from ie_action import *
+import GUICommon
+import LUCommon
+import InventoryCommon
+
+# needed for all the Open*Window callbacks in the OptionsWindow
+import GUIJRNL
+import GUIMA
+import GUIMG
+import GUIINV
+import GUIOPT
+import GUIPR
+import GUIREC
+
+FRAME_PC_SELECTED = 0
+FRAME_PC_TARGET = 1
+
+PortraitWindow = None
+OptionsWindow = None
+ActionsWindow = None
+DraggedPortrait = None
+
+def SetupMenuWindowControls (Window, Gears, ReturnToGame):
+ # FIXME: add "(key)" to tooltips!
+
+ # Return to Game
+ Button = Window.GetControl (0)
+ Button.SetTooltip (16313)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ReturnToGame)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Map
+ Button = Window.GetControl (1)
+ Button.SetTooltip (16310)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIMA.OpenMapWindow)
+
+ # Journal
+ Button = Window.GetControl (2)
+ Button.SetTooltip (16308)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIJRNL.OpenJournalWindow)
+
+ # Inventory
+ Button = Window.GetControl (3)
+ Button.SetTooltip (16307)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIINV.OpenInventoryWindow)
+
+ # Records
+ Button = Window.GetControl (4)
+ Button.SetTooltip (16306)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIREC.OpenRecordsWindow)
+
+ # Mage
+ Button = Window.GetControl (5)
+ Button.SetTooltip (16309)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 5)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIMG.OpenMageWindow)
+ # Priest
+ Button = Window.GetControl (6)
+ Button.SetTooltip (14930)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 6)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIPR.OpenPriestWindow)
+
+ # Options
+ Button = Window.GetControl (7)
+ Button.SetTooltip (16311)
+ #Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 7)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIOPT.OpenOptionsWindow)
+
+ # Party mgmt
+ Button = Window.GetControl (8)
+ Button.SetTooltip (16312)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+
+ if Gears:
+ # Gears (time), how doesn't have this in the right place
+ if GUICommon.HasHOW():
+ pos = GemRB.GetSystemVariable (SV_HEIGHT)-71
+ Window.CreateButton (9, 6, pos, 64, 71)
+ Button = Window.GetControl (9)
+ Button.SetAnimation ("CGEAR")
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GUICommon.GearsClicked)
+ GUICommon.SetGamedaysAndHourToken()
+ Button.SetTooltip(16041)
+
+ MarkMenuButton (Window)
+ return
+
+def MarkMenuButton (WindowIndex):
+ Pressed = WindowIndex.GetControl( GemRB.GetVar ("SelectedWindow") )
+
+ for button in range (9):
+ Button = WindowIndex.GetControl (button)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ if Pressed:
+ Button = Pressed
+ else: # highlight return to game
+ Button = WindowIndex.GetControl (0)
+
+ Button.SetState (IE_GUI_BUTTON_PRESSED)
+
+def AIPress ():
+ Button = PortraitWindow.GetControl (6)
+ AI = GemRB.GetMessageWindowSize () & GS_PARTYAI
+ if AI:
+ GemRB.GameSetScreenFlags (GS_PARTYAI, OP_NAND)
+ Button.SetTooltip (15918)
+ else:
+ GemRB.GameSetScreenFlags (GS_PARTYAI, OP_OR)
+ Button.SetTooltip (15917)
+ return
+
+def EmptyControls ():
+ global ActionsWindow
+
+ GemRB.SetVar ("ActionLevel", 0)
+ Window = ActionsWindow
+ for i in range (12):
+ Button = Window.GetControl (i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetPicture ("")
+ Button.SetText ("")
+ Button.SetActionIcon (globals(), -1)
+ return
+
+def SelectFormationPreset ():
+ """Choose the default formation."""
+ GemRB.GameSetFormation (GemRB.GetVar ("Value"), GemRB.GetVar ("Formation") )
+ GroupControls ()
+ return
+
+def SetupFormation ():
+ """Opens the formation selection section."""
+ global ActionsWindow
+
+ Window = ActionsWindow
+ for i in range (12):
+ Button = Window.GetControl (i)
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetBAM ("FORM%x"%i,0,0,-1)
+ Button.SetVarAssoc ("Value", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectFormationPreset)
+ return
+
+def GroupControls ():
+ """Sections that control group actions."""
+
+ global ActionsWindow
+
+ GemRB.SetVar ("ActionLevel", 0)
+ Window = ActionsWindow
+ Button = Window.GetControl (0)
+ Button.SetActionIcon (globals(), 7)
+ Button = Window.GetControl (1)
+ Button.SetActionIcon (globals(), 15)
+ Button = Window.GetControl (2)
+ Button.SetActionIcon (globals(), 21)
+ Button = Window.GetControl (3)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (4)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (5)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (6)
+ Button.SetActionIcon (globals(), -1)
+ GemRB.SetVar ("Formation", GemRB.GameGetFormation ())
+ for i in range (5):
+ Button = Window.GetControl (7+i)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ idx = GemRB.GameGetFormation (i)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,i*2,i*2+1,i*2+24,i*2+25)
+ Button.SetBAM ("FORM%x"%idx,0,0,-1)
+ Button.SetVarAssoc ("Formation", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectFormation)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, SetupFormation)
+ str = GemRB.GetString (4935)
+ Button.SetTooltip ("F%d - %s"%(8+i,str) )
+ return
+
+def OpenActionsWindowControls (Window):
+ global ActionsWindow
+
+ ActionsWindow = Window
+ UpdateActionsWindow ()
+ return
+
+def SelectItemAbility():
+ pc = GemRB.GameGetFirstSelectedActor ()
+ slot = GemRB.GetVar ("Slot")
+ ability = GemRB.GetVar ("Ability")
+ GemRB.SetupQuickSlot (pc, 0, slot, ability, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ return
+
+def SetupItemAbilities(pc, slot):
+ Window = ActionsWindow
+
+ slot_item = GemRB.GetSlotItem(pc, slot, 1)
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ Tips = item["Tooltips"]
+
+ for i in range (12):
+ Button = Window.GetControl (i)
+ Button.SetPicture ("")
+ if i<len(Tips):
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetItemIcon (slot_item['ItemResRef'], i+6)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectItemAbility)
+ Button.SetVarAssoc ("Ability", i)
+
+ Button.SetTooltip ("F%d - %s"%(i+1,GemRB.GetString(Tips[i])) )
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ return
+
+def UpdateActionsWindow ():
+ """Redraws the actions section of the window."""
+
+ global ActionsWindow, PortraitWindow, OptionsWindow
+ global level, TopIndex
+
+ if ActionsWindow == -1:
+ return
+
+ if ActionsWindow == None:
+ return
+
+ #fully redraw the side panes to cover the actions window
+ #do this only when there is no 'otherwindow'
+ if GemRB.GetVar ("OtherWindow") == -1:
+ if PortraitWindow:
+ PortraitWindow.Invalidate ()
+ if OptionsWindow:
+ OptionsWindow.Invalidate ()
+
+ Selected = GemRB.GetSelectedSize()
+
+ #setting up the disabled button overlay (using the second border slot)
+ for i in range (12):
+ Button = ActionsWindow.GetControl (i)
+ Button.SetBorder (1, 0, 0, 0, 0, 50,30,10,120, 0, 1)
+ Button.SetFont ("NUMBER")
+ Button.SetText ("")
+
+ if Selected == 0:
+ EmptyControls ()
+ return
+ if Selected > 1:
+ GroupControls ()
+ return
+
+ #we are sure there is only one actor selected
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ level = GemRB.GetVar ("ActionLevel")
+ TopIndex = GemRB.GetVar ("TopIndex")
+ if level == 0:
+ #this is based on class
+ ActionsWindow.SetupControls (globals(), pc, 0, 1)
+ elif level == 1:
+ ActionsWindow.SetupEquipmentIcons(globals(), pc, TopIndex, 0, 1)
+ elif level == 2: #spells
+ GemRB.SetVar ("Type", 3)
+ ActionsWindow.SetupSpellIcons(globals(), pc, 3, TopIndex, 0, 1)
+ elif level == 3: #innates
+ GemRB.SetVar ("Type", 4)
+ ActionsWindow.SetupSpellIcons(globals(), pc, 4, TopIndex, 0, 1)
+ elif level == 4: #quick weapon/item ability selection
+ SetupItemAbilities(pc, GemRB.GetVar("Slot") )
+ return
+
+def ActionQWeaponPressed (which):
+ """Selects the given quickslot weapon if possible."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+ qs = GemRB.GetEquippedQuickSlot (pc, 1, 1)
+
+ #38 is the magic slot
+ if ((qs==which) or (qs==38)) and GemRB.GameControlGetTargetMode() != TARGET_MODE_ATTACK:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK, GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
+ else:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_NONE)
+ GemRB.SetEquippedQuickSlot (pc, which, -1, 1)
+
+ ActionsWindow.SetupControls (globals(), pc, 0, 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionQWeapon1Pressed ():
+ ActionQWeaponPressed(0)
+
+def ActionQWeapon2Pressed ():
+ ActionQWeaponPressed(1)
+
+def ActionQWeapon3Pressed ():
+ ActionQWeaponPressed(2)
+
+def ActionQWeapon4Pressed ():
+ ActionQWeaponPressed(3)
+
+#no check needed because the button wouldn't be drawn if illegal
+def ActionLeftPressed ():
+ """Scrolls the actions window left.
+
+ Used primarily for spell selection."""
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ if TopIndex>10:
+ TopIndex -= 10
+ else:
+ TopIndex = 0
+ GemRB.SetVar ("TopIndex", TopIndex)
+ UpdateActionsWindow ()
+ return
+
+#no check needed because the button wouldn't be drawn if illegal
+def ActionRightPressed ():
+ """Scrolls the action window right.
+
+ Used primarily for spell selection."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Type = GemRB.GetVar ("Type")
+ #Type is a bitfield if there is no level given
+ #This is to make sure cleric/mages get all spells listed
+ Max = GemRB.GetMemorizedSpellsCount(pc, Type, -1, 1)
+ TopIndex += 10
+ if TopIndex > Max - 10:
+ if Max>10:
+ TopIndex = Max-10
+ else:
+ TopIndex = 0
+
+ GemRB.SetVar ("TopIndex", TopIndex)
+ UpdateActionsWindow ()
+ return
+
+def ActionBardSongPressed ():
+ """Toggles the battle song."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_BATTLESONG, 1)
+ GemRB.PlaySound ("act_01")
+ UpdateActionsWindow ()
+ return
+
+def ActionSearchPressed ():
+ """Toggles detect traps."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_DETECTTRAPS, 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionStealthPressed ():
+ """Toggles stealth."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_STEALTH, 1)
+ GemRB.PlaySound ("act_07")
+ UpdateActionsWindow ()
+ return
+
+def ActionTurnPressed ():
+ """Toggles turn undead."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_TURNUNDEAD, 1)
+ GemRB.PlaySound ("act_06")
+ UpdateActionsWindow ()
+ return
+
+def ActionUseItemPressed ():
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionCastPressed ():
+ """Opens the spell choice scrollbar."""
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 2)
+ UpdateActionsWindow ()
+ return
+
+def ActionQItemPressed (action):
+ """Uses the given quick item."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ #quick slot
+ GemRB.UseItem (pc, -2, action, -1, 1)
+ return
+
+def ActionQItem1Pressed ():
+ ActionQItemPressed (ACT_QSLOT1)
+ return
+
+def ActionQItem2Pressed ():
+ ActionQItemPressed (ACT_QSLOT2)
+ return
+
+def ActionQItem3Pressed ():
+ ActionQItemPressed (ACT_QSLOT3)
+ return
+
+def ActionQItem4Pressed ():
+ ActionQItemPressed (ACT_QSLOT4)
+ return
+
+def ActionQItem5Pressed ():
+ ActionQItemPressed (ACT_QSLOT5)
+ return
+
+def ActionQItemRightPressed (action):
+ """Selects the used ability of the quick item."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetVar ("Slot", action)
+ GemRB.SetVar ("ActionLevel", 4)
+ UpdateActionsWindow ()
+ return
+
+def ActionQItem1RightPressed ():
+ ActionQItemRightPressed (19)
+
+def ActionQItem2RightPressed ():
+ ActionQItemRightPressed (20)
+
+def ActionQItem3RightPressed ():
+ ActionQItemRightPressed (21)
+
+def ActionQItem4RightPressed ():
+ ActionQItemRightPressed (22)
+
+def ActionQItem5RightPressed ():
+ ActionQItemRightPressed (23)
+
+def ActionQWeapon1RightPressed ():
+ ActionQItemRightPressed (10)
+
+def ActionQWeapon2RightPressed ():
+ ActionQItemRightPressed (11)
+
+def ActionQWeapon3RightPressed ():
+ ActionQItemRightPressed (12)
+
+def ActionQWeapon4RightPressed ():
+ ActionQItemRightPressed (13)
+
+def ActionInnatePressed ():
+ """Opens the innate spell scrollbar."""
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 3)
+ UpdateActionsWindow ()
+ return
+
+def SpellPressed ():
+ """Prepares a spell to be cast."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
+ Spell = GemRB.GetVar ("Spell")
+ Type = GemRB.GetVar ("Type")
+ GemRB.SpellCast (pc, Type, Spell, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ UpdateActionsWindow ()
+ return
+
+def EquipmentPressed ():
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
+ Item = GemRB.GetVar ("Equipment")
+ #equipment index
+ GemRB.UseItem (pc, -1, Item, -1, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ UpdateActionsWindow ()
+ return
+
+SelectionChangeHandler = None
+
+def SetSelectionChangeHandler (handler):
+ """Updates the selection handler."""
+
+ global SelectionChangeHandler
+
+ # Switching from walking to non-walking environment:
+ # set the first selected PC in walking env as a selected
+ # in nonwalking env
+ #if (not SelectionChangeHandler) and handler:
+ if (not SelectionChangeHandler) and handler and (not GUICommon.NextWindowFn):
+ sel = GemRB.GameGetFirstSelectedPC ()
+ if not sel:
+ sel = 1
+ GemRB.GameSelectPCSingle (sel)
+
+ SelectionChangeHandler = handler
+
+ # redraw selection on change main selection | single selection
+ SelectionChanged ()
+ return
+
+def RunSelectionChangeHandler ():
+ if SelectionChangeHandler:
+ SelectionChangeHandler ()
+ return
+
+def OpenPortraitWindow (needcontrols):
+ global PortraitWindow
+
+ #take care, this window is different in how/iwd
+ if GUICommon.HasHOW() and needcontrols:
+ PortraitWindow = Window = GemRB.LoadWindow (26)
+ else:
+ PortraitWindow = Window = GemRB.LoadWindow (1)
+
+ if needcontrols:
+ if GUICommon.HasHOW():
+ # Rest (how)
+ pos = GemRB.GetSystemVariable (SV_HEIGHT) - 37
+ Window.CreateButton (8, 6, pos, 55, 37)
+ Button = Window.GetControl (8)
+ Button.SetSprites ("GUIRSBUT", 0,0,1,0,0)
+ Button.SetTooltip (11942)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.RestPress)
+
+ pos = pos - 37
+ Window.CreateButton (6, 6, pos, 27, 36)
+ # AI
+ Button = Window.GetControl (6)
+ #fixing a gui bug, and while we are at it, hacking it to be easier
+ Button.SetSprites ("GUIBTACT", 0, 46, 47, 48, 49)
+ GSFlags = GemRB.GetMessageWindowSize ()&GS_PARTYAI
+
+ GemRB.SetVar ("AI", GSFlags)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, AIPress)
+ Button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ Button.SetVarAssoc ("AI", 1)
+ if GSFlags:
+ Button.SetTooltip (15917)
+ else:
+ Button.SetTooltip (15918)
+
+ #Select All
+ if GUICommon.HasHOW():
+ Window.CreateButton (7, 33, pos, 27, 36)
+ Button = Window.GetControl (7)
+ Button.SetSprites ("GUIBTACT", 0, 50, 51, 50, 51)
+ Button.SetTooltip (10485)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectAllOnPress)
+ if not GUICommon.HasHOW():
+ # Rest (iwd)
+ Button = PortraitWindow.GetControl (8)
+ Button.SetTooltip (11942)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.RestPress)
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Inventory = GemRB.GetVar ("Inventory")
+
+ for i in range (PARTY_SIZE):
+ Button = Window.GetControl (i)
+ Button.SetFont ("STATES")
+ Button.SetVarAssoc ("PressedPortrait", i+1)
+
+ if (needcontrols):
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, GUIINV.OpenInventoryWindowClick)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, PortraitButtonOnPress)
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitButtonOnPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, PortraitButtonOnShiftPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDropItemToPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT, OnDropPortraitToPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG, PortraitButtonOnDrag)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, PortraitButtonOnMouseEnter)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, PortraitButtonOnMouseLeave)
+ if Inventory and pc != i+1:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetText ("")
+ Button.SetTooltip ("")
+
+ # overlay a label, so we can display the hp with the correct font
+ Button.CreateLabelOnButton(100+i, "NUMFONT", IE_GUI_BUTTON_ALIGN_TOP|IE_GUI_BUTTON_ALIGN_LEFT)
+
+ Button.SetBorder (FRAME_PC_SELECTED, 1, 1, 2, 2, 0, 255, 0, 255)
+ Button.SetBorder (FRAME_PC_TARGET, 3, 3, 4, 4, 255, 255, 0, 255)
+
+ UpdatePortraitWindow ()
+ SelectionChanged ()
+ return Window
+
+def UpdatePortraitWindow ():
+ """Updates all of the portraits."""
+
+ Window = PortraitWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Inventory = GemRB.GetVar ("Inventory")
+
+ for portid in range (PARTY_SIZE):
+ Button = Window.GetControl (portid)
+ pic = GemRB.GetPlayerPortrait (portid+1, 1)
+ if Inventory and pc != portid+1:
+ pic = None
+
+ if not pic:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetText ("")
+ Button.SetTooltip ("")
+ continue
+
+ sel = GemRB.GameGetSelectedPCSingle () == portid + 1
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE| \
+ IE_GUI_BUTTON_HORIZONTAL| \
+ IE_GUI_BUTTON_ALIGN_LEFT| IE_GUI_BUTTON_ALIGN_TOP| \
+ IE_GUI_BUTTON_DRAGGABLE|IE_GUI_BUTTON_MULTILINE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetPicture (pic, "NOPORTSM")
+
+ ratio_str = GUICommon.SetupDamageInfo (portid+1, Button)
+
+ #add effects on the portrait
+ #http://img.jeuxvideo.fr/00002663-photo-icewind-dale-heart-of-winter.jpg
+ effects = GemRB.GetPlayerStates (portid+1)
+ states = ""
+ for col in range(len(effects)):
+ states = effects[col:col+1] + states
+ if col % 3 == 2: states = "\n" + states
+ for x in range(2 - (len(effects)/3)):
+ states = "\n" + states
+ states = "\n" + states
+
+ # blank space
+ # TODO: missing, maybe add another string tag to make glyphs 100% transparent?
+ flag = blank = chr(238)
+
+ # these two are missing too
+ ## shopping icon
+ #if pc==portid+1:
+ #if GemRB.GetStore()!=None:
+ #flag = chr(155)
+ ## talk icon
+ #if GemRB.GameGetSelectedPCSingle(1)==portid+1:
+ #flag = chr(154)
+
+ if LUCommon.CanLevelUp (portid+1):
+ states = flag+blank+chr(255) + states
+ else:
+ #states = flag+blank+blank + states
+ #states = "\n" + states
+ pass
+ Button.SetText(states)
+
+ HPLabel = Window.GetControl (100+portid)
+ HPLabel.SetText (ratio_str) # TODO: color depending on the ratio
+
+ #Button.EnableBorder (FRAME_PC_SELECTED, sel)
+ return
+
+def PortraitButtonOnDrag ():
+ global DraggedPortrait
+
+ #they start from 1
+ DraggedPortrait = GemRB.GetVar ("PressedPortrait")
+ GemRB.DragItem (DraggedPortrait, -1, "")
+ return
+
+def PortraitButtonOnPress ():
+ """Selects the portrait individually."""
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if GemRB.GameControlGetTargetMode() != TARGET_MODE_NONE:
+ GemRB.ActOnPC (i)
+ return
+
+ if (not SelectionChangeHandler):
+ if GemRB.GameIsPCSelected (i):
+ GemRB.GameControlSetScreenFlags (SF_CENTERONACTOR, OP_OR)
+ GemRB.GameSelectPC (i, True, SELECT_REPLACE)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def PortraitButtonOnShiftPress ():
+ """Handles selecting multiple portaits with shift."""
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if (not SelectionChangeHandler):
+ sel = GemRB.GameIsPCSelected (i)
+ sel = not sel
+ GemRB.GameSelectPC (i, sel)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def SelectionChanged ():
+ """Ran by the Game class when a PC selection is changed."""
+
+ global PortraitWindow
+
+ if not PortraitWindow:
+ return
+
+ GemRB.SetVar ("ActionLevel", 0)
+ if (not SelectionChangeHandler):
+ UpdateActionsWindow ()
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, GemRB.GameIsPCSelected (i + 1))
+ else:
+ sel = GemRB.GameGetSelectedPCSingle ()
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, i + 1 == sel)
+ import CommonWindow
+ CommonWindow.CloseContainerWindow()
+ return
+
+def PortraitButtonOnMouseEnter ():
+ global DraggedPortrait
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ GemRB.GameControlSetLastActor( i )
+ if GemRB.IsDraggingItem()==2:
+ if DraggedPortrait != None:
+ GemRB.SwapPCs (DraggedPortrait, i)
+ GemRB.SetVar ("PressedPortrait", DraggedPortrait)
+ DraggedPortrait = i
+ GemRB.SetTimedEvent (CheckDragging, 1)
+ else:
+ OnDropPortraitToPC()
+ return
+
+ if GemRB.IsDraggingItem ():
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_TARGET, 1)
+ return
+
+def OnDropPortraitToPC ():
+ GemRB.SetVar ("PressedPortrait",0)
+ GemRB.DragItem (0, -1, "")
+ DraggedPortrait = None
+ return
+
+def CheckDragging():
+ """Contains portrait dragging in case of mouse out-of-range."""
+
+ global DraggedPortrait
+
+ i = GemRB.GetVar ("PressedPortrait")
+ if not i:
+ GemRB.DragItem (0, -1, "")
+
+ if GemRB.IsDraggingItem()!=2:
+ DraggedPortrait = None
+ return
+
+def PortraitButtonOnMouseLeave ():
+ i = GemRB.GetVar ("PressedPortrait")
+ if not i:
+ return
+
+ Button = PortraitWindow.GetControl (i-1)
+ Button.EnableBorder (FRAME_PC_TARGET, 0)
+ GemRB.SetVar ("PressedPortrait", 0)
+ GemRB.SetTimedEvent (CheckDragging, 1)
+ return
+
+def ActionStopPressed ():
+ for i in GemRB.GetSelectedActors():
+ GemRB.ClearActions (i, 1)
+ return
+
+def ActionTalkPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_TALK,GA_NO_DEAD|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionAttackPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK,GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
+
+def ActionDefendPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_DEFEND,GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionThievingPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_PICK, GA_NO_DEAD|GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def CheckLevelUp(pc):
+ GemRB.SetVar ("CheckLevelUp"+str(pc), LUCommon.CanLevelUp (pc))
diff --git a/gemrb/GUIScripts/iwd/GUIINV.py b/gemrb/GUIScripts/iwd/GUIINV.py
new file mode 100644
index 0000000..01b8f43
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUIINV.py
@@ -0,0 +1,303 @@
+#-*-python-*-
+#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.
+#
+
+
+#GUIINV.py - scripts to control inventory windows from GUIINV winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+import InventoryCommon
+from GUIDefines import *
+from ie_stats import *
+from ie_slots import *
+from ie_spells import *
+
+InventoryWindow = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+def OpenInventoryWindowClick ():
+ tmp = GemRB.GetVar ("PressedPortrait")
+ GemRB.GameSelectPC (tmp, True, SELECT_REPLACE)
+ OpenInventoryWindow ()
+ return
+
+def OpenInventoryWindow ():
+ global InventoryWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenInventoryWindow):
+ if GemRB.IsDraggingItem () == 1:
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #store the item in the inventory before window is closed
+ GemRB.DropDraggedItem (pc, -3)
+ #dropping on ground if cannot store in inventory
+ if GemRB.IsDraggingItem () == 1:
+ GemRB.DropDraggedItem (pc, -2)
+
+ if InventoryWindow:
+ InventoryWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ InventoryWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ GUICommonWindows.UpdatePortraitWindow ()
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIINV", 640, 480)
+ InventoryWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", InventoryWindow.ID)
+ GemRB.SetVar ("MessageLabel", Window.GetControl (0x1000003f).ID )
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenInventoryWindow)
+ OptionsWindow.SetFrame ()
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ #ground items scrollbar
+ ScrollBar = Window.GetControl (66)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RefreshInventoryWindow)
+
+ #Ground Item
+ for i in range (5):
+ Button = Window.GetControl (i+68)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, InventoryCommon.MouseEnterGround)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, InventoryCommon.MouseLeaveGround)
+ Button.SetVarAssoc ("GroundItemButton", i)
+ Button.SetSprites ("STONSLOT",0,0,1,2,3)
+
+ #major & minor clothing color
+ Button = Window.GetControl (62)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.MajorPress)
+ Button.SetTooltip (12007)
+
+ Button = Window.GetControl (63)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.MinorPress)
+ Button.SetTooltip (12008)
+
+ #portrait
+ Button = Window.GetControl (50)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnAutoEquip)
+
+ #encumbrance
+ Label = Window.CreateLabel (0x10000043, 5,385,60,15,"NUMBER","0:",IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_TOP)
+ Label = Window.CreateLabel (0x10000044, 5,455,80,15,"NUMBER","0:",IE_FONT_ALIGN_RIGHT|IE_FONT_ALIGN_TOP)
+
+ #armor class
+ Label = Window.GetControl (0x10000038)
+ Label.SetTooltip (17183)
+
+ #hp current
+ Label = Window.GetControl (0x10000039)
+ Label.SetTooltip (17184)
+
+ #hp max
+ Label = Window.GetControl (0x1000003a)
+ Label.SetTooltip (17378)
+
+ #info label, game paused, etc
+ Label = Window.GetControl (0x1000003f)
+ Label.SetText ("")
+
+ SlotCount = GemRB.GetSlotType (-1)["Count"]
+ for slot in range (SlotCount):
+ SlotType = GemRB.GetSlotType (slot+1)
+ if SlotType["ID"]:
+ Button = Window.GetControl (SlotType["ID"])
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, InventoryCommon.MouseEnterSlot)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, InventoryCommon.MouseLeaveSlot)
+ Button.SetVarAssoc ("ItemButton", slot+1)
+ Button.SetSprites ("STONSLOT",0,0,1,2,3)
+
+ GemRB.SetVar ("TopIndex", 0)
+ GUICommonWindows.SetSelectionChangeHandler (UpdateInventoryWindow)
+ UpdateInventoryWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+#complete update
+def UpdateInventoryWindow ():
+ Window = InventoryWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Container = GemRB.GetContainer (pc, 1)
+ ScrollBar = Window.GetControl (66)
+ Count = Container['ItemCount']
+ if Count<1:
+ Count=1
+ ScrollBar.SetVarAssoc ("TopIndex", Count)
+ RefreshInventoryWindow ()
+ #populate inventory slot controls
+ SlotCount = GemRB.GetSlotType (-1)["Count"]
+ for i in range (SlotCount):
+ InventoryCommon.UpdateSlot (pc, i)
+ return
+
+#partial update without altering TopIndex
+def RefreshInventoryWindow ():
+ Window = InventoryWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ #name
+ Label = Window.GetControl (0x10000032)
+ Label.SetText (GemRB.GetPlayerName (pc, 0))
+
+ #portrait
+ Button = Window.GetControl (50)
+ Color1 = GemRB.GetPlayerStat (pc, IE_METAL_COLOR)
+ Color2 = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR)
+ Color3 = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR)
+ Color4 = GemRB.GetPlayerStat (pc, IE_SKIN_COLOR)
+ Color5 = GemRB.GetPlayerStat (pc, IE_LEATHER_COLOR)
+ Color6 = GemRB.GetPlayerStat (pc, IE_ARMOR_COLOR)
+ Color7 = GemRB.GetPlayerStat (pc, IE_HAIR_COLOR)
+ Button.SetPLT (GUICommon.GetActorPaperDoll (pc),
+ Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 0)
+
+ anim_id = GemRB.GetPlayerStat (pc, IE_ANIMATION_ID)
+ row = "0x%04X" %anim_id
+ size = CommonTables.Pdolls.GetValue (row, "SIZE")
+
+ #Weapon
+ slot_item = GemRB.GetSlotItem (pc, GemRB.GetEquippedQuickSlot (pc) )
+ if slot_item:
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ if (item['AnimationType'] != ''):
+ Button.SetPLT("WP" + size + item['AnimationType'] + "INV", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 1)
+
+ #Shield
+ slot_item = GemRB.GetSlotItem (pc, 3)
+ if slot_item:
+ itemname = slot_item["ItemResRef"]
+ item = GemRB.GetItem (itemname)
+ if (item['AnimationType'] != ''):
+ if (GemRB.CanUseItemType (SLOT_WEAPON, itemname)):
+ #off-hand weapon
+ Button.SetPLT("WP" + size + item['AnimationType'] + "OIN", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 2)
+ else:
+ #shield
+ Button.SetPLT("WP" + size + item['AnimationType'] + "INV", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 2)
+
+ #Helmet
+ slot_item = GemRB.GetSlotItem (pc, 1)
+ if slot_item:
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ if (item['AnimationType'] != ''):
+ Button.SetPLT("WP" + size + item['AnimationType'] + "INV", Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0, 3)
+
+ #encumbrance
+ GUICommon.SetEncumbranceLabels ( Window, 0x10000043, 0x10000044, pc)
+
+ #armor class
+ Label = Window.GetControl (0x10000038)
+ ac = GemRB.GetPlayerStat (pc, IE_ARMORCLASS)
+ ac += GemRB.GetAbilityBonus (IE_DEX, 2, GemRB.GetPlayerStat (pc, IE_DEX) )
+ Label.SetText (str (ac))
+ Label.SetTooltip (10339)
+
+ #hp current
+ hp = GemRB.GetPlayerStat (pc, IE_HITPOINTS)
+ Label = Window.GetControl (0x10000039)
+ Label.SetText (str (hp))
+ Label.SetTooltip (17184)
+
+ #hp max
+ hpmax = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)
+ Label = Window.GetControl (0x1000003a)
+ Label.SetText (str (hpmax))
+ Label.SetTooltip (17378)
+
+ #party gold
+ Label = Window.GetControl (0x10000040)
+ Label.SetText (str (GemRB.GameGetPartyGold ()))
+
+ #class
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ Label = Window.GetControl (0x10000042)
+ Label.SetText (ClassTitle)
+
+ Button = Window.GetControl (62)
+ Color = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 1, 0, Color)
+
+ Button = Window.GetControl (63)
+ Color = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 1, 0, Color)
+
+ #update ground inventory slots
+ Container = GemRB.GetContainer (pc, 1)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ for i in range (5):
+ Button = Window.GetControl (i+68)
+ if GemRB.IsDraggingItem ()==1:
+ Button.SetState (IE_GUI_BUTTON_SECOND)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDragItemGround)
+ Slot = GemRB.GetContainerItem (pc, i+TopIndex)
+
+ if Slot == None:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.OnDragItemGround)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InventoryCommon.OpenGroundItemInfoWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None) #TODO: implement OpenGroundItemAmountWindow
+
+ GUICommon.UpdateInventorySlot (pc, Button, Slot, "ground")
+
+ #making window visible/shaded depending on the pc's state
+ held = GemRB.GetPlayerStat (pc, IE_HELD) + GemRB.GetPlayerStat (pc, IE_CASTERHOLD)
+ if held or GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+#End of file GUIINV.py
diff --git a/gemrb/GUIScripts/iwd/GUIJRNL.py b/gemrb/GUIScripts/iwd/GUIJRNL.py
new file mode 100644
index 0000000..f04ba5f
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUIJRNL.py
@@ -0,0 +1,149 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIJRNL.py - scripts to control journal/diary windows from GUIJRNL winpack
+
+###################################################
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+
+###################################################
+JournalWindow = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+Chapter = 0
+StartTime = 0
+StartYear = 0
+
+###################################################
+def OpenJournalWindow ():
+ global StartTime, StartYear
+ global JournalWindow, PortraitWindow, OptionsWindow
+ global OldPortraitWindow, OldOptionsWindow
+ global Chapter
+
+ if GUICommon.CloseOtherWindow (OpenJournalWindow):
+ if JournalWindow:
+ JournalWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ JournalWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIJRNL", 640, 480)
+ JournalWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", JournalWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenJournalWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ Table = GemRB.LoadTable("YEARS")
+ #StartTime is the time offset for ingame time, beginning from the startyear
+ StartTime = Table.GetValue("STARTTIME", "VALUE") / 4500
+ #StartYear is the year of the lowest ingame date to be printed
+ StartYear = Table.GetValue("STARTYEAR", "VALUE")
+
+ Button = Window.GetControl (3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, JournalPrevSectionPress)
+
+ Button = Window.GetControl (4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, JournalNextSectionPress)
+
+ Chapter = GemRB.GetGameVar("chapter")
+ UpdateJournalWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+
+###################################################
+def UpdateJournalWindow ():
+ Window = JournalWindow
+
+ # Title
+ Title = Window.GetControl (5)
+ Title.SetText (16202 + Chapter)
+
+ # text area
+ Text = Window.GetControl (1)
+ Text.Clear ()
+
+ for i in range (GemRB.GetJournalSize (Chapter)):
+ je = GemRB.GetJournalEntry (Chapter, i)
+
+ if je == None:
+ continue
+
+ hours = je['GameTime'] / 4500
+ days = int(hours/24)
+ year = str (StartYear + int(days/365))
+ dayandmonth = StartTime + days%365
+ GemRB.SetToken("GAMEDAY", str(days) )
+ GemRB.SetToken("HOUR",str(hours%24 ) )
+ GemRB.SetVar("DAYANDMONTH",dayandmonth)
+ GemRB.SetToken("YEAR",year)
+ Text.Append (GemRB.GetString(15980), 3*i)
+
+ Text.Append (je['Text'], 3*i + 1)
+ Text.Append ("", 3*i + 2)
+
+
+###################################################
+def JournalPrevSectionPress ():
+ global Chapter
+
+ if Chapter > 0:
+ Chapter = Chapter - 1
+ UpdateJournalWindow ()
+
+
+###################################################
+def JournalNextSectionPress ():
+ global Chapter
+
+ #if GemRB.GetJournalSize (Chapter + 1) > 0:
+ if Chapter < GemRB.GetGameVar("chapter"):
+ Chapter = Chapter + 1
+ UpdateJournalWindow ()
+
+
+###################################################
+# End of file GUIJRNL.py
diff --git a/gemrb/GUIScripts/iwd/GUILOAD.py b/gemrb/GUIScripts/iwd/GUILOAD.py
new file mode 100644
index 0000000..f8d10bd
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUILOAD.py
@@ -0,0 +1,177 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUILOAD.py - Load window
+
+###################################################
+
+import GemRB
+import LoadScreen
+from GUIDefines import *
+
+LoadWindow = 0
+TextAreaControl = 0
+Games = ()
+ScrollBar = 0
+
+def OnLoad ():
+ global LoadWindow, TextAreaControl, Games, ScrollBar
+
+ GemRB.SetVar ("PlayMode",0) #iwd is always using 'mpsave'
+ GemRB.SetVar ("SaveDir",1) #iwd is always using 'mpsave'
+ GemRB.LoadWindowPack ("GUILOAD", 640, 480)
+ LoadWindow = GemRB.LoadWindow (0)
+ LoadWindow.SetFrame ()
+ CancelButton=LoadWindow.GetControl (34)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.SetVar ("LoadIdx",0)
+ for i in range (4):
+ Button = LoadWindow.GetControl (26+i)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ Button = LoadWindow.GetControl (30+i)
+ Button.SetText (13957)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ #area previews
+ Button = LoadWindow.GetControl (1+i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ #PC portraits
+ for j in range (PARTY_SIZE):
+ Button = LoadWindow.GetControl (40 + i*PARTY_SIZE + j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ ScrollBar=LoadWindow.GetControl (25)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games=GemRB.GetSaveGames ()
+ TopIndex = max (0, len(Games) - 4)
+ GemRB.SetVar ("TopIndex",TopIndex)
+ ScrollBar.SetVarAssoc ("TopIndex", TopIndex+1)
+ ScrollBarPress ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ScrollBarPress ():
+ #draw load game portraits
+ Pos = GemRB.GetVar ("TopIndex")
+ for i in range (4):
+ ActPos = Pos + i
+
+ Button1 = LoadWindow.GetControl (26+i)
+ Button2 = LoadWindow.GetControl (30+i)
+ if ActPos<len(Games):
+ Button1.SetState (IE_GUI_BUTTON_ENABLED)
+ Button2.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState (IE_GUI_BUTTON_DISABLED)
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetName()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl (0x10000008+i)
+ Label.SetText (Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl (0x10000010+i)
+ Label.SetText (Slotname)
+
+ Button=LoadWindow.GetControl (1+i)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture ("")
+ for j in range (PARTY_SIZE):
+ Button=LoadWindow.GetControl (40 + i*PARTY_SIZE + j)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture ("")
+ return
+
+def LoadGamePress ():
+ if LoadWindow:
+ LoadWindow.Unload ()
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ LoadScreen.StartLoadScreen()
+ GemRB.LoadGame(Games[Pos]) #loads and enters savegame
+ GemRB.SetNextScript ("PartyFormation")
+ return
+
+def DeleteGameConfirm():
+ global Games
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Pos = TopIndex +GemRB.GetVar ("LoadIdx")
+ GemRB.DeleteSaveGame(Games[Pos])
+ if TopIndex>0:
+ GemRB.SetVar ("TopIndex",TopIndex-1)
+ del Games[Pos]
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress ()
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel ():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress ():
+ global ConfirmWindow
+
+ LoadWindow.SetVisible (WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow (1)
+ Text=ConfirmWindow.GetControl (0)
+ Text.SetText (15305)
+ DeleteButton=ConfirmWindow.GetControl (1)
+ DeleteButton.SetText (13957)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ CancelButton=ConfirmWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CancelPress ():
+ if LoadWindow:
+ LoadWindow.Unload ()
+ GemRB.SetNextScript ("Start")
+ return
diff --git a/gemrb/GUIScripts/iwd/GUIMA.py b/gemrb/GUIScripts/iwd/GUIMA.py
new file mode 100644
index 0000000..2352005
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUIMA.py
@@ -0,0 +1,295 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMA.py - scripts to control map windows from the GUIMA and GUIWMAP winpacks
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+
+MapWindow = None
+WorldMapWindow = None
+WorldMapControl = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+def RevealMap ():
+ global MapWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (ShowMap):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+
+ PosX = GemRB.GetVar ("MapControlX")
+ PosY = GemRB.GetVar ("MapControlY")
+
+ GemRB.RevealArea (PosX, PosY, 30, 1)
+ GemRB.GamePause (0,0)
+ return
+
+###################################################
+# for farsight effect
+###################################################
+def ShowMap ():
+ global MapWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (ShowMap):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMAP", 640, 480)
+ MapWindow = Window = GemRB.LoadWindow (2)
+ #this window type blocks the game normally, but map window doesn't
+ GemRB.SetVar ("OtherWindow", MapWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, ShowMap)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ OptionsWindow.SetFrame ()
+
+ # World Map
+ Button = Window.GetControl (1)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Hide or Show mapnotes
+ Button = Window.GetControl (3)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Label = Window.GetControl (0x10000003)
+ Label.SetText ("")
+
+ # Map Control
+ Window.CreateMapControl (2, 0, 0, 0, 0)
+ Map = Window.GetControl (2)
+ GemRB.SetVar ("ShowMapNotes",IE_GUI_MAP_REVEAL_MAP)
+ Map.SetVarAssoc ("ShowMapNotes", IE_GUI_MAP_REVEAL_MAP)
+ Map.SetEvent (IE_GUI_MAP_ON_PRESS, RevealMap)
+ Window.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_GRAYED)
+ PortraitWindow.SetVisible (WINDOW_GRAYED)
+ OptionsWindow.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_FRONT)
+ Window.SetVisible (WINDOW_FRONT)
+ Map.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ GemRB.GamePause (0,0)
+ return
+
+###################################################
+def OpenMapWindow ():
+ global MapWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenMapWindow):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMAP", 640, 480)
+ MapWindow = Window = GemRB.LoadWindow (2)
+ #this window type blocks the game normally, but map window doesn't
+ GemRB.SetVar ("OtherWindow", MapWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenMapWindow)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ OptionsWindow.SetFrame ()
+
+ # World Map
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenWorldMapWindowInside)
+
+ # Map Control
+ Window.CreateMapControl (2, 0, 0, 0, 0)
+ Map = Window.GetControl (2)
+ Map.SetEvent (IE_GUI_MAP_ON_DOUBLE_PRESS, LeftDoublePressMap)
+
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ Map.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def LeftDoublePressMap ():
+ OpenMapWindow()
+ return
+
+def OpenWorldMapWindowInside ():
+ global MapWindow
+
+ OpenMapWindow () #closes mapwindow
+ MapWindow = -1
+ print "MapWindow=",MapWindow
+ WorldMapWindowCommon (-1)
+ return
+
+def OpenWorldMapWindow ():
+ WorldMapWindowCommon (GemRB.GetVar ("Travel"))
+ return
+
+def MoveToNewArea ():
+ global WorldMapWindow, WorldMapControl
+
+ tmp = WorldMapControl.GetDestinationArea (1)
+ CloseWorldMapWindow ()
+ GemRB.CreateMovement (tmp["Destination"], tmp["Entrance"], tmp["Direction"])
+ return
+
+def ChangeTooltip ():
+ global WorldMapWindow, WorldMapControl
+ global str
+
+ tmp = WorldMapControl.GetDestinationArea ()
+ if (tmp):
+ str = "%s: %d"%(GemRB.GetString(23084),tmp["Distance"])
+ else:
+ str=""
+
+ WorldMapControl.SetTooltip (str)
+ return
+
+def CloseWorldMapWindow ():
+ global WorldMapWindow, WorldMapControl
+
+ print "CloseWorldMapWindow found Mapwindow = ",MapWindow
+ if MapWindow:
+ # reopen map window
+ if WorldMapWindow:
+ WorldMapWindow.Unload ()
+ WorldMapWindow = None
+ WorldMapControl = None
+ OpenMapWindow ()
+ return
+
+ if WorldMapWindow:
+ WorldMapWindow.Unload ()
+ WorldMapWindow = None
+ WorldMapControl = None
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ return
+
+def WorldMapWindowCommon (Travel):
+ global WorldMapWindow, WorldMapControl
+
+ if WorldMapWindow:
+ CloseWorldMapWindow ()
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIWMAP", 640, 480)
+ WorldMapWindow = Window = GemRB.LoadWindow (0)
+ #saving the original portrait window
+ Window.SetFrame ()
+
+ Window.CreateWorldMapControl (4, 0, 62, 640, 418, Travel, "infofont")
+ WorldMapControl = Window.GetControl (4)
+ WorldMapControl.SetAnimation ("WMDAG")
+ WorldMapControl.SetEvent (IE_GUI_WORLDMAP_ON_PRESS, MoveToNewArea)
+ WorldMapControl.SetEvent (IE_GUI_MOUSE_ENTER_WORLDMAP, ChangeTooltip)
+ #center on current area
+ WorldMapControl.AdjustScrolling (0,0)
+
+ #north
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapN)
+
+ #south
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MapS)
+
+ # Done
+ Button = Window.GetControl (0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseWorldMapWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MapN():
+ WorldMapControl.AdjustScrolling (0, -10)
+ return
+
+def MapS():
+ WorldMapControl.AdjustScrolling (0, 10)
+ return
+
+
+###################################################
+# End of file GUIMA.py
diff --git a/gemrb/GUIScripts/iwd/GUIMG.py b/gemrb/GUIScripts/iwd/GUIMG.py
new file mode 100644
index 0000000..c062efb
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUIMG.py
@@ -0,0 +1,356 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMG.py - scripts to control mage spells windows from GUIMG winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+
+MageWindow = None
+MageSpellInfoWindow = None
+MageSpellLevel = 0
+MageSpellUnmemorizeWindow = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+def OpenMageWindow ():
+ global MageWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenMageWindow):
+ if MageWindow:
+ MageWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MageWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMG", 640, 480)
+ MageWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", MageWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenMageWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MagePrevLevelPress)
+
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageNextLevelPress)
+
+## #unknown usage
+## Button = Window.GetControl (55)
+## Button.SetState (IE_GUI_BUTTON_LOCKED)
+## #Button.SetText (123)
+## #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, xxPress)
+
+## #setup level buttons
+## for i in range (9):
+## Button = Window.GetControl (56 + i)
+## Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RefreshMageLevel)
+## Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+## for i in range (9):
+## Button = Window.GetControl (56 + i)
+## Button.SetVarAssoc ("MageSpellLevel", i)
+
+
+ # Setup memorized spells buttons
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ Button.SetBorder (0,0,0,0,0,0,0,0,64,0,1)
+ Button.SetSprites ("SPELFRAM",0,0,0,0,0)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Setup book spells buttons
+ for i in range (GUICommon.GetIWDSpellButtonCount()):
+ Button = Window.GetControl (27 + i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateMageWindow)
+ UpdateMageWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ #bringing the window front
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def UpdateMageWindow ():
+ global MageMemorizedSpellList, MageKnownSpellList
+
+ MageMemorizedSpellList = []
+ MageKnownSpellList = []
+
+ Window = MageWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+ type = IE_SPELL_TYPE_WIZARD
+ level = MageSpellLevel
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, type, level)
+
+ Label = Window.GetControl (0x10000032)
+ GemRB.SetToken ('LEVEL', str (level + 1))
+ Label.SetText (12137 )
+
+ Name = GemRB.GetPlayerName (pc, 0)
+ Label = Window.GetControl (0x10000035)
+ Label.SetText (Name)
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ if i < mem_cnt:
+ ms = GemRB.GetMemorizedSpell (pc, type, level, i)
+ Button.SetSpellIcon (ms['SpellResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if ms['Flags']:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellUnmemorizeWindow)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageUnmemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenMageSpellInfoWindow)
+ spell = GemRB.GetSpell (ms['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ MageMemorizedSpellList.append (ms['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", i)
+ Button.EnableBorder (0, ms['Flags'] == 0)
+ else:
+ if i < max_mem_cnt:
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+
+ known_cnt = GemRB.GetKnownSpellsCount (pc, type, level)
+ for i in range (GUICommon.GetIWDSpellButtonCount()):
+ Button = Window.GetControl (27 + i)
+ if i < known_cnt:
+ ks = GemRB.GetKnownSpell (pc, type, level, i)
+ Button.SetSpellIcon (ks['SpellResRef'], 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageMemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenMageSpellInfoWindow)
+ spell = GemRB.GetSpell (ks['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ MageKnownSpellList.append (ks['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", 100 + i)
+
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+ if (CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS), 2)=="*") or \
+ GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def MagePrevLevelPress ():
+ global MageSpellLevel
+
+ if MageSpellLevel > 0:
+ MageSpellLevel = MageSpellLevel - 1
+ UpdateMageWindow ()
+ return
+
+def MageNextLevelPress ():
+ global MageSpellLevel
+
+ if MageSpellLevel < 8:
+ MageSpellLevel = MageSpellLevel + 1
+ UpdateMageWindow ()
+ return
+
+def RefreshMageLevel ():
+ global MageSpellLevel
+
+ MageSpellLevel = GemRB.GetVar ("MageSpellLevel")
+ UpdateMageWindow ()
+ return
+
+def OnMageMemorizeSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ if GemRB.MemorizeSpell (pc, type, level, index):
+ UpdateMageWindow ()
+ return
+
+def OpenMageSpellInfoWindow ():
+ global MageSpellInfoWindow
+
+ if MageSpellInfoWindow != None:
+ if MageSpellInfoWindow:
+ MageSpellInfoWindow.Unload ()
+ MageSpellInfoWindow = None
+ return
+
+ MageSpellInfoWindow = Window = GemRB.LoadWindow (3)
+
+ #back
+ Button = Window.GetControl (5)
+ Button.SetText (15416)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellInfoWindow)
+
+ #erase
+ #Button = Window.GetControl (6)
+ index = GemRB.GetVar ("SpellButton")
+ if index < 100:
+ ResRef = MageMemorizedSpellList[index]
+ #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ #Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ else:
+ ResRef = MageKnownSpellList[index - 100]
+ #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellRemoveWindow)
+ #Button.SetText (63668)
+
+ spell = GemRB.GetSpell (ResRef)
+
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (spell['SpellName'])
+
+ Button = Window.GetControl (2)
+ Button.SetSpellIcon (ResRef, 1)
+
+ Text = Window.GetControl (3)
+ Text.SetText (spell['SpellDesc'])
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseMageSpellUnmemorizeWindow ():
+ global MageSpellUnmemorizeWindow
+
+ if MageSpellUnmemorizeWindow:
+ MageSpellUnmemorizeWindow.Unload ()
+ MageSpellUnmemorizeWindow = None
+ return
+
+def OpenMageSpellRemoveWindow ():
+ global MageSpellUnmemorizeWindow
+
+ MageSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (63745)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageRemoveSpell)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseMageSpellUnmemorizeWindow)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OpenMageSpellUnmemorizeWindow ():
+ global MageSpellUnmemorizeWindow
+
+ MageSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (11824)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageUnmemorizeSpell)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseMageSpellUnmemorizeWindow)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnMageUnmemorizeSpell ():
+ if MageSpellUnmemorizeWindow:
+ CloseMageSpellUnmemorizeWindow()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton")
+
+ if GemRB.UnmemorizeSpell (pc, type, level, index):
+ UpdateMageWindow ()
+ return
+
+def OnMageRemoveSpell ():
+ CloseMageSpellUnmemorizeWindow()
+ OpenMageSpellInfoWindow()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton")-100
+
+ #remove spell from book
+ GemRB.RemoveSpell (pc, type, level, index)
+ UpdateMageWindow ()
+ return
+
+###################################################
+# End of file GUIMG.py
diff --git a/gemrb/GUIScripts/iwd/GUIMOVIE.py b/gemrb/GUIScripts/iwd/GUIMOVIE.py
new file mode 100644
index 0000000..f69e772
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUIMOVIE.py
@@ -0,0 +1,85 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMOVIE.py - Play Movies window
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+
+MovieWindow = 0
+TextAreaControl = 0
+MoviesTable = 0
+
+def OnLoad ():
+ global MovieWindow, TextAreaControl, MoviesTable
+
+ GemRB.LoadWindowPack ("GUIMOVIE", 640, 480)
+ MovieWindow = GemRB.LoadWindow (0)
+ MovieWindow.SetFrame ()
+ TextAreaControl = MovieWindow.GetControl (0)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ PlayButton = MovieWindow.GetControl (2)
+ CreditsButton = MovieWindow.GetControl (3)
+ DoneButton = MovieWindow.GetControl (4)
+ MoviesTable = GemRB.LoadTable ("MOVIDESC")
+ for i in range (0, MoviesTable.GetRowCount () ):
+ t = MoviesTable.GetRowName (i)
+ if GemRB.GetVar(t)==1:
+ s = MoviesTable.GetValue (i, 0)
+ TextAreaControl.Append (s,-1)
+ TextAreaControl.SetVarAssoc ("MovieIndex",0)
+ PlayButton.SetText (17318)
+ CreditsButton.SetText (15591)
+ DoneButton.SetText (11973)
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ CreditsButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CreditsPress)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePress)
+ DoneButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ MovieWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def PlayPress ():
+ s = GemRB.GetVar("MovieIndex")
+ for i in range (0, MoviesTable.GetRowCount () ):
+ t = MoviesTable.GetRowName (i)
+ if GemRB.GetVar(t)==1:
+ if s==0:
+ s = MoviesTable.GetRowName (i)
+ MovieWindow.SetVisible (WINDOW_INVISIBLE)
+ GemRB.PlayMovie (s, 1)
+ MovieWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+ s = s - 1
+
+
+def CreditsPress ():
+ MovieWindow.SetVisible (WINDOW_INVISIBLE)
+ GemRB.PlayMovie ("CREDITS", 1)
+ MovieWindow.SetVisible (WINDOW_VISIBLE)
+
+
+def DonePress ():
+ if MovieWindow:
+ MovieWindow.Unload ()
+ GemRB.SetNextScript ("Start")
+
diff --git a/gemrb/GUIScripts/iwd/GUIOPT.py b/gemrb/GUIScripts/iwd/GUIOPT.py
new file mode 100644
index 0000000..ee8e21b
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUIOPT.py
@@ -0,0 +1,724 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIOPT.py - scripts to control options windows mostly from GUIOPT winpack
+
+# GUIOPT:
+# 0 - Main options window (peacock tail)
+# 1 - Video options window
+# 2 - msg win with 1 button
+# 3 - msg win with 2 buttons
+# 4 - msg win with 3 buttons
+# 5 - Audio options window
+# 6 - Gameplay options window
+# 8 - Feedback options window
+# 9 - Autopause options window
+
+
+###################################################
+import GemRB
+import GUICommon
+import GUISAVE
+import GUICommonWindows
+from GUIDefines import *
+
+###################################################
+BackgroundWindow = None
+GameOptionsWindow = None
+PortraitWindow = None
+OldPortraitWindow = None
+OptionsWindow = None
+OldOptionsWindow = None
+HelpTextArea = None
+
+LoadMsgWindow = None
+QuitMsgWindow = None
+
+###################################################
+def CloseOptionsWindow ():
+ global BackgroundWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if BackgroundWindow == None:
+ return
+
+ if BackgroundWindow:
+ BackgroundWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ BackgroundWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ return
+
+def OpenOptionsWindow ():
+ """Open main options window"""
+ global BackgroundWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow(OpenOptionsWindow):
+ CloseOptionsWindow()
+ return
+
+ hideflag = GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIOPT", 640, 480)
+ BackgroundWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", BackgroundWindow.ID)
+ #saving the original portrait window
+ if OldPortraitWindow == None:
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenOptionsWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ # Return to Game
+ Button = Window.GetControl (11)
+ Button.SetText (10308)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenOptionsWindow)
+
+ # Quit Game
+ Button = Window.GetControl (10)
+ Button.SetText (13731)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenQuitMsgWindow)
+
+ # Load Game
+ Button = Window.GetControl (5)
+ Button.SetText (13729)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenLoadMsgWindow)
+
+ # Save Game
+ Button = Window.GetControl (6)
+ Button.SetText (13730)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSaveMsgWindow)
+
+ # Graphics
+ Button = Window.GetControl (7)
+ Button.SetText (17162)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenVideoOptionsWindow)
+
+ # Audio
+ Button = Window.GetControl (8)
+ Button.SetText (17164)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenAudioOptionsWindow)
+
+ # Gameplay
+ Button = Window.GetControl (9)
+ Button.SetText (17165)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenGameplayOptionsWindow)
+
+ # game version, e.g. v1.1.0000
+ Label = Window.GetControl (0x1000000b)
+ Label.SetText (GEMRB_VERSION)
+
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+
+###################################################
+def CloseVideoOptionsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+def OpenVideoOptionsWindow ():
+ """Open video options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (6)
+
+ HelpTextArea = OptHelpText ('VideoOptions', Window, 33, 18038)
+
+ OptDone ('VideoOptions', Window, 21)
+ OptCancel ('VideoOptions', Window, 32)
+
+ OptSlider ('Brightness', Window, 3, 'Brightness Correction', 0)
+ OptSlider ('Contrast', Window, 22, 'Gamma Correction', 0)
+
+ OptRadio ('BPP', Window, 5, 37, 'BitsPerPixel', 16)
+ OptRadio ('BPP', Window, 6, 37, 'BitsPerPixel', 24)
+ OptRadio ('BPP', Window, 7, 37, 'BitsPerPixel', 32)
+ OptCheckbox ('FullScreen', Window, 9, 38, 'Full Screen', 1)
+
+ OptCheckbox ('TransShadow', Window, 51, 50, 'Translucent Shadows', 1)
+ OptCheckbox ('SoftMirrBlt', Window, 40, 44, 'SoftMirrorBlt' ,1)
+ OptCheckbox ('SoftTransBlt', Window, 41, 46, 'SoftSrcKeyBlt' ,1)
+ OptCheckbox ('SoftStandBlt', Window, 42, 48, 'SoftBltFast' ,1)
+ OptCheckbox ('TransBlt', Window, 56, 52, 'Translucent Blts' ,1)
+ OptCheckbox ('StaticAnim', Window, 57, 54, 'Static Animations' ,1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def DisplayHelpFullScreen ():
+ HelpTextArea.SetText (18000)
+ GemRB.SetFullScreen (GemRB.GetVar("Full Screen"))
+
+def DisplayHelpBPP ():
+ HelpTextArea.SetText (17205)
+
+def DisplayHelpBrightness ():
+ HelpTextArea.SetText (17203)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction"))
+
+def DisplayHelpContrast ():
+ HelpTextArea.SetText (17204)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction"))
+
+def DisplayHelpTransShadow ():
+ HelpTextArea.SetText (20620)
+
+def DisplayHelpSoftMirrBlt ():
+ HelpTextArea.SetText (15135)
+
+def DisplayHelpSoftTransBlt ():
+ HelpTextArea.SetText (18006)
+
+def DisplayHelpSoftStandBlt ():
+ HelpTextArea.SetText (18007)
+
+def DisplayHelpTransBlt ():
+ HelpTextArea.SetText (15141)
+
+def DisplayHelpStaticAnim ():
+ HelpTextArea.SetText (18004)
+
+###################################################
+
+def CloseAudioOptionsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+def OpenAudioOptionsWindow ():
+ """Open audio options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (7)
+
+ HelpTextArea = OptHelpText ('AudioOptions', Window, 14, 18040)
+
+ OptDone ('AudioOptions', Window, 24)
+ OptCancel ('AudioOptions', Window, 25)
+ OptButton ('CharacterSounds', Window, 13, 17778)
+
+ OptSlider ('AmbientVolume', Window, 1, 'Volume Ambients', 10)
+ OptSlider ('SoundFXVolume', Window, 2, 'Volume SFX', 10)
+ OptSlider ('VoiceVolume', Window, 3, 'Volume Voices', 10)
+ OptSlider ('MusicVolume', Window, 4, 'Volume Music', 10)
+ OptSlider ('MovieVolume', Window, 22, 'Volume Movie', 10)
+
+ OptCheckbox ('CreativeEAX', Window, 26, 28, 'Environmental Audio', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def DisplayHelpAmbientVolume ():
+ HelpTextArea.SetText (18008)
+ GemRB.UpdateAmbientsVolume ()
+
+def DisplayHelpSoundFXVolume ():
+ HelpTextArea.SetText (18009)
+
+def DisplayHelpVoiceVolume ():
+ HelpTextArea.SetText (18010)
+
+def DisplayHelpMusicVolume ():
+ HelpTextArea.SetText (18011)
+ GemRB.UpdateMusicVolume ()
+
+def DisplayHelpMovieVolume ():
+ HelpTextArea.SetText (18012)
+
+def DisplayHelpCreativeEAX ():
+ HelpTextArea.SetText (18022)
+
+###################################################
+
+def CloseGameplayOptionsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+def OpenGameplayOptionsWindow ():
+ """Open gameplay options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (8)
+
+ HelpTextArea = OptHelpText ('GameOptions', Window, 40, 18042)
+
+ OptDone ('GameplayOptions', Window, 7)
+ OptCancel ('GameplayOptions', Window, 20)
+
+ OptSlider ('TooltipDelay', Window, 1, 'Tooltips', 0)
+ OptSlider ('MouseScrollingSpeed', Window, 2, 'Mouse Scroll Speed', 0)
+ OptSlider ('KeyboardScrollingSpeed', Window, 3, 'Keyboard Scroll Speed', 0)
+ OptSlider ('Difficulty', Window, 12, 'Difficulty Level', 0)
+
+ OptCheckbox ('DitherAlways', Window, 14, 25, 'Always Dither', 1)
+ OptCheckbox ('Gore', Window, 19, 27, 'Gore', 1)
+ OptCheckbox ('Infravision', Window, 42, 44, 'Infravision', 1)
+ OptCheckbox ('Weather', Window, 47, 46, 'Weather', 1)
+ OptCheckbox ('MaxHitPoints', Window, 50, 49, 'Maximum HP', 1)
+
+ OptButton ('FeedbackOptions', Window, 5, 17163)
+ OptButton ('AutopauseOptions', Window, 6, 17166)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpTooltipDelay ():
+ HelpTextArea.SetText (18017)
+ GemRB.SetTooltipDelay (GemRB.GetVar ("Tooltips") )
+
+def DisplayHelpMouseScrollingSpeed ():
+ HelpTextArea.SetText (18018)
+ GemRB.SetMouseScrollSpeed (GemRB.GetVar ("Mouse Scroll Speed") )
+
+def DisplayHelpKeyboardScrollingSpeed ():
+ HelpTextArea.SetText (18019)
+
+def DisplayHelpDifficulty ():
+ HelpTextArea.SetText (18020)
+
+def DisplayHelpDitherAlways ():
+ HelpTextArea.SetText (18021)
+
+def DisplayHelpGore ():
+ HelpTextArea.SetText (18023)
+
+def DisplayHelpInfravision ():
+ HelpTextArea.SetText (11797)
+
+def DisplayHelpWeather ():
+ HelpTextArea.SetText (20619)
+
+def DisplayHelpMaxHitPoints ():
+ HelpTextArea.SetText (15136)
+
+###################################################
+def CloseFeedbackOptionsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+ OpenGameplayOptionsWindow ()
+
+def OpenFeedbackOptionsWindow ():
+ """Open feedback options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (9)
+
+ HelpTextArea = OptHelpText ('FeedbackOptions', Window, 28, 18043)
+
+ OptDone ('FeedbackOptions', Window, 26)
+ OptCancel ('FeedbackOptions', Window, 27)
+
+ OptSlider ('MarkerFeedback', Window, 8, 'GUI Feedback Level', 1)
+ OptSlider ('LocatorFeedback', Window, 9, 'Locator Feedback Level', 1)
+
+ OptCheckbox ('ToHitRolls', Window, 10, 32, 'Rolls', 1)
+ OptCheckbox ('CombatInfo', Window, 11, 33, 'Combat Info', 1)
+ OptCheckbox ('Actions', Window, 12, 34, 'Actions', 1)
+ OptCheckbox ('States', Window, 13, 35, 'State Changes', 1)
+ OptCheckbox ('Selection', Window, 14, 36, 'Selection Text', 1)
+ OptCheckbox ('Miscellaneous', Window, 15, 37, 'Miscellaneous Text', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def DisplayHelpMarkerFeedback ():
+ HelpTextArea.SetText (18024)
+
+def DisplayHelpLocatorFeedback ():
+ HelpTextArea.SetText (18025)
+
+def DisplayHelpToHitRolls ():
+ HelpTextArea.SetText (18026)
+
+def DisplayHelpCombatInfo ():
+ HelpTextArea.SetText (18027)
+
+def DisplayHelpActions ():
+ HelpTextArea.SetText (18028)
+
+def DisplayHelpStates ():
+ HelpTextArea.SetText (18029)
+
+def DisplayHelpSelection ():
+ HelpTextArea.SetText (18030)
+
+def DisplayHelpMiscellaneous ():
+ HelpTextArea.SetText (18031)
+
+###################################################
+def CloseAutopauseOptionsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+ OpenGameplayOptionsWindow ()
+
+def OpenAutopauseOptionsWindow ():
+ """Open autopause options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (10)
+
+ HelpTextArea = OptHelpText ('AutopauseOptions', Window, 15, 18044)
+
+ OptDone ('AutopauseOptions', Window, 11)
+ OptCancel ('AutopauseOptions', Window, 14)
+
+ OptCheckbox ('CharacterHit', Window, 1, 17, 'Auto Pause State', 1)
+ OptCheckbox ('CharacterInjured', Window, 2, 18, 'Auto Pause State', 2)
+ OptCheckbox ('CharacterDead', Window, 3, 19, 'Auto Pause State', 4)
+ OptCheckbox ('CharacterAttacked', Window, 4, 20, 'Auto Pause State', 8)
+ OptCheckbox ('WeaponUnusable', Window, 5, 21, 'Auto Pause State', 16)
+ OptCheckbox ('TargetGone', Window, 13, 22, 'Auto Pause State', 32)
+ OptCheckbox ('EndOfRound', Window, 25, 24, 'Auto Pause State', 64)
+ OptCheckbox ('EnemySighted', Window, 30, 31, 'Auto Pause State', 128)
+ OptCheckbox ('SpellCast', Window, 36, 37, 'Auto Pause State', 256)
+ OptCheckbox ('TrapFound', Window, 26, 28, 'Auto Pause State', 512)
+ OptCheckbox ('CenterOnActor', Window, 33, 34, 'Auto Pause Center', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def DisplayHelpCharacterHit ():
+ HelpTextArea.SetText (18032)
+
+def DisplayHelpCharacterInjured ():
+ HelpTextArea.SetText (18033)
+
+def DisplayHelpCharacterDead ():
+ HelpTextArea.SetText (18034)
+
+def DisplayHelpCharacterAttacked ():
+ HelpTextArea.SetText (18035)
+
+def DisplayHelpWeaponUnusable ():
+ HelpTextArea.SetText (18036)
+
+def DisplayHelpTargetGone ():
+ HelpTextArea.SetText (18037)
+
+def DisplayHelpEndOfRound ():
+ HelpTextArea.SetText (10640)
+
+def DisplayHelpEnemySighted ():
+ HelpTextArea.SetText (23514)
+
+def DisplayHelpSpellCast ():
+ HelpTextArea.SetText (26311)
+
+def DisplayHelpTrapFound ():
+ HelpTextArea.SetText (18560)
+
+def DisplayHelpCenterOnActor ():
+ HelpTextArea.SetText (24888)
+
+###################################################
+def CloseCharacterSoundsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+ OpenAudioOptionsWindow ()
+
+def OpenCharacterSoundsWindow ():
+ """Open character sounds options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (12)
+
+ HelpTextArea = OptHelpText ('CharacterSounds', Window, 16, 18041)
+
+ OptDone ('CharacterSounds', Window, 24)
+ OptCancel ('CharacterSounds', Window, 25)
+
+ OptCheckbox ('Subtitles', Window, 5, 20, 'Subtitles', 1)
+ OptCheckbox ('AttackSounds', Window, 6, 18, 'Attack Sounds', 1)
+ OptCheckbox ('Footsteps', Window, 7, 19, 'Footsteps', 1)
+ OptRadio ('CommandSounds', Window, 8, 21, 'Command Sounds Frequency', 1)
+ OptRadio ('CommandSounds', Window, 9, 21, 'Command Sounds Frequency', 2)
+ OptRadio ('CommandSounds', Window, 10, 21, 'Command Sounds Frequency', 3)
+ OptRadio ('SelectionSounds', Window, 58, 57, 'Selection Sounds Frequency', 1)
+ OptRadio ('SelectionSounds', Window, 59, 57, 'Selection Sounds Frequency', 2)
+ OptRadio ('SelectionSounds', Window, 60, 57, 'Selection Sounds Frequency', 3)
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def DisplayHelpSubtitles ():
+ HelpTextArea.SetText (18015)
+
+def DisplayHelpAttackSounds ():
+ HelpTextArea.SetText (18013)
+
+def DisplayHelpFootsteps ():
+ HelpTextArea.SetText (18014)
+
+def DisplayHelpCommandSounds ():
+ HelpTextArea.SetText (18016)
+
+def DisplayHelpSelectionSounds ():
+ HelpTextArea.SetText (11352)
+
+###################################################
+
+def OpenSaveMsgWindow ():
+ GemRB.SetVar("QuitAfterSave",0)
+ GUISAVE.OpenSaveWindow ()
+ #save the game without quitting
+ return
+
+###################################################
+
+def OpenLoadMsgWindow ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ return
+
+ LoadMsgWindow = Window = GemRB.LoadWindow (4)
+
+ # Load
+ Button = Window.GetControl (0)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadGamePress)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseLoadMsgWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Current game will be destroyed ...
+ Text = Window.GetControl (3)
+ Text.SetText (19531)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseLoadMsgWindow ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ LoadMsgWindow.Unload ()
+ LoadMsgWindow = None
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ BackgroundWindow.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def LoadGamePress ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ LoadMsgWindow.Unload ()
+ LoadMsgWindow = None
+ GemRB.QuitGame ()
+ OpenOptionsWindow()
+ GemRB.SetNextScript ("GUILOAD")
+ return
+
+#save game AND quit
+def SaveGamePress():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ #we need to set a state: quit after save
+ GemRB.SetVar("QuitAfterSave",1)
+ OpenOptionsWindow()
+ GUISAVE.OpenSaveWindow ()
+ return
+
+def QuitGamePress():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ GemRB.QuitGame ()
+ OpenOptionsWindow()
+ GemRB.SetNextScript ("Start")
+ return
+
+###################################################
+
+def OpenQuitMsgWindow ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ return
+
+ QuitMsgWindow = Window = GemRB.LoadWindow (5)
+
+ # Save
+ Button = Window.GetControl (0)
+ Button.SetText (15589)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SaveGamePress)
+
+ # Quit Game
+ Button = Window.GetControl (1)
+ Button.SetText (15417)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitGamePress)
+
+ # Cancel
+ Button = Window.GetControl (2)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseQuitMsgWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Do you wish to save the game ....
+ Text = Window.GetControl (3)
+ Text.SetText (16456)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseQuitMsgWindow ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ BackgroundWindow.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+###################################################
+
+# These functions help to setup controls found
+# in Video, Audio, Gameplay, Feedback and Autopause
+# options windows
+
+# These controls are usually made from an active
+# control (button, slider ...) and a label
+
+
+def OptSlider (name, window, slider_id, variable, value):
+ """Standard slider for option windows"""
+ slider = window.GetControl (slider_id)
+ slider.SetVarAssoc (variable, value)
+ slider.SetEvent (IE_GUI_SLIDER_ON_CHANGE, eval("DisplayHelp" + name))
+ return slider
+
+def OptRadio (name, window, button_id, label_id, variable, value):
+ """Standard radio button for option windows"""
+
+ button = window.GetControl (button_id)
+ button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("DisplayHelp" + name))
+ button.SetVarAssoc (variable, value)
+
+ label = window.GetControl (label_id)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+
+ return button
+
+def OptCheckbox (name, window, button_id, label_id, variable, value):
+ """Standard checkbox for option windows"""
+
+ button = window.GetControl (button_id)
+ button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ button.SetSprites ("GMPPARBC",3, 1,2,3,5)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("DisplayHelp" + name))
+ button.SetVarAssoc (variable, value)
+
+ label = window.GetControl (label_id)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+
+ return button
+
+def OptButton (name, window, button_id, label_strref):
+ """Standard subwindow button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Open%sWindow" %name))
+ button.SetText (label_strref)
+
+def OptDone (name, window, button_id):
+ """Standard `Done' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (11973) # Done
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Close%sWindow" %name))
+ button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+def OptCancel (name, window, button_id):
+ """Standard `Cancel' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (13727) # Cancel
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Close%sWindow" %name))
+ button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+def OptHelpText (name, window, text_id, text_strref):
+ """Standard textarea with context help for option windows"""
+ text = window.GetControl (text_id)
+ text.SetText (text_strref)
+ return text
+
+
+###################################################
+# End of file GUIOPT.py
diff --git a/gemrb/GUIScripts/iwd/GUIPR.py b/gemrb/GUIScripts/iwd/GUIPR.py
new file mode 100644
index 0000000..ff4f5a7
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUIPR.py
@@ -0,0 +1,349 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIPR.py - scripts to control the priest spells windows from the GUIPR winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+from GUIDefines import *
+from ie_stats import *
+from ie_action import ACT_CAST
+
+PriestWindow = None
+PriestSpellInfoWindow = None
+PriestSpellLevel = 0
+PriestSpellUnmemorizeWindow = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+def OpenPriestWindow ():
+ import GUICommonWindows
+ global PriestWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenPriestWindow):
+ if PriestWindow:
+ PriestWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ PriestWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIPR", 640, 480)
+ PriestWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", PriestWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.MarkMenuButton (OptionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenPriestWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestPrevLevelPress)
+
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestNextLevelPress)
+
+ # Setup memorized spells buttons
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ Button.SetBorder (0,0,0,0,0,0,0,0,64,0,1)
+ Button.SetSprites ("SPELFRAM",0,0,0,0,0)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Setup book spells buttons
+ for i in range (GUICommon.GetIWDSpellButtonCount()):
+ Button = Window.GetControl (27 + i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdatePriestWindow)
+ UpdatePriestWindow ()
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ #bringing window front
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def UpdatePriestWindow ():
+ global PriestMemorizedSpellList, PriestKnownSpellList
+
+ PriestMemorizedSpellList = []
+ PriestKnownSpellList = []
+
+ Window = PriestWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+ type = IE_SPELL_TYPE_PRIEST
+ level = PriestSpellLevel
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, type, level)
+
+ Label = Window.GetControl (0x10000032)
+ GemRB.SetToken ('LEVEL', str (level + 1))
+ Label.SetText (12137)
+
+ Name = GemRB.GetPlayerName (pc, 0)
+ Label = Window.GetControl (0x10000035)
+ Label.SetText (Name)
+
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+ for i in range (12):
+ Button = Window.GetControl (3 + i)
+ if i < mem_cnt:
+ ms = GemRB.GetMemorizedSpell (pc, type, level, i)
+ Button.SetSpellIcon (ms['SpellResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if ms['Flags']:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellUnmemorizeWindow)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestUnmemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenPriestSpellInfoWindow)
+ spell = GemRB.GetSpell (ms['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ PriestMemorizedSpellList.append (ms['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", i)
+ Button.EnableBorder (0, ms['Flags'] == 0)
+ else:
+ if i < max_mem_cnt:
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+
+ known_cnt = GemRB.GetKnownSpellsCount (pc, type, level)
+ for i in range (GUICommon.GetIWDSpellButtonCount()):
+ Button = Window.GetControl (27 + i)
+ if i < known_cnt:
+ ks = GemRB.GetKnownSpell (pc, type, level, i)
+ Button.SetSpellIcon (ks['SpellResRef'], 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestMemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenPriestSpellInfoWindow)
+ spell = GemRB.GetSpell (ks['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ PriestKnownSpellList.append (ks['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", 100 + i)
+
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ DivineCaster = CommonTables.ClassSkills.GetValue (Class, 1)
+ if DivineCaster == "*":
+ # also check the DRUIDSPELL column
+ DivineCaster = CommonTables.ClassSkills.GetValue (Class, 0)
+ CantCast = DivineCaster == "*" or GemRB.GetPlayerStat(pc, IE_DISABLEDBUTTON)&(1<<ACT_CAST)
+ if CantCast or GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def PriestPrevLevelPress ():
+ global PriestSpellLevel
+
+ if PriestSpellLevel > 0:
+ PriestSpellLevel = PriestSpellLevel - 1
+ UpdatePriestWindow ()
+ return
+
+def PriestNextLevelPress ():
+ global PriestSpellLevel
+
+ if PriestSpellLevel < 6:
+ PriestSpellLevel = PriestSpellLevel + 1
+ UpdatePriestWindow ()
+ return
+
+def RefreshPriestLevel ():
+ global PriestSpellLevel
+
+ PriestSpellLevel = GemRB.GetVar ("PriestSpellLevel")
+ UpdatePriestWindow ()
+ return
+
+def OpenPriestSpellInfoWindow ():
+ global PriestSpellInfoWindow
+
+ if PriestSpellInfoWindow != None:
+ if PriestSpellInfoWindow:
+ PriestSpellInfoWindow.Unload ()
+ PriestSpellInfoWindow = None
+ return
+
+ PriestSpellInfoWindow = Window = GemRB.LoadWindow (3)
+
+ #back
+ Button = Window.GetControl (5)
+ Button.SetText (15416)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellInfoWindow)
+
+ index = GemRB.GetVar ("SpellButton")
+ if index < 100:
+ ResRef = PriestMemorizedSpellList[index]
+ else:
+ ResRef = PriestKnownSpellList[index - 100]
+
+ spell = GemRB.GetSpell (ResRef)
+
+ #Label = Window.GetControl (0x0fffffff)
+ #Label.SetText (spell['SpellName'])
+
+ Label = Window.GetControl (0x10000000)
+ Label.SetText (spell['SpellName'])
+
+ Button = Window.GetControl (2)
+ Button.SetSpellIcon (ResRef, 1)
+
+ Text = Window.GetControl (3)
+ Text.SetText (spell['SpellDesc'])
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnPriestMemorizeSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ if GemRB.MemorizeSpell (pc, type, level, index):
+ UpdatePriestWindow ()
+ return
+
+def OpenPriestSpellRemoveWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ PriestSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (63745)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestRemoveSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClosePriestSpelliUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ClosePriestSpellUnmemorizeWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ if PriestSpellUnmemorizeWindow:
+ PriestSpellUnmemorizeWindow.Unload ()
+ PriestSpellUnmemorizeWindow = None
+ return
+
+def OpenPriestSpellUnmemorizeWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ PriestSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (11824)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestUnmemorizeSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClosePriestSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnPriestUnmemorizeSpell ():
+ if PriestSpellUnmemorizeWindow:
+ ClosePriestSpellUnmemorizeWindow ()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton")
+
+ if GemRB.UnmemorizeSpell (pc, type, level, index):
+ UpdatePriestWindow ()
+ return
+
+def OnPriestRemoveSpell ():
+ if PriestSpellUnmemorizeWindow:
+ ClosePriestSpellRemoveWindow ()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ #remove spell from book
+ GemRB.RemoveSpell (pc, type, level, index)
+ UpdatePriestWindow ()
+ return
+
+###################################################
+# End of file GUIPR.py
diff --git a/gemrb/GUIScripts/iwd/GUIREC.py b/gemrb/GUIScripts/iwd/GUIREC.py
new file mode 100644
index 0000000..4bb1754
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUIREC.py
@@ -0,0 +1,1468 @@
+# -*-python-*-
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2003-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.
+#
+
+
+# GUIREC.py - scripts to control stats/records windows from the GUIREC winpack
+###################################################
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+from ie_restype import *
+import LUCommon
+import LevelUp
+import GUIWORLD
+import DualClass
+import Portrait
+
+###################################################
+RecordsWindow = None
+InformationWindow = None
+BiographyWindow = None
+PortraitWindow = None
+CustomPortraitWindow = None
+OptionsWindow = None
+CustomizeWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+ExportDoneButton = None
+ExportFileName = ""
+
+ScriptsTable = None
+ColorTable = None
+ColorIndex = None
+ScriptTextArea = None
+SelectedTextArea = None
+OldVoiceSet = None
+
+# the available sounds
+SoundSequence = [ '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', \
+ '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', \
+ '25', '26', '27', '28', '29', '30', '31']
+SoundIndex = 0
+
+###################################################
+def OpenRecordsWindow ():
+ global RecordsWindow, PortraitWindow, OptionsWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenRecordsWindow):
+ if InformationWindow: OpenInformationWindow ()
+
+ if RecordsWindow:
+ RecordsWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ if CustomPortraitWindow:
+ CustomPortraitWindow.Unload()
+ RecordsWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIREC", 640, 480) #TODO: remove?
+ RecordsWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", RecordsWindow.ID)
+ # saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenRecordsWindow)
+ OptionsWindow.SetFrame ()
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+
+ # dual class
+ Button = Window.GetControl (0)
+ Button.SetText (7174)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DualClass.DualClassWindow)
+
+ # levelup
+ Button = Window.GetControl (37)
+ Button.SetText (7175)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LevelUp.OpenLevelUpWindow)
+
+ # information
+ Button = Window.GetControl (1)
+ Button.SetText (11946)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenInformationWindow)
+
+ # reform party
+ Button = Window.GetControl (51)
+ Button.SetText (16559)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIWORLD.OpenReformPartyWindow)
+
+ # customize
+ Button = Window.GetControl (50)
+ Button.SetText (10645)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenCustomizeWindow)
+
+ # export
+ Button = Window.GetControl (36)
+ Button.SetText (13956)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenExportWindow)
+
+## # kit info
+## Button = Window.GetControl (52)
+## Button.SetText (61265)
+## Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, KitInfoWindow)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateRecordsWindow)
+ UpdateRecordsWindow ()
+
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def UpdateRecordsWindow ():
+ global stats_overview, alignment_help
+
+ Window = RecordsWindow
+ if not RecordsWindow:
+ print "SelectionChange handler points to non existing window\n"
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # exportable
+ Button = Window.GetControl (36)
+ if GemRB.GetPlayerStat (pc, IE_MC_FLAGS)&MC_EXPORTABLE:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # dual-classable
+ Button = Window.GetControl (0)
+ if GUICommon.CanDualClass (pc):
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ # levelup
+ Button = Window.GetControl (37)
+ if LUCommon.CanLevelUp (pc):
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # name
+ Label = Window.GetControl (0x1000000e)
+ Label.SetText (GemRB.GetPlayerName (pc, 0))
+
+ # portrait
+ Button = Window.GetControl (2)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetPicture (GemRB.GetPlayerPortrait (pc,0), "NOPORTMD")
+
+ # armorclass
+ Label = Window.GetControl (0x10000028)
+ ac = GemRB.GetPlayerStat (pc, IE_ARMORCLASS)
+ ac += GemRB.GetAbilityBonus (IE_DEX, 2, GemRB.GetPlayerStat (pc, IE_DEX) )
+ Label.SetText (str (ac))
+ Label.SetTooltip (17183)
+
+ # hp now
+ Label = Window.GetControl (0x10000029)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_HITPOINTS)))
+ Label.SetTooltip (17184)
+
+ # hp max
+ Label = Window.GetControl (0x1000002a)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)))
+ Label.SetTooltip (17378)
+
+ # stats
+
+ sstr = GemRB.GetPlayerStat (pc, IE_STR)
+ sstrx = GemRB.GetPlayerStat (pc, IE_STREXTRA)
+ cstr = GetStatColor (pc, IE_STR)
+ if sstrx > 0 and sstr==18:
+ sstr = "%d/%02d" %(sstr, sstrx % 100)
+ else:
+ sstr = str (sstr)
+
+ sint = str (GemRB.GetPlayerStat (pc, IE_INT))
+ cint = GetStatColor (pc, IE_INT)
+ swis = str (GemRB.GetPlayerStat (pc, IE_WIS))
+ cwis = GetStatColor (pc, IE_WIS)
+ sdex = str (GemRB.GetPlayerStat (pc, IE_DEX))
+ cdex = GetStatColor (pc, IE_DEX)
+ scon = str (GemRB.GetPlayerStat (pc, IE_CON))
+ ccon = GetStatColor (pc, IE_CON)
+ schr = str (GemRB.GetPlayerStat (pc, IE_CHR))
+ cchr = GetStatColor (pc, IE_CHR)
+
+ Label = Window.GetControl (0x1000002f)
+ Label.SetText (sstr)
+ Label.SetTextColor (cstr[0], cstr[1], cstr[2])
+
+ Label = Window.GetControl (0x10000009)
+ Label.SetText (sdex)
+ Label.SetTextColor (cdex[0], cdex[1], cdex[2])
+
+ Label = Window.GetControl (0x1000000a)
+ Label.SetText (scon)
+ Label.SetTextColor (ccon[0], ccon[1], ccon[2])
+
+ Label = Window.GetControl (0x1000000b)
+ Label.SetText (sint)
+ Label.SetTextColor (cint[0], cint[1], cint[2])
+
+ Label = Window.GetControl (0x1000000c)
+ Label.SetText (swis)
+ Label.SetTextColor (cwis[0], cwis[1], cwis[2])
+
+ Label = Window.GetControl (0x1000000d)
+ Label.SetText (schr)
+ Label.SetTextColor (cchr[0], cchr[1], cchr[2])
+
+ # class
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ Label = Window.GetControl (0x10000030)
+ Label.SetText (ClassTitle)
+
+ # race
+ text = CommonTables.Races.GetValue (CommonTables.Races.FindValue (3, GemRB.GetPlayerStat (pc, IE_RACE)) ,
+ 0)
+
+ Label = Window.GetControl (0x1000000f)
+ Label.SetText (text)
+
+ Table = GemRB.LoadTable ("aligns")
+
+ text = Table.GetValue (Table.FindValue ( 3, GemRB.GetPlayerStat (pc, IE_ALIGNMENT) ), 0)
+ Label = Window.GetControl (0x10000010)
+ Label.SetText (text)
+
+ Label = Window.GetControl (0x10000011)
+ if GemRB.GetPlayerStat (pc, IE_SEX) == 1:
+ Label.SetText (7198)
+ else:
+ Label.SetText (7199)
+
+ # help, info textarea
+ stats_overview = GetStatOverview (pc)
+ Text = Window.GetControl (45)
+ Text.SetText (stats_overview)
+ #TODO: making window visible/shaded depending on the pc's state
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def GetStatColor (pc, stat):
+ a = GemRB.GetPlayerStat (pc, stat)
+ b = GemRB.GetPlayerStat (pc, stat, 1)
+ if a==b:
+ return (255,255,255)
+ if a<b:
+ return (255,255,0)
+ return (0,255,0)
+
+# GemRB.GetPlayerStat wrapper that only returns nonnegative values
+def GSNN (pc, stat):
+ val = GemRB.GetPlayerStat (pc, stat)
+ if val >= 0:
+ return val
+ else:
+ return 0
+
+# LevelDiff is used only from the level up code and holds the level
+# difference for each class
+def GetStatOverview (pc, LevelDiff=[0,0,0]):
+ StateTable = GemRB.LoadTable ("statdesc")
+
+ GS = lambda s, pc=pc: GemRB.GetPlayerStat (pc, s)
+ GB = lambda s, pc=pc: GemRB.GetPlayerStat (pc, s, 1)
+ GA = lambda s, col, pc=pc: GemRB.GetAbilityBonus (s, col, GS (s) )
+
+ stats = []
+ # class levels
+ # 16480 <CLASS>: Level <LEVEL>
+ # Experience: <EXPERIENCE>
+ # Next Level: <NEXTLEVEL>
+
+ # collecting tokens for stat overview
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ GemRB.SetToken ("CLASS", ClassTitle)
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ Class = CommonTables.Classes.FindValue (5, Class)
+ Class = CommonTables.Classes.GetRowName (Class)
+ Dual = GUICommon.IsDualClassed (pc, 1)
+ Multi = GUICommon.IsMultiClassed (pc, 1)
+ XP = GemRB.GetPlayerStat (pc, IE_XP)
+ LevelDrain = GS (IE_LEVELDRAIN)
+
+ if GS (IE_STATE_ID) & STATE_DEAD:
+ stats.append ( (11829,1,'c') ) # DEAD
+ stats.append (None)
+
+ if Multi[0] > 1: # we're multiclassed
+ print "\tMulticlassed"
+ Levels = [GemRB.GetPlayerStat (pc, IE_LEVEL), GemRB.GetPlayerStat (pc, IE_LEVEL2), GemRB.GetPlayerStat (pc, IE_LEVEL3)]
+
+ stats.append ( (19721,1,'c') )
+ stats.append (None)
+ for i in range (Multi[0]):
+ ClassIndex = CommonTables.Classes.FindValue (5, Multi[i+1])
+ ClassTitle = GemRB.GetString (CommonTables.Classes.GetValue (ClassIndex, 2))
+ GemRB.SetToken ("CLASS", ClassTitle)
+ Class = CommonTables.Classes.GetRowName (ClassIndex)
+ GemRB.SetToken ("LEVEL", str (Levels[i]+LevelDiff[i]-int(LevelDrain/Multi[0])) )
+ GemRB.SetToken ("EXPERIENCE", str (XP/Multi[0]) )
+ if LevelDrain:
+ stats.append ( (GemRB.GetString (19720),1,'d') )
+ stats.append ( (GemRB.GetString (57435),1,'d') ) # LEVEL DRAINED
+ else:
+ GemRB.SetToken ("NEXTLEVEL", LUCommon.GetNextLevelExp (Levels[i]+LevelDiff[i], Class) )
+ stats.append ( (GemRB.GetString (16480),"",'d') )
+ stats.append (None)
+ print "\t\tClass (Level):",Class,"(",Levels[i],")"
+
+ elif Dual[0] > 0: # dual classed; first show the new class
+ print "\tDual classed"
+ stats.append ( (19722,1,'c') )
+ stats.append (None)
+
+ Levels = [GemRB.GetPlayerStat (pc, IE_LEVEL), GemRB.GetPlayerStat (pc, IE_LEVEL2), GemRB.GetPlayerStat (pc, IE_LEVEL3)]
+
+ # the levels are stored in the class order (eg. FIGHTER_MAGE)
+ # the current active class does not matter!
+ if GUICommon.IsDualSwap (pc):
+ Levels = [Levels[1], Levels[0], Levels[2]]
+
+ Levels[0] += LevelDiff[0]
+
+ ClassTitle = GemRB.GetString (CommonTables.Classes.GetValue (Dual[2], 2))
+ GemRB.SetToken ("CLASS", ClassTitle)
+ GemRB.SetToken ("LEVEL", str (Levels[0]-LevelDrain))
+ Class = CommonTables.Classes.GetRowName (Dual[2])
+ XP2 = GemRB.GetPlayerStat (pc, IE_XP)
+ GemRB.SetToken ("EXPERIENCE", str (XP2) )
+ if LevelDrain:
+ stats.append ( (GemRB.GetString (19720),1,'d') )
+ stats.append ( (GemRB.GetString (57435),1,'d') ) # LEVEL DRAINED
+ else:
+ GemRB.SetToken ("NEXTLEVEL", LUCommon.GetNextLevelExp (Levels[0], Class) )
+ stats.append ( (GemRB.GetString (16480),"",'d') )
+ stats.append (None)
+ # the first class (shown second)
+ if Dual[0] == 1:
+ ClassTitle = GemRB.GetString (CommonTables.KitList.GetValue (Dual[1], 2))
+ elif Dual[0] == 2:
+ ClassTitle = GemRB.GetString (CommonTables.Classes.GetValue (Dual[1], 2))
+ GemRB.SetToken ("CLASS", ClassTitle)
+ GemRB.SetToken ("LEVEL", str (Levels[1]) )
+
+ # the xp table contains only classes, so we have to determine the base class for kits
+ if Dual[0] == 2:
+ BaseClass = CommonTables.Classes.GetRowName (Dual[1])
+ else:
+ BaseClass = GUICommon.GetKitIndex (pc)
+ BaseClass = CommonTables.KitList.GetValue (BaseClass, 7)
+ BaseClass = CommonTables.Classes.FindValue (5, BaseClass)
+ BaseClass = CommonTables.Classes.GetRowName (BaseClass)
+ # the first class' XP is discarded and set to the minimum level
+ # requirement, so if you don't dual class right after a levelup,
+ # the game would eat some of your XP
+ XP1 = CommonTables.NextLevel.GetValue (BaseClass, str (Levels[1]))
+ GemRB.SetToken ("EXPERIENCE", str (XP1) )
+
+ # inactive until the new class SURPASSES the former
+ if Levels[0] <= Levels[1]:
+ # inactive
+ stats.append ( (19719,1,'c') )
+ else:
+ stats.append ( (19720,1,'c') )
+ stats.append (None)
+ else: # single classed
+ print "\tSingle classed"
+ Level = GemRB.GetPlayerStat (pc, IE_LEVEL) + LevelDiff[0]
+ GemRB.SetToken ("LEVEL", str (Level-LevelDrain))
+ GemRB.SetToken ("EXPERIENCE", str (XP) )
+ if LevelDrain:
+ stats.append ( (19720,1,'c') )
+ stats.append ( (57435,1,'c') ) # LEVEL DRAINED
+ else:
+ GemRB.SetToken ("NEXTLEVEL", LUCommon.GetNextLevelExp (Level, Class) )
+ stats.append ( (16480,1,'c') )
+ stats.append (None)
+ print "\t\tClass (Level):",Class,"(",Level,")"
+
+ # check to see if we have a level diff anywhere
+ if sum (LevelDiff) == 0:
+ effects = GemRB.GetPlayerStates (pc)
+ if len (effects):
+ for c in effects:
+ tmp = StateTable.GetValue (ord(c)-66, 0)
+ stats.append ( (tmp,c,'a') )
+ stats.append (None)
+
+ stats.append (None)
+
+ #proficiencies
+ stats.append ( (8442,1,'c') )
+
+ stats.append ( (9457, str(GS (IE_TOHIT))+" ("+str(GemRB.GetCombatDetails(pc, 0)["ToHit"])+")", '0') )
+ tmp = GS (IE_NUMBEROFATTACKS)
+ if (tmp&1):
+ tmp2 = str (tmp/2) + chr (188)
+ else:
+ tmp2 = str (tmp/2)
+ stats.append ( (9458, tmp2, '') )
+ stats.append ( (9459, GSNN (pc, IE_LORE), '0') )
+ stats.append ( (19224, GS (IE_RESISTMAGIC), '') )
+
+ # party's reputation
+ reptxt = GetReputation (GemRB.GameGetReputation ()/10)
+ stats.append ( (9465, reptxt, '') )
+ stats.append ( (9460, GSNN (pc, IE_LOCKPICKING), '') )
+ stats.append ( (9462, GSNN (pc, IE_TRAPS), '') )
+ stats.append ( (9463, GSNN (pc, IE_PICKPOCKET), '') )
+ stats.append ( (9461, GSNN (pc, IE_STEALTH), '') )
+ stats.append ( (34120, GSNN (pc, IE_HIDEINSHADOWS), '') )
+ stats.append ( (34121, GSNN (pc, IE_DETECTILLUSIONS), '') )
+ stats.append ( (34122, GSNN (pc, IE_SETTRAPS), '') )
+
+ HatedRace = GS (IE_HATEDRACE)
+ if HatedRace:
+ HateTable = GemRB.LoadTable ("haterace")
+ Racist = HateTable.FindValue (1, HatedRace)
+ if Racist != -1:
+ HatedRace = HateTable.GetValue (Racist, 3)
+ stats.append ( (15982, GemRB.GetString (HatedRace), '') )
+
+ stats.append ( (12128, GS (IE_BACKSTABDAMAGEMULTIPLIER), 'x') )
+ stats.append ( (12126, GS (IE_TURNUNDEADLEVEL), '') )
+
+ #this hack only displays LOH if we know the spell
+ #TODO: the core should just not set LOH if the paladin can't learn it
+ if (GUICommon.HasSpell (pc, IE_SPELL_TYPE_INNATE, 0, "SPCL211") >= 0):
+ stats.append ( (12127, GS (IE_LAYONHANDSAMOUNT), '') )
+
+ #script
+ aiscript = GemRB.GetPlayerScript (pc )
+ stats.append ( (2078, aiscript, '') )
+ stats.append (None)
+
+ # 17379 Saving throws
+ stats.append (17379)
+ # 17380 Paralyze/Poison/Death
+ stats.append ( (17380, IE_SAVEVSDEATH, 's') )
+ # 17381 Rod/Staff/Wand
+ stats.append ( (17381, IE_SAVEVSWANDS, 's') )
+ # 17382 Petrify/Polymorph
+ stats.append ( (17382, IE_SAVEVSPOLY, 's') )
+ # 17383 Breath weapon
+ stats.append ( (17383, IE_SAVEVSBREATH, 's') )
+ # 17384 Spells
+ stats.append ( (17384, IE_SAVEVSSPELL, 's') )
+ stats.append (None)
+
+ # 9466 Weapon proficiencies
+ stats.append (9466)
+ table = GemRB.LoadTable ("weapprof")
+ RowCount = table.GetRowCount ()
+ for i in range (RowCount):
+ text = table.GetValue (i, 3)
+ stat = table.GetValue (i, 0)
+ stats.append ( (text, GS (stat)&0x07, '+') )
+ stats.append (None)
+
+ # 11766 AC Bonuses
+ stats.append (11766)
+ # 11770 AC vs. Crushing
+ stats.append ((11770, GS (IE_ACCRUSHINGMOD), ''))
+ # 11767 AC vs. Missile
+ stats.append ((11767, GS (IE_ACMISSILEMOD), ''))
+ # 11769 AC vs. Piercing
+ stats.append ((11769, GS (IE_ACPIERCINGMOD), ''))
+ # 11768 AC vs. Slashing
+ stats.append ((11768, GS (IE_ACSLASHINGMOD), ''))
+ stats.append (None)
+
+ # 10315 Ability bonuses
+ stats.append (10315)
+ value = GemRB.GetPlayerStat (pc, IE_STR)
+ ex = GemRB.GetPlayerStat (pc, IE_STREXTRA)
+ # 10332 to hit
+ stats.append ( (10332, GemRB.GetAbilityBonus (IE_STR,0,value,ex), '0') )
+ # 10336 damage
+ stats.append ( (10336, GemRB.GetAbilityBonus (IE_STR,1,value,ex), '0') )
+ # 10337 open doors (bend bars lift gates)
+ stats.append ( (10337, GemRB.GetAbilityBonus (IE_STR,2,value,ex), '0') )
+ # 10338 weight allowance
+ stats.append ( (10338, GemRB.GetAbilityBonus (IE_STR,3,value,ex), '0') )
+ # 10339 AC
+ stats.append ( (10339, GA (IE_DEX,2), '0') )
+ # 10340 Missile adjustment
+ stats.append ( (10340, GA (IE_DEX,1), '0') )
+ # 10341 Reaction adjustment
+ stats.append ( (10341, GA (IE_DEX,0), '0') )
+ # 10342 CON HP Bonus/Level
+ stats.append ( (10342, GA (IE_CON,0), 'p') )
+ # 10343 Chance To Learn spell
+ if GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_WIZARD, 0, 0)>0:
+ stats.append ( (10343, GA (IE_INT,0), '%' ) )
+ # 10347 Reaction
+ stats.append ( (10347, GA (IE_REPUTATION,0), '0') )
+ stats.append (None)
+
+ # 10344 Bonus Priest spells
+ if GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, 0, 0)>0:
+ stats.append (10344)
+ for level in range (7):
+ GemRB.SetToken ("SPELLLEVEL", str (level+1) )
+ #get the bonus spell count
+ base = GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, level, 0)
+ if base:
+ count = GemRB.GetMemorizableSpellsCount (pc, IE_SPELL_TYPE_PRIEST, level)
+ stats.append ( (GemRB.GetString (10345), count-base, 'b') )
+ stats.append (None)
+
+ # 32204 Resistances
+ stats.append (15544)
+ # 67216 Slashing Attacks
+ stats.append ((11768, GS (IE_RESISTSLASHING), '%'))
+ # 67217 Piercing Attacks
+ stats.append ((11769, GS (IE_RESISTPIERCING), '%'))
+ # 67218 Crushing Attacks
+ stats.append ((11770, GS (IE_RESISTCRUSHING), '%'))
+ # 67219 Missile Attacks
+ stats.append ((11767, GS (IE_RESISTMISSILE), '%'))
+ # Poison
+ stats.append ((14017, GS (IE_RESISTPOISON), '%'))
+ stats.append (None)
+
+ res = []
+ lines = 0
+ for s in stats:
+ try:
+ strref, val, type = s
+ if val == 0 and type != '0':
+ continue
+ if type == '+': #pluses
+ res.append ("[capital=0]"+GemRB.GetString (strref) + ' '+ '+' * val)
+ elif type == 'p': #a plus prefix if positive
+ if val > 0:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ' +' + str (val) )
+ else:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ' ' + str (val) )
+ elif type == 's': #both base and (modified) stat, but only if they differ
+ base = str (GB (val))
+ stat = str (GS (val))
+ if base == stat:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ': ' + base)
+ else:
+ res.append ("[capital=0]" + GemRB.GetString (strref) + ': ' + base + " (" + stat + ")")
+ elif type == 'x': #x character before value
+ res.append ("[capital=0]"+GemRB.GetString (strref) +': x' + str (val) )
+ elif type == 'a': #value (portrait icon) + string
+ res.append ("[capital=2]"+val+" "+GemRB.GetString (strref))
+ elif type == 'b': #strref is an already resolved string
+ res.append ("[capital=0]"+strref+": "+str (val))
+ elif type == 'c': #normal string
+ res.append ("[capital=0]"+GemRB.GetString (strref))
+ elif type == 'd': #strref is an already resolved string
+ res.append ("[capital=0]"+strref)
+ elif type == '0': #normal value
+ res.append (GemRB.GetString (strref) + ': ' + str (val))
+ else: #normal value + type character, for example percent sign
+ res.append ("[capital=0]"+GemRB.GetString (strref) + ': ' + str (val) + type)
+ lines = 1
+ except:
+ if s != None:
+ res.append ( GemRB.GetString (s) )
+ lines = 0
+ else:
+ if lines:
+ res.append ("")
+ lines = 0
+
+ return "\n".join (res)
+
+def GetReputation (repvalue):
+ table = GemRB.LoadTable ("reptxt")
+ if repvalue>20:
+ repvalue=20
+ txt = GemRB.GetString (table.GetValue (repvalue, 0) )
+ return txt+"("+str (repvalue)+")"
+
+def OpenInformationWindow ():
+ global InformationWindow
+
+ if InformationWindow != None:
+ if BiographyWindow: OpenBiographyWindow ()
+
+ if InformationWindow:
+ InformationWindow.Unload ()
+ InformationWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+ return
+
+ InformationWindow = Window = GemRB.LoadWindow (4)
+ GemRB.SetVar ("FloatWindow", InformationWindow.ID)
+
+ # Biography
+ Button = Window.GetControl (26)
+ Button.SetText (18003)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyWindow)
+
+ # Done
+ Button = Window.GetControl (24)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenInformationWindow)
+
+ TotalPartyExp = 0
+ ChapterPartyExp = 0
+ TotalCount = 0
+ ChapterCount = 0
+ for i in range (1, GemRB.GetPartySize () + 1):
+ stat = GemRB.GetPCStats(i)
+ TotalPartyExp = TotalPartyExp + stat['KillsTotalXP']
+ ChapterPartyExp = ChapterPartyExp + stat['KillsChapterXP']
+ TotalCount = TotalCount + stat['KillsTotalCount']
+ ChapterCount = ChapterCount + stat['KillsChapterCount']
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+ stat = GemRB.GetPCStats (pc)
+
+ Label = Window.GetControl (0x10000000)
+ Label.SetText (GemRB.GetPlayerName (pc, 1))
+ # class
+ ClassTitle = GUICommon.GetActorClassTitle (pc)
+ Label = Window.GetControl (0x10000018)
+ Label.SetText (ClassTitle)
+
+ #most powerful vanquished
+ Label = Window.GetControl (0x10000005)
+ #we need getstring, so -1 will translate to empty string
+ Label.SetText (GemRB.GetString (stat['BestKilledName']))
+
+ # NOTE: currentTime is in seconds, joinTime is in seconds * 15
+ # (script updates???). In each case, there are 60 seconds
+ # in a minute, 24 hours in a day, but ONLY 5 minutes in an hour!!
+ # Hence currentTime (and joinTime after div by 15) has
+ # 7200 secs a day (60 * 5 * 24)
+ currentTime = GemRB.GetGameTime ()
+ joinTime = stat['JoinDate'] - stat['AwayTime']
+
+ party_time = currentTime - (joinTime / 15)
+ days = party_time / 7200
+ hours = (party_time % 7200) / 300
+
+ GemRB.SetToken ('GAMEDAYS', str (days))
+ GemRB.SetToken ('HOUR', str (hours))
+ Label = Window.GetControl (0x10000006)
+ #actually it is 16043 <DURATION>, but duration is translated to
+ #16041, hopefully this won't cause problem with international version
+ Label.SetText (16041)
+
+ #favourite spell
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (stat['FavouriteSpell'])
+
+ #favourite weapon
+ Label = Window.GetControl (0x10000008)
+ #actually it is 10479 <WEAPONNAME>, but weaponname is translated to
+ #the real weapon name (which we should set using SetToken)
+ #there are other strings like bow+wname/xbow+wname/sling+wname
+ #are they used?
+ Label.SetText (stat['FavouriteWeapon'])
+
+ #total xp
+ Label = Window.GetControl (0x1000000f)
+ if TotalPartyExp != 0:
+ PartyExp = int ((stat['KillsTotalXP'] * 100) / TotalPartyExp)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ Label = Window.GetControl (0x10000013)
+ if ChapterPartyExp != 0:
+ PartyExp = int ((stat['KillsChapterXP'] * 100) / ChapterPartyExp)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ #total xp
+ Label = Window.GetControl (0x10000010)
+ if TotalCount != 0:
+ PartyExp = int ((stat['KillsTotalCount'] * 100) / TotalCount)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ Label = Window.GetControl (0x10000014)
+ if ChapterCount != 0:
+ PartyExp = int ((stat['KillsChapterCount'] * 100) / ChapterCount)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ Label = Window.GetControl (0x10000011)
+ Label.SetText (str (stat['KillsChapterXP']))
+ Label = Window.GetControl (0x10000015)
+ Label.SetText (str (stat['KillsTotalXP']))
+
+ #count of kills in chapter/game
+ Label = Window.GetControl (0x10000012)
+ Label.SetText (str (stat['KillsChapterCount']))
+ Label = Window.GetControl (0x10000016)
+ Label.SetText (str (stat['KillsTotalCount']))
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OpenBiographyWindow ():
+ global BiographyWindow
+
+ if BiographyWindow != None:
+ if BiographyWindow:
+ BiographyWindow.Unload ()
+ BiographyWindow = None
+ GemRB.SetVar ("FloatWindow", InformationWindow.ID)
+
+ InformationWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+ BiographyWindow = Window = GemRB.LoadWindow (12)
+ GemRB.SetVar ("FloatWindow", BiographyWindow.ID)
+
+ TextArea = Window.GetControl (0)
+ pc = GemRB.GameGetSelectedPCSingle ()
+ TextArea.SetText (GemRB.GetPlayerString (pc, 63) )
+
+ # Done
+ Button = Window.GetControl (2)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyWindow)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OpenExportWindow ():
+ global ExportWindow, NameField, ExportDoneButton
+
+ ExportWindow = GemRB.LoadWindow (13)
+
+ TextArea = ExportWindow.GetControl (2)
+ TextArea.SetText (10962)
+
+ TextArea = ExportWindow.GetControl (0)
+ TextArea.GetCharacters ()
+
+ ExportDoneButton = ExportWindow.GetControl (4)
+ ExportDoneButton.SetText (11973)
+ ExportDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = ExportWindow.GetControl (5)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ NameField = ExportWindow.GetControl (6)
+
+ ExportDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExportDonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExportCancelPress)
+ NameField.SetEvent (IE_GUI_EDIT_ON_CHANGE, ExportEditChanged)
+ ExportWindow.ShowModal (MODAL_SHADOW_GRAY)
+ NameField.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def ExportDonePress():
+ if ExportWindow:
+ ExportWindow.Unload()
+ #save file under name from EditControl
+ return
+
+def ExportCancelPress():
+ if ExportWindow:
+ ExportWindow.Unload()
+ return
+
+def ExportEditChanged():
+ ExportFileName = NameField.QueryText ()
+ if ExportFileName == "":
+ ExportDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ ExportDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def OpenCustomizeWindow ():
+ global CustomizeWindow
+ global PortraitsTable, ScriptsTable, ColorTable
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ if GemRB.GetPlayerStat (pc, IE_MC_FLAGS)&MC_EXPORTABLE:
+ Exportable = 1
+ else:
+ Exportable = 0
+
+ PortraitsTable = GemRB.LoadTable ("PICTURES")
+ ScriptsTable = GemRB.LoadTable ("SCRPDESC")
+ ColorTable = GemRB.LoadTable ("CLOWNCOL")
+ CustomizeWindow = GemRB.LoadWindow (17)
+
+ PortraitSelectButton = CustomizeWindow.GetControl (0)
+ PortraitSelectButton.SetText (11961)
+ if not Exportable:
+ PortraitSelectButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ SoundButton = CustomizeWindow.GetControl (1)
+ SoundButton.SetText (10647)
+ if not Exportable:
+ SoundButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ColorButton = CustomizeWindow.GetControl (2)
+ ColorButton.SetText (10646)
+ if not Exportable:
+ ColorButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ScriptButton = CustomizeWindow.GetControl (3)
+ ScriptButton.SetText (17111)
+
+ BiographyButton = CustomizeWindow.GetControl (9)
+ BiographyButton.SetText (18003)
+ if not Exportable:
+ BiographyButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ TextArea = CustomizeWindow.GetControl (5)
+ TextArea.SetText (11327)
+
+ CustomizeDoneButton = CustomizeWindow.GetControl (7)
+ CustomizeDoneButton.SetText (11973)
+ CustomizeDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ CancelButton = CustomizeWindow.GetControl (8);
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PortraitSelectButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPortraitSelectWindow)
+ SoundButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSoundWindow)
+ ColorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenColorWindow)
+ ScriptButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenScriptWindow)
+ BiographyButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyEditWindow)
+ CustomizeDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomizeDonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomizeCancelPress)
+
+ CustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CustomizeDonePress ():
+ CloseCustomizeWindow ()
+ UpdateRecordsWindow ()
+ return
+
+def CustomizeCancelPress ():
+ CloseCustomizeWindow ()
+ UpdateRecordsWindow ()
+ return
+
+def CloseCustomizeWindow ():
+ global CustomizeWindow
+
+ if CustomizeWindow:
+ CustomizeWindow.Unload ()
+ CustomizeWindow = None
+ return
+
+def OpenSoundWindow ():
+ global SubCustomizeWindow
+ global VoiceList
+ global Gender
+ global OldVoiceSet
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ OldVoiceSet = GemRB.GetPlayerSound (pc)
+ SubCustomizeWindow = GemRB.LoadWindow (20)
+
+ VoiceList = SubCustomizeWindow.GetControl (5)
+ VoiceList.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ Gender = GemRB.GetPlayerStat (pc, IE_SEX, 1)
+
+ VoiceList.SetVarAssoc ("Selected", 0)
+ VoiceList.GetCharSounds()
+ VoiceList.SelectText (OldVoiceSet)
+
+ PlayButton = SubCustomizeWindow.GetControl (7)
+ PlayButton.SetText (17318)
+
+ TextArea = SubCustomizeWindow.GetControl (8)
+ TextArea.SetText (11315)
+
+ DoneButton = SubCustomizeWindow.GetControl (10)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (11)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlaySoundPressed)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneSoundWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSoundWindow)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseSoundWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerSound (pc, OldVoiceSet)
+ CloseSubCustomizeWindow ()
+ return
+
+def DoneSoundWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ CharSound = VoiceList.QueryText ()
+ GemRB.SetPlayerSound (pc, CharSound)
+
+ CloseSubCustomizeWindow ()
+ return
+
+def PlaySoundPressed():
+ global CharSoundWindow, SoundIndex, SoundSequence
+
+ CharSound = VoiceList.QueryText ()
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerSound (pc, CharSound)
+ VoiceSet = GemRB.GetPlayerSound (pc, 1)
+ tmp = SoundIndex
+ while (not GemRB.HasResource (VoiceSet + SoundSequence[SoundIndex], RES_WAV)):
+ NextSound()
+ if SoundIndex == tmp:
+ break
+ else:
+ NextSound()
+
+ GemRB.PlaySound (VoiceSet + SoundSequence[SoundIndex], 0, 0, 5)
+ return
+
+def NextSound():
+ global SoundIndex, SoundSequence
+ SoundIndex += 1
+ if SoundIndex >= len(SoundSequence):
+ SoundIndex = 0
+ return
+
+def OpenColorWindow ():
+ global SubCustomizeWindow
+ global PortraitWindow
+ global PortraitButton
+ global HairButton, SkinButton, MajorButton, MinorButton
+ global HairColor, SkinColor, MajorColor, MinorColor
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ MinorColor = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR)
+ MajorColor = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR)
+ SkinColor = GemRB.GetPlayerStat (pc, IE_SKIN_COLOR)
+ HairColor = GemRB.GetPlayerStat (pc, IE_HAIR_COLOR)
+ SubCustomizeWindow = GemRB.LoadWindow (21)
+
+ PortraitButton = SubCustomizeWindow.GetControl (0)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ HairButton = SubCustomizeWindow.GetControl (3)
+ SkinButton = SubCustomizeWindow.GetControl (4)
+ MajorButton = SubCustomizeWindow.GetControl (5)
+ MinorButton = SubCustomizeWindow.GetControl (6)
+
+ DoneButton = SubCustomizeWindow.GetControl (12)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (13)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ HairButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetHairColor)
+ SkinButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetSkinColor)
+ MajorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetMajorColor)
+ MinorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetMinorColor)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneColorWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ UpdatePaperDoll ()
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DoneColorWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerStat (pc, IE_MINOR_COLOR, MinorColor)
+ GemRB.SetPlayerStat (pc, IE_MAJOR_COLOR, MajorColor)
+ GemRB.SetPlayerStat (pc, IE_SKIN_COLOR, SkinColor)
+ GemRB.SetPlayerStat (pc, IE_HAIR_COLOR, HairColor)
+ CloseSubCustomizeWindow ()
+ return
+
+def UpdatePaperDoll ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Color1 = GemRB.GetPlayerStat (pc, IE_METAL_COLOR)
+ MinorButton.SetBAM ("COLGRAD", 0, 0, MinorColor&0xff)
+ MajorButton.SetBAM ("COLGRAD", 0, 0, MajorColor&0xff)
+ SkinButton.SetBAM ("COLGRAD", 0, 0, SkinColor&0xff)
+ Color5 = GemRB.GetPlayerStat (pc, IE_LEATHER_COLOR)
+ Color6 = GemRB.GetPlayerStat (pc, IE_ARMOR_COLOR)
+ HairButton.SetBAM ("COLGRAD", 0, 0, HairColor&0xff)
+ PortraitButton.SetPLT (GUICommon.GetActorPaperDoll (pc),
+ Color1, MinorColor, MajorColor, SkinColor, Color5, Color6, HairColor, 0, 0)
+ return
+
+def SetHairColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 0
+ PickedColor = HairColor
+ OpenColorPicker ()
+ return
+
+def SetSkinColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 1
+ PickedColor = SkinColor
+ OpenColorPicker ()
+ return
+
+def SetMinorColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 2
+ PickedColor = MinorColor
+ OpenColorPicker ()
+ return
+
+def SetMajorColor ():
+ global ColorIndex, PickedColor
+
+ ColorIndex = 3
+ PickedColor = MajorColor
+ OpenColorPicker ()
+ return
+
+def OpenColorPicker ():
+ global SubSubCustomizeWindow
+ #global Selected
+
+ SubSubCustomizeWindow = GemRB.LoadWindow (22)
+
+ GemRB.SetVar ("Selected",-1)
+ for i in range (1,35):
+ Button = SubSubCustomizeWindow.GetControl (i)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ #Selected = -1
+ for i in range (34):
+ MyColor = ColorTable.GetValue (ColorIndex, i)
+ if MyColor == "*":
+ break
+ Button = SubSubCustomizeWindow.GetControl (i+1)
+ Button.SetBAM("COLGRAD", 2, 0, MyColor)
+ if PickedColor == MyColor:
+ GemRB.SetVar ("Selected",i)
+ #Selected = i
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetVarAssoc("Selected",i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePress)
+
+ SubSubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DonePress():
+ global HairColor, SkinColor, MajorColor, MinorColor
+ global PickedColor
+
+ CloseSubSubCustomizeWindow ()
+ PickedColor=ColorTable.GetValue (ColorIndex, GemRB.GetVar ("Selected"))
+ if ColorIndex==0:
+ HairColor=PickedColor
+ UpdatePaperDoll ()
+ return
+ if ColorIndex==1:
+ SkinColor=PickedColor
+ UpdatePaperDoll ()
+ return
+ if ColorIndex==2:
+ MinorColor=PickedColor
+ UpdatePaperDoll ()
+ return
+
+ MajorColor=PickedColor
+ UpdatePaperDoll ()
+ return
+
+def OpenScriptWindow ():
+ global SubCustomizeWindow
+ global ScriptTextArea, SelectedTextArea
+
+ SubCustomizeWindow = GemRB.LoadWindow (11)
+
+ ScriptTextArea = SubCustomizeWindow.GetControl (2)
+ ScriptTextArea.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ FillScriptList ()
+ pc = GemRB.GameGetSelectedPCSingle ()
+ script = GemRB.GetPlayerScript (pc)
+ scriptindex = ScriptsTable.GetRowIndex (script)
+ GemRB.SetVar ("Selected", scriptindex)
+ ScriptTextArea.SetVarAssoc ("Selected", scriptindex)
+
+ SelectedTextArea = SubCustomizeWindow.GetControl (4)
+ UpdateScriptSelection ()
+
+ DoneButton = SubCustomizeWindow.GetControl (5)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (6)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneScriptWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ ScriptTextArea.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, UpdateScriptSelection)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def FillScriptList ():
+ ScriptTextArea.Clear ()
+ row = ScriptsTable.GetRowCount ()
+ for i in range (row):
+ GemRB.SetToken ("script", ScriptsTable.GetRowName (i) )
+ title = ScriptsTable.GetValue (i,0)
+ if title!=-1:
+ desc = ScriptsTable.GetValue (i,1)
+ txt = GemRB.GetString (title)
+
+ if (desc!=-1):
+ txt += GemRB.GetString (desc)
+
+ ScriptTextArea.Append (txt+"\n", -1)
+
+ else:
+ ScriptTextArea.Append (ScriptsTable.GetRowName (i)+"\n" ,-1)
+
+ return
+
+def DoneScriptWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ script = ScriptsTable.GetRowName (GemRB.GetVar ("Selected") )
+ GemRB.SetPlayerScript (pc, script)
+ CloseSubCustomizeWindow ()
+ return
+
+def UpdateScriptSelection():
+ text = ScriptTextArea.QueryText ()
+ SelectedTextArea.SetText (text)
+ return
+
+def OpenBiographyEditWindow ():
+ global SubCustomizeWindow
+ global BioStrRef
+ global TextArea
+
+ Changed = 0
+ pc = GemRB.GameGetSelectedPCSingle ()
+ BioStrRef = GemRB.GetPlayerString (pc, 74)
+ if BioStrRef != 33347:
+ Changed = 1
+
+ SubCustomizeWindow = GemRB.LoadWindow (51)
+
+ ClearButton = SubCustomizeWindow.GetControl (5)
+ ClearButton.SetText (34881)
+
+ DoneButton = SubCustomizeWindow.GetControl (1)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ RevertButton = SubCustomizeWindow.GetControl (3)
+ RevertButton.SetText (2240)
+ if not Changed:
+ RevertButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = SubCustomizeWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ TextArea = SubCustomizeWindow.GetControl (4)
+ TextArea.SetBufferLength (65535)
+ TextArea.SetText (BioStrRef)
+
+ ClearButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClearBiography)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneBiographyWindow)
+ RevertButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RevertBiography)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ClearBiography():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ BioStrRef = 62015+pc
+ #GemRB.CreateString (BioStrRef, "")
+ TextArea.SetText ("")
+ return
+
+def DoneBiographyWindow ():
+ global BioStrRef
+
+ #TODO set bio
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #pc is 1 based
+ BioStrRef = 62015+pc
+ GemRB.CreateString (BioStrRef, TextArea.QueryText())
+ GemRB.SetPlayerString (pc, 74, BioStrRef)
+ CloseSubCustomizeWindow ()
+ return
+
+def RevertBiography():
+ global BioStrRef
+
+ BioStrRef = 33347
+ TextArea.SetText (33347)
+ return
+
+def CloseSubCustomizeWindow ():
+ global SubCustomizeWindow
+
+ if SubCustomizeWindow:
+ SubCustomizeWindow.Unload ()
+ SubCustomizeWindow = None
+ return
+
+def CloseSubSubCustomizeWindow ():
+ global SubSubCustomizeWindow
+
+ if SubSubCustomizeWindow:
+ SubSubCustomizeWindow.Unload ()
+ SubSubCustomizeWindow = None
+ return
+
+def OpenPortraitSelectWindow ():
+ global CharGenWindow, PortraitWindow, PortraitPictureButton
+
+ PortraitWindow = GemRB.LoadWindow (18)
+
+ PortraitPictureButton = PortraitWindow.GetControl (0)
+ PortraitPictureButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+
+ PortraitLeftButton = PortraitWindow.GetControl (1)
+ PortraitLeftButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitLeftButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitLeftPress)
+ PortraitLeftButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ PortraitRightButton = PortraitWindow.GetControl (2)
+ PortraitRightButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitRightButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitRightPress)
+ PortraitRightButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ PortraitCustomButton = PortraitWindow.GetControl (5)
+ PortraitCustomButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitCustomButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenCustomPortraitWindow)
+ PortraitCustomButton.SetText (17545)
+
+ PortraitDoneButton = PortraitWindow.GetControl (3)
+ PortraitDoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitDonePress)
+ PortraitDoneButton.SetText (11973)
+ PortraitDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ PortraitCancelButton = PortraitWindow.GetControl (4)
+ PortraitCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ PortraitCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitCancelPress)
+ PortraitCancelButton.SetText (13727)
+ PortraitCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # get players gender and portrait
+ Pc = GemRB.GameGetSelectedPCSingle ()
+ PcGender = GemRB.GetPlayerStat (Pc, IE_SEX)
+ PcPortrait = GemRB.GetPlayerPortrait(Pc,0)
+
+ # initialize and set portrait
+ Portrait.Init (PcGender)
+ Portrait.Set (PcPortrait)
+ PortraitPictureButton.SetPicture (Portrait.Name () + "G")
+
+ PortraitWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def PortraitLeftPress ():
+ global PortraitPictureButton
+
+ PortraitPictureButton.SetPicture (Portrait.Previous () + "G")
+
+def PortraitRightPress ():
+ global PortraitPictureButton
+
+ PortraitPictureButton.SetPicture (Portrait.Next () + "G" )
+
+def ClosePortraitSelectWindow ():
+ global PortraitWindow
+
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ PortraitWindow = None
+
+ #UpdateRecordsWindow ()
+ CustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def PortraitCancelPress ():
+ ClosePortraitSelectWindow ()
+ return
+
+def PortraitDonePress ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.FillPlayerInfo (pc, Portrait.Name () + "L", Portrait.Name () + "S")
+ ClosePortraitWindow ()
+ return
+
+def ClosePortraitWindow ():
+ global PortraitWindow
+
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ PortraitWindow = None
+
+ CustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OpenCustomPortraitWindow ():
+ global CustomPortraitWindow
+ global PortraitList1, PortraitList2
+ global RowCount1, RowCount2
+
+ CustomPortraitWindow = GemRB.LoadWindow (19)
+
+ CustomPortraitDoneButton = CustomPortraitWindow.GetControl (10)
+ CustomPortraitDoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ CustomPortraitDoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomPortraitDonePress)
+ CustomPortraitDoneButton.SetText (11973)
+ CustomPortraitDoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CustomPortraitCancelButton = CustomPortraitWindow.GetControl (11)
+ CustomPortraitCancelButton.SetState (IE_GUI_BUTTON_ENABLED)
+ CustomPortraitCancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseCustomPortraitWindow)
+ CustomPortraitCancelButton.SetText (13727)
+ CustomPortraitCancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Portrait List Large
+ PortraitList1 = CustomPortraitWindow.GetControl (2)
+ RowCount1 = PortraitList1.GetPortraits (0)
+ PortraitList1.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, LargeCustomPortrait)
+ GemRB.SetVar ("Row1", RowCount1)
+ PortraitList1.SetVarAssoc ("Row1",RowCount1)
+
+ # Portrait List Small
+ PortraitList2 = CustomPortraitWindow.GetControl (3)
+ RowCount2 = PortraitList2.GetPortraits (1)
+ PortraitList2.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, SmallCustomPortrait)
+ GemRB.SetVar ("Row2", RowCount2)
+ PortraitList2.SetVarAssoc ("Row2",RowCount2)
+
+ CustomPortraitWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+ return
+
+def CustomPortraitDonePress ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.FillPlayerInfo (pc, PortraitList1.QueryText () , PortraitList2.QueryText ())
+
+ CloseCustomPortraitWindow ()
+ ClosePortraitWindow ()
+ return
+
+def CloseCustomPortraitWindow ():
+ global CustomPortraitWindow
+
+ if CustomPortraitWindow:
+ CustomPortraitWindow.ShowModal (MODAL_SHADOW_BLACK)
+ CustomPortraitWindow.Unload ()
+ CustomPortraitWindow = None
+
+ return
+
+def LargeCustomPortrait():
+ global PortraitList1
+
+ Window = CustomPortraitWindow
+
+ Portrait = PortraitList1.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row1") == RowCount1:
+ return
+
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (10)
+ if Portrait=="":
+ Portrait = "NOPORTMD"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList2.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (0)
+ Button.SetPicture (Portrait, "NOPORTMD")
+ return
+
+def SmallCustomPortrait():
+ global PortraitList2
+
+ Window = CustomPortraitWindow
+
+ Portrait = PortraitList2.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row2") == RowCount2:
+ return
+
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (10)
+ if Portrait=="":
+ Portrait = "NOPORTSM"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList1.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (1)
+ Button.SetPicture (Portrait, "NOPORTSM")
+ return
+
+
+###################################################
+# End of file GUIREC.py
diff --git a/gemrb/GUIScripts/iwd/GUISAVE.py b/gemrb/GUIScripts/iwd/GUISAVE.py
new file mode 100644
index 0000000..e6a2091
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUISAVE.py
@@ -0,0 +1,279 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUISAVE.py - Save window
+
+###################################################
+
+import GemRB
+import GUICommon
+import LoadScreen
+from GUIDefines import *
+
+SaveWindow = 0
+ConfirmWindow = 0
+NameField = 0
+SaveButton = 0
+TextAreaControl = 0
+Games = ()
+ScrollBar = 0
+
+def OpenSaveWindow ():
+ global SaveWindow, TextAreaControl, Games, ScrollBar
+
+ if GUICommon.CloseOtherWindow (OpenSaveWindow):
+ CloseSaveWindow ()
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUISAVE", 640, 480)
+ Window = SaveWindow = GemRB.LoadWindow (0)
+ Window.SetFrame ()
+ CancelButton=Window.GetControl (34)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSaveWindow)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ GemRB.SetVar ("LoadIdx",0)
+
+ for i in range(4):
+ Button = Window.GetControl (26+i)
+ Button.SetText (15588)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SavePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ Button = Window.GetControl (30+i)
+ Button.SetText (13957)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ #area previews
+ Button = Window.GetControl (1+i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ #PC portraits
+ for j in range(PARTY_SIZE):
+ Button = Window.GetControl (40+i*PARTY_SIZE+j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ ScrollBar=Window.GetControl (25)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games=GemRB.GetSaveGames ()
+ TopIndex = max (0, len(Games) - 4 + 1) #one more for the 'new game'
+ GemRB.SetVar ("TopIndex",TopIndex)
+ ScrollBar.SetVarAssoc ("TopIndex", TopIndex+1)
+ ScrollBar.SetDefaultScrollBar ()
+ ScrollBarPress ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ScrollBarPress():
+ Window = SaveWindow
+
+ #draw load game portraits
+ Pos = GemRB.GetVar ("TopIndex")
+ for i in range(4):
+ ActPos = Pos + i
+
+ Button1 = Window.GetControl (26+i)
+ Button2 = Window.GetControl (30+i)
+ if ActPos<=len(Games):
+ Button1.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetName()
+ Button2.SetState (IE_GUI_BUTTON_ENABLED)
+ elif ActPos == len(Games):
+ Slotname = 15304
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Slotname = ""
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Label = Window.GetControl (0x10000008+i)
+ Label.SetText (Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = Window.GetControl (0x10000010+i)
+ Label.SetText (Slotname)
+
+ Button=Window.GetControl (1+i)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture("")
+ for j in range(PARTY_SIZE):
+ Button=Window.GetControl (40+i*PARTY_SIZE+j)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture("")
+ return
+
+def AbortedSaveGame():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ConfirmedSaveGame():
+ global ConfirmWindow
+
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ Label = ConfirmWindow.GetControl (3)
+ Slotname = Label.QueryText ()
+ LoadScreen.StartLoadScreen()
+ if Pos < len(Games):
+ GemRB.SaveGame(Games[Pos], Slotname)
+ else:
+ GemRB.SaveGame(None, Slotname)
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def SavePress():
+ global ConfirmWindow, NameField, SaveButton
+
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ ConfirmWindow = GemRB.LoadWindow (1)
+
+ #slot name
+ if Pos<len(Games):
+ Slotname = Games[Pos].GetName()
+ save_strref = 15306
+ else:
+ Slotname = ""
+ save_strref = 15588
+ NameField = ConfirmWindow.GetControl (3)
+ NameField.SetText (Slotname)
+ NameField.SetEvent (IE_GUI_EDIT_ON_CHANGE, EditChange)
+
+ #game hours (should be generated from game)
+ if Pos<len(Games):
+ Slotname = Games[Pos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = ConfirmWindow.GetControl (0x10000004)
+ Label.SetText (Slotname)
+
+ #areapreview
+ Button=ConfirmWindow.GetControl (0)
+ if Pos<len(Games):
+ Button.SetSprite2D(Games[Pos].GetPreview())
+ else:
+ Button.SetPicture("")
+
+ #portraits
+ for j in range(PARTY_SIZE):
+ Button=ConfirmWindow.GetControl (40+j)
+ if Pos<len(Games):
+ Button.SetSprite2D(Games[Pos].GetPortrait(j))
+ else:
+ Button.SetPicture("")
+
+ #save
+ SaveButton=ConfirmWindow.GetControl (7)
+ SaveButton.SetText (save_strref)
+ SaveButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ConfirmedSaveGame)
+ SaveButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ #SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+ if Slotname == "":
+ SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ #cancel
+ CancelButton=ConfirmWindow.GetControl (8)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbortedSaveGame)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ NameField.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def EditChange():
+ Name = NameField.QueryText ()
+ if len(Name)==0:
+ SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ SaveButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def DeleteGameConfirm():
+ global Games
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Pos = TopIndex +GemRB.GetVar ("LoadIdx")
+ GemRB.DeleteSaveGame(Games[Pos])
+ if TopIndex>0:
+ GemRB.SetVar ("TopIndex",TopIndex-1)
+ del Games[pos]
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress()
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress():
+ global ConfirmWindow
+
+ SaveWindow.SetVisible (WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow (2)
+ Text=ConfirmWindow.GetControl (0)
+ Text.SetText (15305)
+ DeleteButton=ConfirmWindow.GetControl (1)
+ DeleteButton.SetText (13957)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ CancelButton=ConfirmWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CloseSaveWindow ():
+ if SaveWindow:
+ SaveWindow.Unload ()
+ if GemRB.GetVar ("QuitAfterSave"):
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("Start")
+ return
+
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE) #enabling the game control screen
+ GemRB.UnhideGUI () #enabling the other windows
+ return
diff --git a/gemrb/GUIScripts/iwd/GUIWORLD.py b/gemrb/GUIScripts/iwd/GUIWORLD.py
new file mode 100644
index 0000000..7b74a6d
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/GUIWORLD.py
@@ -0,0 +1,312 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIW.py - scripts to control some windows from GUIWORLD winpack
+# except of Actions, Portrait, Options and Dialog windows
+#################################################################
+
+import GemRB
+from GUIDefines import *
+from ie_restype import *
+import GUICommon
+import GUICommonWindows
+import GUIClasses
+import MessageWindow
+import CommonWindow
+
+FRAME_PC_SELECTED = 0
+FRAME_PC_TARGET = 1
+
+ContinueWindow = None
+ReformPartyWindow = None
+OldActionsWindow = None
+OldMessageWindow = None
+
+def DialogStarted ():
+ global ContinueWindow, OldActionsWindow
+
+ # try to force-close anything which is open
+ GUICommon.CloseOtherWindow(None)
+ CommonWindow.CloseContainerWindow()
+
+ # we need GUI for dialogs
+ GemRB.UnhideGUI()
+
+ # opening control size to maximum, enabling dialog window
+ GemRB.GameSetScreenFlags(GS_HIDEGUI, OP_NAND)
+ GemRB.GameSetScreenFlags(GS_DIALOG, OP_OR)
+
+ if GUICommonWindows.PortraitWindow:
+ GUICommonWindows.UpdatePortraitWindow ()
+
+ # we want this to happen before we start fiddling with the GUI
+ MessageWindow.UpdateControlStatus()
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ContinueWindow = Window = GemRB.LoadWindow (9)
+
+ GUICommonWindows.EmptyControls()
+ OldActionsWindow = GUICommonWindows.ActionsWindow
+ #GUICommonWindows.ActionsWindow = None
+ OldActionsWindow.SetVisible(WINDOW_INVISIBLE)
+ GemRB.SetVar ("ActionsWindow", -1)
+
+def DialogEnded ():
+ global ContinueWindow, OldActionsWindow
+
+ # TODO: why is this being called at game start?!
+ if not ContinueWindow:
+ return
+
+ #GUICommonWindows.ActionsWindow = OldActionsWindow
+ OldActionsWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.SetVar ("ActionsWindow", OldActionsWindow.ID)
+ GUICommonWindows.UpdateActionsWindow()
+
+ ContinueWindow.Unload ()
+ ContinueWindow = None
+ OldActionsWindow = None
+
+ if GUICommonWindows.PortraitWindow:
+ GUICommonWindows.UpdatePortraitWindow ()
+
+def CloseContinueWindow ():
+ # don't close the actual window now to avoid flickering: we might still want it open
+ GemRB.SetVar ("DialogChoose", GemRB.GetVar ("DialogOption"))
+
+def NextDialogState ():
+ if not ContinueWindow:
+ return
+
+ ContinueWindow.SetVisible(WINDOW_INVISIBLE)
+ OldActionsWindow.SetVisible(WINDOW_VISIBLE)
+
+ MessageWindow.TMessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenEndMessageWindow ():
+ ContinueWindow.SetVisible(WINDOW_VISIBLE)
+ OldActionsWindow.SetVisible(WINDOW_INVISIBLE)
+ Button = ContinueWindow.GetControl (0)
+ Button.SetText (9371)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenContinueMessageWindow ():
+ ContinueWindow.SetVisible(WINDOW_VISIBLE)
+ OldActionsWindow.SetVisible(WINDOW_INVISIBLE)
+ #continue
+ Button = ContinueWindow.GetControl (0)
+ Button.SetText (9372)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def UpdateReformWindow ():
+ Window = ReformPartyWindow
+
+ select = GemRB.GetVar ("Selected")
+
+ need_to_drop = GemRB.GetPartySize ()-PARTY_SIZE
+ if need_to_drop<0:
+ need_to_drop = 0
+
+ #excess player number
+ Label = Window.GetControl (0x1000000f)
+ Label.SetText (str(need_to_drop) )
+
+ #done
+ Button = Window.GetControl (8)
+ if need_to_drop:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ #remove
+ Button = Window.GetControl (15)
+ if select:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ for i in range (PARTY_SIZE+1):
+ Button = Window.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, select == i+2 )
+ #+2 because protagonist is skipped
+ pic = GemRB.GetPlayerPortrait (i+2,1)
+ if not pic:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ continue
+
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ALIGN_BOTTOM|IE_GUI_BUTTON_ALIGN_LEFT, OP_SET)
+ Button.SetPicture (pic, "NOPORTSM")
+ GUICommonWindows.UpdatePortraitWindow ()
+ return
+
+def RemovePlayer ():
+ global ReformPartyWindow
+
+ hideflag = GemRB.HideGUI ()
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+ ReformPartyWindow = Window = GemRB.LoadWindow (25)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+
+ #are you sure
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (17518)
+
+ #confirm
+ Button = Window.GetControl (1)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemovePlayerConfirm)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ #cancel
+ Button = Window.GetControl (2)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemovePlayerCancel)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+def RemovePlayerConfirm ():
+ global ReformPartyWindow
+
+ hideflag = GemRB.HideGUI ()
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+ GemRB.SetVar ("OtherWindow", -1)
+ #removing selected player
+ ReformPartyWindow = None
+ if hideflag:
+ GemRB.UnhideGUI ()
+ GemRB.LeaveParty (GemRB.GetVar("Selected") )
+ OpenReformPartyWindow ()
+ return
+
+def RemovePlayerCancel ():
+ global ReformPartyWindow
+
+ hideflag = GemRB.HideGUI ()
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+ GemRB.SetVar ("OtherWindow", -1)
+ ReformPartyWindow = None
+ if hideflag:
+ GemRB.UnhideGUI ()
+ OpenReformPartyWindow ()
+ return
+
+def OpenReformPartyWindow ():
+ global ReformPartyWindow
+
+ GemRB.SetVar ("Selected", 0)
+ hideflag = GemRB.HideGUI ()
+
+ if ReformPartyWindow:
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+ ReformPartyWindow = None
+
+ GemRB.SetVar ("OtherWindow", -1)
+ #GemRB.LoadWindowPack ("GUIREC")
+ if hideflag:
+ GemRB.UnhideGUI ()
+ #re-enabling party size control
+ GemRB.GameSetPartySize (PARTY_SIZE)
+ return
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ReformPartyWindow = Window = GemRB.LoadWindow (24)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+
+ #PC portraits
+ for j in range (PARTY_SIZE+1):
+ Button = Window.GetControl (j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+ Button.SetBorder (FRAME_PC_SELECTED, 1, 1, 2, 2, 0, 255, 0, 255)
+ #protagonist is skipped
+ index = j + 2
+ Button.SetVarAssoc ("Selected", index)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateReformWindow)
+
+ # Remove
+ Button = Window.GetControl (15)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemovePlayer)
+
+ # Done
+ Button = Window.GetControl (8)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenReformPartyWindow)
+ GemRB.SetVar ("ActionsWindow", -1)
+ UpdateReformWindow ()
+ if hideflag:
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DeathWindow ():
+ #no death movie, but music is changed
+ GemRB.LoadMusicPL ("Theme.mus",1)
+ GemRB.HideGUI ()
+ GemRB.SetTimedEvent (DeathWindowEnd, 10)
+ return
+
+def DeathWindowEnd ():
+ GemRB.GamePause (1,1)
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ Window = GemRB.LoadWindow (17)
+
+ #reason for death
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (16498)
+
+ #load
+ Button = Window.GetControl (1)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadPress)
+
+ #quit
+ Button = Window.GetControl (2)
+ Button.SetText (15417)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitPress)
+
+ GemRB.HideGUI ()
+ GemRB.SetVar ("MessageWindow", -1)
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def QuitPress():
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("Start")
+ return
+
+def LoadPress():
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("GUILOAD")
+ return
+
diff --git a/gemrb/GUIScripts/iwd/LoadScreen.py b/gemrb/GUIScripts/iwd/LoadScreen.py
new file mode 100644
index 0000000..95aa27e
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/LoadScreen.py
@@ -0,0 +1,73 @@
+# -*-python-*-
+# 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.
+#
+
+# LoadScreen.py - display Loading screen
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+
+LoadScreen = None
+
+def SetLoadScreen ():
+ Table = GemRB.LoadTable ("areaload")
+ Area = GemRB.GetGameString (STR_AREANAME)
+ LoadPic = Table.GetValue (Area, Table.GetColumnName(0) )
+ Middle = LoadScreen.GetControl (4)
+ if LoadPic=="*":
+ #HoW loadscreens are GTRSK001-GTRSK010
+ LoadPic = "GTRSK0"+str(GemRB.Roll(1,10,0)).zfill(2)
+ Middle.SetMOS (LoadPic)
+ return
+
+def StartLoadScreen ():
+ global LoadScreen
+
+ GemRB.LoadWindowPack ("guils", 640, 480)
+ LoadScreen = GemRB.LoadWindow (0)
+ LoadScreen.SetFrame ()
+
+ SetLoadScreen()
+ Bar = LoadScreen.GetControl (0)
+ Progress = 0
+ GemRB.SetVar ("Progress", Progress)
+ Bar.SetVarAssoc ("Progress", Progress)
+ Bar.SetEvent (IE_GUI_PROGRESS_END_REACHED, EndLoadScreen)
+ LoadScreen.SetVisible (WINDOW_VISIBLE)
+ return
+
+def EndLoadScreen ():
+ global LoadScreen
+
+ if LoadScreen:
+ Skull = LoadScreen.GetControl (3)
+ Skull.SetMOS ("GTRBPSK2")
+ LoadScreen.SetVisible (WINDOW_VISIBLE)
+ LoadScreen.Unload()
+ LoadScreen = None
+ return
+
+def CloseLoadScreen ():
+ global LoadScreen
+
+ if LoadScreen:
+ LoadScreen.Unload()
+ LoadScreen = None
+ return
diff --git a/gemrb/GUIScripts/iwd/Makefile.am b/gemrb/GUIScripts/iwd/Makefile.am
new file mode 100644
index 0000000..63c2a69
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/Makefile.am
@@ -0,0 +1,4 @@
+iwdscript_DATA = *.py
+iwdscriptdir = $(moddir)/GUIScripts/iwd/
+EXTRA_DIST = *.py
+MOSTLYCLEANFILES = *.pyc
diff --git a/gemrb/GUIScripts/iwd/MessageWindow.py b/gemrb/GUIScripts/iwd/MessageWindow.py
new file mode 100644
index 0000000..34b39bb
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/MessageWindow.py
@@ -0,0 +1,137 @@
+# -*-python-*-
+# 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.
+#
+
+# MessageWindow.py - scripts and GUI for main (walk) window
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+import CommonWindow
+from GUIDefines import *
+import GUIClasses
+
+MessageWindow = 0
+PortraitWindow = 0
+OptionsWindow = 0
+ExpandButton = 0
+ContractButton = 0
+TMessageTA = 0 # for dialog code
+
+def OnLoad():
+ global PortraitWindow, OptionsWindow
+
+ GemRB.GameSetPartySize (PARTY_SIZE)
+ GemRB.GameSetProtagonistMode (2)
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+
+ GUICommonWindows.PortraitWindow = None
+ GUICommonWindows.ActionsWindow = None
+ GUICommonWindows.OptionsWindow = None
+
+ #this is different in IWD (0) and HoW (25)
+ if GUICommon.HasHOW():
+ OptionsWindow = GemRB.LoadWindow (25)
+ else:
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 1, None)
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (1)
+
+ ActionsWindow = GemRB.LoadWindow (3)
+ GUICommonWindows.OpenActionsWindowControls (ActionsWindow)
+
+ GemRB.SetVar ("PortraitWindow", PortraitWindow.ID)
+ GemRB.SetVar ("ActionsWindow", ActionsWindow.ID)
+ GemRB.SetVar ("OptionsWindow", OptionsWindow.ID)
+ GemRB.SetVar ("TopWindow", -1)
+ GemRB.SetVar ("OtherWindow", -1)
+ GemRB.SetVar ("FloatWindow", -1)
+ GemRB.SetVar ("PortraitPosition", 2) #Right
+ GemRB.SetVar ("ActionsPosition", 4) #BottomAdded
+ GemRB.SetVar ("OptionsPosition", 0) #Left
+ GemRB.SetVar ("MessagePosition", 4) #BottomAdded
+ GemRB.SetVar ("OtherPosition", 5) #Inactivating
+ GemRB.SetVar ("TopPosition", 5) #Inactivating
+
+ UpdateControlStatus ()
+
+def UpdateControlStatus ():
+ global MessageWindow, ExpandButton, ContractButton, TMessageTA
+
+ TMessageWindow = 0
+ TMessageTA = 0
+ GSFlags = GemRB.GetMessageWindowSize ()
+ Expand = GSFlags&GS_DIALOGMASK
+ Override = GSFlags&GS_DIALOG
+ GSFlags = GSFlags-Expand
+
+ if Override:
+ Expand = GS_LARGEDIALOG
+
+ MessageWindow = GemRB.GetVar ("MessageWindow")
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+
+ if Expand == GS_MEDIUMDIALOG:
+ TMessageWindow = GemRB.LoadWindow (12)
+ TMessageTA = TMessageWindow.GetControl (1)
+ ExpandButton = TMessageWindow.GetControl (0)
+ ExpandButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnIncreaseSize)
+ ContractButton = TMessageWindow.GetControl (3)
+ ContractButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnDecreaseSize)
+
+ elif Expand == GS_LARGEDIALOG:
+ TMessageWindow = GemRB.LoadWindow (7)
+ TMessageTA = TMessageWindow.GetControl (1)
+ ContractButton = TMessageWindow.GetControl (0)
+ ContractButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnDecreaseSize)
+ else:
+ TMessageWindow = GemRB.LoadWindow (4)
+ TMessageTA = TMessageWindow.GetControl (3)
+ ExpandButton = TMessageWindow.GetControl (2)
+ ExpandButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnIncreaseSize)
+
+ TMessageTA.SetFlags (IE_GUI_TEXTAREA_AUTOSCROLL)
+ TMessageTA.SetHistory (100)
+
+ hideflag = GemRB.HideGUI ()
+ MessageTA = GUIClasses.GTextArea (MessageWindow,GemRB.GetVar ("MessageTextArea"))
+ if MessageWindow>0 and MessageWindow!=TMessageWindow.ID:
+ MessageTA.MoveText (TMessageTA)
+ GUIClasses.GWindow(MessageWindow).Unload()
+
+ GemRB.SetVar ("MessageWindow", TMessageWindow.ID)
+ GemRB.SetVar ("MessageTextArea", TMessageTA.ID)
+ if Override:
+ TMessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ else:
+ GUICommon.GameControl.SetStatus(IE_GUI_CONTROL_FOCUSED)
+
+ if hideflag:
+ GemRB.UnhideGUI ()
+ return
+
+#upgrade savegame to next version
+def GameExpansion():
+ #the original savegames got 0, but the engine upgrades all saves to 3
+ #this is a good place to perform one-time adjustments if needed
+ GemRB.GameSetExpansion(3)
+ return
+
diff --git a/gemrb/GUIScripts/iwd/PartyFormation.py b/gemrb/GUIScripts/iwd/PartyFormation.py
new file mode 100644
index 0000000..b934284
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/PartyFormation.py
@@ -0,0 +1,202 @@
+# -*-python-*-
+# 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.
+#
+
+
+# PartyFormation.py - Single Player Party Formation
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+import GUICommon
+import GUICommonWindows
+import LoadScreen
+
+PartyFormationWindow = 0
+CreateCharWindow = 0
+ExitWindow = 0
+
+def OnLoad ():
+ global PartyFormationWindow
+
+ GUICommonWindows.PortraitWindow = None
+ GUICommonWindows.SelectionChangeHandler = None
+
+ GemRB.LoadWindowPack ("GUISP", 640, 480)
+ PartyFormationWindow = GemRB.LoadWindow (0)
+ PartyFormationWindow.SetFrame ()
+
+ ModifyCharsButton = PartyFormationWindow.GetControl (43)
+ ModifyCharsButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, None) #TODO: ModifyPress
+ ModifyCharsButton.SetStatus (IE_GUI_BUTTON_DISABLED)
+ ModifyCharsButton.SetText (18816)
+
+ ExitButton = PartyFormationWindow.GetControl (30)
+ ExitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitPress)
+ ExitButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ ExitButton.SetText (13906)
+ ExitButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton = PartyFormationWindow.GetControl (28)
+ DoneButton.SetText (11973)
+ Portraits = 0
+
+ for i in range(18,24):
+ Label = PartyFormationWindow.GetControl (0x10000012+i)
+ #removing this label, it just disturbs us
+ Label.SetSize (0, 0)
+ Button = PartyFormationWindow.GetControl (i-12)
+ ResRef = GemRB.GetPlayerPortrait (i-17, 1)
+ if ResRef == "":
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL,OP_SET)
+ else:
+ Button.SetPicture (ResRef)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Portraits = Portraits+1
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ CreateCharButton = PartyFormationWindow.GetControl (i)
+ CreateCharButton.SetVarAssoc ("Slot", i-17)
+ CreateCharButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CreateCharPress)
+ CreateCharButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ CreateCharButton.SetFont ("NORMAL")
+ if ResRef == "":
+ CreateCharButton.SetText (10264)
+ else:
+ CreateCharButton.SetText (GemRB.GetPlayerName (i-17,0) )
+
+ if Portraits == 0:
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, EnterGamePress)
+
+ if not GUICommon.HasHOW():
+ GemRB.SetVar ("SaveDir",1) #using mpsave??
+ GemRB.SetVar ("PlayMode",0) #using second row??
+ else:
+ GemRB.SetGlobal ("EXPANSION_DOOR", "GLOBAL", 1) # entrance to the HOW start
+ if GUICommon.HasTOTL():
+ GemRB.SetGlobal ("9101_SPAWN_HOBART", "GLOBAL", 1)
+ if GemRB.GetVar("ExpansionGame") == 1:
+ GemRB.SetGlobal ("CHAPTER", "GLOBAL", 1)
+ GemRB.SetVar ("SaveDir",1) #using mpsave
+ GemRB.SetVar ("PlayMode",2) #using second row
+ else:
+ GemRB.SetVar ("SaveDir",1) #using mpsave
+ GemRB.SetVar ("PlayMode",0) #using first row
+
+ LoadScreen.CloseLoadScreen()
+ PartyFormationWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CreateCharPress ():
+ global PartyFormationWindow, CreateCharWindow
+
+ PartyFormationWindow.SetVisible (WINDOW_INVISIBLE)
+ CreateCharWindow = GemRB.LoadWindow (3)
+
+ CreateButton = CreateCharWindow.GetControl (0)
+ CreateButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CreateCharCreatePress)
+ CreateButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ CreateButton.SetText (13954)
+ CreateButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ DeleteButton = CreateCharWindow.GetControl (3)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CreateCharDeletePress)
+ DeleteButton.SetStatus (IE_GUI_BUTTON_DISABLED)
+ DeleteButton.SetText (13957)
+
+ CancelButton = CreateCharWindow.GetControl (4)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CreateCharCancelPress)
+ CancelButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ CreateCharWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CreateCharCreatePress ():
+ global PartyFormationWindow, CreateCharWindow
+
+ if CreateCharWindow:
+ CreateCharWindow.Unload ()
+ if PartyFormationWindow:
+ PartyFormationWindow.Unload ()
+ GemRB.SetNextScript ("CharGen")
+ return
+
+def CreateCharDeletePress ():
+ return
+
+def CreateCharCancelPress ():
+ global PartyFormationWindow, CreateCharWindow
+
+ if CreateCharWindow:
+ CreateCharWindow.Unload ()
+ PartyFormationWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ModifyCharsPress ():
+ return
+
+def EnterGamePress ():
+ GemRB.EnterGame ()
+ return
+
+def ExitPress ():
+ global PartyFormationWindow, ExitWindow
+
+ PartyFormationWindow.SetVisible (WINDOW_INVISIBLE)
+ ExitWindow = GemRB.LoadWindow (7)
+
+ ExitButton = ExitWindow.GetControl (1)
+ ExitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitExitPress)
+ ExitButton.SetText (13906)
+ ExitButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = ExitWindow.GetControl (2)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitCancelPress)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ TextArea = ExitWindow.GetControl (0)
+ TextArea.SetText (11329)
+
+ ExitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ExitCancelPress ():
+ global PartyFormationWindow, ExitWindow
+
+ if ExitWindow:
+ ExitWindow.Unload ()
+ PartyFormationWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ExitExitPress ():
+ global PartyFormationWindow, ExitWindow
+
+ if ExitWindow:
+ ExitWindow.Unload ()
+ if PartyFormationWindow:
+ PartyFormationWindow.Unload ()
+ GemRB.SetNextScript ("Start")
+ return
diff --git a/gemrb/GUIScripts/iwd/Portrait.py b/gemrb/GUIScripts/iwd/Portrait.py
new file mode 100644
index 0000000..709407e
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/Portrait.py
@@ -0,0 +1,95 @@
+# -*-python-*-
+# 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.
+#
+
+
+
+# Portrait.py - scripts to control portrait selection and scrolling
+###################################################
+
+import GemRB
+
+###################################################
+
+PortraitCount = 0
+PortraitsTable = None
+Gender = None
+
+# initializes gender and portrait table
+# PortraitGender: 1 Male
+# 2 Female
+def Init (PortraitGender):
+ global PortraitsTable, PortraitCount, Gender
+
+ if PortraitsTable is None:
+ PortraitsTable = GemRB.LoadTable ("PICTURES")
+
+ PortraitCount = 0
+ Gender = PortraitGender
+
+# sets index to given protraitname
+def Set (PortraitName):
+ global PortraitCount
+
+ # removes l or s character at the end
+ PortraitName = PortraitName.rstrip ("[ls]")
+
+ # capitalize PortraitName
+ PortraitName = PortraitName.upper ()
+
+ # search table
+ for i in range(0, PortraitsTable.GetRowCount ()):
+ if PortraitName == PortraitsTable.GetRowName (i).upper ():
+ PortraitCount = i
+ break;
+
+ return
+
+# returns next portrait name
+def Next ():
+ global PortraitCount
+
+ while True:
+ PortraitCount = PortraitCount + 1
+ if PortraitCount == PortraitsTable.GetRowCount ():
+ PortraitCount = 0
+ if PortraitsTable.GetValue (PortraitCount, 0) == Gender:
+ return Name ()
+
+# return previous portrait name
+def Previous ():
+ global PortraitCount
+
+ while True:
+ PortraitCount = PortraitCount - 1
+ if PortraitCount < 0:
+ PortraitCount = PortraitsTable.GetRowCount () - 1
+ if PortraitsTable.GetValue (PortraitCount, 0) == Gender:
+ return Name ()
+
+# gets current portrait name
+def Name ():
+ global PortraitCount
+
+ # if portrait matches not current gender, it will be skipped to
+ # the next portrait that matches
+ while PortraitsTable.GetValue (PortraitCount, 0) != Gender:
+ PortraitCount = PortraitCount + 1
+
+ PortraitName = PortraitsTable.GetRowName (PortraitCount)
+ return PortraitName
diff --git a/gemrb/GUIScripts/iwd/QuitGame.py b/gemrb/GUIScripts/iwd/QuitGame.py
new file mode 100644
index 0000000..0c3e95d
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/QuitGame.py
@@ -0,0 +1,29 @@
+# -*-python-*-
+# 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.
+#
+
+# QuitGame.py - display EndGame sequence
+
+###################################################
+
+import GemRB
+
+def OnLoad ():
+ GemRB.HideGUI ()
+ GemRB.QuitGame ()
+ GemRB.SetNextScript("Start")
diff --git a/gemrb/GUIScripts/iwd/Start.py b/gemrb/GUIScripts/iwd/Start.py
new file mode 100644
index 0000000..d450262
--- /dev/null
+++ b/gemrb/GUIScripts/iwd/Start.py
@@ -0,0 +1,323 @@
+# -*-python-*-
+# 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.
+#
+
+
+# Start.py - intro and main menu screens
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+from ie_restype import *
+import GUICommon
+
+StartWindow = 0
+JoinGameButton = 0
+ProtocolWindow = 0
+GameTypeWindow = 0
+GameType2Window = 0
+ExpansionGame = 0
+QuitWindow = 0
+
+def OnLoad ():
+ global StartWindow, JoinGameButton
+
+ GemRB.SetVar("ExpansionGame", 0)
+
+ skip_videos = GemRB.GetVar ("SkipIntroVideos")
+ if not skip_videos:
+ GemRB.PlayMovie ("BISLOGO", 1)
+ if GUICommon.HasHOW():
+ GemRB.PlayMovie ("WOTC", 1)
+ else:
+ GemRB.PlayMovie ("TSRLOGO", 1)
+ GemRB.PlayMovie("INTRO", 1)
+ GemRB.SetVar ("SkipIntroVideos", 1)
+
+ if GUICommon.HasHOW():
+ GemRB.SetMasterScript("BALDUR","WORLDMAP","EXPMAP")
+ else:
+ GemRB.SetMasterScript("BALDUR","WORLDMAP")
+
+ # Find proper window border for higher resolutions
+ screen_width = GemRB.GetSystemVariable (SV_WIDTH)
+ screen_height = GemRB.GetSystemVariable (SV_HEIGHT)
+ if GemRB.HasResource ("STON08L", RES_MOS):
+ if screen_width == 800:
+ GemRB.LoadWindowFrame ("STON08L", "STON08R", "STON08T", "STON08B")
+ elif screen_width == 1024:
+ GemRB.LoadWindowFrame ("STON10L", "STON10R", "STON10T", "STON10B")
+
+ GemRB.LoadWindowPack("GUICONN", 640, 480)
+
+#main window
+ StartWindow = GemRB.LoadWindow (0)
+ StartWindow.SetFrame ()
+ ProtocolButton = StartWindow.GetControl (0x00)
+ CreateGameButton = StartWindow.GetControl (0x02)
+ LoadGameButton = StartWindow.GetControl (0x07)
+ JoinGameButton = StartWindow.GetControl (0x03)
+ MoviesButton = StartWindow.GetControl (0x08)
+ QuitGameButton = StartWindow.GetControl (0x01)
+ StartWindow.CreateLabel (0x0fff0000, 0,0,800,30, "REALMS2", "", 1)
+ VersionLabel = StartWindow.GetControl (0x0fff0000)
+ VersionLabel.SetText (GEMRB_VERSION)
+ ProtocolButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ CreateGameButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ LoadGameButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ MoviesButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ QuitGameButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ LastProtocol = GemRB.GetVar ("Last Protocol Used")
+ if LastProtocol == 0:
+ ProtocolButton.SetText (15413)
+ JoinGameButton.SetStatus (IE_GUI_BUTTON_DISABLED)
+ elif LastProtocol == 1:
+ ProtocolButton.SetText (13967)
+ JoinGameButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ elif LastProtocol == 2:
+ ProtocolButton.SetText (13968)
+ JoinGameButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ CreateGameButton.SetText (13963)
+ LoadGameButton.SetText (13729)
+ JoinGameButton.SetText (13964)
+ MoviesButton.SetText (15415)
+ QuitGameButton.SetText (13731)
+ QuitGameButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitPress)
+ ProtocolButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProtocolPress)
+ MoviesButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, MoviesPress)
+ LoadGameButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadPress)
+ CreateGameButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CreatePress)
+ StartWindow.SetVisible (WINDOW_VISIBLE)
+ GemRB.LoadMusicPL("Theme.mus",1)
+ return
+
+def ProtocolPress ():
+ global StartWindow, ProtocolWindow
+ StartWindow.SetVisible (WINDOW_INVISIBLE)
+ ProtocolWindow = GemRB.LoadWindow (1)
+
+ #Disabling Unused Buttons in this Window
+ Button = ProtocolWindow.GetControl (2)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button = ProtocolWindow.GetControl (3)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button = ProtocolWindow.GetControl (9)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+
+ SinglePlayerButton = ProtocolWindow.GetControl (10)
+ SinglePlayerButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ SinglePlayerButton.SetText (15413)
+
+ IPXButton = ProtocolWindow.GetControl (0)
+ IPXButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ IPXButton.SetText (13967)
+
+ TCPIPButton = ProtocolWindow.GetControl (1)
+ TCPIPButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ TCPIPButton.SetText (13968)
+
+ SinglePlayerButton.SetVarAssoc ("Last Protocol Used", 0)
+ IPXButton.SetVarAssoc ("Last Protocol Used", 1)
+ TCPIPButton.SetVarAssoc ("Last Protocol Used", 2)
+
+ TextArea = ProtocolWindow.GetControl (7)
+ TextArea.SetText (11316)
+
+ DoneButton = ProtocolWindow.GetControl (6)
+ DoneButton.SetText (11973)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ProtocolDonePress)
+
+ ProtocolWindow.ShowModal (1)
+ return
+
+def ProtocolDonePress ():
+ global StartWindow, ProtocolWindow, JoinGameButton
+ if ProtocolWindow:
+ ProtocolWindow.Unload ()
+ ProtocolWindow = None
+
+ ProtocolButton = StartWindow.GetControl (0x00)
+
+ LastProtocol = GemRB.GetVar ("Last Protocol Used")
+ if LastProtocol == 0:
+ ProtocolButton.SetText (15413)
+ JoinGameButton.SetStatus (IE_GUI_BUTTON_DISABLED)
+ elif LastProtocol == 1:
+ ProtocolButton.SetText (13967)
+ JoinGameButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+ elif LastProtocol == 2:
+ ProtocolButton.SetText (13968)
+ JoinGameButton.SetStatus (IE_GUI_BUTTON_ENABLED)
+
+ StartWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CreatePress ():
+ global StartWindow, GameTypeWindow, ExpansionType
+
+ if not GUICommon.HasHOW():
+ GameTypeReallyDonePress()
+
+ GameTypeWindow = GemRB.LoadWindow (24)
+
+ CancelButton = GameTypeWindow.GetControl (1)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitGameTypePress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton = GameTypeWindow.GetControl (2)
+ DoneButton.SetText (11973)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, GameTypeDonePress)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ FullGameButton = GameTypeWindow.GetControl (4)
+ FullGameButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ FullGameButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, GameTypeButtonPress)
+ FullGameButton.SetText (24869)
+
+ ExpansionGameButton = GameTypeWindow.GetControl (5)
+ ExpansionGameButton.SetFlags (IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ ExpansionGameButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, GameTypeButtonPress)
+ ExpansionGameButton.SetText (24871)
+
+ FullGameButton.SetVarAssoc ("ExpansionGame", 0)
+ ExpansionGameButton.SetVarAssoc ("ExpansionGame", 1)
+
+ GameTypeButtonPress()
+
+ GameTypeWindow.ShowModal (1)
+ return
+
+def GameTypeButtonPress():
+ global GameTypeWindow, ExpansionGame
+
+ ExpansionGame=GemRB.GetVar("ExpansionGame")
+ GameTypeTextArea = GameTypeWindow.GetControl(3)
+ if ExpansionGame == 0:
+ GameTypeTextArea.SetText (24870)
+ elif ExpansionGame == 1:
+ GameTypeTextArea.SetText (24872)
+ return
+
+def GameTypeDonePress():
+ #todo: importing team members from final save (string 26317)?
+ global StartWindow, GameTypeWindow, GameType2Window
+
+ if GameTypeWindow:
+ GameTypeWindow.Unload()
+ GameTypeWindow = None
+
+ if ExpansionGame == 0: #start in Easthaven
+ GameTypeReallyDonePress()
+ elif ExpansionGame == 1: #show a warning message first
+ GameType2Window = GemRB.LoadWindow (25)
+
+ TextArea = GameType2Window.GetControl(0)
+ TextArea.SetText(26318)
+
+ YesButton = GameType2Window.GetControl (1)
+ YesButton.SetText (13912)
+ YesButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, GameTypeReallyDonePress)
+
+ NoButton = GameType2Window.GetControl (2)
+ NoButton.SetText (13913)
+ NoButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitGameTypePress)
+ NoButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ CancelButton = GameType2Window.GetControl (3)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitGameTypePress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GameType2Window.ShowModal(1)
+
+def GameTypeReallyDonePress():
+ global StartWindow, GameTypeWindow, GameType2Window
+
+ if GameType2Window:
+ GameType2Window.Unload()
+ GameType2Window = None
+ if StartWindow:
+ StartWindow.Unload ()
+ StartWindow = None
+
+ GemRB.LoadGame(None)
+ GemRB.SetNextScript ("PartyFormation")
+
+def QuitGameTypePress ():
+ global StartWindow, GameTypeWindow, GameType2Window
+ if GameType2Window:
+ GameType2Window.Unload ()
+ GameType2Window = None
+ if GameTypeWindow:
+ GameTypeWindow.Unload ()
+ GameTypeWindow = None
+ GemRB.SetVar("ExpansionGame", 0)
+ StartWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def LoadPress ():
+ global StartWindow
+ if StartWindow:
+ StartWindow.Unload ()
+ StartWindow = None
+ GemRB.SetNextScript ("GUILOAD")
+ return
+
+def MoviesPress ():
+ global StartWindow
+ if StartWindow:
+ StartWindow.Unload ()
+ StartWindow = None
+ GemRB.SetNextScript ("GUIMOVIE")
+ return
+
+def QuitPress ():
+ global StartWindow, QuitWindow
+ StartWindow.SetVisible (WINDOW_INVISIBLE)
+ QuitWindow = GemRB.LoadWindow (22)
+ CancelButton = QuitWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitCancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ QuitButton = QuitWindow.GetControl (1)
+ QuitButton.SetText (15417)
+ QuitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitQuitPress)
+ QuitButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ TextArea = QuitWindow.GetControl (0)
+ TextArea.SetText (19532)
+ QuitWindow.ShowModal (1)
+ return
+
+def QuitCancelPress ():
+ global StartWindow, QuitWindow
+ if QuitWindow:
+ QuitWindow.Unload ()
+ QuitWindow = None
+ StartWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def QuitQuitPress ():
+ GemRB.Quit ()
+ return
diff --git a/gemrb/GUIScripts/iwd2/Abilities.py b/gemrb/GUIScripts/iwd2/Abilities.py
new file mode 100644
index 0000000..3eeff02
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Abilities.py
@@ -0,0 +1,246 @@
+# 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.
+#
+#
+#character generation, ability (GUICG4)
+import GemRB
+from GUIDefines import *
+import CommonTables
+
+AbilityWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+AbilityTable = 0
+PointsLeft = 16
+Minimum = 0
+Maximum = 0
+Add = 0
+KitIndex = 0
+
+def CalcLimits(Abidx):
+ global Minimum, Maximum, Add
+
+ Abracead = GemRB.LoadTable("ABRACEAD")
+ RaceID = GemRB.GetVar("Race")
+ RowIndex = CommonTables.Races.FindValue(3, RaceID)
+ RaceName = CommonTables.Races.GetRowName(RowIndex)
+
+ Minimum = 3
+ Maximum = 18
+
+ Abclasrq = GemRB.LoadTable("ABCLASRQ")
+ tmp = Abclasrq.GetValue(KitIndex, Abidx)
+ if tmp!=0 and tmp>Minimum:
+ Minimum = tmp
+
+ Abracerq = GemRB.LoadTable("ABRACERQ")
+ Race = Abracerq.GetRowIndex(RaceName)
+ tmp = Abracerq.GetValue(Race, Abidx*2)
+ if tmp!=0 and tmp>Minimum:
+ Minimum = tmp
+
+ tmp = Abracerq.GetValue(Race, Abidx*2+1)
+ if tmp!=0 and tmp>Maximum:
+ Maximum = tmp
+
+ Race = Abracead.GetRowIndex(RaceName)
+ Add = Abracead.GetValue(Race, Abidx)
+ Maximum = Maximum + Add
+ Minimum = Minimum + Add
+ if Minimum<1:
+ Minimum=1
+
+ return
+
+def RollPress():
+ global PointsLeft, Add
+
+ AbilityWindow.Invalidate()
+ GemRB.SetVar("Ability",0)
+ SumLabel = AbilityWindow.GetControl(0x10000002)
+ SumLabel.SetTextColor(255, 255, 0)
+ PointsLeft=16
+ SumLabel.SetUseRGB(1)
+ SumLabel.SetText(str(PointsLeft))
+
+ for i in range(0,6):
+ CalcLimits(i)
+ v = 10+Add
+ b = v//2-5
+ GemRB.SetVar("Ability "+str(i), v )
+ Label = AbilityWindow.GetControl(0x10000003+i)
+ Label.SetText(str(v) )
+
+ Label = AbilityWindow.GetControl(0x10000024+i)
+ Label.SetUseRGB(1)
+ if b<0:
+ Label.SetTextColor(255,0,0)
+ elif b>0:
+ Label.SetTextColor(0,255,0)
+ else:
+ Label.SetTextColor(255,255,255)
+ Label.SetText("%+d"%(b))
+ return
+
+def OnLoad():
+ global AbilityWindow, TextAreaControl, DoneButton
+ global PointsLeft
+ global AbilityTable
+ global KitIndex, Minimum, Maximum
+
+ #enable repeated clicks
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+ Kit = GemRB.GetVar("Class Kit")
+ Class = GemRB.GetVar("Class")-1
+ if Kit == 0:
+ KitName = CommonTables.Classes.GetRowName(Class)
+ else:
+ #rowname is just a number, first value row what we need here
+ KitName = CommonTables.KitList.GetValue(Kit, 0)
+
+ Abclasrq = GemRB.LoadTable("ABCLASRQ")
+ KitIndex = Abclasrq.GetRowIndex(KitName)
+
+ GemRB.LoadWindowPack("GUICG", 800 ,600)
+ AbilityTable = GemRB.LoadTable("ability")
+ AbilityWindow = GemRB.LoadWindow(4)
+
+ RollPress()
+ for i in range(0,6):
+ Button = AbilityWindow.GetControl(i+30)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, JustPress)
+ Button.SetVarAssoc("Ability", i)
+
+ Button = AbilityWindow.GetControl(i*2+16)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, LeftPress)
+ Button.SetVarAssoc("Ability", i )
+
+ Button = AbilityWindow.GetControl(i*2+17)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RightPress)
+ Button.SetVarAssoc("Ability", i )
+
+ BackButton = AbilityWindow.GetControl(36)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = AbilityWindow.GetControl(0)
+ DoneButton.SetText(36789)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ TextAreaControl = AbilityWindow.GetControl(29)
+ TextAreaControl.SetText(17247)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ AbilityWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def RightPress():
+ global PointsLeft
+
+ AbilityWindow.Invalidate()
+ Abidx = GemRB.GetVar("Ability")
+ Ability = GemRB.GetVar("Ability "+str(Abidx) )
+ #should be more elaborate
+ CalcLimits(Abidx)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(Abidx, 1) )
+ if Ability<=Minimum:
+ return
+ Ability -= 1
+ GemRB.SetVar("Ability "+str(Abidx), Ability)
+ PointsLeft = PointsLeft + 1
+ SumLabel = AbilityWindow.GetControl(0x10000002)
+ SumLabel.SetText(str(PointsLeft) )
+ SumLabel.SetTextColor(255, 255, 0)
+ Label = AbilityWindow.GetControl(0x10000003+Abidx)
+ Label.SetText(str(Ability) )
+ Label = AbilityWindow.GetControl(0x10000024+Abidx)
+ b = Ability // 2 - 5
+ if b<0:
+ Label.SetTextColor(255,0,0)
+ elif b>0:
+ Label.SetTextColor(0,255,0)
+ else:
+ Label.SetTextColor(255,255,255)
+ Label.SetText("%+d"%(b))
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ return
+
+def JustPress():
+ Abidx = GemRB.GetVar("Ability")
+ Ability = GemRB.GetVar("Ability "+str(Abidx) )
+ #should be more elaborate
+ CalcLimits(Abidx)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ TextAreaControl.SetText(AbilityTable.GetValue(Abidx, 1) )
+ return
+
+def LeftPress():
+ global PointsLeft
+
+ Abidx = GemRB.GetVar("Ability")
+ AbilityWindow.Invalidate()
+ CalcLimits(Abidx)
+ GemRB.SetToken("MINIMUM",str(Minimum) )
+ GemRB.SetToken("MAXIMUM",str(Maximum) )
+ Ability = GemRB.GetVar("Ability "+str(Abidx) )
+ TextAreaControl.SetText(AbilityTable.GetValue(Abidx, 1) )
+ if PointsLeft == 0:
+ return
+ if Ability>=Maximum: #should be more elaborate
+ return
+ Ability += 1
+ GemRB.SetVar("Ability "+str(Abidx), Ability)
+ PointsLeft = PointsLeft - 1
+ SumLabel = AbilityWindow.GetControl(0x10000002)
+ if PointsLeft == 0:
+ SumLabel.SetTextColor(255, 255, 255)
+ SumLabel.SetText(str(PointsLeft) )
+ Label = AbilityWindow.GetControl(0x10000003+Abidx)
+ Label.SetText(str(Ability) )
+ Label = AbilityWindow.GetControl(0x10000024+Abidx)
+ b = Ability // 2 - 5
+ if b<0:
+ Label.SetTextColor(255,0,0)
+ elif b>0:
+ Label.SetTextColor(0,255,0)
+ else:
+ Label.SetTextColor(255,255,255)
+ Label.SetText("%+d"%(b))
+ if PointsLeft == 0:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def BackPress():
+ #disable repeated clicks
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+ if AbilityWindow:
+ AbilityWindow.Unload()
+ GemRB.SetNextScript("CharGen5")
+ for i in range(6):
+ GemRB.SetVar("Ability "+str(i),0) #scrapping the abilities
+ return
+
+def NextPress():
+ if AbilityWindow:
+ AbilityWindow.Unload()
+ GemRB.SetNextScript("CharGen6") #skills
+ return
diff --git a/gemrb/GUIScripts/iwd2/Alignment.py b/gemrb/GUIScripts/iwd2/Alignment.py
new file mode 100644
index 0000000..356b780
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Alignment.py
@@ -0,0 +1,88 @@
+# 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.
+#
+#
+#character generation, alignment (GUICG3)
+import GemRB
+from GUIDefines import *
+import CommonTables
+
+AlignmentWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+AlignmentTable = 0
+
+def OnLoad():
+ global AlignmentWindow, TextAreaControl, DoneButton
+ global AlignmentTable
+
+ Class = GemRB.GetVar("Class")-1
+ KitName = CommonTables.Classes.GetRowName(Class)
+
+ AlignmentOk = GemRB.LoadTable("ALIGNMNT")
+
+ GemRB.LoadWindowPack("GUICG", 800, 600)
+ AlignmentTable = GemRB.LoadTable("aligns")
+ AlignmentWindow = GemRB.LoadWindow(3)
+ for i in range(9):
+ Button = AlignmentWindow.GetControl(i+2)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetText(AlignmentTable.GetValue(i,0) )
+
+ for i in range(9):
+ Button = AlignmentWindow.GetControl(i+2)
+ if AlignmentOk.GetValue(KitName, AlignmentTable.GetValue(i, 4) ) != 0:
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, AlignmentPress)
+ Button.SetVarAssoc("Alignment", i+1)
+
+ BackButton = AlignmentWindow.GetControl(13)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = AlignmentWindow.GetControl(0)
+ DoneButton.SetText(36789)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = AlignmentWindow.GetControl(11)
+ TextAreaControl.SetText(9602)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ AlignmentWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def AlignmentPress():
+ Alignment = GemRB.GetVar("Alignment")-1
+ TextAreaControl.SetText(AlignmentTable.GetValue(Alignment,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def BackPress():
+ if AlignmentWindow:
+ AlignmentWindow.Unload()
+ GemRB.SetNextScript("CharGen4")
+ GemRB.SetVar("Alignment",-1) #scrapping the alignment value
+ return
+
+def NextPress():
+ if AlignmentWindow:
+ AlignmentWindow.Unload()
+ GemRB.SetNextScript("CharGen5") #appearance
+ return
diff --git a/gemrb/GUIScripts/iwd2/Appearance.py b/gemrb/GUIScripts/iwd2/Appearance.py
new file mode 100644
index 0000000..33d27fe
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Appearance.py
@@ -0,0 +1,271 @@
+# 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.
+#
+#
+#character generation, color (GUICG13)
+import GemRB
+from GUIDefines import *
+import CommonTables
+
+ColorTable = 0
+HairTable = 0
+SkinTable = 0
+ColorWindow = 0
+ColorPicker = 0
+DoneButton = 0
+ColorIndex = 0
+PickedColor = 0
+HairButton = 0
+SkinButton = 0
+MajorButton = 0
+MinorButton = 0
+Color1 = 0
+Color2 = 0
+Color3 = 0
+Color4 = 0
+PDollButton = 0
+PDollResRef = 0
+
+def RefreshPDoll():
+ global ColorWindow, PDollButton
+ global Color1, Color2, Color3, Color4, PDollResRef
+
+ PDollButton.SetFlags(IE_GUI_BUTTON_ANIMATED,OP_OR)
+ PDollButton.SetPLT(PDollResRef, 0, Color4, Color3, Color2, 0, 0, Color1, 0)
+ PDollButton.SetBAM(PDollResRef,10,0,0)
+ return
+
+def OnLoad():
+ global ColorWindow, DoneButton, PDollButton
+ global HairTable, SkinTable, ColorTable
+ global HairButton, SkinButton, MajorButton, MinorButton
+ global Color1, Color2, Color3, Color4, PDollResRef
+
+ GemRB.LoadWindowPack("GUICG", 800, 600)
+ ColorWindow=GemRB.LoadWindow(13)
+
+ Race = CommonTables.Races.FindValue (3, GemRB.GetVar ("Race") )
+ HairTable = GemRB.LoadTable(CommonTables.Races.GetValue(Race, 5))
+ SkinTable = GemRB.LoadTable(CommonTables.Races.GetValue(Race, 6))
+ ColorTable = GemRB.LoadTable("clowncol")
+
+ #set these colors to some default
+ PortraitTable = GemRB.LoadTable("pictures")
+ PortraitIndex = GemRB.GetVar("PortraitIndex")
+ Color1=PortraitTable.GetValue(PortraitIndex,1)
+ Color2=PortraitTable.GetValue(PortraitIndex,2)
+ Color3=PortraitTable.GetValue(PortraitIndex,3)
+ Color4=PortraitTable.GetValue(PortraitIndex,4)
+ PDollButton = ColorWindow.GetControl(1)
+ PDollButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ PDollButton.SetState(IE_GUI_BUTTON_LOCKED)
+
+ HairButton = ColorWindow.GetControl(2)
+ HairButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ HairButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, HairPress)
+ HairButton.SetBAM("COLGRAD", 1, 0, Color1)
+
+ SkinButton = ColorWindow.GetControl(3)
+ SkinButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ SkinButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SkinPress)
+ SkinButton.SetBAM("COLGRAD", 1, 0, Color2)
+
+ MajorButton = ColorWindow.GetControl(5)
+ MajorButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ MajorButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MajorPress)
+ MajorButton.SetBAM("COLGRAD", 1, 0, Color3)
+
+ MinorButton = ColorWindow.GetControl(4)
+ MinorButton.SetFlags(IE_GUI_BUTTON_PICTURE,OP_OR)
+ MinorButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MinorPress)
+ MinorButton.SetBAM("COLGRAD", 1, 0, Color4)
+
+ BackButton = ColorWindow.GetControl(13)
+ BackButton.SetText(15416)
+ DoneButton = ColorWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+
+ # calculate the paperdoll animation id from the race, class and gender
+ PDollTable = GemRB.LoadTable ("avatars")
+ table = GemRB.LoadTable ("avprefr")
+ AnimID = 0x6000 + table.GetValue (GemRB.GetVar("BaseRace"), 0)
+
+ table = GemRB.LoadTable ("avprefc")
+ AnimID = AnimID + table.GetValue (GemRB.GetVar("BaseClass"), 0)
+
+ table = GemRB.LoadTable ("avprefg")
+ AnimID = AnimID + table.GetValue (GemRB.GetVar("Gender"), 0)
+
+ PDollResRef = PDollTable.GetValue (hex(AnimID), "AT_1") + "G1"
+ if PDollResRef == "*G1":
+ print "ERROR, couldn't find the paperdoll! AnimID is", hex(AnimID)
+ print "Falling back to an elven paperdoll."
+ PDollResRef = "CEMB1G1"
+
+ RefreshPDoll()
+ ColorWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def RandomDonePress():
+ #should be better
+ GemRB.SetVar("Selected", GemRB.Roll(1,5,0) )
+ DonePress()
+
+def DonePress():
+ global Color1, Color2, Color3, Color4, ColorWindow, ColorIndex, PickedColor, ColorPicker
+ if ColorPicker:
+ ColorPicker.Unload()
+ ColorWindow.SetVisible(WINDOW_VISIBLE)
+
+ if ColorIndex==0:
+ PickedColor=HairTable.GetValue(GemRB.GetVar("Selected"),0)
+ Color1=PickedColor
+ HairButton.SetBAM("COLGRAD", 1, 0, Color1)
+ RefreshPDoll()
+ return
+ if ColorIndex==1:
+ PickedColor=SkinTable.GetValue(GemRB.GetVar("Selected"),0)
+ Color2=PickedColor
+ SkinButton.SetBAM("COLGRAD", 1, 0, Color2)
+ RefreshPDoll()
+ return
+ if ColorIndex==2:
+ PickedColor=ColorTable.GetValue(0, GemRB.GetVar("Selected"))
+ Color3=PickedColor
+ MajorButton.SetBAM("COLGRAD", 1, 0, Color3)
+ RefreshPDoll()
+ return
+
+ PickedColor=ColorTable.GetValue(1, GemRB.GetVar("Selected"))
+ Color4=PickedColor
+ MinorButton.SetBAM("COLGRAD", 1, 0, Color4)
+ RefreshPDoll()
+ return
+
+def CancelPress():
+ global ColorPicker, ColorWindow
+ if ColorPicker:
+ ColorPicker.Unload ()
+ ColorWindow.SetVisible (WINDOW_VISIBLE)
+
+def GetColor():
+ global ColorPicker, ColorIndex, PickedColor
+
+ ColorPicker=GemRB.LoadWindow(14)
+ GemRB.SetVar("Selected",-1)
+ for i in range(33):
+ Button = ColorPicker.GetControl(i)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ Selected = -1
+ m = 33
+ if ColorIndex==0:
+ m=HairTable.GetRowCount()
+ t=HairTable
+ if ColorIndex==1:
+ m=SkinTable.GetRowCount()
+ t=SkinTable
+ for i in range(m):
+ if ColorIndex<2:
+ MyColor=t.GetValue(i,0)
+ else:
+ MyColor=ColorTable.GetValue(ColorIndex-2, i)
+ if MyColor == "*":
+ break
+ Button = ColorPicker.GetControl(i)
+ Button.SetBAM("COLGRAD", 2, 0, MyColor)
+ if PickedColor == MyColor:
+ GemRB.SetVar("Selected",i)
+ Selected = i
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetVarAssoc("Selected",i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+
+ Button = ColorPicker.GetControl(33)
+ #default button
+ Button.SetVarAssoc("Selected", 0)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RandomDonePress)
+ Button.SetText("RND")
+
+ CancelButton = ColorPicker.GetControl(35)
+ CancelButton.SetText(13727)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ColorPicker.SetVisible(WINDOW_VISIBLE)
+ return
+
+def HairPress():
+ global ColorIndex, PickedColor
+
+# ColorWindow.Unload()
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 0
+ PickedColor = Color1
+ GetColor()
+ return
+
+def SkinPress():
+ global ColorIndex, PickedColor
+
+# ColorWindow.Unload()
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 1
+ PickedColor = Color2
+ GetColor()
+ return
+
+def MajorPress():
+ global ColorIndex, PickedColor
+
+# ColorWindow.Unload()
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 2
+ PickedColor = Color3
+ GetColor()
+ return
+
+def MinorPress():
+ global ColorIndex, PickedColor
+
+# ColorWindow.Unload()
+ ColorWindow.SetVisible(WINDOW_INVISIBLE)
+ ColorIndex = 3
+ PickedColor = Color4
+ GetColor()
+ return
+
+def BackPress():
+ if ColorWindow:
+ ColorWindow.Unload()
+ GemRB.SetNextScript("CharGen7")
+ return
+
+def NextPress():
+ if ColorWindow:
+ ColorWindow.Unload()
+ GemRB.SetVar("Color1",Color1)
+ GemRB.SetVar("Color2",Color2)
+ GemRB.SetVar("Color3",Color3)
+ GemRB.SetVar("Color4",Color4)
+ GemRB.SetNextScript("CSound") #character sound
+ return
diff --git a/gemrb/GUIScripts/iwd2/AutoPause.py b/gemrb/GUIScripts/iwd2/AutoPause.py
new file mode 100644
index 0000000..853aad5
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/AutoPause.py
@@ -0,0 +1,206 @@
+# -*-python-*-
+# 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.
+#
+
+# autopause options using GUIOPT
+
+import GemRB
+from GUIDefines import *
+
+def OnLoad ():
+ global AutoPauseWindow, TextAreaControl
+
+ GemRB.LoadWindowPack ("GUIOPT", 800, 600)
+
+ AutoPauseWindow = GemRB.LoadWindow (10)
+ AutoPauseWindow.SetFrame ( )
+
+ TextAreaControl = AutoPauseWindow.GetControl (15)
+
+ ChHitButton = AutoPauseWindow.GetControl (17)
+ ChHitButtonB = AutoPauseWindow.GetControl (1)
+ ChHitButtonB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ ChInjured = AutoPauseWindow.GetControl (18)
+ ChInjuredB = AutoPauseWindow.GetControl (2)
+ ChInjuredB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ ChDeath = AutoPauseWindow.GetControl (19)
+ ChDeathB = AutoPauseWindow.GetControl (3)
+ ChDeathB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ ChAttacked = AutoPauseWindow.GetControl (20)
+ ChAttackedB = AutoPauseWindow.GetControl (4)
+ ChAttackedB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ WeaponUnusable = AutoPauseWindow.GetControl (21)
+ WeaponUnusableB = AutoPauseWindow.GetControl (5)
+ WeaponUnusableB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ TargetDestroyed = AutoPauseWindow.GetControl (22)
+ TargetDestroyedB = AutoPauseWindow.GetControl (13)
+ TargetDestroyedB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ EndOfRound = AutoPauseWindow.GetControl (24)
+ EndOfRoundB = AutoPauseWindow.GetControl (25)
+ EndOfRoundB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ EnemySighted = AutoPauseWindow.GetControl (31)
+ EnemySightedB = AutoPauseWindow.GetControl (30)
+ EnemySightedB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ SpellCast = AutoPauseWindow.GetControl (37)
+ SpellCastB = AutoPauseWindow.GetControl (36)
+ SpellCastB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ TrapFound = AutoPauseWindow.GetControl (28)
+ TrapFoundB = AutoPauseWindow.GetControl (26)
+ TrapFoundB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ AutopauseCenter = AutoPauseWindow.GetControl (34)
+ AutopauseCenterB = AutoPauseWindow.GetControl (33)
+ AutopauseCenterB.SetSprites ("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ OkButton = AutoPauseWindow.GetControl (11)
+ CancelButton = AutoPauseWindow.GetControl (14)
+
+ TextAreaControl.SetText (18044)
+ OkButton.SetText (11973)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ChHitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ChHitButtonPress)
+ ChHitButtonB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ChHitButtonB.SetEvent (IE_GUI_BUTTON_ON_PRESS, ChHitButtonPress)
+
+ ChInjured.SetEvent (IE_GUI_BUTTON_ON_PRESS, ChInjuredPress)
+ ChInjuredB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ChInjuredB.SetEvent (IE_GUI_BUTTON_ON_PRESS, ChInjuredPress)
+
+ ChDeath.SetEvent (IE_GUI_BUTTON_ON_PRESS, ChDeathPress)
+ ChDeathB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ChDeathB.SetEvent (IE_GUI_BUTTON_ON_PRESS, ChDeathPress)
+
+ ChAttacked.SetEvent (IE_GUI_BUTTON_ON_PRESS, ChAttackedPress)
+ ChAttackedB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ChAttackedB.SetEvent (IE_GUI_BUTTON_ON_PRESS, ChAttackedPress)
+
+ WeaponUnusable.SetEvent (IE_GUI_BUTTON_ON_PRESS, WeaponUnusablePress)
+ WeaponUnusableB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ WeaponUnusableB.SetEvent (IE_GUI_BUTTON_ON_PRESS, WeaponUnusablePress)
+
+ TargetDestroyed.SetEvent (IE_GUI_BUTTON_ON_PRESS, TargetDestroyedPress)
+ TargetDestroyedB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ TargetDestroyedB.SetEvent (IE_GUI_BUTTON_ON_PRESS, TargetDestroyedPress)
+
+ EndOfRound.SetEvent (IE_GUI_BUTTON_ON_PRESS, EndOfRoundPress)
+ EndOfRoundB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ EndOfRoundB.SetEvent (IE_GUI_BUTTON_ON_PRESS, EndOfRoundPress)
+
+ EnemySighted.SetEvent (IE_GUI_BUTTON_ON_PRESS, EnemySightedPress)
+ EnemySightedB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ EnemySightedB.SetEvent (IE_GUI_BUTTON_ON_PRESS, EnemySightedPress)
+
+ SpellCast.SetEvent (IE_GUI_BUTTON_ON_PRESS, SpellCastPress)
+ SpellCastB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ SpellCastB.SetEvent (IE_GUI_BUTTON_ON_PRESS, SpellCastPress)
+
+ TrapFound.SetEvent (IE_GUI_BUTTON_ON_PRESS, TrapFoundPress)
+ TrapFoundB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ TrapFoundB.SetEvent (IE_GUI_BUTTON_ON_PRESS, TrapFoundPress)
+
+ AutopauseCenter.SetEvent (IE_GUI_BUTTON_ON_PRESS, AutopauseCenterPress)
+ AutopauseCenterB.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ AutopauseCenterB.SetEvent (IE_GUI_BUTTON_ON_PRESS, AutopauseCenterPress)
+
+ OkButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OkPress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+
+ AutopauseCenterB.SetVarAssoc ("Auto Pause Center",1)
+
+ ChHitButtonB.SetVarAssoc ("Auto Pause State",1)
+ ChInjuredB.SetVarAssoc ("Auto Pause State",2)
+ ChDeathB.SetVarAssoc ("Auto Pause State",4)
+ ChAttackedB.SetVarAssoc ("Auto Pause State",8)
+ WeaponUnusableB.SetVarAssoc ("Auto Pause State",16)
+ TargetDestroyedB.SetVarAssoc ("Auto Pause State",32)
+ EndOfRoundB.SetVarAssoc ("Auto Pause State",64)
+ EnemySightedB.SetVarAssoc ("Auto Pause State",128)
+ SpellCastB.SetVarAssoc ("Auto Pause State",256)
+ TrapFoundB.SetVarAssoc ("Auto Pause State",512)
+
+ AutoPauseWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ChHitButtonPress ():
+ TextAreaControl.SetText (18032)
+ return
+
+def ChInjuredPress ():
+ TextAreaControl.SetText (18033)
+ return
+
+def ChDeathPress ():
+ TextAreaControl.SetText (18034)
+ return
+
+def ChAttackedPress ():
+ TextAreaControl.SetText (18035)
+ return
+
+def WeaponUnusablePress ():
+ TextAreaControl.SetText (18036)
+ return
+
+def TargetDestroyedPress ():
+ TextAreaControl.SetText (18037)
+ return
+
+def EndOfRoundPress ():
+ TextAreaControl.SetText (10640)
+ return
+
+def EnemySightedPress ():
+ TextAreaControl.SetText (23514)
+ return
+
+def SpellCastPress ():
+ TextAreaControl.SetText (26311)
+ return
+
+def TrapFoundPress ():
+ TextAreaControl.SetText (18560) #iwd2 has it here
+ return
+
+def AutopauseCenterPress ():
+ TextAreaControl.SetText (24888) #iwd2 has it here
+ return
+
+def OkPress ():
+ if AutoPauseWindow:
+ AutoPauseWindow.Unload ()
+ GemRB.SetNextScript ("GamePlay")
+ return
+
+def CancelPress ():
+ if AutoPauseWindow:
+ AutoPauseWindow.Unload ()
+ GemRB.SetNextScript ("GamePlay")
+ return
diff --git a/gemrb/GUIScripts/iwd2/Autodetect.py b/gemrb/GUIScripts/iwd2/Autodetect.py
new file mode 100644
index 0000000..eb6324c
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Autodetect.py
@@ -0,0 +1,41 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+# GemRB - Infinity Engine Emulator
+# Copyright (C) 2010 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.
+#
+
+import GemRB
+from ie_restype import *
+from AutodetectCommon import CheckFiles
+
+files = (
+ ("START", "CHU", RES_CHU),
+ ("STARTPOS", "2DA", RES_2DA),
+ ("STARTARE", "2DA", RES_2DA),
+
+ ("EXPTABLE", "2DA", RES_2DA),
+ ("MUSIC", "2DA", RES_2DA),
+ ("CHMB1A1", "BAM", RES_BAM),
+ ("TRACKING", "2DA", RES_2DA),
+
+ ("SUBRACES", "2DA", RES_2DA),
+)
+
+
+if CheckFiles(files):
+ GemRB.AddGameTypeHint ("iwd2", 100)
+
diff --git a/gemrb/GUIScripts/iwd2/CMakeLists.txt b/gemrb/GUIScripts/iwd2/CMakeLists.txt
new file mode 100644
index 0000000..b6ffcaa
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CMakeLists.txt
@@ -0,0 +1,3 @@
+FILE( GLOB FILES_TO_INSTALL *.py )
+
+INSTALL( FILES ${FILES_TO_INSTALL} DESTINATION ${DATA_DIR}/GUIScripts/iwd2 )
diff --git a/gemrb/GUIScripts/iwd2/CSound.py b/gemrb/GUIScripts/iwd2/CSound.py
new file mode 100644
index 0000000..561150b
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CSound.py
@@ -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.
+#
+#
+#character generation, sound (GUICG19)
+import GemRB
+from GUIDefines import *
+
+SoundWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+TopIndex = 0
+
+def OnLoad():
+ global SoundWindow, TextAreaControl, DoneButton, TopIndex
+
+ GemRB.LoadWindowPack("GUICG", 800, 600)
+ #this hack will redraw the base CG window
+ SoundWindow = GemRB.LoadWindow(19)
+ GemRB.SetVar("Sound",0) #scrapping the sound value
+
+ BackButton = SoundWindow.GetControl(10)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = SoundWindow.GetControl(0)
+ DoneButton.SetText(36789)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = SoundWindow.GetControl(50)
+ TextAreaControl.SetText(17236)
+
+ TextAreaControl = SoundWindow.GetControl(45)
+ TextAreaControl.SetFlags(IE_GUI_TEXTAREA_SELECTABLE)
+ TextAreaControl.SetVarAssoc("Sound", 0)
+ RowCount=TextAreaControl.GetCharSounds()
+
+ DefaultButton = SoundWindow.GetControl(47)
+ DefaultButton.SetText(33479)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ DefaultButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DefaultPress)
+ SoundWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def DefaultPress():
+ GemRB.SetVar("Sound",0) #scrapping the sound value
+ TextAreaControl.SetVarAssoc("Sound", 0)
+ return
+
+def BackPress():
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("Appearance")
+ GemRB.SetVar("Sound",0) #scrapping the sound value
+ return
+
+def NextPress():
+ GemRB.SetToken("VoiceSet", TextAreaControl.QueryText())
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("CharGen8") #name
+ return
diff --git a/gemrb/GUIScripts/iwd2/CharGen.py b/gemrb/GUIScripts/iwd2/CharGen.py
new file mode 100644
index 0000000..2d5ddf2
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharGen.py
@@ -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.
+#
+#
+#character generation (GUICG 0)
+import GemRB
+import CharOverview
+
+def OnLoad():
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.CreatePlayer ("charbase", MyChar | 0x8000 )
+
+ CharOverview.UpdateOverview(1)
+ return
+
diff --git a/gemrb/GUIScripts/iwd2/CharGen2.py b/gemrb/GUIScripts/iwd2/CharGen2.py
new file mode 100644
index 0000000..237978c
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharGen2.py
@@ -0,0 +1,24 @@
+# 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.
+#
+#
+#character generation (GUICG 0)
+import CharOverview
+
+def OnLoad():
+ CharOverview.UpdateOverview(2)
+ return
diff --git a/gemrb/GUIScripts/iwd2/CharGen3.py b/gemrb/GUIScripts/iwd2/CharGen3.py
new file mode 100644
index 0000000..e42e018
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharGen3.py
@@ -0,0 +1,31 @@
+# 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.
+#
+#
+#character generation (GUICG 0)
+import GemRB
+from ie_stats import *
+import CharOverview
+
+def OnLoad():
+ MyChar = GemRB.GetVar("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_SEX, GemRB.GetVar ("Gender") )
+ GemRB.SetPlayerStat (MyChar, IE_RACE, GemRB.GetVar ("BaseRace") )
+ race = GemRB.GetVar ("Race")
+ GemRB.SetPlayerStat (MyChar, IE_SUBRACE, race & 255 )
+ CharOverview.UpdateOverview(3)
+ return
diff --git a/gemrb/GUIScripts/iwd2/CharGen4.py b/gemrb/GUIScripts/iwd2/CharGen4.py
new file mode 100644
index 0000000..a85f2ff
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharGen4.py
@@ -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.
+#
+#
+#character generation (GUICG 0)
+import GemRB
+from ie_stats import *
+import CharOverview
+
+#this is the same list as in GUIREC
+#barbarian, bard, cleric, druid, fighter, monk, paladin, ranger, rogue, sorcerer, wizard
+Classes = [IE_LEVELBARBARIAN, IE_LEVELBARD, IE_LEVELCLERIC, IE_LEVELDRUID, \
+IE_LEVEL, IE_LEVELMONK, IE_LEVELPALADIN, IE_LEVELRANGER, IE_LEVEL3, \
+IE_LEVELSORCEROR, IE_LEVEL2]
+
+def OnLoad():
+ MyChar = GemRB.GetVar ("Slot")
+ #base class
+ Class=GemRB.GetVar ("BaseClass")
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, Class)
+ #kit
+ GemRB.SetPlayerStat (MyChar, IE_KIT, GemRB.GetVar ("Class") )
+
+ #works only for the first level character generation
+ #if this code ever needs to be more versatile, consider saving the
+ #class values somewhere
+ for i in range(len(Classes)):
+ GemRB.SetPlayerStat (MyChar, Classes[i], 0)
+
+ GemRB.SetPlayerStat (MyChar, Classes[Class-1], 1)
+ print "Set class stat ",Classes[Class-1], " to 1"
+ CharOverview.UpdateOverview(4)
+ return
diff --git a/gemrb/GUIScripts/iwd2/CharGen5.py b/gemrb/GUIScripts/iwd2/CharGen5.py
new file mode 100644
index 0000000..fad072c
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharGen5.py
@@ -0,0 +1,24 @@
+# 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.
+#
+#
+#character generation (GUICG 0)
+import CharOverview
+
+def OnLoad():
+ CharOverview.UpdateOverview(5)
+ return
diff --git a/gemrb/GUIScripts/iwd2/CharGen6.py b/gemrb/GUIScripts/iwd2/CharGen6.py
new file mode 100644
index 0000000..3ebf8ab
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharGen6.py
@@ -0,0 +1,34 @@
+# 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.
+#
+#
+#character generation (GUICG 0)
+import GemRB
+from ie_stats import *
+import CharOverview
+
+def OnLoad():
+ #setting the stats so the feat code will work
+ MyChar = GemRB.GetVar("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_STR, GemRB.GetVar ("Ability 1"))
+ GemRB.SetPlayerStat (MyChar, IE_DEX, GemRB.GetVar ("Ability 2"))
+ GemRB.SetPlayerStat (MyChar, IE_CON, GemRB.GetVar ("Ability 3"))
+ GemRB.SetPlayerStat (MyChar, IE_INT, GemRB.GetVar ("Ability 4"))
+ GemRB.SetPlayerStat (MyChar, IE_WIS, GemRB.GetVar ("Ability 5"))
+ GemRB.SetPlayerStat (MyChar, IE_CHR, GemRB.GetVar ("Ability 6"))
+ CharOverview.UpdateOverview(6)
+ return
diff --git a/gemrb/GUIScripts/iwd2/CharGen7.py b/gemrb/GUIScripts/iwd2/CharGen7.py
new file mode 100644
index 0000000..7a3656e
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharGen7.py
@@ -0,0 +1,25 @@
+# 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.
+#
+#
+#character generation (GUICG 0)
+import CharOverview
+
+def OnLoad():
+ CharOverview.UpdateOverview(7)
+ return
+
diff --git a/gemrb/GUIScripts/iwd2/CharGen8.py b/gemrb/GUIScripts/iwd2/CharGen8.py
new file mode 100644
index 0000000..5315745
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharGen8.py
@@ -0,0 +1,24 @@
+# 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.
+#
+#
+#character generation (GUICG 0)
+import CharOverview
+
+def OnLoad():
+ CharOverview.UpdateOverview(8)
+ return
diff --git a/gemrb/GUIScripts/iwd2/CharGen9.py b/gemrb/GUIScripts/iwd2/CharGen9.py
new file mode 100644
index 0000000..d49bc60
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharGen9.py
@@ -0,0 +1,218 @@
+# 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.
+#
+#
+#character generation (GUICG 0)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import GUICommon
+import CommonTables
+import CharOverview
+
+BioWindow = 0
+EditControl = 0
+AlignmentTable = 0
+PortraitName = ""
+
+def OnLoad():
+ CharOverview.UpdateOverview(9)
+ if CharOverview.CharGenWindow:
+ CharOverview.PersistButtons['Next'].SetState(IE_GUI_BUTTON_UNPRESSED) # Fixes button being pre-pressed
+ return
+
+def SetRaceAbilities(MyChar, racetitle):
+ ability = GemRB.LoadTable ("racespab")
+ resource = ability.GetValue (racetitle, "SPECIAL_ABILITIES_FILE")
+ if resource=="*":
+ return
+
+ ability = GemRB.LoadTable (resource)
+ rows = ability.GetRowCount ()
+ for i in range(rows):
+ resource = ability.GetValue (i, 0)
+ count = ability.GetValue (i,1)
+ for j in range(count):
+ GemRB.LearnSpell (MyChar, resource)
+ return
+
+def SetRaceResistances(MyChar, racetitle):
+ resistances = GemRB.LoadTable ("racersmd")
+ GemRB.SetPlayerStat (MyChar, IE_RESISTFIRE, resistances.GetValue ( racetitle, "FIRE") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTCOLD, resistances.GetValue ( racetitle, "COLD") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTELECTRICITY, resistances.GetValue ( racetitle, "ELEC") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTACID, resistances.GetValue ( racetitle, "ACID") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTMAGIC, resistances.GetValue ( racetitle, "SPELL") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTMAGICFIRE, resistances.GetValue ( racetitle, "MAGIC_FIRE") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTMAGICCOLD, resistances.GetValue ( racetitle, "MAGIC_COLD") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTSLASHING, resistances.GetValue ( racetitle, "SLASHING") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTCRUSHING, resistances.GetValue ( racetitle, "BLUDGEONING") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTPIERCING, resistances.GetValue ( racetitle, "PIERCING") )
+ GemRB.SetPlayerStat (MyChar, IE_RESISTMISSILE, resistances.GetValue ( racetitle, "MISSILE") )
+ return
+
+def ClearPress():
+ global BioData
+
+ GemRB.SetToken("BIO", "")
+ EditControl.SetText (GemRB.GetToken("BIO") )
+ return
+
+def RevertPress():
+ BioTable = GemRB.LoadTable ("bios")
+ Class = GemRB.GetVar ("BaseClass")
+ StrRef = BioTable.GetValue(Class,1)
+ GemRB.SetToken ("BIO", GemRB.GetString(StrRef) )
+ EditControl.SetText (GemRB.GetToken("BIO") )
+ return
+
+def BioCancelPress():
+ GemRB.SetToken("BIO",BioData)
+ if BioWindow:
+ BioWindow.Unload ()
+ return
+
+def BioDonePress():
+ if BioWindow:
+ BioWindow.Unload ()
+ return
+
+def BioPress():
+ global BioWindow, EditControl, BioData
+
+ BioData = GemRB.GetToken("BIO")
+ BioWindow = Window = GemRB.LoadWindow (51)
+ Button = Window.GetControl (5)
+ Button.SetText (2240)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RevertPress)
+
+ Button = Window.GetControl (6)
+ Button.SetText (18622)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClearPress)
+
+ Button = Window.GetControl (1)
+ Button.SetText (11962)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, BioDonePress)
+
+ Button = Window.GetControl (2)
+ Button.SetText (36788)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, BioCancelPress)
+
+ EditControl = Window.GetControl (4)
+ BioData = GemRB.GetToken("BIO")
+ if BioData == "":
+ RevertPress()
+ else:
+ EditControl.SetText (BioData )
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def NextPress():
+ #set my character up
+ MyChar = GemRB.GetVar ("Slot")
+ GemRB.SetPlayerStat (MyChar, IE_SEX, GemRB.GetVar ("Gender") )
+ GemRB.SetPlayerStat (MyChar, IE_RACE, GemRB.GetVar ("BaseRace") )
+ race = GemRB.GetVar ("Race")
+ GemRB.SetPlayerStat (MyChar, IE_SUBRACE, race & 255 )
+ row = CommonTables.Races.FindValue (3, race )
+ racename = CommonTables.Races.GetRowName (row)
+ if row!=-1:
+ SetRaceResistances( MyChar, racename )
+ SetRaceAbilities( MyChar, racename )
+
+ #base class
+ Class=GemRB.GetVar ("BaseClass")
+ GemRB.SetPlayerStat (MyChar, IE_CLASS, Class)
+ #kit
+ GemRB.SetPlayerStat (MyChar, IE_KIT, GemRB.GetVar ("Class") )
+ AlignmentTable = GemRB.LoadTable ("aligns")
+ t=GemRB.GetVar ("Alignment")
+ GemRB.SetPlayerStat (MyChar, IE_ALIGNMENT, AlignmentTable.GetValue (t, 3) )
+ TmpTable=GemRB.LoadTable ("repstart")
+ #t=AlignmentTable.FindValue (3,t)
+ t=TmpTable.GetValue (t,0)
+ GemRB.SetPlayerStat (MyChar, IE_REPUTATION, t)
+ TmpTable=GemRB.LoadTable ("strtgold")
+ a = TmpTable.GetValue (Class, 1) #number of dice
+ b = TmpTable.GetValue (Class, 0) #size
+ c = TmpTable.GetValue (Class, 2) #adjustment
+ d = TmpTable.GetValue (Class, 3) #external multiplier
+ e = TmpTable.GetValue (Class, 4) #level bonus rate
+ t = GemRB.GetPlayerStat (MyChar, IE_LEVEL)
+ if t>1:
+ e=e*(t-1)
+ else:
+ e=0
+ t = GemRB.Roll(a,b,c)*d+e
+ GemRB.SetPlayerStat (MyChar, IE_GOLD, t)
+ GemRB.SetPlayerStat (MyChar, IE_HATEDRACE, GemRB.GetVar ("HatedRace") )
+ TmpTable = GemRB.LoadTable ("ability")
+ AbilityCount = TmpTable.GetRowCount ()
+ for i in range (AbilityCount):
+ StatID=TmpTable.GetValue (i,4)
+ GemRB.SetPlayerStat (MyChar, StatID, GemRB.GetVar ("Ability "+str(i) ) )
+
+# TmpTable=GemRB.LoadTable ("weapprof")
+# ProfCount = TmpTable.GetRowCount ()
+# for i in range(ProfCount):
+# StatID=TmpTable.GetValue (i, 0)
+# GemRB.SetPlayerStat (MyChar, StatID, GemRB.GetVar ("Prof "+str(i) ) )
+ GUICommon.SetColorStat (MyChar, IE_HAIR_COLOR, GemRB.GetVar ("Color1") )
+ GUICommon.SetColorStat (MyChar, IE_SKIN_COLOR, GemRB.GetVar ("Color2") )
+ GUICommon.SetColorStat (MyChar, IE_MAJOR_COLOR, GemRB.GetVar ("Color4") )
+ GUICommon.SetColorStat (MyChar, IE_MINOR_COLOR, GemRB.GetVar ("Color3") )
+ GUICommon.SetColorStat (MyChar, IE_METAL_COLOR, 0x1B )
+ GUICommon.SetColorStat (MyChar, IE_LEATHER_COLOR, 0x16 )
+ GUICommon.SetColorStat (MyChar, IE_ARMOR_COLOR, 0x17 )
+ GemRB.SetPlayerStat (MyChar, IE_EA, 2 )
+ Str=GemRB.GetVar ("Ability 1")
+ GemRB.SetPlayerStat (MyChar, IE_STR, Str)
+ if Str==18:
+ GemRB.SetPlayerStat (MyChar,IE_STREXTRA,GemRB.GetVar ("StrExtra"))
+ else:
+ GemRB.SetPlayerStat (MyChar, IE_STREXTRA,0)
+
+ GemRB.SetPlayerStat (MyChar, IE_DEX, GemRB.GetVar ("Ability 2"))
+ GemRB.SetPlayerStat (MyChar, IE_CON, GemRB.GetVar ("Ability 3"))
+ GemRB.SetPlayerStat (MyChar, IE_INT, GemRB.GetVar ("Ability 4"))
+ GemRB.SetPlayerStat (MyChar, IE_WIS, GemRB.GetVar ("Ability 5"))
+ GemRB.SetPlayerStat (MyChar, IE_CHR, GemRB.GetVar ("Ability 6"))
+ GemRB.SetPlayerName (MyChar, GemRB.GetToken ("CHARNAME"), 0)
+
+ #setting skills
+ TmpTable = GemRB.LoadTable ("skillsta")
+ SkillCount = TmpTable.GetRowCount ()
+ for i in range (SkillCount):
+ StatID=TmpTable.GetValue (i, 2)
+ GemRB.SetPlayerStat (MyChar, StatID, GemRB.GetVar ("Skill "+str(i) ) )
+
+ #setting feats
+
+ #does all the rest
+ LargePortrait = GemRB.GetToken ("LargePortrait")
+ SmallPortrait = GemRB.GetToken ("SmallPortrait")
+ GemRB.FillPlayerInfo(MyChar, LargePortrait, SmallPortrait)
+ GemRB.SetNextScript ("SPPartyFormation")
+
+ TmpTable = GemRB.LoadTable ("strtxp")
+
+ #starting xp is race dependent
+ xp = TmpTable.GetValue (racename, "VALUE")
+ GemRB.SetPlayerStat (MyChar, IE_XP, xp )
+
+ return
+
diff --git a/gemrb/GUIScripts/iwd2/CharOverview.py b/gemrb/GUIScripts/iwd2/CharOverview.py
new file mode 100644
index 0000000..eb6533a
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharOverview.py
@@ -0,0 +1,304 @@
+# 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.
+#
+#
+# Some small attempt to re-use code in character generation.
+# Attempting to emulate lynx's loop method from BG2 GUI
+
+# Should be called CharGenCommon for continuity but I started
+# this with something else in mind and then changed it
+# - Fyorl
+
+# CharOverview.py (GUICG)
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+
+CharGenWindow = 0
+TextAreaControl = 0
+StartOverWindow = 0
+PortraitButton = 0
+StepButtons = {}
+PersistButtons = {}
+Steps = ['Gender', 'Race', 'Class', 'Alignment', 'Abilities', 'Skills', 'Appearance', 'Name']
+GlobalStep = 0
+
+### Utility functions
+def AddText(strref, row = False):
+ if row: return TextAreaControl.Append(strref, row);
+ return TextAreaControl.Append(strref)
+### End utility functions
+
+def UpdateOverview(CurrentStep):
+ global CharGenWindow, TextAreaControl, StartOverWindow, PortraitButton
+ global StepButtons, Steps, PersistButtons, GlobalStep
+
+ GlobalStep = CurrentStep
+
+ GemRB.LoadWindowPack("GUICG", 800 ,600)
+ CharGenWindow = GemRB.LoadWindow(0)
+ CharGenWindow.SetFrame()
+ PortraitButton = CharGenWindow.GetControl(12)
+ PortraitButton.SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+
+ # Handle portrait
+ PortraitName = GemRB.GetToken('LargePortrait')
+ if PortraitName != '' and CurrentStep > 1:
+ PortraitButton.SetPicture(PortraitName, 'NOPORTLG')
+
+ # Handle step buttons
+ TextLookup = [11956, 11957, 11959, 11958, 11960, 11983, 11961, 11963]
+ for i, Step in enumerate(Steps):
+ StepButtons[Step] = CharGenWindow.GetControl(i)
+ StepButtons[Step].SetText(TextLookup[i])
+ State = IE_GUI_BUTTON_DISABLED
+ if CurrentStep - 1 == i:
+ State = IE_GUI_BUTTON_ENABLED
+ StepButtons[Step].SetFlags(IE_GUI_BUTTON_DEFAULT, OP_OR)
+ StepButtons[Step].SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ StepButtons[Step].SetState(State)
+
+ # Handle (not so) persistent buttons
+ # This array handles all the default values for the buttons
+ # Exceptions are handled within the loop
+ ControlLookup = {
+ 'Bio': [16, 18003, 0, None],
+ 'Import': [13, 13955, 0, None],
+ 'Back': [11, 15416, 1, BackPress],
+ 'Next': [8, 28210, 0, None],
+ 'Start': [15, 36788, 1, StartOver]
+ }
+ States = [IE_GUI_BUTTON_DISABLED, IE_GUI_BUTTON_ENABLED]
+ for Key in ControlLookup:
+ PersistButtons[Key] = CharGenWindow.GetControl(ControlLookup[Key][0])
+ Text = ControlLookup[Key][1]
+ State = States[ControlLookup[Key][2]]
+ Event = ControlLookup[Key][3]
+
+ if Key == 'Bio' and CurrentStep == 9:
+ State = States[1]
+ import CharGen9
+ Event = CharGen9.BioPress
+
+ if Key == 'Import' and CurrentStep == 1:
+ State = States[1]
+ Event = ImportPress
+
+ if Key == 'Back':
+ if CurrentStep == 1:
+ State = States[0]
+ Event = None
+ else:
+ PersistButtons[Key].SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ if Key == 'Next' and CurrentStep == 9:
+ Text = 11962
+ State = 1
+ Event = NextPress
+
+ if Key == 'Start' and CurrentStep == 1:
+ Text = 13727
+ Event = CancelPress
+ PersistButtons[Key].SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PersistButtons[Key].SetText(Text)
+ PersistButtons[Key].SetState(State)
+
+ if Event:
+ PersistButtons[Key].SetEvent(IE_GUI_BUTTON_ON_PRESS, Event)
+
+ # Handle character overview information
+ TextAreaControl = CharGenWindow.GetControl(9)
+ Tables = []
+ for tbl in ['races', 'classes', 'aligns', 'ability', 'skillsta', 'skills', 'featreq', 'feats']:
+ Tables.append(GemRB.LoadTable(tbl))
+
+ if GemRB.GetVar('Gender') > 0:
+ if GemRB.GetToken('CHARNAME') == '':
+ TextAreaControl.SetText(12135)
+ else:
+ TextAreaControl.SetText(1047)
+ AddText(': ' + GemRB.GetToken('CHARNAME'))
+ AddText(12135, -1)
+ AddText(': ')
+ strref = 1049 + GemRB.GetVar('Gender')
+ AddText(strref)
+
+ if GemRB.GetVar('Race') > 0:
+ AddText(1048, -1)
+ AddText(': ')
+ AddText(Tables[0].GetValue(Tables[0].FindValue(3, GemRB.GetVar('Race')), 2))
+
+ if GemRB.GetVar('Class') > 0:
+ AddText(11959, -1)
+ AddText(': ')
+ AddText(Tables[1].GetValue(GemRB.GetVar('Class') - 1, 0))
+
+ if GemRB.GetVar('Alignment') > 0:
+ AddText(11958, -1)
+ AddText(': ')
+ AddText(Tables[2].GetValue(GemRB.GetVar('Alignment') - 1, 0))
+
+ if GemRB.GetVar('Ability 0') > 0:
+ AddText('\n[color=FFFF00]', -1)
+ AddText(17088)
+ AddText('[/color]')
+ for i in range(0, 6):
+ strref = Tables[3].GetValue(i, 2)
+ AddText(strref, -1)
+ abl = GemRB.GetVar('Ability ' + str(i))
+ AddText(': %d (%+d)' % (abl, abl / 2 - 5))
+
+ if CurrentStep > 6:
+ AddText('\n[color=FFFF00]', -1)
+ AddText(11983)
+ AddText('[/color]')
+
+ ClassColumn = Tables[1].GetValue(GemRB.GetVar('Class') - 1, 3, 1) # Finds base class row id
+ if ClassColumn < 1: ClassColumn = GemRB.GetVar('Class') - 1; # If 0 then already a base class so need actual row
+ else: ClassColumn -= 1; # 'CLASS' column in classes.2da is out by 1 for some reason
+ ClassColumn += 4 # There are 4 columns before the classes in skills.2da
+ # At the moment only cleric kits get skill bonuses but their column names in skills.2da don't match up
+ # to their kit names. All classes aren't covered in skills.2da either which is why I have to resort
+ # to calculating the base class. This isn't ideal. Recommend a new 2DA be created with *all* classes
+ # as rows and skills as columns. Something like SKILCLAS.2DA
+
+ ### Cleric kit hack:
+ if GemRB.GetVar('Class') in range(27, 36):
+ ClassColumn = GemRB.GetVar('Class') - 12
+
+ RaceName = Tables[0].GetRowName(Tables[0].FindValue(3, GemRB.GetVar('Race')))
+ SkillColumn = Tables[0].GetValue(RaceName, 'SKILL_COLUMN', 1) + 1
+ Lookup = {'STR': 0, 'DEX': 1, 'CON': 2, 'INT': 3, 'WIS': 4, 'CHR': 5} # Probably a better way to do this
+ for i in range(Tables[4].GetRowCount()):
+ SkillName = Tables[5].GetRowName(i)
+ Abl = Tables[4].GetValue(i, 1, 0)
+ Ranks = GemRB.GetVar('Skill ' + str(i))
+ value = Ranks
+ value += (GemRB.GetVar('Ability ' + str(Lookup[Abl])) / 2 - 5)
+ value += Tables[5].GetValue(i, SkillColumn, 1)
+ value += Tables[5].GetValue(i, ClassColumn, 1)
+
+ untrained = Tables[5].GetValue(i, 3, 1)
+ if not untrained and Ranks < 1:
+ value = 0
+
+ if value:
+ strref = Tables[5].GetValue(i, 1)
+ AddText(strref, -1)
+ strn = ': ' + str(value)
+ if value != Ranks: strn += ' (' + str(Ranks) + ')'
+ AddText(strn)
+
+ AddText('\n[color=FFFF00]', -1)
+ AddText(36310)
+ AddText('[/color]')
+
+ for i in range(Tables[6].GetRowCount()):
+ value = GemRB.GetVar('Feat ' + str(i))
+ if value:
+ strref = Tables[7].GetValue(i, 1)
+ AddText(strref, -1)
+ if value > 1: AddText(': ' + str(value))
+
+ # Handle StartOverWindow
+ StartOverWindow = GemRB.LoadWindow(53)
+
+ YesButton = StartOverWindow.GetControl(0)
+ YesButton.SetText(13912)
+ YesButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, RestartGen)
+
+ NoButton = StartOverWindow.GetControl(1)
+ NoButton.SetText(13913)
+ NoButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NoExitPress)
+
+ TextAreaControl = StartOverWindow.GetControl(2)
+ TextAreaControl.SetText(40275)
+
+ # And we're done, w00t!
+ CharGenWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def NextPress():
+ if CharGenWindow:
+ CharGenWindow.Unload()
+ if StartOverWindow:
+ StartOverWindow.Unload()
+ if len(Steps) > GlobalStep - 1:
+ GemRB.SetNextScript(Steps[GlobalStep - 1])
+ else: #start the game
+ import CharGen9
+ CharGen9.NextPress()
+ return
+
+def CancelPress():
+ #destroy the half generated character
+ slot = GemRB.GetVar("Slot")
+ GemRB.CreatePlayer("", slot|0x8000)
+ if CharGenWindow:
+ CharGenWindow.Unload()
+ if StartOverWindow:
+ StartOverWindow.Unload()
+ GemRB.SetNextScript('SPPartyFormation')
+ return
+
+def StartOver():
+ StartOverWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def NoExitPress():
+ StartOverWindow.SetVisible(WINDOW_INVISIBLE)
+ CharGenWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ImportPress():
+ if CharGenWindow:
+ CharGenWindow.Unload()
+ if StartOverWindow:
+ StartOverWindow.Unload()
+ GemRB.SetToken('NextScript', 'CharGen')
+ GemRB.SetNextScript('ImportFile')
+ return
+
+def RestartGen():
+ if CharGenWindow:
+ CharGenWindow.Unload()
+ if StartOverWindow:
+ StartOverWindow.Unload()
+ GemRB.SetNextScript('CharGen')
+ return
+
+def BackPress():
+ if CharGenWindow:
+ CharGenWindow.Unload()
+ if StartOverWindow:
+ StartOverWindow.Unload()
+
+ # Need to clear relevant variables
+ if GlobalStep == 2: GemRB.SetVar('Gender', 0);
+ elif GlobalStep == 3: GemRB.SetVar('Race', 0);
+ elif GlobalStep == 4: GemRB.SetVar('Class', 0);
+ elif GlobalStep == 5: GemRB.SetVar('Alignment', 0);
+ elif GlobalStep == 6:
+ for i in range(0, 6): GemRB.SetVar('Ability ' + str(i), 0);
+ elif GlobalStep == 9: GemRB.SetToken('CHARNAME', '')
+
+ ScrName = 'CharGen' + str(GlobalStep - 1)
+ if GlobalStep == 2: ScrName = 'CharGen';
+ GemRB.SetNextScript(ScrName)
+ return
diff --git a/gemrb/GUIScripts/iwd2/CharSound.py b/gemrb/GUIScripts/iwd2/CharSound.py
new file mode 100644
index 0000000..3b14e9a
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/CharSound.py
@@ -0,0 +1,139 @@
+# -*-python-*-
+# 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.
+#
+
+#Character Sound Options Menu using GUIOPT
+
+import GemRB
+from GUIDefines import *
+
+CSoundWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global CSoundWindow, TextAreaControl
+
+ GemRB.LoadWindowPack("GUIOPT", 800, 600)
+ CSoundWindow = GemRB.LoadWindow(12)
+ CSoundWindow.SetFrame( )
+ TextAreaControl = CSoundWindow.GetControl(16)
+ SubtitlesButton = CSoundWindow.GetControl(20)
+ AttackSoundButton = CSoundWindow.GetControl(18)
+ MovementSoundButton = CSoundWindow.GetControl(19)
+ CommandSoundButton = CSoundWindow.GetControl(21)
+ SelectionSoundButton = CSoundWindow.GetControl(57)
+ SubtitlesButtonB = CSoundWindow.GetControl(5)
+ AttackSoundButtonB = CSoundWindow.GetControl(6)
+ MovementSoundButtonB = CSoundWindow.GetControl(7)
+ CSAlwaysButtonB = CSoundWindow.GetControl(8)
+ CSSeldomButtonB = CSoundWindow.GetControl(9)
+ CSNeverButtonB = CSoundWindow.GetControl(10)
+ SSAlwaysButtonB = CSoundWindow.GetControl(58)
+ SSSeldomButtonB = CSoundWindow.GetControl(59)
+ SSNeverButtonB = CSoundWindow.GetControl(60)
+ OkButton = CSoundWindow.GetControl(24)
+ CancelButton = CSoundWindow.GetControl(25)
+ TextAreaControl.SetText(18041)
+ OkButton.SetText(11973)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton.SetText(13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ SubtitlesButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SubtitlesPress)
+ SubtitlesButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SubtitlesPress)
+ SubtitlesButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ SubtitlesButtonB.SetVarAssoc("Subtitles", 1)
+ SubtitlesButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ AttackSoundButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, AttackSoundPress)
+ AttackSoundButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, AttackSoundPress)
+ AttackSoundButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ AttackSoundButtonB.SetVarAssoc("Attack Sound", 1) #can't find the right variable name, this is a dummy name
+ AttackSoundButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ MovementSoundButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MovementSoundPress)
+ MovementSoundButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, MovementSoundPress)
+ MovementSoundButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ MovementSoundButtonB.SetVarAssoc("Movement Sound", 1) #can't find the right variable name, this is a dummy name
+ MovementSoundButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ CommandSoundButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommandSoundPress)
+ CSAlwaysButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommandSoundPress)
+ CSAlwaysButtonB.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ CSAlwaysButtonB.SetVarAssoc("Command Sounds Frequency", 3)
+ CSAlwaysButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+ CSSeldomButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommandSoundPress)
+ CSSeldomButtonB.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ CSSeldomButtonB.SetVarAssoc("Command Sounds Frequency", 2)
+ CSSeldomButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+ CSNeverButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, CommandSoundPress)
+ CSNeverButtonB.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ CSNeverButtonB.SetVarAssoc("Command Sounds Frequency", 1)
+ CSNeverButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ SelectionSoundButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionSoundPress)
+ SSAlwaysButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionSoundPress)
+ SSAlwaysButtonB.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ SSAlwaysButtonB.SetVarAssoc("Selection Sounds Frequency", 3)
+ SSAlwaysButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+ SSSeldomButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionSoundPress)
+ SSSeldomButtonB.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ SSSeldomButtonB.SetVarAssoc("Selection Sounds Frequency", 2)
+ SSSeldomButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+ SSNeverButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionSoundPress)
+ SSNeverButtonB.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ SSNeverButtonB.SetVarAssoc("Selection Sounds Frequency", 1)
+ SSNeverButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CSoundWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def SubtitlesPress():
+ TextAreaControl.SetText(18015)
+ return
+
+def AttackSoundPress():
+ TextAreaControl.SetText(18013)
+ return
+
+def MovementSoundPress():
+ TextAreaControl.SetText(18014)
+ return
+
+def CommandSoundPress():
+ TextAreaControl.SetText(18016)
+ return
+
+def SelectionSoundPress():
+ TextAreaControl.SetText(11352)
+ return
+
+def OkPress():
+ if CSoundWindow:
+ CSoundWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
+
+def CancelPress():
+ if CSoundWindow:
+ CSoundWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
diff --git a/gemrb/GUIScripts/iwd2/Class.py b/gemrb/GUIScripts/iwd2/Class.py
new file mode 100644
index 0000000..4eeae5c
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Class.py
@@ -0,0 +1,192 @@
+# 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.
+#
+#
+#character generation, class (GUICG2)
+import GemRB
+from GUIDefines import *
+import CommonTables
+
+ClassWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+BackButton = 0
+ClassCount = 0
+HasSubClass = 0
+ClassID = 0
+
+def AdjustTextArea():
+ global HasSubClass, ClassID
+
+ Class = GemRB.GetVar("Class")-1
+ TextAreaControl.SetText(CommonTables.Classes.GetValue(Class,1) )
+ ClassName = CommonTables.Classes.GetRowName(Class)
+ ClassID = CommonTables.Classes.GetValue(ClassName, "ID")
+ #determining if this class has any subclasses
+ HasSubClass = 0
+ for i in range(1, ClassCount):
+ ClassName = CommonTables.Classes.GetRowName(i-1)
+ #determining if this is a kit or class
+ Allowed = CommonTables.Classes.GetValue(ClassName, "CLASS")
+ if Allowed != ClassID:
+ continue
+ HasSubClass = 1
+ break
+
+ if HasSubClass == 0:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ return
+
+def OnLoad():
+ global ClassWindow, TextAreaControl, DoneButton, BackButton
+ global ClassCount
+
+ GemRB.LoadWindowPack("GUICG", 800, 600)
+ #this replaces help02.2da for class restrictions
+ ClassCount = CommonTables.Classes.GetRowCount()+1
+ ClassWindow = GemRB.LoadWindow(2)
+ rid = CommonTables.Races.FindValue(3, GemRB.GetVar('BaseRace'))
+ RaceName = CommonTables.Races.GetRowName(rid)
+
+ #radiobutton groups must be set up before doing anything else to them
+ j = 0
+ for i in range(1,ClassCount):
+ ClassName = CommonTables.Classes.GetRowName(i-1)
+ Allowed = CommonTables.Classes.GetValue(ClassName, "CLASS")
+ if Allowed > 0:
+ continue
+ Button = ClassWindow.GetControl(j+2)
+ j = j+1
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+
+ j = 0
+ for i in range(1,ClassCount):
+ ClassName = CommonTables.Classes.GetRowName(i-1)
+ #determining if this is a kit or class
+ Allowed = CommonTables.Classes.GetValue(ClassName, "CLASS")
+ if Allowed > 0:
+ continue
+ Allowed = CommonTables.Classes.GetValue(ClassName, RaceName)
+ Button = ClassWindow.GetControl(j+2)
+ j = j+1
+ t = CommonTables.Classes.GetValue(ClassName, "NAME_REF")
+ Button.SetText(t )
+
+ if Allowed==0:
+ continue
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClassPress)
+ Button.SetVarAssoc("Class", i)
+
+ BackButton = ClassWindow.GetControl(17)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = ClassWindow.GetControl(0)
+ DoneButton.SetText(36789)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ ScrollBarControl = ClassWindow.GetControl(15)
+
+ TextAreaControl = ClassWindow.GetControl(16)
+
+ Class = GemRB.GetVar("Class")-1
+ if Class<0:
+ TextAreaControl.SetText(17242)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ AdjustTextArea()
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ ClassWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ClassPress():
+ global HasSubClass
+
+ AdjustTextArea()
+ if HasSubClass == 0:
+ return
+
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ j = 0
+ for i in range(1,ClassCount):
+ ClassName = CommonTables.Classes.GetRowName(i-1)
+ Allowed = CommonTables.Classes.GetValue(ClassName, "CLASS")
+ if Allowed > 0:
+ continue
+ Button = ClassWindow.GetControl(j+2)
+ j = j+1
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetText("")
+
+ j=0
+ for i in range(1, ClassCount):
+ ClassName = CommonTables.Classes.GetRowName(i-1)
+ #determining if this is a kit or class
+ Allowed = CommonTables.Classes.GetValue(ClassName, "CLASS")
+ if Allowed != ClassID:
+ continue
+ Button = ClassWindow.GetControl(j+2)
+ j = j+1
+ t = CommonTables.Classes.GetValue(ClassName, "NAME_REF")
+ Button.SetText(t )
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClassPress2)
+ Button.SetVarAssoc("Class", i)
+
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress2)
+ return
+
+def ClassPress2():
+ Class = GemRB.GetVar("Class")-1
+ TextAreaControl.SetText(CommonTables.Classes.GetValue(Class,1) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def BackPress2():
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ if ClassWindow:
+ ClassWindow.Unload()
+ OnLoad()
+ return
+
+def BackPress():
+ if ClassWindow:
+ ClassWindow.Unload()
+ GemRB.SetNextScript("CharGen3")
+ GemRB.SetVar("Class",0) #scrapping the class value
+ MyChar = GemRB.GetVar("Slot")
+ GemRB.SetPlayerStat (IE_CLASS, 0)
+ return
+
+def NextPress():
+ #classcolumn is base class
+ Class = GemRB.GetVar("Class")
+ ClassColumn = CommonTables.Classes.GetValue(Class - 1, 3)
+ if ClassColumn <= 0: #it was already a base class
+ ClassColumn = Class
+ GemRB.SetVar("BaseClass", ClassColumn)
+ if ClassWindow:
+ ClassWindow.Unload()
+ GemRB.SetNextScript("CharGen4") #alignment
+ return
diff --git a/gemrb/GUIScripts/iwd2/Enemy.py b/gemrb/GUIScripts/iwd2/Enemy.py
new file mode 100644
index 0000000..5bbf9c9
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Enemy.py
@@ -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.
+#
+#
+#character generation, racial enemy (GUICG15)
+import GemRB
+import CommonTables
+from GUIDefines import *
+
+RaceWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+RacialEnemyTable = 0
+RaceCount = 0
+TopIndex = 0
+
+def DisplayRaces():
+ global TopIndex
+
+ TopIndex=GemRB.GetVar("TopIndex")
+ for i in range(11):
+ Button = RaceWindow.GetControl(i+22)
+ Val = RacialEnemyTable.GetValue(i+TopIndex,0)
+ if Val==0:
+ Button.SetText("")
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetText(Val)
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RacePress)
+ Button.SetVarAssoc("HatedRace",RacialEnemyTable.GetValue(i+TopIndex,1) )
+ return
+
+def OnLoad():
+ global RaceWindow, TextAreaControl, DoneButton
+ global RacialEnemyTable, RaceCount, TopIndex
+
+ GemRB.LoadWindowPack("GUICG", 800 ,600)
+ RaceWindow = GemRB.LoadWindow(15)
+
+ Class = GemRB.GetVar("BaseClass")
+ TableName = CommonTables.ClassSkills.GetValue(Class, 0)
+ if TableName == "*":
+ GemRB.SetNextScript("Skills")
+ return
+ RacialEnemyTable = GemRB.LoadTable(TableName)
+ RaceCount = RacialEnemyTable.GetRowCount()-11
+ if RaceCount<0:
+ RaceCount=0
+
+ for i in range(11):
+ Button = RaceWindow.GetControl(i+22)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ BackButton = RaceWindow.GetControl(10)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = RaceWindow.GetControl(11)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ TextAreaControl = RaceWindow.GetControl(8)
+ TextAreaControl.SetText(17256)
+ TopIndex = 0
+ GemRB.SetVar("TopIndex",0)
+ ScrollBarControl = RaceWindow.GetControl(1)
+ ScrollBarControl.SetVarAssoc("TopIndex",RaceCount)
+ ScrollBarControl.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, DisplayRaces)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ RaceWindow.SetVisible(WINDOW_VISIBLE)
+ DisplayRaces()
+ return
+
+def RacePress():
+ Race = GemRB.GetVar("HatedRace")
+ Row = RacialEnemyTable.FindValue(1, Race)
+ TextAreaControl.SetText(RacialEnemyTable.GetValue(Row, 2) )
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def BackPress():
+ if RaceWindow:
+ RaceWindow.Unload()
+ GemRB.SetVar("HatedRace",0) #scrapping the race value
+ GemRB.SetNextScript("CharGen6")
+ return
+
+def NextPress():
+ if RaceWindow:
+ RaceWindow.Unload()
+ GemRB.SetNextScript("Skills")
+ return
diff --git a/gemrb/GUIScripts/iwd2/Feats.py b/gemrb/GUIScripts/iwd2/Feats.py
new file mode 100644
index 0000000..6b0cdf3
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Feats.py
@@ -0,0 +1,390 @@
+# 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.
+#
+#
+#character generation, skills (GUICG6)
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CommonTables
+
+FeatWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+FeatTable = 0
+FeatReqTable = 0
+TopIndex = 0
+Level = 0
+ClassColumn = 0
+KitColumn = 0
+RaceColumn = 0
+FeatsClassColumn = 0
+PointsLeft = 0
+
+# returns the number of feat levels (for example cleave can be taken twice)
+def MultiLevelFeat(feat):
+ global FeatReqTable
+ return FeatReqTable.GetValue(feat, "MAX_LEVEL")
+
+# FIXME: CheckFeatCondition doesn't check for higher level prerequisites
+# (eg. cleave2 needs +4 BAB and weapon specialisation needs 4 fighter levels)
+
+# NOTE: cleave formula is now:
+# HITBONUS>=4 OR FEAT_CLEAVE<1
+#
+# specialisation formulas:
+# FIGHTERLEVEL>=4 OR FEAT_*<2
+# The default operator was set to 4 (greater or equal), so the majority of the formulas
+# don't need any more change
+# Avenger
+
+def IsFeatUsable(feat):
+ global FeatReqTable
+
+ a_value = FeatReqTable.GetValue(feat, "A_VALUE")
+ if a_value<0:
+ #string
+ a_stat = FeatReqTable.GetValue(feat, "A_STAT", 0)
+ else:
+ #stat
+ a_stat = FeatReqTable.GetValue(feat, "A_STAT",2)
+ b_stat = FeatReqTable.GetValue(feat, "B_STAT",2)
+ c_stat = FeatReqTable.GetValue(feat, "C_STAT",2)
+ d_stat = FeatReqTable.GetValue(feat, "D_STAT",2)
+ b_value = FeatReqTable.GetValue(feat, "B_VALUE")
+ c_value = FeatReqTable.GetValue(feat, "C_VALUE")
+ d_value = FeatReqTable.GetValue(feat, "D_VALUE")
+ a_op = FeatReqTable.GetValue(feat, "A_OP")
+ b_op = FeatReqTable.GetValue(feat, "B_OP")
+ c_op = FeatReqTable.GetValue(feat, "C_OP")
+ d_op = FeatReqTable.GetValue(feat, "D_OP")
+ slot = GemRB.GetVar("Slot")
+
+ return GemRB.CheckFeatCondition(slot, a_stat, a_value, b_stat, b_value, c_stat, c_value, d_stat, d_value, a_op, b_op, c_op, d_op)
+
+# checks if a feat was granted due to class/kit/race and returns the number
+# of granted levels. The bonuses aren't cumulative.
+def GetBaseValue(feat):
+ global FeatsClassColumn, RaceColumn, KitName
+
+ Val1 = FeatTable.GetValue(feat, FeatsClassColumn)
+ Val2 = FeatTable.GetValue(feat, RaceColumn)
+ if Val2 < Val1:
+ Val = Val1
+ else:
+ Val = Val2
+
+ Val3 = 0
+ # only cleric kits have feat bonuses in the original, but the column names are shortened
+ KitName = KitName.replace("CLERIC_","C_")
+ KitColumn = FeatTable.GetColumnIndex(KitName)
+ if KitColumn != 0:
+ Val3 = FeatTable.GetValue(feat, KitColumn)
+ if Val3 > Val:
+ Val = Val3
+
+ return Val
+
+def RedrawFeats():
+ global TopIndex, PointsLeft, FeatWindow, FeatReqTable
+
+ SumLabel = FeatWindow.GetControl(0x1000000c)
+ if PointsLeft == 0:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ SumLabel.SetTextColor(255, 255, 255)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ SumLabel.SetTextColor(255, 255, 0)
+
+ SumLabel.SetText(str(PointsLeft) )
+
+ for i in range(0,10):
+ Pos=TopIndex+i
+ FeatName = FeatTable.GetValue(Pos, 1)
+ Label = FeatWindow.GetControl(0x10000001+i)
+ Label.SetText(FeatName)
+
+ FeatName=FeatTable.GetRowName(Pos) #row name
+ FeatValue = GemRB.GetVar("Feat "+str(Pos))
+
+ ButtonPlus = FeatWindow.GetControl(i*2+14)
+ ButtonMinus = FeatWindow.GetControl(i*2+15)
+ if FeatValue == 0:
+ ButtonMinus.SetState(IE_GUI_BUTTON_DISABLED)
+ # check if feat is usable - can be taken
+ if IsFeatUsable(FeatName):
+ ButtonPlus.SetState(IE_GUI_BUTTON_ENABLED)
+ Label.SetTextColor(255, 255, 255)
+ else:
+ ButtonPlus.SetState(IE_GUI_BUTTON_DISABLED)
+ Label.SetTextColor(150, 150, 150)
+ else:
+ # check for maximum if there are more feat levels
+ # FIXME also verify that the next level of the feat is usable
+ if MultiLevelFeat(FeatName) > FeatValue:
+ ButtonPlus.SetState(IE_GUI_BUTTON_ENABLED)
+ Label.SetTextColor(255, 255, 255)
+ else:
+ ButtonPlus.SetState(IE_GUI_BUTTON_DISABLED)
+ Label.SetTextColor(150, 150, 150)
+ BaseValue = GemRB.GetVar("BaseFeatValue " + str(Pos))
+ if FeatValue > BaseValue:
+ ButtonMinus.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ ButtonMinus.SetState(IE_GUI_BUTTON_DISABLED)
+
+ if PointsLeft == 0:
+ ButtonPlus.SetState(IE_GUI_BUTTON_DISABLED)
+ Label.SetTextColor(150, 150, 150)
+
+ levels = FeatReqTable.GetValue(FeatName, "MAX_LEVEL")
+ FeatValueCounter = FeatValue
+ # count backwards, since the controls follow each other in rtl order,
+ # while we need to change the bams in ltr order
+ for j in range(4, -1, -1):
+ Star = FeatWindow.GetControl(i*5+j+36)
+ if 5 - j - 1 < levels:
+ # the star should be there, but which one?
+ if FeatValueCounter > 0:
+ # the full one - the character has already taken a level of this feat
+ Star.SetState(IE_GUI_BUTTON_LOCKED)
+ Star.SetBAM("GUIPFC", 0, 0, -1)
+ Star.SetFlags(IE_GUI_BUTTON_PICTURE, OP_OR)
+ FeatValueCounter = FeatValueCounter - 1
+ else:
+ # the empty one - the character hasn't taken any levels of this feat yet
+ Star.SetState(IE_GUI_BUTTON_LOCKED)
+ Star.SetBAM("GUIPFC", 0, 1, -1)
+ Star.SetFlags(IE_GUI_BUTTON_PICTURE, OP_OR)
+ else:
+ # no star, no bad bam crap
+ Star.SetState(IE_GUI_BUTTON_DISABLED)
+ Star.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Star.SetFlags(IE_GUI_BUTTON_PICTURE, OP_NAND)
+ return
+
+def ScrollBarPress():
+ global TopIndex
+
+ TopIndex = GemRB.GetVar("TopIndex")
+ RedrawFeats()
+ return
+
+def OnLoad():
+ global FeatWindow, TextAreaControl, DoneButton, TopIndex
+ global FeatTable, FeatReqTable
+ global KitName, Level, PointsLeft
+ global ClassColumn, KitColumn, RaceColumn, FeatsClassColumn
+
+ GemRB.SetVar("Level",1) #for simplicity
+
+ Race = GemRB.GetVar("Race")
+ RaceColumn = CommonTables.Races.FindValue(3, Race)
+ RaceName = CommonTables.Races.GetRowName(RaceColumn)
+ # could use column ID as well, but they tend to change :)
+ RaceColumn = CommonTables.Races.GetValue(RaceName, "SKILL_COLUMN")
+
+ Class = GemRB.GetVar("Class") - 1
+ KitName = CommonTables.Classes.GetRowName(Class)
+ # classcolumn is base class or 0 if it is not a kit
+ ClassColumn = CommonTables.Classes.GetValue(Class, 3) - 1
+ if ClassColumn < 0: #it was already a base class
+ ClassColumn = Class
+ FeatsClassColumn = CommonTables.Classes.GetValue(Class, 2) + 2
+ else:
+ FeatsClassColumn = CommonTables.Classes.GetValue(Class, 3) + 2
+
+ FeatTable = GemRB.LoadTable("feats")
+ RowCount = FeatTable.GetRowCount()
+ FeatReqTable = GemRB.LoadTable("featreq")
+
+ for i in range(RowCount):
+ GemRB.SetVar("Feat "+str(i), GetBaseValue(i))
+ GemRB.SetVar("BaseFeatValue " + str(i), GetBaseValue(i))
+
+ FeatLevelTable = GemRB.LoadTable("featlvl")
+ FeatClassTable = GemRB.LoadTable("featclas")
+ #calculating the number of new feats for the next level
+ PointsLeft = 0
+ #levels start with 1
+ Level = GemRB.GetVar("Level")-1
+
+ #this one exists only for clerics
+ # Although it should be made extendable to all kits
+ # A FEAT_COLUMN is needed in classes.2da or better yet, a whole new 2da
+ if CommonTables.Classes.GetValue(Class, 3) == "CLERIC":
+ ClassColumn += 3
+ if KitColumn:
+ KitColumn = 3 + KitColumn + 11
+
+ #Always raise one level at once
+ PointsLeft += FeatLevelTable.GetValue(Level, 0)
+ PointsLeft += FeatClassTable.GetValue(Level, ClassColumn)
+
+ #racial abilities which seem to be hardcoded in the IWD2 engine
+ #are implemented in races.2da
+ if Level<1:
+ PointsLeft += CommonTables.Races.GetValue(RaceName,'FEATBONUS')
+ ###
+
+ GemRB.SetToken("number",str(PointsLeft) )
+
+ GemRB.LoadWindowPack("GUICG", 800, 600)
+ FeatWindow = GemRB.LoadWindow(55)
+ for i in range(10):
+ Button = FeatWindow.GetControl(i+93)
+ Button.SetVarAssoc("Feat",i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, JustPress)
+
+ Button = FeatWindow.GetControl(i*2+14)
+ Button.SetVarAssoc("Feat",i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, LeftPress)
+
+ Button = FeatWindow.GetControl(i*2+15)
+ Button.SetVarAssoc("Feat",i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RightPress)
+ for j in range(5):
+ Star=FeatWindow.GetControl(i*5+j+36)
+ Star.SetState(IE_GUI_BUTTON_DISABLED)
+ Star.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_OR)
+
+ BackButton = FeatWindow.GetControl(105)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = FeatWindow.GetControl(0)
+ DoneButton.SetText(36789)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = FeatWindow.GetControl(92)
+ TextAreaControl.SetText(36476)
+
+ ScrollBarControl = FeatWindow.GetControl(104)
+ ScrollBarControl.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ #decrease it with the number of controls on screen (list size)
+ TopIndex = 0
+ GemRB.SetVar("TopIndex",0)
+ ScrollBarControl.SetVarAssoc("TopIndex",RowCount-10)
+ ScrollBarControl.SetDefaultScrollBar ()
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ RedrawFeats()
+ FeatWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+
+def JustPress():
+ Pos = GemRB.GetVar("Feat")+TopIndex
+ TextAreaControl.SetText(FeatTable.GetValue(Pos,2) )
+ return
+
+def RightPress():
+ global PointsLeft
+
+ Pos = GemRB.GetVar("Feat")+TopIndex
+
+ TextAreaControl.SetText(FeatTable.GetValue(Pos,2) )
+ ActPoint = GemRB.GetVar("Feat "+str(Pos) )
+ BaseValue = GemRB.GetVar("BaseFeatValue " + str(Pos))
+ if ActPoint <= 0 or ActPoint <= BaseValue:
+ return
+ GemRB.SetVar("Feat "+str(Pos),ActPoint-1)
+ PointsLeft = PointsLeft + 1
+ RedrawFeats()
+ return
+
+def LeftPress():
+ global PointsLeft
+
+ Pos = GemRB.GetVar("Feat")+TopIndex
+
+ TextAreaControl.SetText(FeatTable.GetValue(Pos,2) )
+ if PointsLeft < 1:
+ return
+ ActPoint = GemRB.GetVar("Feat "+str(Pos) )
+# if ActPoint > Level: #Level is 0 for level 1
+# return
+ GemRB.SetVar("Feat "+str(Pos), ActPoint+1)
+ PointsLeft = PointsLeft - 1
+ RedrawFeats()
+ return
+
+def BackPress():
+ if FeatWindow:
+ FeatWindow.Unload()
+ for i in range(FeatTable.GetRowCount()):
+ GemRB.SetVar("Feat "+str(i),0)
+ GemRB.SetNextScript("Skills")
+ return
+
+def NextPress():
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_OR)
+ if FeatWindow:
+ FeatWindow.Unload()
+ GemRB.SetNextScript("CharGen7")
+ return
+
+#Custom feat check functions
+def Check_AnyOfThree(pl, ass, a, bs, b, cs, c, *garbage):
+
+ if GemRB.GetPlayerStat(pl, ass)==a: return True
+ if GemRB.GetPlayerStat(pl, bs)==b: return True
+ if GemRB.GetPlayerStat(pl, cs)==c: return True
+ return False
+
+#Custom feat check functions
+def Check_AnyOfThreeGE(pl, ass, a, bs, b, cs, c, *garbage):
+
+ if GemRB.GetPlayerStat(pl, ass)>=a: return True
+ if GemRB.GetPlayerStat(pl, bs)>=b: return True
+ if GemRB.GetPlayerStat(pl, cs)>=c: return True
+ return False
+
+def Check_AllOfThreeGE(pl, ass, a, bs, b, cs, c, *garbage):
+
+ if GemRB.GetPlayerStat(pl, ass) < a: return False
+ if GemRB.GetPlayerStat(pl, bs) < b: return False
+ if GemRB.GetPlayerStat(pl, cs) < c: return False
+ return True
+
+def Check_IsCaster(pl, *garbage):
+ # CLASSLEVELMAGE is IE_LEVEL2 (pst)
+ possible_casters = { IE_LEVEL2:1, IE_LEVELCLERIC:1, IE_LEVELDRUID:1,
+ IE_LEVELSORCEROR:1, IE_LEVELPALADIN:4, IE_LEVELRANGER:4, IE_LEVELBARD:2 }
+ Caster = False
+
+ for stat in possible_casters:
+ if GemRB.GetPlayerStat(pl, stat) >= possible_casters[stat]:
+ Caster = True
+ break
+
+ return Caster
+
+# besides Concentration > 3, this feat requires Weapon Specialization in 2 weapons.
+def Check_MaximizedAttacks(pl, a, ass, *garbage):
+ if GemRB.GetPlayerStat(pl, ass) < a: return False
+ # tuple of all weapon proficiency types
+ proficiencies = ( IE_FEAT_BASTARDSWORD, IE_FEAT_AXE, IE_FEAT_BOW, IE_FEAT_FLAIL,
+ IE_FEAT_GREAT_SWORD, IE_FEAT_HAMMER, IE_FEAT_LARGE_SWORD, IE_FEAT_POLEARM )
+ SpecializationCount = 0
+
+ for proficiency in proficiencies:
+ if GemRB.GetPlayerStat(pl, proficiency) == 3:
+ SpecializationCount += 1
+
+ return SpecializationCount >= 2
diff --git a/gemrb/GUIScripts/iwd2/Feedback.py b/gemrb/GUIScripts/iwd2/Feedback.py
new file mode 100644
index 0000000..0985fe7
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Feedback.py
@@ -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.
+#
+#
+#feedback
+import GemRB
+from GUIDefines import *
+
+FeedbackWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global FeedbackWindow, TextAreaControl
+
+ GemRB.LoadWindowPack("GUIOPT", 800, 600)
+ FeedbackWindow = GemRB.LoadWindow(9)
+ FeedbackWindow.SetFrame( )
+
+ MarkerSlider = FeedbackWindow.GetControl(30)
+ MarkerSliderS = FeedbackWindow.GetControl(8)
+
+ LocatorSlider = FeedbackWindow.GetControl(31)
+ LocatorSliderS = FeedbackWindow.GetControl(9)
+
+ THac0Rolls = FeedbackWindow.GetControl(32)
+ THac0RollsB = FeedbackWindow.GetControl(10)
+ THac0RollsB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ CombatInfo = FeedbackWindow.GetControl(33)
+ CombatInfoB = FeedbackWindow.GetControl(11)
+ CombatInfoB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ Actions = FeedbackWindow.GetControl(34)
+ ActionsB = FeedbackWindow.GetControl(12)
+ ActionsB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ StateChanges = FeedbackWindow.GetControl(35)
+ StateChangesB = FeedbackWindow.GetControl(13)
+ StateChangesB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ SelectionText = FeedbackWindow.GetControl(36)
+ SelectionTextB = FeedbackWindow.GetControl(14)
+ SelectionTextB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ Miscellaneous = FeedbackWindow.GetControl(37)
+ MiscellaneousB = FeedbackWindow.GetControl(15)
+ MiscellaneousB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ OkButton = FeedbackWindow.GetControl(26)
+ CancelButton = FeedbackWindow.GetControl(27)
+ TextAreaControl = FeedbackWindow.GetControl(28)
+
+ TextAreaControl.SetText(18043)
+ OkButton.SetText(11973)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton.SetText(13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ MarkerSlider.SetEvent(IE_GUI_BUTTON_ON_PRESS, MarkerSliderPress)
+ MarkerSliderS.SetEvent(IE_GUI_SLIDER_ON_CHANGE, MarkerSliderPress)
+ MarkerSliderS.SetVarAssoc("GUI Feedback Level",1)
+
+ LocatorSlider.SetEvent(IE_GUI_BUTTON_ON_PRESS, LocatorSliderPress)
+ LocatorSliderS.SetEvent(IE_GUI_SLIDER_ON_CHANGE, LocatorSliderPress)
+ LocatorSliderS.SetVarAssoc("Locator Feedback Level",1)
+
+ THac0Rolls.SetEvent(IE_GUI_BUTTON_ON_PRESS, THac0RollsPress)
+ THac0RollsB.SetEvent(IE_GUI_BUTTON_ON_PRESS, THac0RollsBPress)
+ THac0RollsB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ THac0RollsB.SetVarAssoc("Rolls",1)
+
+ CombatInfo.SetEvent(IE_GUI_BUTTON_ON_PRESS, CombatInfoPress)
+ CombatInfoB.SetEvent(IE_GUI_BUTTON_ON_PRESS, CombatInfoBPress)
+ CombatInfoB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ CombatInfoB.SetVarAssoc("Combat Info",1)
+
+ Actions.SetEvent(IE_GUI_BUTTON_ON_PRESS, ActionsPress)
+ ActionsB.SetEvent(IE_GUI_BUTTON_ON_PRESS, ActionsBPress)
+ ActionsB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ ActionsB.SetVarAssoc("Actions",1)
+
+ StateChanges.SetEvent(IE_GUI_BUTTON_ON_PRESS, StateChangesPress)
+ StateChangesB.SetEvent(IE_GUI_BUTTON_ON_PRESS, StateChangesBPress)
+ StateChangesB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ StateChangesB.SetVarAssoc("State Changes",1)
+
+ SelectionText.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionTextPress)
+ SelectionTextB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SelectionTextBPress)
+ SelectionTextB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ SelectionTextB.SetVarAssoc("Selection Text",1)
+
+ Miscellaneous.SetEvent(IE_GUI_BUTTON_ON_PRESS, MiscellaneousPress)
+ MiscellaneousB.SetEvent(IE_GUI_BUTTON_ON_PRESS, MiscellaneousBPress)
+ MiscellaneousB.SetFlags(IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ MiscellaneousB.SetVarAssoc("Miscellaneous Text",1)
+
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ FeedbackWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def MarkerSliderPress():
+ TextAreaControl.SetText(18024)
+ return
+
+def LocatorSliderPress():
+ TextAreaControl.SetText(18025)
+ return
+
+def THac0RollsPress():
+ TextAreaControl.SetText(18026)
+ return
+
+def THac0RollsBPress():
+ TextAreaControl.SetText(18026)
+ return
+
+def CombatInfoPress():
+ TextAreaControl.SetText(18027)
+ return
+
+def CombatInfoBPress():
+ TextAreaControl.SetText(18027)
+ return
+
+def ActionsPress():
+ TextAreaControl.SetText(18028)
+ return
+
+def ActionsBPress():
+ TextAreaControl.SetText(18028)
+ return
+
+def StateChangesPress():
+ TextAreaControl.SetText(18029)
+ return
+
+def StateChangesBPress():
+ TextAreaControl.SetText(18029)
+ return
+
+def SelectionTextPress():
+ TextAreaControl.SetText(18030)
+ return
+
+def SelectionTextBPress():
+ TextAreaControl.SetText(18030)
+ return
+
+def MiscellaneousPress():
+ TextAreaControl.SetText(18031)
+ return
+
+def MiscellaneousBPress():
+ TextAreaControl.SetText(18031)
+ return
+
+def OkPress():
+ if FeedbackWindow:
+ FeedbackWindow.Unload()
+ GemRB.SetNextScript("GamePlay")
+ return
+
+def CancelPress():
+ if FeedbackWindow:
+ FeedbackWindow.Unload()
+ GemRB.SetNextScript("GamePlay")
+ return
+
diff --git a/gemrb/GUIScripts/iwd2/GUICommonWindows.py b/gemrb/GUIScripts/iwd2/GUICommonWindows.py
new file mode 100644
index 0000000..cd40539
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUICommonWindows.py
@@ -0,0 +1,769 @@
+# -*-python-*-
+# 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.
+#
+
+# GUICommonWindows.py - functions to open common
+# windows in lower part of the screen
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+from GUIDefines import *
+from ie_stats import *
+from ie_modal import *
+from ie_action import *
+
+import GUIINV
+import GUIJRNL
+import GUIMA
+import GUIOPT
+import GUISPL
+import GUIREC
+import LUCommon
+import InventoryCommon
+import LoadScreen
+
+FRAME_PC_SELECTED = 0
+FRAME_PC_TARGET = 1
+
+PortraitWindow = None
+OptionsWindow = None
+DraggedPortrait = None
+
+def SetupMenuWindowControls (Window, Gears, ReturnToGame):
+ """Sets up all of the basic control windows."""
+
+ global OptionsWindow
+
+ OptionsWindow = Window
+ #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ReturnToGame)
+
+ # Spellbook
+ Button = Window.GetControl (4)
+ Button.SetTooltip (16309)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUISPL.OpenSpellBookWindow)
+
+ # Inventory
+ Button = Window.GetControl (5)
+ Button.SetTooltip (16307)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIINV.OpenInventoryWindow)
+
+ # Journal
+ Button = Window.GetControl (6)
+ Button.SetTooltip (16308)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIJRNL.OpenJournalWindow)
+
+ # Map
+ Button = Window.GetControl (7)
+ Button.SetTooltip (16310)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIMA.OpenMapWindow)
+
+ # Records
+ Button = Window.GetControl (8)
+ Button.SetTooltip (16306)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIREC.OpenRecordsWindow)
+
+ # Options
+ Button = Window.GetControl (9)
+ Button.SetTooltip (16311)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("SelectedWindow", 7)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIOPT.OpenOptionsWindow)
+
+ # Gears
+ Button = Window.GetControl (10)
+ Button.SetAnimation ("CGEAR")
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_ANIMATED, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ if Gears:
+ # Select All
+ Button = Window.GetControl (11)
+ Button.SetTooltip (10485)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectAllOnPress)
+ # Rest
+ Button = Window.GetControl (12)
+ Button.SetTooltip (11942)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.RestPress)
+
+ # Character Arbitration
+ Button = Window.GetControl (13)
+ Button.SetTooltip (16312)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None) #TODO: CharacterWindow
+ return
+
+def AIPress ():
+ """Toggles the party AI."""
+
+ Button = PortraitWindow.GetControl (6)
+ AI = GemRB.GetMessageWindowSize () & GS_PARTYAI
+
+ if AI:
+ GemRB.GameSetScreenFlags (GS_PARTYAI, OP_NAND)
+ Button.SetTooltip (15918)
+ GemRB.SetVar ("AI", 0)
+ else:
+ GemRB.GameSetScreenFlags (GS_PARTYAI, OP_OR)
+ Button.SetTooltip (15917)
+ GemRB.SetVar ("AI", GS_PARTYAI)
+ return
+
+def EmptyControls ():
+ global PortraitWindow
+
+ Window = PortraitWindow
+ for i in range (12):
+ Button = Window.GetControl (i+6)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetPicture ("")
+ Button.SetText ("")
+ Button.SetActionIcon (globals(), -1)
+ return
+
+def SelectFormationPreset ():
+ """Choose the default formation."""
+ GemRB.GameSetFormation (GemRB.GetVar ("Value"), GemRB.GetVar ("Formation") )
+ GroupControls ()
+ return
+
+def SetupFormation ():
+ global PortraitWindow
+
+ Window = PortraitWindow
+ for i in range (12):
+ Button = Window.GetControl (i+6)
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetBAM ("FORM%x"%i,0,0,-1)
+ Button.SetVarAssoc ("Value", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectFormationPreset)
+ return
+
+def GroupControls ():
+ global PortraitWindow
+
+ Window = PortraitWindow
+ Button = Window.GetControl (6)
+ Button.SetActionIcon (globals(), 7)
+ Button = Window.GetControl (7)
+ Button.SetActionIcon (globals(), 15)
+ Button = Window.GetControl (8)
+ Button.SetActionIcon (globals(), 21)
+ Button = Window.GetControl (9)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (10)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (11)
+ Button.SetActionIcon (globals(), -1)
+ Button = Window.GetControl (12)
+ Button.SetActionIcon (globals(), -1)
+ GemRB.SetVar ("Formation", GemRB.GameGetFormation ())
+ for i in range (5):
+ Button = Window.GetControl (13+i)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ idx = GemRB.GameGetFormation (i)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON|IE_GUI_BUTTON_NORMAL, OP_SET)
+ Button.SetSprites ("GUIBTBUT",0,0,1,2,3)
+ Button.SetBAM ("FORM%x"%idx,0,0,-1)
+ Button.SetVarAssoc ("Formation", i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectFormation)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, SetupFormation)
+ str = GemRB.GetString (4935)
+ Button.SetTooltip ("F%d - %s"%(8+i,str) )
+ return
+
+def UpdateActionsWindow ():
+ global PortraitWindow
+ global level, TopIndex
+
+ if PortraitWindow == -1:
+ return
+
+ if PortraitWindow == None:
+ return
+
+ #do this only when there is no 'otherwindow'
+ if GemRB.GetVar ("OtherWindow") != -1:
+ return
+
+ Selected = GemRB.GetSelectedSize()
+ if Selected == 0:
+ EmptyControls ()
+ return
+ if Selected > 1:
+ GroupControls ()
+ return
+
+ #we are sure there is only one actor selected
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ level = GemRB.GetVar ("ActionLevel")
+ TopIndex = GemRB.GetVar ("TopIndex")
+ if level == 0:
+ PortraitWindow.SetupControls (globals(), pc, 6, 1)
+ elif level == 1:
+ PortraitWindow.SetupEquipmentIcons (globals(), pc, TopIndex, 6, 1)
+ elif level == 2: #spells
+ GemRB.SetVar ("Type", 3)
+ PortraitWindow.SetupSpellIcons (globals(), pc, 3, TopIndex, 6, 1)
+ elif level == 3: #innates
+ GemRB.SetVar ("Type", 4)
+ PortraitWindow.SetupSpellIcons (globals(), pc, 4, TopIndex, 6, 1)
+ return
+
+def ActionQWeaponPressed (which):
+ """Selects the given quickslot weapon if possible."""
+ global PortraitWindow
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+ qs = GemRB.GetEquippedQuickSlot (pc, 1, 1)
+
+ #38 is the magic slot
+ if ((qs==which) or (qs==38)) and GemRB.GameControlGetTargetMode() != TARGET_MODE_ATTACK:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK, GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
+ else:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_NONE)
+ GemRB.SetEquippedQuickSlot (pc, which, -1, 1)
+
+ PortraitWindow.SetupControls (globals(), pc, 6)
+ UpdateActionsWindow ()
+ return
+
+def ActionQWeapon1Pressed ():
+ ActionQWeaponPressed (0)
+
+def ActionQWeapon2Pressed ():
+ ActionQWeaponPressed (1)
+
+def ActionQWeapon3Pressed ():
+ ActionQWeaponPressed (2)
+
+def ActionQWeapon4Pressed ():
+ ActionQWeaponPressed (3)
+
+#no check needed because the button wouldn't be drawn if illegal
+def ActionLeftPressed ():
+ """Scrolls the actions window left.
+
+ Used primarily for spell selection."""
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ if TopIndex>10:
+ TopIndex -= 10
+ else:
+ TopIndex = 0
+ GemRB.SetVar ("TopIndex", TopIndex)
+ UpdateActionsWindow ()
+ return
+
+#no check needed because the button wouldn't be drawn if illegal
+def ActionRightPressed ():
+ """Scrolls the action window right.
+
+ Used primarily for spell selection."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Type = GemRB.GetVar ("Type")
+ #Type is a bitfield if there is no level given
+ #This is to make sure cleric/mages get all spells listed
+ Max = GemRB.GetMemorizedSpellsCount(pc, Type, -1, 1)
+ TopIndex += 10
+ if TopIndex > Max - 10:
+ if Max>10:
+ TopIndex = Max-10
+ else:
+ TopIndex = 0
+
+ GemRB.SetVar ("TopIndex", TopIndex)
+ UpdateActionsWindow ()
+ return
+
+def ActionBardSongPressed ():
+ """Toggles the battle song."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_BATTLESONG, 1)
+ GemRB.PlaySound ("act_01")
+ UpdateActionsWindow ()
+ return
+
+def ActionSearchPressed ():
+ """Toggles detect traps."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_DETECTTRAPS, 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionStealthPressed ():
+ """Toggles stealth."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_STEALTH, 1)
+ GemRB.PlaySound ("act_07")
+ UpdateActionsWindow ()
+ return
+
+def ActionTurnPressed ():
+ """Toggles turn undead."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ GemRB.SetModalState (pc, MS_TURNUNDEAD, 1)
+ GemRB.PlaySound ("act_06")
+ UpdateActionsWindow ()
+ return
+
+def ActionUseItemPressed ():
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 1)
+ UpdateActionsWindow ()
+ return
+
+def ActionCastPressed ():
+ """Opens the spell choice scrollbar."""
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 2)
+ UpdateActionsWindow ()
+ return
+
+def ActionQItemPressed (action):
+ """Uses the given quick item."""
+ pc = GemRB.GameGetFirstSelectedActor ()
+ #quick slot
+ GemRB.UseItem (pc, -2, action, -1, 1)
+ return
+
+def ActionQItem1Pressed ():
+ ActionQItemPressed (ACT_QSLOT1)
+ return
+
+def ActionQItem2Pressed ():
+ ActionQItemPressed (ACT_QSLOT2)
+ return
+
+def ActionQItem3Pressed ():
+ ActionQItemPressed (ACT_QSLOT3)
+ return
+
+def ActionQItem4Pressed ():
+ ActionQItemPressed (ACT_QSLOT4)
+ return
+
+def ActionQItem5Pressed ():
+ ActionQItemPressed (ACT_QSLOT5)
+ return
+
+def ActionInnatePressed ():
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 3)
+ UpdateActionsWindow ()
+ return
+
+def ActionSkillsPressed ():
+ GemRB.SetVar ("TopIndex", 0)
+ GemRB.SetVar ("ActionLevel", 4)
+ UpdateActionsWindow ()
+ return
+
+def SpellPressed ():
+ """Prepares a spell to be cast."""
+
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
+ Spell = GemRB.GetVar ("Spell")
+ Type = GemRB.GetVar ("Type")
+ GemRB.SpellCast (pc, Type, Spell, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ UpdateActionsWindow ()
+ return
+
+def EquipmentPressed ():
+ pc = GemRB.GameGetFirstSelectedActor ()
+
+ GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
+ Item = GemRB.GetVar ("Equipment")
+ #equipment index
+ GemRB.UseItem (pc, -1, Item, -1, 1)
+ GemRB.SetVar ("ActionLevel", 0)
+ UpdateActionsWindow ()
+ return
+
+def GetActorRaceTitle (actor):
+ RaceID = GemRB.GetPlayerStat (actor, IE_SUBRACE)
+ if RaceID:
+ RaceID += GemRB.GetPlayerStat (actor, IE_RACE)<<16
+ else:
+ RaceID = GemRB.GetPlayerStat (actor, IE_RACE)
+ row = CommonTables.Races.FindValue (3, RaceID )
+ RaceTitle = CommonTables.Races.GetValue (row, 2)
+ return RaceTitle
+
+# NOTE: this function is called with the primary classes
+def GetKitIndex (actor, ClassIndex):
+ Kit = GemRB.GetPlayerStat (actor, IE_KIT)
+
+ KitIndex = -1
+ ClassID = CommonTables.Classes.GetValue (ClassIndex, 2)
+ # skip the primary classes
+ for ci in range (10, CommonTables.Classes.GetRowCount ()):
+ BaseClass = CommonTables.Classes.GetValue (ci, 3)
+ if BaseClass == ClassID and Kit & CommonTables.Classes.GetValue (ci, 2):
+ KitIndex = ci
+
+ if KitIndex == -1:
+ return 0
+
+ return KitIndex
+
+def GetActorClassTitle (actor, ClassIndex):
+ ClassTitle = GemRB.GetPlayerStat (actor, IE_TITLE1)
+ if ClassTitle:
+ return ClassTitle
+
+ #Class = GemRB.GetPlayerStat (actor, IE_CLASS)
+ KitIndex = GetKitIndex (actor, ClassIndex)
+ if KitIndex == 0:
+ ClassTitle = CommonTables.Classes.GetValue (ClassIndex, 0)
+ else:
+ ClassTitle = CommonTables.Classes.GetValue (KitIndex, 0)
+
+ if ClassTitle == "*":
+ return 0
+ return ClassTitle
+
+# overriding the one in GUICommon, since we use a different table and animations
+def GetActorPaperDoll (actor):
+ PortraitTable = GemRB.LoadTable ("avatars")
+ anim_id = GemRB.GetPlayerStat (actor, IE_ANIMATION_ID)
+ level = GemRB.GetPlayerStat (actor, IE_ARMOR_TYPE)
+ row = "0x%04X" %anim_id
+ which = "AT_%d" %(level+1)
+ return PortraitTable.GetValue (row, which)
+
+
+SelectionChangeHandler = None
+
+def SetSelectionChangeHandler (handler):
+ """Updates the selection handler."""
+
+ global SelectionChangeHandler
+
+ # Switching from walking to non-walking environment:
+ # set the first selected PC in walking env as a selected
+ # in nonwalking env
+ #if (not SelectionChangeHandler) and handler:
+ if (not SelectionChangeHandler) and handler and (not GUICommon.NextWindowFn):
+ sel = GemRB.GameGetFirstSelectedPC ()
+ if not sel:
+ sel = 1
+ GemRB.GameSelectPCSingle (sel)
+
+ SelectionChangeHandler = handler
+
+ # redraw selection on change main selection | single selection
+ SelectionChanged ()
+ return
+
+def RunSelectionChangeHandler ():
+ if SelectionChangeHandler:
+ SelectionChangeHandler ()
+ return
+
+def OpenPortraitWindow ():
+ global PortraitWindow
+
+ PortraitWindow = Window = GemRB.LoadWindow (1)
+
+ for i in range (PARTY_SIZE):
+ Button = Window.GetControl (i)
+ Button.SetFont ("STATES2")
+ Button.SetVarAssoc ("PressedPortrait", i+1)
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitButtonOnPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, PortraitButtonOnShiftPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDropItemToPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT, OnDropPortraitToPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG, PortraitButtonOnDrag)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, PortraitButtonOnMouseEnter)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, PortraitButtonOnMouseLeave)
+
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_TOP|IE_GUI_BUTTON_ALIGN_LEFT|IE_GUI_BUTTON_PICTURE, OP_SET)
+
+ Button.SetBorder (FRAME_PC_SELECTED, 1, 1, 2, 2, 0, 255, 0, 255)
+ Button.SetBorder (FRAME_PC_TARGET, 3, 3, 4, 4, 255, 255, 0, 255)
+
+ UpdatePortraitWindow ()
+ SelectionChanged ()
+ return Window
+
+def UpdatePortraitWindow ():
+ """Updates all of the portraits."""
+
+ Window = PortraitWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Inventory = GemRB.GetVar ("Inventory")
+
+ for portid in range (PARTY_SIZE):
+ Button = Window.GetControl (portid)
+ pic = GemRB.GetPlayerPortrait (portid+1, 1)
+ if Inventory and pc != portid+1:
+ pic = None
+
+ if not pic:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetText ("")
+ Button.SetTooltip ("")
+ continue
+
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE| \
+ IE_GUI_BUTTON_ALIGN_BOTTOM| \
+ IE_GUI_BUTTON_ALIGN_LEFT| \
+ IE_GUI_BUTTON_HORIZONTAL| \
+ IE_GUI_BUTTON_DRAGGABLE, OP_SET)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetPicture (pic, "NOPORTSM")
+ GUICommon.SetupDamageInfo (portid+1, Button)
+
+ #add effects on the portrait
+ effects = GemRB.GetPlayerStates (portid+1)
+ states = ""
+ for col in range(len(effects)):
+ states = effects[col:col+1] + states
+ if col % 3 == 2: states = "\n" + states
+ for x in range(3 - (len(effects)/3)):
+ states = "\n" + states
+ states = "\n" + states
+
+ # blank space
+ flag = blank = chr(238)
+
+ # shopping icon
+ if pc==portid+1:
+ if GemRB.GetStore()!=None:
+ flag = chr(155)
+ # talk icon
+ if GemRB.GameGetSelectedPCSingle(1)==portid+1:
+ flag = chr(154)
+
+ if LUCommon.CanLevelUp (portid+1):
+ states = flag+blank+chr(255) + states
+ else:
+ states = flag+blank+blank + states
+ Button.SetText(states)
+ return
+
+def PortraitButtonOnDrag ():
+ global DraggedPortrait
+
+ #they start from 1
+ DraggedPortrait = GemRB.GetVar ("PressedPortrait")
+ GemRB.DragItem (DraggedPortrait, -1, "")
+ return
+
+def PortraitButtonOnPress ():
+ """Selects the portrait individually."""
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if GemRB.GameControlGetTargetMode() != TARGET_MODE_NONE:
+ GemRB.ActOnPC (i)
+ return
+
+ if (not SelectionChangeHandler):
+ if GemRB.GameIsPCSelected (i):
+ GemRB.GameControlSetScreenFlags (SF_CENTERONACTOR, OP_OR)
+ GemRB.GameSelectPC (i, True, SELECT_REPLACE)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def PortraitButtonOnShiftPress ():
+ """Handles selecting multiple portaits with shift."""
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if (not SelectionChangeHandler):
+ sel = GemRB.GameIsPCSelected (i)
+ sel = not sel
+ GemRB.GameSelectPC (i, sel)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def SelectionChanged ():
+ """Ran by the Game class when a PC selection is changed."""
+
+ global PortraitWindow
+
+ if not PortraitWindow:
+ return
+
+ GemRB.SetVar ("ActionLevel", 0)
+ if (not SelectionChangeHandler):
+ UpdateActionsWindow ()
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, GemRB.GameIsPCSelected (i + 1))
+ else:
+ sel = GemRB.GameGetSelectedPCSingle ()
+
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, i + 1 == sel)
+ import CommonWindow
+ CommonWindow.CloseContainerWindow()
+ return
+
+def PortraitButtonOnMouseEnter ():
+ global DraggedPortrait
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ GemRB.GameControlSetLastActor( i )
+ if GemRB.IsDraggingItem()==2:
+ if DraggedPortrait != None:
+ GemRB.SwapPCs (DraggedPortrait, i)
+ GemRB.SetVar ("PressedPortrait", DraggedPortrait)
+ DraggedPortrait = i
+ GemRB.SetTimedEvent (CheckDragging, 1)
+ else:
+ OnDropPortraitToPC()
+ return
+
+ if GemRB.IsDraggingItem ():
+ Button = PortraitWindow.GetControl (i-1)
+ Button.EnableBorder (FRAME_PC_TARGET, 1)
+ return
+
+def OnDropPortraitToPC ():
+ GemRB.SetVar ("PressedPortrait",0)
+ GemRB.DragItem (0, -1, "")
+ DraggedPortrait = None
+ return
+
+def CheckDragging():
+ """Contains portrait dragging in case of mouse out-of-range."""
+
+ global DraggedPortrait
+
+ i = GemRB.GetVar ("PressedPortrait")
+ if not i:
+ GemRB.DragItem (0, -1, "")
+
+ if GemRB.IsDraggingItem()!=2:
+ DraggedPortrait = None
+ return
+
+def PortraitButtonOnMouseLeave ():
+ i = GemRB.GetVar ("PressedPortrait")
+ if not i:
+ return
+
+ Button = PortraitWindow.GetControl (i-1)
+ Button.EnableBorder (FRAME_PC_TARGET, 0)
+ GemRB.SetVar ("PressedPortrait", 0)
+ GemRB.SetTimedEvent (CheckDragging, 1)
+ return
+
+def ActionStopPressed ():
+ for i in GemRB.GetSelectedActors():
+ GemRB.ClearActions (i, 1)
+ return
+
+def ActionTalkPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_TALK,GA_NO_DEAD|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionAttackPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK,GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
+
+def ActionDefendPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_DEFEND,GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionThievingPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_PICK, GA_NO_DEAD|GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def OpenWaitForDiscWindow ():
+ global DiscWindow
+
+ if DiscWindow:
+ GemRB.HideGUI ()
+ if DiscWindow:
+ DiscWindow.Unload ()
+ GemRB.SetVar ("OtherWindow", -1)
+ # ...LoadWindowPack()
+ EnableAnimatedWindows ()
+ DiscWindow = None
+ GemRB.UnhideGUI ()
+ return
+
+ try:
+ GemRB.HideGUI ()
+ except:
+ pass
+
+ GemRB.LoadWindowPack ("GUIID")
+ DiscWindow = Window = GemRB.LoadWindow (0)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+ label = DiscWindow.GetControl (0)
+
+ disc_num = GemRB.GetVar ("WaitForDisc")
+ #disc_path = GemRB.GetVar ("WaitForDiscPath")
+ disc_path = 'XX:'
+
+ text = GemRB.GetString (31483) + " " + str (disc_num) + " " + GemRB.GetString (31569) + " " + disc_path + "\n" + GemRB.GetString (49152)
+ label.SetText (text)
+ DisableAnimatedWindows ()
+ # 31483 - Please place PS:T disc number
+ # 31568 - Please place the PS:T DVD
+ # 31569 - in drive
+ # 31570 - Wrong disc in drive
+ # 31571 - There is no disc in drive
+ # 31578 - No disc could be found in drive. Please place Disc 1 in drive.
+ # 49152 - To quit the game, press Alt-F4
+
+ try:
+ GemRB.UnhideGUI ()
+ except:
+ DiscWindow.SetVisible (WINDOW_VISIBLE)
+
+def CheckLevelUp(pc):
+ GemRB.SetVar ("CheckLevelUp"+str(pc), LUCommon.CanLevelUp (pc))
diff --git a/gemrb/GUIScripts/iwd2/GUIINV.py b/gemrb/GUIScripts/iwd2/GUIINV.py
new file mode 100644
index 0000000..296201c
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUIINV.py
@@ -0,0 +1,293 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIINV.py - scripts to control inventory windows from GUIINV winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+import InventoryCommon
+from GUIDefines import *
+from ie_stats import *
+from ie_slots import *
+from ie_spells import *
+
+InventoryWindow = None
+PortraitWindow = None
+OptionsWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+def OpenInventoryWindow ():
+ global InventoryWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenInventoryWindow):
+ if GemRB.IsDraggingItem () == 1:
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #store the item in the inventory before window is closed
+ GemRB.DropDraggedItem (pc, -3)
+ #dropping on ground if cannot store in inventory
+ if GemRB.IsDraggingItem () == 1:
+ GemRB.DropDraggedItem (pc, -2)
+
+ if InventoryWindow:
+ InventoryWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ InventoryWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ GUICommonWindows.UpdatePortraitWindow ()
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIINV", 800, 600)
+ InventoryWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", InventoryWindow.ID)
+ #TODO: Setup the MessageLabel here if needed
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow ()
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenInventoryWindow)
+ Window.SetFrame ()
+
+ #ground items scrollbar
+ ScrollBar = Window.GetControl (66)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RefreshInventoryWindow)
+
+ # Ground Items (6)
+ for i in range (5):
+ Button = Window.GetControl (i+68)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, InventoryCommon.MouseEnterGround)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, InventoryCommon.MouseLeaveGround)
+ Button.SetVarAssoc ("GroundItemButton", i)
+ Button.SetFont ("NUMFONT")
+
+ Button = Window.GetControl (81)
+ Button.SetTooltip (12011)
+ Button.SetVarAssoc ("GroundItemButton", 6)
+ Button.SetFont ("NUMFONT")
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_BOTTOM | IE_GUI_BUTTON_PICTURE, OP_OR)
+
+ #major & minor clothing color
+ Button = Window.GetControl (62)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.MajorPress)
+ Button.SetTooltip (12007)
+
+ Button = Window.GetControl (63)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.MinorPress)
+ Button.SetTooltip (12008)
+
+ #hair & skin color
+ Button = Window.GetControl (82)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.HairPress)
+ Button.SetTooltip (37560)
+
+ Button = Window.GetControl (83)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.SkinPress)
+ Button.SetTooltip (37559)
+
+ # paperdoll
+ Button = Window.GetControl (50)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_ANIMATED, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnAutoEquip)
+
+ # portrait
+ Button = Window.GetControl (84)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+
+ # armor class
+ Label = Window.GetControl (0x10000038)
+ Label.SetTooltip (17183)
+
+ # hp current
+ Label = Window.GetControl (0x10000039)
+ Label.SetTooltip (17184)
+
+ # hp max
+ Label = Window.GetControl (0x1000003a)
+ Label.SetTooltip (17378)
+
+ #info label, game paused, etc
+ Label = Window.GetControl (0x1000003f)
+ Label.SetText ("")
+
+ SlotCount = GemRB.GetSlotType (-1)["Count"]
+ for slot in range (SlotCount):
+ SlotType = GemRB.GetSlotType (slot+1)
+ if SlotType["ID"]:
+ Button = Window.GetControl (SlotType["ID"])
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, InventoryCommon.MouseEnterSlot)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, InventoryCommon.MouseLeaveSlot)
+ Button.SetVarAssoc ("ItemButton", slot+1)
+ Button.SetFont ("NUMFONT")
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_BOTTOM | IE_GUI_BUTTON_PICTURE, OP_OR)
+
+ GemRB.SetVar ("TopIndex", 0)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateInventoryWindow)
+
+ UpdateInventoryWindow ()
+ return
+
+#complete update
+def UpdateInventoryWindow ():
+ Window = InventoryWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Container = GemRB.GetContainer (pc, 1)
+ ScrollBar = Window.GetControl (66)
+ Count = Container['ItemCount']
+ if Count<1:
+ Count=1
+ ScrollBar.SetVarAssoc ("TopIndex", Count)
+ RefreshInventoryWindow ()
+ # populate inventory slot controls
+ SlotCount = GemRB.GetSlotType (-1)["Count"]
+ for i in range (SlotCount):
+ InventoryCommon.UpdateSlot (pc, i)
+ return
+
+def RefreshInventoryWindow ():
+ Window = InventoryWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # name
+ Label = Window.GetControl (0x10000032)
+ Label.SetText (GemRB.GetPlayerName (pc, 0))
+
+ # paperdoll
+ Button = Window.GetControl (50)
+ Color1 = GemRB.GetPlayerStat (pc, IE_METAL_COLOR)
+ Color2 = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR)
+ Color3 = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR)
+ Color4 = GemRB.GetPlayerStat (pc, IE_SKIN_COLOR)
+ Color5 = GemRB.GetPlayerStat (pc, IE_LEATHER_COLOR)
+ Color6 = GemRB.GetPlayerStat (pc, IE_ARMOR_COLOR)
+ Color7 = GemRB.GetPlayerStat (pc, IE_HAIR_COLOR)
+
+ Button.SetFlags (IE_GUI_BUTTON_CENTER_PICTURES, OP_OR)
+ Button.SetAnimation (GUICommonWindows.GetActorPaperDoll (pc)+"G11")
+ Button.SetAnimationPalette (Color1, Color2, Color3, Color4, Color5, Color6, Color7, 0)
+
+ # portrait
+ Button = Window.GetControl (84)
+ Button.SetPicture (GemRB.GetPlayerPortrait (pc,0))
+
+ # encumbrance
+ GUICommon.SetEncumbranceLabels (Window, 0x10000042, None, pc)
+
+ # armor class
+ ac = GemRB.GetPlayerStat (pc, IE_ARMORCLASS)
+ Label = Window.GetControl (0x10000038)
+ Label.SetText (str (ac))
+
+ # hp current
+ hp = GemRB.GetPlayerStat (pc, IE_HITPOINTS)
+ Label = Window.GetControl (0x10000039)
+ Label.SetText (str (hp))
+
+ # hp max
+ hpmax = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)
+ Label = Window.GetControl (0x1000003a)
+ Label.SetText (str (hpmax))
+
+ # party gold
+ Label = Window.GetControl (0x10000040)
+ Label.SetText (str (GemRB.GameGetPartyGold ()))
+
+ Button = Window.GetControl (62)
+ Color = GemRB.GetPlayerStat (pc, IE_MAJOR_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 0, 0, Color)
+
+ Button = Window.GetControl (63)
+ Color = GemRB.GetPlayerStat (pc, IE_MINOR_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 0, 0, Color)
+
+ Button = Window.GetControl (82)
+ Color = GemRB.GetPlayerStat (pc, IE_HAIR_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 0, 0, Color)
+
+ Button = Window.GetControl (83)
+ Color = GemRB.GetPlayerStat (pc, IE_SKIN_COLOR, 1) & 0xFF
+ Button.SetBAM ("COLGRAD", 0, 0, Color)
+
+ # update ground inventory slots
+ Container = GemRB.GetContainer(pc, 1)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ for i in range (6):
+ if i<5:
+ Button = Window.GetControl (i+68)
+ else:
+ Button = Window.GetControl (i+76)
+
+ if GemRB.IsDraggingItem ()==1:
+ Button.SetState (IE_GUI_BUTTON_SECOND)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDragItemGround)
+
+ Slot = GemRB.GetContainerItem (pc, i+TopIndex)
+ if Slot == None:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.OnDragItemGround)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InventoryCommon.OpenGroundItemInfoWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None) #TODO: implement OpenGroundItemAmountWindow
+
+ GUICommon.UpdateInventorySlot (pc, Button, Slot, "ground")
+
+ #if actor is uncontrollable, make this grayed
+ held = GemRB.GetPlayerStat (pc, IE_HELD) + GemRB.GetPlayerStat (pc, IE_CASTERHOLD)
+ if held or GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+# End of file GUIINV.py
diff --git a/gemrb/GUIScripts/iwd2/GUIJRNL.py b/gemrb/GUIScripts/iwd2/GUIJRNL.py
new file mode 100644
index 0000000..6e4479f
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUIJRNL.py
@@ -0,0 +1,123 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIJRNL.py - scripts to control journal/diary windows from GUIJRNL winpack
+
+###################################################
+import GemRB
+from GUIDefines import *
+import GUICommon
+
+###################################################
+JournalWindow = None
+Chapter = 0
+StartTime = 0
+StartYear = 0
+
+###################################################
+def OpenJournalWindow ():
+ global StartTime, StartYear
+ global JournalWindow, Chapter
+
+ Table = GemRB.LoadTable("YEARS")
+ #StartTime is the time offset for ingame time, beginning from the startyear
+ StartTime = Table.GetValue("STARTTIME", "VALUE") / 4500
+ #StartYear is the year of the lowest ingame date to be printed
+ StartYear = Table.GetValue("STARTYEAR", "VALUE")
+
+ if GUICommon.CloseOtherWindow (OpenJournalWindow):
+ GemRB.HideGUI ()
+ if JournalWindow:
+ JournalWindow.Unload ()
+ JournalWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack ("GUIJRNL", 800, 600)
+ JournalWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar("OtherWindow", JournalWindow.ID)
+
+
+ Button = Window.GetControl (3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, JournalPrevSectionPress)
+
+ Button = Window.GetControl (4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, JournalNextSectionPress)
+
+ Chapter = GemRB.GetGameVar("chapter")
+ UpdateJournalWindow ()
+ GemRB.UnhideGUI ()
+
+
+###################################################
+def UpdateJournalWindow ():
+ Window = JournalWindow
+
+ # Title
+ Title = Window.GetControl (5)
+ Title.SetText (16202 + Chapter)
+
+ # text area
+ Text = Window.GetControl (1)
+ Text.Clear ()
+
+ for i in range (GemRB.GetJournalSize (Chapter)):
+ je = GemRB.GetJournalEntry (Chapter, i)
+
+ if je == None:
+ continue
+
+ hours = je['GameTime'] / 4500
+ days = int(hours/24)
+ year = str (StartYear + int(days/365))
+ dayandmonth = StartTime + days%365
+ GemRB.SetToken("GAMEDAY", str(days) )
+ GemRB.SetToken("HOUR",str(hours%24 ) )
+ GemRB.SetVar("DAYANDMONTH",dayandmonth)
+ GemRB.SetToken("YEAR",year)
+ Text.Append ("[color=FFFF00]"+GemRB.GetString(15980)+"[/color]", 3*i)
+ Text.Append (je['Text'], 3*i + 1)
+ Text.Append ("", 3*i + 2)
+
+
+###################################################
+def JournalPrevSectionPress ():
+ global Chapter
+
+ if Chapter > 0:
+ Chapter = Chapter - 1
+ UpdateJournalWindow ()
+
+
+###################################################
+def JournalNextSectionPress ():
+ global Chapter
+
+ #if GemRB.GetJournalSize (Chapter + 1) > 0:
+ if Chapter < GemRB.GetGameVar("chapter"):
+ Chapter = Chapter + 1
+ UpdateJournalWindow ()
+
+
+###################################################
+# End of file GUIJRNL.py
diff --git a/gemrb/GUIScripts/iwd2/GUILOAD.py b/gemrb/GUIScripts/iwd2/GUILOAD.py
new file mode 100644
index 0000000..bb84e11
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUILOAD.py
@@ -0,0 +1,185 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUILOAD.py - Load window
+
+###################################################
+
+import GemRB
+import LoadScreen
+from GUIDefines import *
+
+LoadWindow = 0
+TextAreaControl = 0
+Games = ()
+ScrollBar = 0
+
+def OnLoad ():
+ global LoadWindow, TextAreaControl, Games, ScrollBar
+
+ GemRB.SetVar ("SaveDir",1) #iwd2 is always using 'mpsave'
+ GemRB.LoadWindowPack ("GUILOAD", 800 ,600)
+ LoadWindow = GemRB.LoadWindow (0)
+ LoadWindow.SetFrame ()
+
+ CancelButton=LoadWindow.GetControl (22)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.SetVar ("LoadIdx",0)
+ for i in range (5):
+ Button = LoadWindow.GetControl (55+i)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ Button = LoadWindow.GetControl (60+i)
+ Button.SetText (13957)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ #area previews
+ Button = LoadWindow.GetControl (1+i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ #PC portraits
+ for j in range (PARTY_SIZE):
+ Button = LoadWindow.GetControl (25 + i*PARTY_SIZE + j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+ Button.SetSize (21, 21)
+
+ ScrollBar=LoadWindow.GetControl (23)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games=GemRB.GetSaveGames()
+ TopIndex = max (0, len(Games) - 5)
+
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ScrollBarPress ():
+ #draw load game portraits
+ Pos = GemRB.GetVar ("TopIndex")
+ for i in range (5):
+ ActPos = Pos + i
+
+ Button1 = LoadWindow.GetControl (55+i)
+ Button2 = LoadWindow.GetControl (60+i)
+ if ActPos<len(Games):
+ Button1.SetState (IE_GUI_BUTTON_ENABLED)
+ Button2.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState (IE_GUI_BUTTON_DISABLED)
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetName()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl (0x10000005+i)
+ Label.SetText (Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl (0x1000000a+i)
+ Label.SetText (Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetDate()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl (0x1000000f+i)
+ Label.SetText (Slotname)
+
+ Button=LoadWindow.GetControl (1+i)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture ("")
+ for j in range (PARTY_SIZE):
+ Button=LoadWindow.GetControl (25 + i*PARTY_SIZE + j)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture ("")
+ return
+
+def LoadGamePress ():
+ if LoadWindow:
+ LoadWindow.Unload ()
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ LoadScreen.StartLoadScreen()
+ GemRB.LoadGame(Games[Pos]) #loads and enters savegame
+ GemRB.EnterGame ()
+ return
+
+def DeleteGameConfirm():
+ global Games
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Pos = TopIndex +GemRB.GetVar ("LoadIdx")
+ GemRB.DeleteSaveGame(Games[Pos])
+ if TopIndex>0:
+ GemRB.SetVar ("TopIndex",TopIndex-1)
+ del Games[Pos]
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress ()
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel ():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress ():
+ global ConfirmWindow
+
+ LoadWindow.SetVisible (WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow (1)
+ Text=ConfirmWindow.GetControl (0)
+ Text.SetText (15305)
+ DeleteButton=ConfirmWindow.GetControl (1)
+ DeleteButton.SetText (13957)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ CancelButton=ConfirmWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CancelPress ():
+ if LoadWindow:
+ LoadWindow.Unload ()
+ GemRB.SetNextScript ("Start")
+ return
diff --git a/gemrb/GUIScripts/iwd2/GUIMA.py b/gemrb/GUIScripts/iwd2/GUIMA.py
new file mode 100644
index 0000000..55fc4c5
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUIMA.py
@@ -0,0 +1,385 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMA.py - scripts to control map windows from GUIMA and GUIWMAP winpacks
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+
+MapWindow = None
+NoteWindow = None
+WorldMapWindow = None
+OptionsWindow = None
+OldOptionsWindow = None
+PortraitWindow = None
+OldPortraitWindow = None
+
+def RevealMap ():
+ global MapWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (ShowMap):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+
+ PosX = GemRB.GetVar ("MapControlX")
+ PosY = GemRB.GetVar ("MapControlY")
+
+ GemRB.RevealArea (PosX, PosY, 30, 1)
+ GemRB.GamePause (0,0)
+ return
+
+###################################################
+# for farsight effect
+###################################################
+def ShowMap ():
+ global MapWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (ShowMap):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ #this window type should block the game
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMAP", 640, 480)
+ MapWindow = Window = GemRB.LoadWindow (2)
+ #this window type blocks the game normally, but map window doesn't
+ GemRB.SetVar ("OtherWindow", MapWindow.ID)
+ #saving the original portrait window
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, ShowMap)
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow ()
+ OptionsWindow.SetFrame ()
+
+ # World Map
+ Button = Window.GetControl (1)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Hide or Show mapnotes
+ Button = Window.GetControl (3)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Label = Window.GetControl (0x10000003)
+ Label.SetText ("")
+
+ # Map Control
+ Window.CreateMapControl (2, 0, 0, 0, 0, 0x10000003, "FLAG1")
+ Map = Window.GetControl (2)
+ GemRB.SetVar ("ShowMapNotes",IE_GUI_MAP_REVEAL_MAP)
+ Map.SetVarAssoc ("ShowMapNotes", IE_GUI_MAP_REVEAL_MAP)
+ Map.SetEvent (IE_GUI_MAP_ON_PRESS, RevealMap)
+ Window.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_GRAYED)
+ PortraitWindow.SetVisible (WINDOW_GRAYED)
+ OptionsWindow.SetVisible (WINDOW_FRONT)
+ PortraitWindow.SetVisible (WINDOW_FRONT)
+ Window.SetVisible (WINDOW_FRONT)
+ Map.SetStatus(IE_GUI_CONTROL_FOCUSED)
+ GemRB.GamePause (0,0)
+ return
+
+###################################################
+def OpenMapWindow ():
+ global MapWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenMapWindow):
+ if MapWindow:
+ MapWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ MapWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIMAP", 800, 600)
+ MapWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", MapWindow.ID)
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow ()
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenMapWindow)
+ OptionsWindow.SetFrame ()
+
+ # World Map
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenWorldMapWindowInside)
+
+ # Hide or Show mapnotes
+ Button = Window.GetControl (3)
+ Button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ # Is this an option?
+ GemRB.SetVar ("ShowMapNotes", IE_GUI_MAP_VIEW_NOTES)
+ Button.SetVarAssoc ("ShowMapNotes", IE_GUI_MAP_VIEW_NOTES)
+
+ Label = Window.GetControl (0x10000003)
+ Label.SetText ("")
+
+ # Map Control
+ Window.CreateMapControl (2, 0, 0, 0, 0, 0x10000003, "FLAG1")
+ Map = Window.GetControl (2)
+ Map.SetVarAssoc ("ShowMapNotes", IE_GUI_MAP_VIEW_NOTES)
+ Map.SetEvent (IE_GUI_MAP_ON_RIGHT_PRESS, AddNoteWindow)
+ Map.SetEvent (IE_GUI_MAP_ON_DOUBLE_PRESS, LeftDoublePressMap)
+ OptionsWindow.SetVisible(WINDOW_VISIBLE)
+ PortraitWindow.SetVisible(WINDOW_VISIBLE)
+ Window.SetVisible(WINDOW_VISIBLE)
+
+ GUICommonWindows.SetSelectionChangeHandler(None)
+
+def LeftDoublePressMap ():
+ OpenMapWindow()
+ return
+
+def CloseNoteWindow ():
+ if NoteWindow:
+ NoteWindow.Unload ()
+ MapWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def RemoveMapNote ():
+ PosX = GemRB.GetVar ("MapControlX")
+ PosY = GemRB.GetVar ("MapControlY")
+ GemRB.SetMapnote (PosX, PosY, 0, "")
+ CloseNoteWindow ()
+ return
+
+def QueryText ():
+ Data = ""
+ row = 0
+ while 1:
+ GemRB.SetVar ("row", row)
+ NoteLabel.SetVarAssoc ("row", row)
+ line = NoteLabel.QueryText ()
+ if len(line)<=0:
+ break
+ Data += line+"\n"
+ row += 1
+ return Data
+
+def SetMapNote ():
+ PosX = GemRB.GetVar ("MapControlX")
+ PosY = GemRB.GetVar ("MapControlY")
+ Label = NoteWindow.GetControl (1)
+ Text = QueryText ()
+ Color = GemRB.GetVar ("Color")
+ GemRB.SetMapnote (PosX, PosY, Color, Text)
+ CloseNoteWindow ()
+ return
+
+def SetFocusBack ():
+ NoteLabel.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def AddNoteWindow ():
+ global NoteWindow, NoteLabel
+
+ Label = MapWindow.GetControl (0x10000003)
+ Text = Label.QueryText ()
+ NoteWindow = GemRB.LoadWindow (5)
+ #convert to multiline, destroy unwanted resources
+ NoteLabel = NoteWindow.GetControl (1)
+ #0 is the default Scrollbar ID
+ NoteLabel = NoteLabel.ConvertEdit (0)
+ NoteLabel.SetText (Text)
+ NoteLabel.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ print "Just set this:", NoteLabel.QueryText()
+
+ for i in range(8):
+ Label = NoteWindow.GetControl (4+i)
+ Label.SetSprites ("FLAG1", i,0,1,2,0)
+ Label.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+ Label.SetVarAssoc ("Color", i)
+ Label.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetFocusBack)
+
+ #set
+ Label = NoteWindow.GetControl (0)
+ Label.SetEvent (IE_GUI_BUTTON_ON_PRESS, SetMapNote)
+ Label.SetText (11973)
+ Label.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ #cancel
+ Label = NoteWindow.GetControl (2)
+ Label.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseNoteWindow)
+ Label.SetText (13727)
+ Label.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ #remove
+ Label = NoteWindow.GetControl (3)
+ Label.SetEvent (IE_GUI_BUTTON_ON_PRESS, RemoveMapNote)
+ Label.SetText (13957)
+
+ MapWindow.SetVisible (WINDOW_GRAYED)
+ NoteWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenWorldMapWindowInside ():
+ global MapWindow
+
+ OpenMapWindow () #closes mapwindow
+ MapWindow = -1
+ print "MapWindow=",MapWindow
+ WorldMapWindowCommon (-1)
+ return
+
+def OpenWorldMapWindow ():
+ WorldMapWindowCommon (GemRB.GetVar ("Travel"))
+ return
+
+def MoveToNewArea ():
+ global WorldMapWindow, WorldMapControl
+
+ tmp = WorldMapControl.GetDestinationArea (1)
+ if tmp["Distance"]==-1:
+ print "Invalid target", tmp
+ return
+
+ CloseWorldMapWindow ()
+ GemRB.CreateMovement (tmp["Destination"], tmp["Entrance"], tmp["Direction"])
+ return
+
+def ChangeTooltip ():
+ global WorldMapWindow, WorldMapControl
+ global str
+
+ tmp = WorldMapControl.GetDestinationArea ()
+ if (tmp):
+ str = "%s: %d"%(GemRB.GetString(23084),tmp["Distance"])
+ else:
+ str=""
+
+ WorldMapControl.SetTooltip (str)
+ return
+
+def CloseWorldMapWindow ():
+ global WorldMapWindow, WorldMapControl
+ global OldPortraitWindow, OldOptionsWindow
+
+ assert GUICommon.CloseOtherWindow (CloseWorldMapWindow)
+
+ if WorldMapWindow:
+ WorldMapWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ WorldMapWindow = None
+ WorldMapControl = None
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ return
+
+def WorldMapWindowCommon (Travel):
+ global WorldMapWindow, WorldMapControl
+ global OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (CloseWorldMapWindow):
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIWMAP",800, 600)
+ WorldMapWindow = Window = GemRB.LoadWindow (2)
+
+ #(fuzzie just copied this from the map window code..)
+ GemRB.SetVar ("OtherWindow", WorldMapWindow.ID)
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow ()
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenMapWindow)
+ OptionsWindow.SetFrame ()
+
+ Window.CreateWorldMapControl (4, 0, 62, 640, 418, Travel, "infofont")
+ WorldMapControl = Window.GetControl (4)
+ WorldMapControl.SetAnimation ("WMDAG")
+ WorldMapControl.SetEvent (IE_GUI_WORLDMAP_ON_PRESS, MoveToNewArea)
+ WorldMapControl.SetEvent (IE_GUI_MOUSE_ENTER_WORLDMAP, ChangeTooltip)
+
+ # Done
+ Button = Window.GetControl (0)
+ if Travel>=0:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenWorldMapWindow)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMapWindow)
+ Window.SetVisible (WINDOW_VISIBLE)
+
+###################################################
+# End of file GUIMA.py
diff --git a/gemrb/GUIScripts/iwd2/GUIOPT.py b/gemrb/GUIScripts/iwd2/GUIOPT.py
new file mode 100644
index 0000000..45f8f9c
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUIOPT.py
@@ -0,0 +1,778 @@
+# -*-python-*-
+# 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.
+#
+
+# GUIOPT.py - scripts to control options windows mostly from GUIOPT winpack
+# Ingame options
+
+###################################################
+import GemRB
+import GUICommon
+import GUICommonWindows
+import GUISAVE
+from GUIDefines import *
+
+###################################################
+GameOptionsWindow = None
+VideoOptionsWindow = None
+AudioOptionsWindow = None
+GameplayOptionsWindow = None
+FeedbackOptionsWindow = None
+AutopauseOptionsWindow = None
+LoadMsgWindow = None
+SaveMsgWindow = None
+QuitMsgWindow = None
+MovieWindow = None
+KeysWindow = None
+OptionsWindow = None
+PortraitWindow = None
+OldPortraitWindow = None
+OldOptionsWindow = None
+
+###################################################
+def CloseOptionsWindow ():
+ global GameOptionsWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GameOptionsWindow == None:
+ return
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ GameOptionsWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ return
+
+###################################################
+def OpenOptionsWindow ():
+ global GameOptionsWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenOptionsWindow):
+ CloseOptionsWindow ()
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIOPT", 800, 600)
+ GameOptionsWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", GameOptionsWindow.ID)
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow ()
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenOptionsWindow)
+ Window.SetFrame ()
+
+ LoadButton = Window.GetControl (5)
+ SaveButton = Window.GetControl (6)
+ QuitButton = Window.GetControl (10)
+ GraphicsButton = Window.GetControl (7)
+ SoundButton = Window.GetControl (8)
+ GamePlayButton = Window.GetControl (9)
+ MoviesButton = Window.GetControl (14)
+ KeyboardButton = Window.GetControl (13)
+ ReturnButton = Window.GetControl (11)
+
+ LoadButton.SetText (13729)
+ LoadButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenLoadMsgWindow)
+ SaveButton.SetText (13730)
+ SaveButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSaveMsgWindow)
+ QuitButton.SetText (13731)
+ QuitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenQuitMsgWindow)
+ GraphicsButton.SetText (17162)
+ GraphicsButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenVideoOptionsWindow)
+ SoundButton.SetText (17164)
+ SoundButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenAudioOptionsWindow)
+ GamePlayButton.SetText (17165)
+ GamePlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenGameplayOptionsWindow)
+ MoviesButton.SetText (15415)
+ MoviesButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMovieWindow)
+ KeyboardButton.SetText (33468)
+ KeyboardButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, None) #TODO: OpenKeyboardWindow
+ ReturnButton.SetText (10308)
+ ReturnButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenOptionsWindow)
+ ReturnButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ VersionLabel = Window.GetControl (0x1000000B)
+ VersionLabel.SetText (GEMRB_VERSION)
+
+ Window.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CloseVideoOptionsWindow ():
+ OpenOptionsWindow ()
+ OpenOptionsWindow ()
+
+def OpenVideoOptionsWindow ():
+ """Open video options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (6)
+
+ HelpTextArea = OptHelpText ('VideoOptions', Window, 33, 18038)
+
+ OptDone ('VideoOptions', Window, 21)
+ OptCancel ('VideoOptions', Window, 32)
+
+ OptSlider ('Brightness', Window, 3, 'Brightness Correction', 4)
+ OptSlider ('Contrast', Window, 22, 'Gamma Correction', 1)
+
+ OptRadio ('BPP', Window, 5, 37, 'BitsPerPixel', 16)
+ OptRadio ('BPP', Window, 6, 37, 'BitsPerPixel', 24)
+ OptRadio ('BPP', Window, 7, 37, 'BitsPerPixel', 32)
+ OptCheckbox ('FullScreen', Window, 9, 38, 'Full Screen', 1)
+
+ OptCheckbox ('TransShadow', Window, 51, 50, 'Translucent Shadows', 1)
+ OptCheckbox ('SoftMirrBlt', Window, 40, 44, 'SoftMirrorBlt' ,1)
+ OptCheckbox ('SoftTransBlt', Window, 41, 46, 'SoftSrcKeyBlt' ,1)
+ OptCheckbox ('SoftStandBlt', Window, 42, 48, 'SoftBltFast' ,1)
+
+ GameOptionsWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpFullScreen ():
+ HelpTextArea.SetText (18000)
+ GemRB.SetFullScreen (GemRB.GetVar("Full Screen"))
+
+def DisplayHelpBPP ():
+ HelpTextArea.SetText (17205)
+
+def DisplayHelpBrightness ():
+ HelpTextArea.SetText (17203)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction")/2)
+ return
+
+def DisplayHelpContrast ():
+ HelpTextArea.SetText (17204)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction")/2)
+ return
+
+def DisplayHelpSoftMirrBlt ():
+ HelpTextArea.SetText (18004)
+
+def DisplayHelpSoftTransBlt ():
+ HelpTextArea.SetText (18006)
+
+def DisplayHelpSoftStandBlt ():
+ HelpTextArea.SetText (18007)
+
+def DisplayHelpTransShadow ():
+ HelpTextArea.SetText (20620)
+
+
+###################################################
+
+def CloseAudioOptionsWindow ():
+ OpenOptionsWindow ()
+ OpenOptionsWindow ()
+
+def OpenAudioOptionsWindow ():
+ """Open audio options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (7)
+
+ HelpTextArea = OptHelpText ('AudioOptions', Window, 14, 18040)
+
+ OptDone ('AudioOptions', Window, 24)
+ OptCancel ('AudioOptions', Window, 25)
+ OptButton ('CharacterSounds', Window, 13, 17778)
+
+ OptSlider ('AmbientVolume', Window, 1, 'Volume Ambients', 10)
+ OptSlider ('SoundFXVolume', Window, 2, 'Volume SFX', 10)
+ OptSlider ('VoiceVolume', Window, 3, 'Volume Voices', 10)
+ OptSlider ('MusicVolume', Window, 4, 'Volume Music', 10)
+ OptSlider ('MovieVolume', Window, 22, 'Volume Movie', 10)
+
+ OptCheckbox ('CreativeEAX', Window, 26, 28, 'Environmental Audio', 1)
+ GameOptionsWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+
+def DisplayHelpAmbientVolume ():
+ HelpTextArea.SetText (18008)
+ GemRB.UpdateAmbientsVolume()
+
+def DisplayHelpSoundFXVolume ():
+ HelpTextArea.SetText (18009)
+
+def DisplayHelpVoiceVolume ():
+ HelpTextArea.SetText (18010)
+
+def DisplayHelpMusicVolume ():
+ HelpTextArea.SetText (18011)
+ GemRB.UpdateMusicVolume()
+
+def DisplayHelpMovieVolume ():
+ HelpTextArea.SetText (18012)
+
+def DisplayHelpCreativeEAX ():
+ HelpTextArea.SetText (18022)
+
+
+###################################################
+
+def CloseGameplayOptionsWindow ():
+ OpenOptionsWindow ()
+ OpenOptionsWindow ()
+
+def OpenGameplayOptionsWindow ():
+ """Open gameplay options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ #gameplayoptions
+ GameOptionsWindow = Window = GemRB.LoadWindow (8)
+
+
+ HelpTextArea = OptHelpText ('GameplayOptions', Window, 40, 18042)
+
+ OptDone ('GameplayOptions', Window, 7)
+ OptCancel ('GameplayOptions', Window, 20)
+
+ OptSlider ('TooltipDelay', Window, 1, 'Tooltips', TOOLTIP_DELAY_FACTOR)
+ OptSlider ('MouseScrollingSpeed', Window, 2, 'Mouse Scroll Speed', 5)
+ OptSlider ('KeyboardScrollingSpeed', Window, 3, 'Keyboard Scroll Speed', 5)
+ OptSlider ('Difficulty', Window, 12, 'Difficulty Level', 0)
+
+ OptCheckbox ('DitherAlways', Window, 14, 25, 'Always Dither', 1)
+ OptCheckbox ('Gore', Window, 19, 27, 'Gore', 1)
+ OptCheckbox ('Infravision', Window, 42, 44, 'Infravision', 1)
+ OptCheckbox ('Weather', Window, 47, 46, 'Weather', 1)
+ OptCheckbox ('MaxHitpoints', Window, 50, 49, 'Maximum HP', 1)
+
+ OptButton ('FeedbackOptions', Window, 5, 17163)
+ OptButton ('AutopauseOptions', Window, 6, 17166)
+
+ GameOptionsWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpTooltipDelay ():
+ HelpTextArea.SetText (18017)
+ GemRB.SetTooltipDelay (GemRB.GetVar ("Tooltips") )
+
+def DisplayHelpMouseScrollingSpeed ():
+ HelpTextArea.SetText (18018)
+ GemRB.SetMouseScrollSpeed (GemRB.GetVar ("Mouse Scroll Speed") )
+
+def DisplayHelpKeyboardScrollingSpeed ():
+ HelpTextArea.SetText (18019)
+
+def DisplayHelpDifficulty ():
+ HelpTextArea.SetText (18020)
+
+def DisplayHelpDitherAlways ():
+ HelpTextArea.SetText (18021)
+
+def DisplayHelpGore ():
+ HelpTextArea.SetText (18023)
+
+def DisplayHelpInfravision ():
+ HelpTextArea.SetText (11797)
+
+def DisplayHelpWeather ():
+ HelpTextArea.SetText (20619)
+
+def DisplayHelpRestUntilHealed ():
+ HelpTextArea.SetText (2242)
+
+def DisplayMaxHitpoints ():
+ HelpTextArea.SetText (15136)
+
+###################################################
+
+def CloseFeedbackOptionsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+ OpenGameplayOptionsWindow ()
+
+
+def OpenFeedbackOptionsWindow ():
+ """Open feedback options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ #feedback
+ GameOptionsWindow = Window = GemRB.LoadWindow (9)
+
+ HelpTextArea = OptHelpText ('FeedbackOptions', Window, 28, 18043)
+
+ OptDone ('FeedbackOptions', Window, 26)
+ OptCancel ('FeedbackOptions', Window, 27)
+
+ OptSlider ('MarkerFeedback', Window, 8, 'GUI Feedback Level', 1)
+ OptSlider ('LocatorFeedback', Window, 9, 'Locator Feedback Level', 1)
+
+ OptCheckbox ('ToHitRolls', Window, 10, 32, 'Rolls', 1)
+ OptCheckbox ('CombatInfo', Window, 11, 33, 'Combat Info', 1)
+ OptCheckbox ('Actions', Window, 12, 34, 'Actions', 1)
+ OptCheckbox ('States', Window, 13, 35, 'State Changes', 1)
+ OptCheckbox ('Selection', Window, 14, 36, 'Selection Text', 1)
+ OptCheckbox ('Miscellaneous', Window, 15, 37, 'Miscellaneous Text', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpMarkerFeedback ():
+ HelpTextArea.SetText (18024)
+
+def DisplayHelpLocatorFeedback ():
+ HelpTextArea.SetText (18025)
+
+def DisplayHelpToHitRolls ():
+ HelpTextArea.SetText (18026)
+
+def DisplayHelpCombatInfo ():
+ HelpTextArea.SetText (18027)
+
+def DisplayHelpActions ():
+ HelpTextArea.SetText (18028)
+
+def DisplayHelpStates ():
+ HelpTextArea.SetText (18029)
+
+def DisplayHelpSelection ():
+ HelpTextArea.SetText (18030)
+
+def DisplayHelpMiscellaneous ():
+ HelpTextArea.SetText (18031)
+
+
+###################################################
+
+def CloseAutopauseOptionsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+ OpenGameplayOptionsWindow ()
+ return
+
+def OpenAutopauseOptionsWindow ():
+ """Open autopause options window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (10)
+
+ HelpTextArea = OptHelpText ('AutopauseOptions', Window, 15, 18044)
+
+ OptDone ('AutopauseOptions', Window, 11)
+ OptCancel ('AutopauseOptions', Window, 14)
+
+ OptCheckbox ('CharacterHit', Window, 1, 17, 'Auto Pause State', 1)
+ OptCheckbox ('CharacterInjured', Window, 2, 18, 'Auto Pause State', 2)
+ OptCheckbox ('CharacterDead', Window, 3, 19, 'Auto Pause State', 4)
+ OptCheckbox ('CharacterAttacked', Window, 4, 20, 'Auto Pause State', 8)
+ OptCheckbox ('WeaponUnusable', Window, 5, 21, 'Auto Pause State', 16)
+ OptCheckbox ('TargetGone', Window, 13, 22, 'Auto Pause State', 32)
+ OptCheckbox ('EndOfRound', Window, 25, 24, 'Auto Pause State', 64)
+ OptCheckbox ('EnemySighted', Window, 26, 27, 'Auto Pause State', 128)
+ OptCheckbox ('SpellCast', Window, 34, 30, 'Auto Pause State', 256)
+ OptCheckbox ('TrapFound', Window, 31, 33, 'Auto Pause State', 512)
+ OptCheckbox ('CenterOnActor', Window, 31, 33, 'Auto Pause Center', 1)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpCharacterHit ():
+ HelpTextArea.SetText (18032)
+
+def DisplayHelpCharacterInjured ():
+ HelpTextArea.SetText (18033)
+
+def DisplayHelpCharacterDead ():
+ HelpTextArea.SetText (18034)
+
+def DisplayHelpCharacterAttacked ():
+ HelpTextArea.SetText (18035)
+
+def DisplayHelpWeaponUnusable ():
+ HelpTextArea.SetText (18036)
+
+def DisplayHelpTargetGone ():
+ HelpTextArea.SetText (18037)
+
+def DisplayHelpEndOfRound ():
+ HelpTextArea.SetText (10640)
+
+def DisplayHelpEnemySighted ():
+ HelpTextArea.SetText (23514)
+
+def DisplayHelpSpellCast ():
+ HelpTextArea.SetText (58171)
+
+def DisplayHelpTrapFound ():
+ HelpTextArea.SetText (31872)
+
+def DisplayHelpCenterOnActor ():
+ HelpTextArea.SetText (10571)
+
+###################################################
+
+def CloseCharacterSoundsWindow ():
+ global GameOptionsWindow
+
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+ OpenGameplayOptionsWindow ()
+ return
+
+def OpenCharacterSoundsWindow ():
+ """Open character sounds window"""
+ global GameOptionsWindow, HelpTextArea
+
+ if GameOptionsWindow:
+ if GameOptionsWindow:
+ GameOptionsWindow.Unload ()
+ GameOptionsWindow = None
+
+ GameOptionsWindow = Window = GemRB.LoadWindow (12)
+
+ HelpTextArea = OptHelpText ('CharacterSounds', Window, 16, 18041)
+
+ OptDone ('CharacterSounds', Window, 24)
+ OptCancel ('CharacterSounds', Window, 25)
+
+ OptCheckbox ('Subtitles', Window, 5, 20, 'Subtitles', 1)
+ OptCheckbox ('AttackSounds', Window, 6, 18, 'Attack Sounds', 1)
+ OptCheckbox ('Footsteps', Window, 7, 19, 'Footsteps', 1)
+ OptRadio ('CommandSounds', Window, 8, 21, 'Command Sounds Frequency', 1)
+ OptRadio ('CommandSounds', Window, 9, 21, 'Command Sounds Frequency', 2)
+ OptRadio ('CommandSounds', Window, 10, 21, 'Command Sounds Frequency', 3)
+ OptRadio ('SelectionSounds', Window, 58, 57, 'Selection Sounds Frequency', 1)
+ OptRadio ('SelectionSounds', Window, 59, 57, 'Selection Sounds Frequency', 2)
+ OptRadio ('SelectionSounds', Window, 60, 57, 'Selection Sounds Frequency', 3)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def DisplayHelpSubtitles ():
+ HelpTextArea.SetText (18015)
+
+def DisplayHelpAttackSounds ():
+ HelpTextArea.SetText (18013)
+
+def DisplayHelpFootsteps ():
+ HelpTextArea.SetText (18014)
+
+def DisplayHelpCommandSounds ():
+ HelpTextArea.SetText (18016)
+
+def DisplayHelpSelectionSounds ():
+ HelpTextArea.SetText (11352)
+
+def DisplayHelpMaxHitpoints ():
+ #TODO
+ return
+
+###################################################
+
+def CloseMovieWindow ():
+ if MovieWindow:
+ MovieWindow.Unload ()
+ return
+
+def MoviePlayPress():
+ s = GemRB.GetVar("MovieIndex")
+ for i in range(0, MoviesTable.GetRowCount() ):
+ t = MoviesTable.GetRowName(i)
+ if GemRB.GetVar(t)==1:
+ if s==0:
+ s = MoviesTable.GetRowName(i)
+ GemRB.PlayMovie(s, 1)
+ MovieWindow.Invalidate()
+ return
+ s = s - 1
+ return
+
+def MovieCreditsPress():
+ GemRB.PlayMovie("CREDITS")
+ MovieWindow.Invalidate()
+ return
+
+def OpenMovieWindow ():
+ global MovieWindow, TextAreaControl, MoviesTable
+
+ GemRB.LoadWindowPack("GUIMOVIE", 800, 600)
+ MovieWindow = Window = GemRB.LoadWindow(2)
+ Window.SetFrame ()
+ #reloading the guiopt windowpack
+ GemRB.LoadWindowPack ("GUIOPT", 800, 600)
+ TextAreaControl = Window.GetControl(0)
+ TextAreaControl.SetFlags(IE_GUI_TEXTAREA_SELECTABLE)
+ PlayButton = Window.GetControl(2)
+ CreditsButton = Window.GetControl(3)
+ DoneButton = Window.GetControl(4)
+ MoviesTable = GemRB.LoadTable("MOVIDESC")
+ for i in range(0, MoviesTable.GetRowCount() ):
+ t = MoviesTable.GetRowName(i)
+ if GemRB.GetVar(t)==1:
+ s = MoviesTable.GetValue(i, 0)
+ TextAreaControl.Append(s,-1)
+ TextAreaControl.SetVarAssoc("MovieIndex",0)
+ PlayButton.SetText(17318)
+ CreditsButton.SetText(15591)
+ DoneButton.SetText(11973)
+ PlayButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MoviePlayPress)
+ CreditsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MovieCreditsPress)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CloseMovieWindow)
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+###################################################
+
+def OpenSaveMsgWindow ():
+ GemRB.SetVar("QuitAfterSave",0)
+ GUISAVE.OpenSaveWindow ()
+ #save the game without quitting
+ return
+
+###################################################
+
+def OpenLoadMsgWindow ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ return
+
+ LoadMsgWindow = Window = GemRB.LoadWindow (4)
+
+ # Load
+ Button = Window.GetControl (0)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadGamePress)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseLoadMsgWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Loading a game will destroy ...
+ Text = Window.GetControl (3)
+ Text.SetText (19531)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseLoadMsgWindow ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ LoadMsgWindow.Unload ()
+ LoadMsgWindow = None
+ GameOptionsWindow.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def LoadGamePress ():
+ global LoadMsgWindow
+
+ if LoadMsgWindow:
+ LoadMsgWindow.Unload ()
+ LoadMsgWindow = None
+ GemRB.QuitGame ()
+ OpenOptionsWindow()
+ GemRB.SetNextScript ("GUILOAD")
+ return
+
+#save game AND quit
+def SaveGamePress ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ #we need to set a state: quit after save
+ GemRB.SetVar("QuitAfterSave",1)
+ OpenOptionsWindow()
+ GUISAVE.OpenSaveWindow ()
+ return
+
+def QuitGamePress ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ GemRB.QuitGame ()
+ OpenOptionsWindow()
+ GemRB.SetNextScript ("Start")
+ return
+
+###################################################
+
+def OpenQuitMsgWindow ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ return
+
+ QuitMsgWindow = Window = GemRB.LoadWindow (5)
+
+ # Save
+ Button = Window.GetControl (0)
+ Button.SetText (15589)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SaveGamePress)
+
+ # Quit Game
+ Button = Window.GetControl (1)
+ Button.SetText (15417)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitGamePress)
+
+ # Cancel
+ Button = Window.GetControl (2)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseQuitMsgWindow)
+
+ # The game has not been saved ....
+ Text = Window.GetControl (3)
+ Text.SetText (16456)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseQuitMsgWindow ():
+ global QuitMsgWindow
+
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ GameOptionsWindow.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+###################################################
+###################################################
+
+# These functions help to setup controls found
+# in Video, Audio, Gameplay, Feedback and Autopause
+# options windows
+
+# These controls are usually made from an active
+# control (button, slider ...) and a label
+
+
+def OptSlider (name, window, slider_id, variable, value):
+ """Standard slider for option windows"""
+ slider = window.GetControl (slider_id)
+ slider.SetVarAssoc (variable, value)
+ slider.SetEvent (IE_GUI_SLIDER_ON_CHANGE, eval("DisplayHelp" + name))
+ return slider
+
+def OptRadio (name, window, button_id, label_id, variable, value):
+ """Standard radio button for option windows"""
+
+ button = window.GetControl (button_id)
+ button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("DisplayHelp" + name))
+ button.SetVarAssoc (variable, value)
+ button.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ label = window.GetControl (label_id)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+
+ return button
+
+def OptCheckbox (name, window, button_id, label_id, variable, value):
+ """Standard checkbox for option windows"""
+
+ button = window.GetControl (button_id)
+ button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("DisplayHelp" + name))
+ button.SetVarAssoc (variable, value)
+ button.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ label = window.GetControl (label_id)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+
+ return button
+
+def OptButton (name, window, button_id, label_strref):
+ """Standard subwindow button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Open%sWindow" %name))
+ button.SetText (label_strref)
+
+def OptDone (name, window, button_id):
+ """Standard `Done' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (11973) # Done
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Close%sWindow" %name))
+
+def OptCancel (name, window, button_id):
+ """Standard `Cancel' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (13727) # Cancel
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Close%sWindow" %name))
+
+def OptHelpText (name, window, text_id, text_strref):
+ """Standard textarea with context help for option windows"""
+ text = window.GetControl (text_id)
+ text.SetText (text_strref)
+ return text
+
+###################################################
+# End of file GUIOPT.py
diff --git a/gemrb/GUIScripts/iwd2/GUIREC.py b/gemrb/GUIScripts/iwd2/GUIREC.py
new file mode 100644
index 0000000..f0bb1a1
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUIREC.py
@@ -0,0 +1,1513 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIREC.py - scripts to control character record windows from GUIREC winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+from ie_restype import *
+
+SelectWindow = 0
+Topic = None
+HelpTable = None
+DescTable = None
+RecordsWindow = None
+RecordsTextArea = None
+ItemInfoWindow = None
+ItemAmountWindow = None
+ItemIdentifyWindow = None
+PortraitWindow = None
+OldPortraitWindow = None
+OptionsWindow = None
+OldOptionsWindow = None
+OldVoiceSet = None
+
+# the available sounds
+SoundSequence = [ '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', \
+ '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', \
+ '25', '26', '27', '28', '29', '30', '31']
+SoundIndex = 0
+
+def OpenRecordsWindow ():
+ global RecordsWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow, SelectWindow
+
+ if GUICommon.CloseOtherWindow (OpenRecordsWindow):
+ if RecordsWindow:
+ RecordsWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ RecordsWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUIREC", 800, 600)
+ RecordsWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", RecordsWindow.ID)
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow ()
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenRecordsWindow)
+ Window.SetFrame ()
+
+ #portrait icon
+ Button = Window.GetControl (2)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+
+ #information (help files)
+ Button = Window.GetControl (1)
+ Button.SetText (11946)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenHelpWindow)
+
+ #biography
+ Button = Window.GetControl (59)
+ Button.SetText (18003)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyWindow)
+
+ #export
+ Button = Window.GetControl (36)
+ Button.SetText (13956)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenExportWindow)
+
+ #customize
+ Button = Window.GetControl (50)
+ Button.SetText (10645)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenCustomizeWindow)
+
+ #general
+ GemRB.SetVar ("SelectWindow", 1)
+
+ Button = Window.GetControl (60)
+ Button.SetTooltip (40316)
+ Button.SetVarAssoc ("SelectWindow", 1)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateRecordsWindow)
+
+ #weapons and armour
+ Button = Window.GetControl (61)
+ Button.SetTooltip (40317)
+ Button.SetVarAssoc ("SelectWindow", 2)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateRecordsWindow)
+
+ #skills and feats
+ Button = Window.GetControl (62)
+ Button.SetTooltip (40318)
+ Button.SetVarAssoc ("SelectWindow", 3)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateRecordsWindow)
+
+ #miscellaneous
+ Button = Window.GetControl (63)
+ Button.SetTooltip (33500)
+ Button.SetVarAssoc ("SelectWindow", 4)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateRecordsWindow)
+
+ #level up
+ Button = Window.GetControl (37)
+ Button.SetText (7175)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateRecordsWindow) #TODO: OpenLevelUpWindow
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateRecordsWindow)
+
+ UpdateRecordsWindow ()
+ return
+
+def ColorDiff (Window, Label, diff):
+ if diff>0:
+ Label.SetTextColor (0, 255, 0)
+ elif diff<0:
+ Label.SetTextColor (255, 0, 0)
+ else:
+ Label.SetTextColor (255, 255, 255)
+ return
+
+def ColorDiff2 (Window, Label, diff):
+ if diff:
+ Label.SetTextColor (255, 255, 0)
+ else:
+ Label.SetTextColor (255, 255, 255)
+ return
+
+def HasBonusSpells (pc):
+ return False
+
+def HasClassFeatures (pc):
+ #clerics turning
+ if GemRB.GetPlayerStat(pc, IE_TURNUNDEADLEVEL):
+ return True
+ #thieves backstabbing damage
+ if GemRB.GetPlayerStat(pc, IE_BACKSTABDAMAGEMULTIPLIER):
+ return True
+ #paladins healing
+ if GemRB.GetPlayerStat(pc, IE_LAYONHANDSAMOUNT):
+ return True
+ return False
+
+def GetFavoredClass (pc, code):
+ if GemRB.GetPlayerStat (pc, IE_SEX)==1:
+ code = code&15
+ else:
+ code = (code>>8)&15
+
+ return code-1
+
+def GetAbilityBonus (pc, stat):
+ Ability = GemRB.GetPlayerStat (pc, stat)
+ return Ability//2-5
+
+#class is ignored
+def GetNextLevelExp (Level, Adjustment):
+ if Adjustment>5:
+ Adjustment = 5
+ if (Level < CommonTables.NextLevel.GetColumnCount (4) - 5):
+ return str(CommonTables.NextLevel.GetValue (4, Level + Adjustment ) )
+
+ return GemRB.GetString(24342) #godhood
+
+#barbarian, bard, cleric, druid, fighter, monk, paladin, ranger, rogue, sorcerer, wizard
+Classes = [IE_LEVELBARBARIAN, IE_LEVELBARD, IE_LEVELCLERIC, IE_LEVELDRUID, \
+IE_LEVEL, IE_LEVELMONK, IE_LEVELPALADIN, IE_LEVELRANGER, IE_LEVEL3, \
+IE_LEVELSORCEROR, IE_LEVEL2]
+
+def DisplayGeneral (pc):
+ Window = RecordsWindow
+
+ #levels
+ RecordsTextArea.Append ("[color=ffff00]")
+ RecordsTextArea.Append (40308)
+ RecordsTextArea.Append (" - ")
+ RecordsTextArea.Append (40309)
+ levelsum = GemRB.GetPlayerStat (pc, IE_CLASSLEVELSUM)
+ #TODO: get special level penalty for subrace
+ adj = 0
+ RecordsTextArea.Append (": "+str(levelsum) )
+ RecordsTextArea.Append ("[/color]")
+ #the class name for highest
+ highest = None
+ tmp = 0
+ for i in range(11):
+ level = GemRB.GetPlayerStat (pc, Classes[i])
+
+ if level:
+ Class = GUICommonWindows.GetActorClassTitle (pc, i )
+ RecordsTextArea.Append (Class, -1)
+ RecordsTextArea.Append (": "+str(level) )
+ if tmp<level:
+ highest = i
+ tmp = level
+
+ RecordsTextArea.Append ("\n")
+ #effective character level
+ if adj:
+ RecordsTextArea.Append (40311,-1)
+ RecordsTextArea.Append (": "+str(levelsum+adj) )
+
+ #favoured class
+ RecordsTextArea.Append (40310,-1)
+ AlignTable = GemRB.LoadTable("aligns")
+
+ #get the subrace value
+ Value = GemRB.GetPlayerStat(pc,IE_RACE)
+ Value2 = GemRB.GetPlayerStat(pc,IE_SUBRACE)
+ if Value2:
+ Value = Value<<16 | Value2
+ tmp = CommonTables.Races.FindValue (3, Value)
+ Race = CommonTables.Races.GetValue (tmp, 2)
+ tmp = CommonTables.Races.GetValue (tmp, 8)
+
+ Label = Window.GetControl (0x1000000f)
+ Label.SetText (Race)
+
+ if tmp == -1:
+ tmp = highest
+ else:
+ tmp = GetFavoredClass(pc, tmp)
+
+ tmp = CommonTables.Classes.GetValue (tmp, 0)
+ RecordsTextArea.Append (": ")
+ RecordsTextArea.Append (tmp)
+
+ #experience
+ RecordsTextArea.Append ("\n\n[color=ffff00]")
+ RecordsTextArea.Append (17089)
+ RecordsTextArea.Append ("[/color]")
+
+ RecordsTextArea.Append (36928,-1)
+ xp = GemRB.GetPlayerStat (pc, IE_XP)
+ RecordsTextArea.Append (": "+str(xp) )
+ RecordsTextArea.Append (17091,-1)
+ tmp = GetNextLevelExp (levelsum, adj)
+ RecordsTextArea.Append (": "+tmp )
+
+ #current effects
+ effects = GemRB.GetPlayerStates (pc)
+ if len(effects):
+ RecordsTextArea.Append ("\n\n[color=ffff00]")
+ RecordsTextArea.Append (32052)
+ RecordsTextArea.Append ("[/color][capital=2]")
+ StateTable = GemRB.LoadTable ("statdesc")
+ for c in effects:
+ tmp = StateTable.GetValue (ord(c)-66, 0)
+ RecordsTextArea.Append (c+" ", -1)
+ RecordsTextArea.Append (tmp)
+
+ #race
+ RecordsTextArea.Append ("\n\n[capital=0][color=ffff00]")
+ RecordsTextArea.Append (1048)
+ RecordsTextArea.Append ("[/color]")
+
+ RecordsTextArea.Append (Race,-1)
+
+ #alignment
+ RecordsTextArea.Append ("\n\n[color=ffff00]")
+ RecordsTextArea.Append (1049)
+ RecordsTextArea.Append ("[/color]")
+ tmp = AlignTable.FindValue ( 3, GemRB.GetPlayerStat (pc, IE_ALIGNMENT) )
+ Align = AlignTable.GetValue (tmp, 2)
+ RecordsTextArea.Append (Align,-1)
+
+ #saving throws
+ RecordsTextArea.Append ("\n\n[color=ffff00]")
+ RecordsTextArea.Append (17379)
+ RecordsTextArea.Append ("[/color]")
+ tmp = GemRB.GetPlayerStat (pc, IE_SAVEFORTITUDE)
+ tmp -= GemRB.GetPlayerStat (pc, IE_SAVEFORTITUDE, 1)
+ if tmp<0: stmp = str(tmp)
+ else: stmp = "+"+str(tmp)
+ RecordsTextArea.Append (17380,-1)
+ RecordsTextArea.Append (": "+stmp )
+ tmp = GemRB.GetPlayerStat (pc, IE_SAVEREFLEX)
+ tmp -= GemRB.GetPlayerStat (pc, IE_SAVEREFLEX, 1)
+ if tmp<0: stmp = str(tmp)
+ else: stmp = "+"+str(tmp)
+ RecordsTextArea.Append (17381,-1)
+ RecordsTextArea.Append (": "+stmp )
+ tmp = GemRB.GetPlayerStat (pc, IE_SAVEWILL)
+ tmp -= GemRB.GetPlayerStat (pc, IE_SAVEWILL, 1)
+ if tmp<0: stmp = str(tmp)
+ else: stmp = "+"+str(tmp)
+ RecordsTextArea.Append (17382,-1)
+ RecordsTextArea.Append (": "+stmp )
+
+ #class features
+ if HasClassFeatures(pc):
+ RecordsTextArea.Append ("\n\n[color=ffff00]")
+ RecordsTextArea.Append (40314)
+ RecordsTextArea.Append ("[/color]\n")
+ tmp = GemRB.GetPlayerStat (pc, IE_TURNUNDEADLEVEL)
+ if tmp:
+ RecordsTextArea.Append (12146,-1)
+ RecordsTextArea.Append (": "+str(tmp) )
+ tmp = GemRB.GetPlayerStat (pc, IE_BACKSTABDAMAGEMULTIPLIER)
+ if tmp:
+ RecordsTextArea.Append (24898,-1)
+ RecordsTextArea.Append (": "+str(tmp)+"d6" )
+ tmp = GemRB.GetPlayerStat (pc, IE_LAYONHANDSAMOUNT)
+ if tmp:
+ RecordsTextArea.Append (12127,-1)
+ RecordsTextArea.Append (": "+str(tmp) )
+
+ #bonus spells
+ if HasBonusSpells(pc):
+ RecordsTextArea.Append ("\n\n[color=ffff00]")
+ RecordsTextArea.Append (10344)
+ RecordsTextArea.Append ("[/color]\n")
+
+ #ability statistics
+ RecordsTextArea.Append ("\n\n[color=ffff00]")
+ RecordsTextArea.Append (40315)
+ RecordsTextArea.Append ("[/color]")
+
+ RecordsTextArea.Append (10338,-1) #strength
+ tmp = GemRB.GetAbilityBonus( IE_STR, 3, GemRB.GetPlayerStat(pc, IE_STR) )
+ RecordsTextArea.Append (": " + str(tmp) )
+ tmp = GetAbilityBonus(pc, IE_CON)
+ RecordsTextArea.Append (10342,-1)
+ RecordsTextArea.Append (": " + str(tmp) ) #con bonus
+
+ RecordsTextArea.Append (15581,-1) #spell resistance
+ tmp = GemRB.GetPlayerStat (pc, IE_MAGICDAMAGERESISTANCE)
+ RecordsTextArea.Append (": "+str(tmp) )
+
+ return
+
+#FIXME: display only nonzero values except for casting failure
+#TODO: +/- prefix
+#TODO: check if there are any other entries
+def DisplayWeapons (pc):
+ Window = RecordsWindow
+
+ GS = lambda s, pc=pc: GemRB.GetPlayerStat (pc, s)
+ GB = lambda s, pc=pc: GemRB.GetPlayerStat (pc, s, 1)
+ # maybe add an iwd2 mode to GemRB.GetAbilityBonus
+ GA = lambda s, pc=pc: int((GS(s)-10)/2)
+
+ ###################
+ # Attack Roll Modifiers
+ RecordsTextArea.Append ("[color=ffff00]")
+ RecordsTextArea.Append (9457)
+ RecordsTextArea.Append ("[/color]\n")
+
+ combatdet = GemRB.GetCombatDetails(pc, 0)
+
+ # Main Hand
+ #TODO: display all the attack values (+15/+10/+5)
+ current = combatdet["ToHit"]
+ RecordsTextArea.Append (delimited_txt (734, ":", str (current), 0))
+ # Base
+ RecordsTextArea.Append (" ", -1) # indentation
+ RecordsTextArea.Append (delimited_txt (31353, ":", str (GS(IE_TOHIT)), 0))
+ # Weapon
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (32560 , ":", str (0), 0))
+ # Proficiency
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (32561, ":", str (0), 0))
+ # Armor Penalty
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (39816 , ":", str (0), 0))
+ #TODO: check if there's also a @39822 = ~Shield Penalty~
+
+ # Abilities
+ #FIXME: this is different for ranged weapons (dex) or if weapon finese is chosen (dex or str)
+ strbon = GA(IE_STR)
+ if strbon:
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (33547, ":", str (strbon), 0))
+ # Others
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (33548, ":", str (current - GS(IE_TOHIT) - strbon)))
+ RecordsTextArea.Append ("\n")
+
+ # Off Hand
+ if (GemRB.IsDualWielding(pc)):
+ RecordsTextArea.Append (delimited_txt (733, ":", str (GemRB.GetCombatDetails(pc, 1)["ToHit"]), 0))
+ RecordsTextArea.Append ("\n")
+ #TODO: probably all the same categories as above
+
+
+ ###################
+ # Number of Attacks
+ RecordsTextArea.Append (delimited_txt (9458, ":", str (GS (IE_NUMBEROFATTACKS)/2)))
+ RecordsTextArea.Append ("\n")
+
+ ###################
+ # Armor Class
+ RecordsTextArea.Append ("[color=ffff00]")
+ RecordsTextArea.Append (33553)
+ RecordsTextArea.Append ("[/color]\n")
+ RecordsTextArea.Append (delimited_txt (33553, ":", str (GS(IE_ARMORCLASS)), 0))
+
+ # Base
+ RecordsTextArea.Append (" ", -1) # indentation
+ RecordsTextArea.Append (delimited_txt (31353, ":", str (10), 0))
+ # Armor
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (11997, ":", str (0), 0))
+ # Shield
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (6347, ":", str (0), 0))
+ # Deflection
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (33551, ":", str (0), 0))
+ # Generic
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (33552, ":", str (0), 0))
+ # Dexterity
+ if GA(IE_DEX):
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (1151, ":", str (GA(IE_DEX)), 0))
+ # Monk Wisdom Bonus: <number> to AC
+ if GS(IE_LEVELMONK):
+ RecordsTextArea.Append (" ", -1)
+ GemRB.SetToken ("number", str (GA(IE_WIS)))
+ RecordsTextArea.Append (39431, -1)
+ #TODO: Dodge?
+
+ RecordsTextArea.Append ("\n\n")
+
+ ###################
+ # Armor Class Modifiers
+ stat = GS (IE_ACMISSILEMOD) + GS (IE_ACSLASHINGMOD) + GS (IE_ACPIERCINGMOD) + GS (IE_ACCRUSHINGMOD)
+ if stat:
+ RecordsTextArea.Append ("[color=ffff00]")
+ RecordsTextArea.Append (11766)
+ RecordsTextArea.Append ("[/color]")
+
+ # Missile
+ if GS (IE_ACMISSILEMOD):
+ RecordsTextArea.Append (" ", -1) # indentation
+ RecordsTextArea.Append (delimited_txt (11767, ":", str (GS (IE_ACMISSILEMOD)), 0))
+ # Slashing
+ if GS (IE_ACSLASHINGMOD):
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (11768, ":", str (GS (IE_ACSLASHINGMOD)), 0))
+ # Piercing
+ if GS (IE_ACPIERCINGMOD):
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (11769, ":", str (GS (IE_ACPIERCINGMOD)), 0))
+ # Bludgeoning
+ if GS (IE_ACCRUSHINGMOD):
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (11770, ":", str (GS (IE_ACCRUSHINGMOD))))
+
+ RecordsTextArea.Append ("\n")
+
+ ###################
+ # Arcane spell failure
+ if GS(IE_LEVELBARD) + GS(IE_LEVELSORCEROR) + GS(IE_LEVELMAGE):
+ RecordsTextArea.Append ("[color=ffff00]")
+ RecordsTextArea.Append (41391)
+ RecordsTextArea.Append ("[/color]\n")
+
+ # Casting Failure
+ RecordsTextArea.Append (delimited_txt (41390 , ":", str (GS(IE_SPELLFAILUREMAGE)), 0))
+ # Armor Penalty
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (39816 , ":", str (0), 0))
+ # Shield Penalty
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (39822, ":", str (0), 0))
+ #TODO: check if there's also the bonus from armored arcana
+
+ RecordsTextArea.Append ("\n\n")
+
+ ###################
+ # Weapon Statistics
+ RecordsTextArea.Append ("[color=ffff00]")
+ RecordsTextArea.Append (41119)
+ RecordsTextArea.Append ("[/color]\n")
+
+ slot_item = GemRB.GetSlotItem (pc, GemRB.GetEquippedQuickSlot (pc) )
+ if not slot_item:
+ print "ARGHH, no slot item, bailing out"
+ return
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ ##FIXME: display Ranged (41123) + ammo for ranged weapons
+ RecordsTextArea.Append (delimited_str (734, " -", item["ItemNameIdentified"], 0))
+
+ # Damage
+ # TODO: display the unresolved damage string (2d6)
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (39518, ":", str (0), 0))
+ # Strength
+ # TODO: check if the weapon takes strength bonus at all
+ if GA(IE_STR):
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (1145, ":", str (GA(IE_STR)), 0))
+ # Launcher
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (41408, ":", str (0), 0))
+ # Damage Potential
+ # TODO: display the unresolved total damage potential (2-12)
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (41120, ":", str (0), 0))
+ # Critical Hit (19-20 / x2)
+ # TODO: display the number of rolls and check if the critical range is already ok
+ crange = 20 - combatdet["CriticalBonus"]
+ if crange == 20:
+ crange = "20 / x" + str(1)
+ else:
+ crange = str(crange) + "-20 / x" + str(1)
+ RecordsTextArea.Append (" ", -1)
+ RecordsTextArea.Append (delimited_txt (41122, ":", crange, 0))
+
+ #TODO: probably repeat for the off-hand
+
+ return
+
+def DisplaySkills (pc):
+ Window = RecordsWindow
+
+ SkillTable = GemRB.LoadTable ("skillsta")
+ SkillName = GemRB.LoadTable ("skills")
+ rows = SkillTable.GetRowCount ()
+
+ #skills
+ RecordsTextArea.Append ("[color=ffff00]")
+ RecordsTextArea.Append (11983)
+ RecordsTextArea.Append ("[/color]\n")
+
+ skills = []
+ for i in range(rows):
+ stat = SkillTable.GetValue (i, 0, 2)
+ value = GemRB.GetPlayerStat (pc, stat)
+ base = GemRB.GetPlayerStat (pc, stat, 1)
+
+ if value:
+ skill = SkillName.GetValue (i, 1)
+ skills.append (GemRB.GetString(skill) + ": " + str(value) + " (" + str(base) + ")\n")
+
+ skills.sort()
+ for i in skills:
+ RecordsTextArea.Append (i)
+
+ FeatTable = GemRB.LoadTable ("featreq")
+ FeatName = GemRB.LoadTable ("feats")
+ rows = FeatTable.GetRowCount ()
+ #feats
+ featbits = [GemRB.GetPlayerStat (pc, IE_FEATS1), GemRB.GetPlayerStat (pc, IE_FEATS2), GemRB.GetPlayerStat (pc, IE_FEATS3)]
+ RecordsTextArea.Append ("\n\n[color=ffff00]")
+ RecordsTextArea.Append (36361)
+ RecordsTextArea.Append ("[/color]\n")
+
+ feats = []
+ for i in range(rows):
+ featidx = i/32
+ pos = 1<<(i%32)
+ if featbits[featidx]&pos:
+ feat = FeatName.GetValue (i, 1)
+ stat = FeatTable.GetValue (i, 9, 2)
+ if stat:
+ multi = GemRB.GetPlayerStat (pc, stat)
+ feats.append (GemRB.GetString(feat) + ": " + str(multi) + "\n")
+ else:
+ feats.append (GemRB.GetString(feat) + "\n")
+
+ feats.sort()
+ for i in feats:
+ RecordsTextArea.Append (i)
+
+ return
+
+def delimited_str(strref, delimiter, strref2, newline=1):
+ if strref2:
+ val = GemRB.GetString(strref) + delimiter + " " + GemRB.GetString(strref2)
+ else:
+ val = GemRB.GetString(strref) + delimiter
+ if newline:
+ return val + "\n"
+ else:
+ return val
+
+def delimited_txt(strref, delimiter, text, newline=1):
+ val = GemRB.GetString(strref) + delimiter + " " + str(text)
+ if newline:
+ return val + "\n"
+ else:
+ return val
+
+#character information
+def DisplayMisc (pc):
+ Window = RecordsWindow
+
+ TotalPartyExp = 0
+ TotalCount = 0
+ for i in range (1, GemRB.GetPartySize() + 1):
+ stat = GemRB.GetPCStats(i)
+ TotalPartyExp = TotalPartyExp + stat['KillsTotalXP']
+ TotalCount = TotalCount + stat['KillsTotalCount']
+
+ stat = GemRB.GetPCStats (pc)
+
+ #favourites
+ RecordsTextArea.Append ("[color=ffff00]")
+ RecordsTextArea.Append (40320)
+ RecordsTextArea.Append ("[/color]\n")
+
+ #favourite spell and weapon
+ RecordsTextArea.Append (delimited_str (11949, ":", stat['FavouriteSpell']))
+ RecordsTextArea.Append (delimited_str (11950, ":", stat['FavouriteWeapon']))
+
+ # combat details
+ RecordsTextArea.Append ("\n[color=ffff00]")
+ RecordsTextArea.Append (40322)
+ RecordsTextArea.Append ("[/color]\n")
+
+ #most powerful vanquished, time spent, xp and kills
+ RecordsTextArea.Append (delimited_str (11947, ":", stat['BestKilledName']))
+
+ # NOTE: currentTime is in seconds, joinTime is in seconds * 15
+ # (script updates???). In each case, there are 60 seconds
+ # in a minute, 24 hours in a day, but ONLY 5 minutes in an hour!!
+ # Hence currentTime (and joinTime after div by 15) has
+ # 7200 secs a day (60 * 5 * 24)
+ currentTime = GemRB.GetGameTime()
+ joinTime = stat['JoinDate'] - stat['AwayTime']
+
+ party_time = currentTime - (joinTime / 15)
+ days = party_time / 7200
+ hours = (party_time % 7200) / 300
+
+ GemRB.SetToken ('GAMEDAYS', str (days))
+ GemRB.SetToken ('HOUR', str (hours))
+
+ # construct <GAMEDAYS> days ~and~ ~<HOUR> hours~
+ if days == 1:
+ time = GemRB.GetString (10698)
+ else:
+ time = GemRB.GetString (10697)
+ time += " " + GemRB.GetString (10699) + " "
+ if hours == 1:
+ time += GemRB.GetString (10701)
+ else:
+ time += GemRB.GetString (10700)
+
+ RecordsTextArea.Append (delimited_txt (11948, ":", time))
+
+ # Experience Value of Kills
+ RecordsTextArea.Append (delimited_txt (11953, ":", stat['KillsTotalXP']))
+
+ # Number of Kills
+ RecordsTextArea.Append (delimited_txt (11954, ":", stat['KillsTotalCount']))
+
+ # Total Experience Value in Party
+ if TotalPartyExp:
+ val = stat['KillsTotalXP']*100/TotalPartyExp
+ else:
+ val = 0
+ RecordsTextArea.Append (delimited_txt (11951, ":", str(val) + "%"))
+
+ # Percentage of Total Kills in Party
+ if TotalPartyExp:
+ val = stat['KillsTotalCount']*100/TotalCount
+ else:
+ val = 0
+ RecordsTextArea.Append (delimited_txt (11954, ":", str(val) + "%"))
+
+ return
+
+def UpdateRecordsWindow ():
+ global RecordsTextArea
+
+ Window = RecordsWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ #name
+ Label = Window.GetControl (0x1000000e)
+ Label.SetText (GemRB.GetPlayerName (pc, 0))
+
+ #portrait
+ Button = Window.GetControl (2)
+ Button.SetPicture (GemRB.GetPlayerPortrait (pc,0))
+
+ # armorclass
+ Label = Window.GetControl (0x10000028)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_ARMORCLASS)))
+ Label.SetTooltip (17183)
+
+ # hp now
+ Label = Window.GetControl (0x10000029)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_HITPOINTS)))
+ Label.SetTooltip (17184)
+
+ # hp max
+ Label = Window.GetControl (0x1000002a)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)))
+ Label.SetTooltip (17378)
+
+ # stats
+
+ sstr = GemRB.GetPlayerStat (pc, IE_STR)
+ dstr = sstr-GemRB.GetPlayerStat (pc, IE_STR,1)
+ bstr = GetAbilityBonus (pc, IE_STR)
+ sint = GemRB.GetPlayerStat (pc, IE_INT)
+ dint = sint-GemRB.GetPlayerStat (pc, IE_INT,1)
+ bint = GetAbilityBonus (pc, IE_INT)
+ swis = GemRB.GetPlayerStat (pc, IE_WIS)
+ dwis = swis-GemRB.GetPlayerStat (pc, IE_WIS,1)
+ bwis = GetAbilityBonus (pc, IE_WIS)
+ sdex = GemRB.GetPlayerStat (pc, IE_DEX)
+ ddex = sdex-GemRB.GetPlayerStat (pc, IE_DEX,1)
+ bdex = GetAbilityBonus (pc, IE_DEX)
+ scon = GemRB.GetPlayerStat (pc, IE_CON)
+ dcon = scon-GemRB.GetPlayerStat (pc, IE_CON,1)
+ bcon = GetAbilityBonus (pc, IE_CON)
+ schr = GemRB.GetPlayerStat (pc, IE_CHR)
+ dchr = schr-GemRB.GetPlayerStat (pc, IE_CHR,1)
+ bchr = GetAbilityBonus (pc, IE_CHR)
+
+ Label = Window.GetControl (0x1000002f)
+ Label.SetText (str(sstr))
+ ColorDiff2 (Window, Label, dstr)
+
+ Label = Window.GetControl (0x10000009)
+ Label.SetText (str(sdex))
+ ColorDiff2 (Window, Label, ddex)
+
+ Label = Window.GetControl (0x1000000a)
+ Label.SetText (str(scon))
+ ColorDiff2 (Window, Label, dcon)
+
+ Label = Window.GetControl (0x1000000b)
+ Label.SetText (str(sint))
+ ColorDiff2 (Window, Label, dint)
+
+ Label = Window.GetControl (0x1000000c)
+ Label.SetText (str(swis))
+ ColorDiff2 (Window, Label, dwis)
+
+ Label = Window.GetControl (0x1000000d)
+ Label.SetText (str(schr))
+ ColorDiff2 (Window, Label, dchr)
+
+ Label = Window.GetControl (0x10000035)
+ Label.SetText (str(bstr))
+ ColorDiff (Window, Label, bstr)
+
+ Label = Window.GetControl (0x10000036)
+ Label.SetText (str(bdex))
+ ColorDiff (Window, Label, bdex)
+
+ Label = Window.GetControl (0x10000037)
+ Label.SetText (str(bcon))
+ ColorDiff (Window, Label, bcon)
+
+ Label = Window.GetControl (0x10000038)
+ Label.SetText (str(bint))
+ ColorDiff (Window, Label, bint)
+
+ Label = Window.GetControl (0x10000039)
+ Label.SetText (str(bwis))
+ ColorDiff (Window, Label, bwis)
+
+ Label = Window.GetControl (0x1000003a)
+ Label.SetText (str(bchr))
+ ColorDiff (Window, Label, bchr)
+
+ RecordsTextArea = Window.GetControl (45)
+ RecordsTextArea.SetText ("")
+ RecordsTextArea.Append ("[capital=0]")
+
+ SelectWindow = GemRB.GetVar ("SelectWindow")
+ if SelectWindow == 1:
+ DisplayGeneral (pc)
+ elif SelectWindow == 2:
+ DisplayWeapons (pc)
+ elif SelectWindow == 3:
+ DisplaySkills (pc)
+ elif SelectWindow == 4:
+ DisplayMisc (pc)
+
+ #if actor is uncontrollable, make this grayed
+ Window.SetVisible (WINDOW_VISIBLE)
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CloseHelpWindow ():
+ global DescTable
+
+ if InformationWindow:
+ InformationWindow.Unload ()
+ if DescTable:
+ DescTable = None
+ return
+
+#ingame help
+def OpenHelpWindow ():
+ global HelpTable, InformationWindow
+
+ InformationWindow = Window = GemRB.LoadWindow (57)
+
+ HelpTable = GemRB.LoadTable ("topics")
+ GemRB.SetVar("Topic", 0)
+ GemRB.SetVar("TopIndex", 0)
+
+ for i in range(11):
+ title = HelpTable.GetValue (i, 0)
+ Button = Window.GetControl (i+27)
+ Label = Window.GetControl (i+0x10000004)
+
+ Button.SetVarAssoc ("Topic", i)
+ if title:
+ Label.SetText (title)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateHelpWindow)
+ else:
+ Label.SetText ("")
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ #done
+ Button = Window.GetControl (1)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseHelpWindow)
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ UpdateHelpWindow ()
+ return
+
+def UpdateHelpWindow ():
+ global DescTable, Topic
+
+ Window = InformationWindow
+
+ if Topic!=GemRB.GetVar ("Topic"):
+ Topic = GemRB.GetVar ("Topic")
+ GemRB.SetVar ("TopIndex",0)
+ GemRB.SetVar ("Selected",0)
+
+ for i in range(11):
+ Button = Window.GetControl (i+27)
+ Label = Window.GetControl (i+0x10000004)
+ if Topic==i:
+ Label.SetTextColor (255,255,0)
+ else:
+ Label.SetTextColor (255,255,255)
+
+ resource = HelpTable.GetValue (Topic, 1)
+ if DescTable:
+ DescTable = None
+
+ DescTable = GemRB.LoadTable (resource)
+
+ ScrollBar = Window.GetControl (4)
+
+ startrow = HelpTable.GetValue (Topic, 4)
+ if startrow<0:
+ i=-startrow-10
+ else:
+ i = DescTable.GetRowCount ()-10-startrow
+
+ if i<1: i=1
+
+ ScrollBar.SetVarAssoc ("TopIndex", i)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RefreshHelpWindow)
+
+ RefreshHelpWindow ()
+ return
+
+def RefreshHelpWindow ():
+ Window = InformationWindow
+ Topic = GemRB.GetVar ("Topic")
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Selected = GemRB.GetVar ("Selected")
+
+ titlecol = HelpTable.GetValue (Topic, 2)
+ desccol = HelpTable.GetValue (Topic, 3)
+ startrow = HelpTable.GetValue (Topic, 4)
+ if startrow<0: startrow = 0
+
+ for i in range(11):
+ title = DescTable.GetValue (i+startrow+TopIndex, titlecol)
+
+ Button = Window.GetControl (i+71)
+ Label = Window.GetControl (i+0x10000030)
+
+ if i+TopIndex==Selected:
+ Label.SetTextColor (255,255,0)
+ else:
+ Label.SetTextColor (255,255,255)
+ if title>0:
+ Label.SetText (title)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetVarAssoc ("Selected", i+TopIndex)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RefreshHelpWindow)
+ else:
+ Label.SetText ("")
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if Selected<0:
+ desc=""
+ else:
+ desc = DescTable.GetValue (Selected+startrow, desccol)
+
+ Window = InformationWindow
+ TextArea = Window.GetControl (2)
+ TextArea.SetText (desc)
+ return
+
+def CloseBiographyWindow ():
+ if BiographyWindow:
+ BiographyWindow.Unload ()
+ return
+
+def OpenBiographyWindow ():
+ global BiographyWindow
+
+ BiographyWindow = Window = GemRB.LoadWindow (12)
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ TextArea = Window.GetControl (0)
+ TextArea.SetText (GemRB.GetPlayerString(pc, 63) )
+
+ # Done
+ Button = Window.GetControl (2)
+ Button.SetText (11973)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseBiographyWindow)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OpenCustomizeWindow ():
+ global CustomizeWindow
+ global PortraitsTable, ScriptsTable, ColorTable
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ if GemRB.GetPlayerStat (pc, IE_MC_FLAGS)&MC_EXPORTABLE:
+ Exportable = 1
+ else:
+ Exportable = 0
+ PortraitsTable = GemRB.LoadTable ("PICTURES")
+ ScriptsTable = GemRB.LoadTable ("SCRPDESC")
+ ColorTable = GemRB.LoadTable ("CLOWNCOL")
+ CustomizeWindow = GemRB.LoadWindow (17)
+
+ AppearanceButton = CustomizeWindow.GetControl (0)
+ AppearanceButton.SetText (11961)
+ if not Exportable:
+ AppearanceButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ SoundButton = CustomizeWindow.GetControl (1)
+ SoundButton.SetText (10647)
+ if not Exportable:
+ SoundButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ #ColorButton = CustomizeWindow.GetControl (2)
+ #ColorButton.SetText (10646)
+ #if not Exportable:
+ # ColorButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ScriptButton = CustomizeWindow.GetControl (3)
+ ScriptButton.SetText (17111)
+
+ BiographyButton = CustomizeWindow.GetControl (9)
+ BiographyButton.SetText (18003)
+ if not Exportable:
+ BiographyButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ TextArea = CustomizeWindow.GetControl (5)
+ TextArea.SetText (11327)
+
+ DoneButton = CustomizeWindow.GetControl (7)
+ DoneButton.SetText (11973)
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+
+ CancelButton = CustomizeWindow.GetControl (8);
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ AppearanceButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenAppearanceWindow)
+ SoundButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSoundWindow)
+ #ColorButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenColorWindow)
+ ScriptButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenScriptWindow)
+ BiographyButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyEditWindow)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomizeDonePress)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomizeCancelPress)
+
+ CustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CustomizeDonePress ():
+ global CustomizeWindow
+
+ if CustomizeWindow:
+ CustomizeWindow.Unload ()
+ CustomizeWindow = None
+
+ UpdateRecordsWindow ()
+ return
+
+def CustomizeCancelPress ():
+ global CustomizeWindow
+
+ if CustomizeWindow:
+ CustomizeWindow.Unload ()
+ CustomizeWindow = None
+
+ UpdateRecordsWindow ()
+ return
+
+def OpenAppearanceWindow ():
+ global SubCustomizeWindow
+ global PortraitButton
+ global Gender, LastPortrait
+
+ SubCustomizeWindow = GemRB.LoadWindow (18)
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Gender = GemRB.GetPlayerStat (pc, IE_SEX, 1)
+ PortraitName = GemRB.GetPlayerPortrait (pc, 0)
+ LastPortrait = PortraitsTable.GetRowIndex (PortraitName[0:len(PortraitName)-1])
+
+ PortraitButton = SubCustomizeWindow.GetControl (0)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ LeftButton = SubCustomizeWindow.GetControl (1)
+ RightButton = SubCustomizeWindow.GetControl (2)
+
+ DoneButton = SubCustomizeWindow.GetControl (3)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (4)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ CustomPortraitButton = SubCustomizeWindow.GetControl (5)
+ CustomPortraitButton.SetText (17545)
+
+ LeftButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitLeftPress)
+ RightButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitRightPress)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePortraitWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ CustomPortraitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenCustomPortraitWindow)
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+
+ while True:
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ UpdatePortrait ()
+ break
+ LastPortrait = LastPortrait + 1
+
+ return
+
+def DonePortraitWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Name = PortraitsTable.GetRowName (LastPortrait)
+ GemRB.FillPlayerInfo (pc, Name + "M", Name + "S")
+ CloseSubCustomizeWindow ()
+ return
+
+def PortraitRightPress():
+ global LastPortrait
+
+ while True:
+ LastPortrait = LastPortrait + 1
+ if LastPortrait >= PortraitsTable.GetRowCount ():
+ LastPortrait = 0
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ UpdatePortrait ()
+ return
+
+ return
+
+def PortraitLeftPress():
+ global LastPortrait
+
+ while True:
+ LastPortrait = LastPortrait - 1
+ if LastPortrait < 0:
+ LastPortrait = PortraitsTable.GetRowCount ()-1
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ UpdatePortrait ()
+ return
+
+ return
+
+def UpdatePortrait ():
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"L"
+ PortraitButton.SetPicture (PortraitName, "NOPORTLG")
+ return
+
+def OpenCustomPortraitWindow ():
+ global SubSubCustomizeWindow
+ global PortraitList1, PortraitList2
+ global RowCount1, RowCount2
+
+ SubSubCustomizeWindow = GemRB.LoadWindow (19)
+
+ SmallPortraitButton = SubSubCustomizeWindow.GetControl (1)
+ SmallPortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+
+ LargePortraitButton = SubSubCustomizeWindow.GetControl (0)
+ LargePortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+
+ DoneButton = SubSubCustomizeWindow.GetControl (10)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = SubSubCustomizeWindow.GetControl (11)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Portrait List Large
+ PortraitList1 = SubSubCustomizeWindow.GetControl (2)
+ RowCount1 = PortraitList1.GetPortraits (0)
+ PortraitList1.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, LargeCustomPortrait)
+ GemRB.SetVar ("Row1", RowCount1)
+ PortraitList1.SetVarAssoc ("Row1",RowCount1)
+
+ # Portrait List Small
+ PortraitList2 = SubSubCustomizeWindow.GetControl (3)
+ RowCount2 = PortraitList2.GetPortraits (1)
+ PortraitList2.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, SmallCustomPortrait)
+ GemRB.SetVar ("Row2", RowCount2)
+ PortraitList2.SetVarAssoc ("Row2",RowCount2)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePortraitCustomizeWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubSubCustomizeWindow)
+
+ SubSubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DonePortraitCustomizeWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.FillPlayerInfo (pc, PortraitList1.QueryText () , PortraitList2.QueryText ())
+ CloseSubSubCustomizeWindow ()
+ #closing the generic portraits, because we just set a custom one
+ CloseSubCustomizeWindow ()
+ return
+
+def LargeCustomPortrait ():
+ Window = SubSubCustomizeWindow
+
+ Portrait = PortraitList1.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row1") == RowCount1:
+ return
+
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (10)
+ if Portrait=="":
+ Portrait = "NOPORTMD"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList2.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (0)
+ Button.SetPicture (Portrait, "NOPORTMD")
+ return
+
+def SmallCustomPortrait ():
+ Window = SubSubCustomizeWindow
+
+ Portrait = PortraitList2.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row2") == RowCount2:
+ return
+
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (10)
+ if Portrait=="":
+ Portrait = "NOPORTSM"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList1.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (1)
+ Button.SetPicture (Portrait, "NOPORTSM")
+ return
+
+def OpenSoundWindow ():
+ global SubCustomizeWindow
+ global VoiceList
+ global Gender
+ global OldVoiceSet
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ OldVoiceSet = GemRB.GetPlayerSound (pc)
+ SubCustomizeWindow = GemRB.LoadWindow (20)
+
+ VoiceList = SubCustomizeWindow.GetControl (5)
+ VoiceList.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ Gender = GemRB.GetPlayerStat (pc, IE_SEX, 1)
+
+ VoiceList.SetVarAssoc ("Selected", 0)
+ VoiceList.GetCharSounds()
+ VoiceList.SelectText (OldVoiceSet)
+
+ PlayButton = SubCustomizeWindow.GetControl (7)
+ PlayButton.SetText (17318)
+
+ TextArea = SubCustomizeWindow.GetControl (8)
+ TextArea.SetText (11315)
+
+ DoneButton = SubCustomizeWindow.GetControl (10)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (11)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PlayButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, PlaySoundPressed)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneSoundWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSoundWindow)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def CloseSoundWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerSound (pc, OldVoiceSet)
+ CloseSubCustomizeWindow ()
+ return
+
+def DoneSoundWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ CharSound = VoiceList.QueryText ()
+ GemRB.SetPlayerSound (pc, CharSound)
+
+ CloseSubCustomizeWindow ()
+ return
+
+def PlaySoundPressed():
+ global CharSoundWindow, SoundIndex, SoundSequence
+
+ CharSound = VoiceList.QueryText ()
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerSound (pc, CharSound)
+ VoiceSet = GemRB.GetPlayerSound (pc, 1)
+ tmp = SoundIndex
+ while (not GemRB.HasResource (VoiceSet + SoundSequence[SoundIndex], RES_WAV)):
+ NextSound()
+ if SoundIndex == tmp:
+ break
+ else:
+ NextSound()
+
+ GemRB.PlaySound (VoiceSet + SoundSequence[SoundIndex], 0, 0, 5)
+ return
+
+def NextSound():
+ global SoundIndex, SoundSequence
+ SoundIndex += 1
+ if SoundIndex >= len(SoundSequence):
+ SoundIndex = 0
+ return
+
+def OpenScriptWindow ():
+ global SubCustomizeWindow
+ global ScriptTextArea, SelectedTextArea
+
+ SubCustomizeWindow = GemRB.LoadWindow (11)
+
+ ScriptTextArea = SubCustomizeWindow.GetControl (2)
+ ScriptTextArea.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ FillScriptList ()
+ pc = GemRB.GameGetSelectedPCSingle ()
+ script = GemRB.GetPlayerScript (pc)
+ scriptindex = ScriptsTable.GetRowIndex (script)
+ GemRB.SetVar ("Selected", scriptindex)
+ ScriptTextArea.SetVarAssoc ("Selected", scriptindex)
+
+ SelectedTextArea = SubCustomizeWindow.GetControl (4)
+ UpdateScriptSelection ()
+
+ DoneButton = SubCustomizeWindow.GetControl (5)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton = SubCustomizeWindow.GetControl (6)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneScriptWindow)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+ ScriptTextArea.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, UpdateScriptSelection)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def FillScriptList ():
+ ScriptTextArea.Clear ()
+ row = ScriptsTable.GetRowCount ()
+ for i in range (row):
+ GemRB.SetToken ("script", ScriptsTable.GetRowName (i) )
+ title = ScriptsTable.GetValue (i,0)
+ if title!=-1:
+ desc = ScriptsTable.GetValue (i,1)
+ txt = GemRB.GetString (title)
+
+ if (desc!=-1):
+ txt += GemRB.GetString (desc)
+
+ ScriptTextArea.Append (txt+"\n", -1)
+
+ else:
+ ScriptTextArea.Append (ScriptsTable.GetRowName (i)+"\n" ,-1)
+
+ return
+
+def DoneScriptWindow ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ script = ScriptsTable.GetRowName (GemRB.GetVar ("Selected") )
+ GemRB.SetPlayerScript (pc, script)
+ CloseSubCustomizeWindow ()
+ return
+
+def UpdateScriptSelection():
+ text = ScriptTextArea.QueryText ()
+ SelectedTextArea.SetText (text)
+ return
+
+def OpenBiographyEditWindow ():
+ global SubCustomizeWindow
+ global BioStrRef
+ global TextArea
+
+ Changed = 0
+ pc = GemRB.GameGetSelectedPCSingle ()
+ BioStrRef = GemRB.GetPlayerString (pc, 74)
+ if BioStrRef != 33347:
+ Changed = 1
+
+ SubCustomizeWindow = GemRB.LoadWindow (51)
+
+ ClearButton = SubCustomizeWindow.GetControl (5)
+ ClearButton.SetText (18622)
+
+ DoneButton = SubCustomizeWindow.GetControl (1)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ RevertButton = SubCustomizeWindow.GetControl (6)
+ RevertButton.SetText (2240)
+ if not Changed:
+ RevertButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = SubCustomizeWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ TextArea = SubCustomizeWindow.GetControl (4)
+ TextArea.SetBufferLength (65535)
+ TextArea.SetText (BioStrRef)
+
+ ClearButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ClearBiography)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DoneBiographyWindow)
+ RevertButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RevertBiography)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseSubCustomizeWindow)
+
+ SubCustomizeWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ClearBiography():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ BioStrRef = 62015+pc
+ #GemRB.CreateString (BioStrRef, "")
+ TextArea.SetText ("")
+ return
+
+def DoneBiographyWindow ():
+ global BioStrRef
+
+ #TODO set bio
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #pc is 1 based
+ BioStrRef = 62015+pc
+ GemRB.CreateString (BioStrRef, TextArea.QueryText())
+ GemRB.SetPlayerString (pc, 74, BioStrRef)
+ CloseSubCustomizeWindow ()
+ return
+
+def RevertBiography():
+ global BioStrRef
+
+ BioTable = GemRB.LoadTable ("bios")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS)
+ BioStrRef = BioTable.GetValue(Class,1)
+ TextArea.SetText (BioStrRef)
+ CloseSubCustomizeWindow ()
+ return
+
+def CloseSubCustomizeWindow ():
+ global SubCustomizeWindow
+
+ if SubCustomizeWindow:
+ SubCustomizeWindow.Unload ()
+ SubCustomizeWindow = None
+ return
+
+def CloseSubSubCustomizeWindow ():
+ global SubSubCustomizeWindow
+
+ if SubSubCustomizeWindow:
+ SubSubCustomizeWindow.Unload ()
+ SubSubCustomizeWindow = None
+ return
+
+def OpenExportWindow ():
+ global ExportWindow, NameField, ExportDoneButton
+
+ ExportWindow = GemRB.LoadWindow(13)
+
+ TextArea = ExportWindow.GetControl(2)
+ TextArea.SetText(10962)
+
+ TextArea = ExportWindow.GetControl(0)
+ TextArea.GetCharacters ()
+
+ ExportDoneButton = ExportWindow.GetControl(4)
+ ExportDoneButton.SetText(11973)
+ ExportDoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = ExportWindow.GetControl(5)
+ CancelButton.SetText(13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ NameField = ExportWindow.GetControl(6)
+
+ ExportDoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExportDonePress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExportCancelPress)
+ NameField.SetEvent(IE_GUI_EDIT_ON_CHANGE, ExportEditChanged)
+ ExportWindow.ShowModal (MODAL_SHADOW_GRAY)
+ NameField.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def ExportDonePress():
+ if ExportWindow:
+ ExportWindow.Unload()
+ #save file under name from EditControl
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SaveCharacter(pc, ExportFileName)
+ return
+
+def ExportCancelPress():
+ if ExportWindow:
+ ExportWindow.Unload()
+ return
+
+def ExportEditChanged():
+ global ExportFileName
+
+ ExportFileName = NameField.QueryText()
+ if ExportFileName == "":
+ ExportDoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ ExportDoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+###################################################
+# End of file GUIREC.py
diff --git a/gemrb/GUIScripts/iwd2/GUISAVE.py b/gemrb/GUIScripts/iwd2/GUISAVE.py
new file mode 100644
index 0000000..71d14cd
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUISAVE.py
@@ -0,0 +1,279 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUISAVE.py - Save window
+
+###################################################
+
+import GemRB
+import GUICommon
+import LoadScreen
+from GUIDefines import *
+
+SaveWindow = 0
+ConfirmWindow = 0
+NameField = 0
+SaveButton = 0
+TextAreaControl = 0
+Games = ()
+ScrollBar = 0
+
+def OpenSaveWindow ():
+ global SaveWindow, TextAreaControl, Games, ScrollBar
+
+ if GUICommon.CloseOtherWindow (OpenSaveWindow):
+ CloseSaveWindow ()
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUISAVE", 800, 600)
+ Window = SaveWindow = GemRB.LoadWindow (0)
+ Window.SetFrame ()
+ CancelButton=Window.GetControl (22)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSaveWindow)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ GemRB.SetVar ("LoadIdx",0)
+
+ for i in range(5):
+ Button = Window.GetControl (55+i)
+ Button.SetText (15588)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SavePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ Button = Window.GetControl (60+i)
+ Button.SetText (13957)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ #area previews
+ Button = Window.GetControl (1+i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ #PC portraits
+ for j in range(PARTY_SIZE):
+ Button = Window.GetControl (25+i*PARTY_SIZE+j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ ScrollBar=Window.GetControl (23)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games=GemRB.GetSaveGames ()
+ TopIndex = max (0, len(Games) - 5 + 1) #one more for the 'new game'
+ GemRB.SetVar ("TopIndex",TopIndex)
+ ScrollBar.SetVarAssoc ("TopIndex", TopIndex+1)
+ ScrollBar.SetDefaultScrollBar ()
+ ScrollBarPress ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ScrollBarPress():
+ Window = SaveWindow
+
+ #draw load game portraits
+ Pos = GemRB.GetVar ("TopIndex")
+ for i in range(5):
+ ActPos = Pos + i
+
+ Button1 = Window.GetControl (55+i)
+ Button2 = Window.GetControl (60+i)
+ if ActPos<=len(Games):
+ Button1.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetName()
+ Button2.SetState (IE_GUI_BUTTON_ENABLED)
+ elif ActPos == len(Games):
+ Slotname = 15304
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Slotname = ""
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Label = Window.GetControl (0x10000005+i)
+ Label.SetText (Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = Window.GetControl (0x1000000a+i)
+ Label.SetText (Slotname)
+
+ Button=Window.GetControl (1+i)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture("")
+ for j in range(PARTY_SIZE):
+ Button=Window.GetControl (25+i*PARTY_SIZE+j)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture("")
+ return
+
+def AbortedSaveGame():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ConfirmedSaveGame():
+ global ConfirmWindow
+
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ Label = ConfirmWindow.GetControl (3)
+ Slotname = Label.QueryText ()
+ LoadScreen.StartLoadScreen()
+ if Pos < len(Games):
+ GemRB.SaveGame(Games[Pos], Slotname, 22) #saves a game with version 2.2
+ else:
+ GemRB.SaveGame(None, Slotname, 22) #saves a game with version 2.2
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def SavePress():
+ global ConfirmWindow, NameField, SaveButton
+
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ ConfirmWindow = GemRB.LoadWindow (1)
+
+ #slot name
+ if Pos<len(Games):
+ Slotname = Games[Pos].GetName()
+ save_strref = 15306
+ else:
+ Slotname = ""
+ save_strref = 15588
+ NameField = ConfirmWindow.GetControl (3)
+ NameField.SetText (Slotname)
+ NameField.SetEvent (IE_GUI_EDIT_ON_CHANGE, EditChange)
+
+ #game hours (should be generated from game)
+ if Pos<len(Games):
+ Slotname = Games[Pos].GetGameDate()
+ else:
+ Slotname = ""
+ Label = ConfirmWindow.GetControl (0x10000004)
+ Label.SetText (Slotname)
+
+ #areapreview
+ #Button=ConfirmWindow.GetControl (0)
+ #if Pos<len(Games):
+ # Button.SetSprite2D(Games[Pos].GetPreview())
+ #else:
+ # Button.SetPicture("")
+
+ #portraits
+ #for j in range(PARTY_SIZE):
+ # Button=ConfirmWindow.GetControl (25+j)
+ # if Pos<len(Games):
+ # Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ # else:
+ # Button.SetPicture("")
+
+ #save
+ SaveButton=ConfirmWindow.GetControl (7)
+ SaveButton.SetText (save_strref)
+ SaveButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ConfirmedSaveGame)
+ SaveButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ #SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+ if Slotname == "":
+ SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ #cancel
+ CancelButton=ConfirmWindow.GetControl (8)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, AbortedSaveGame)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ NameField.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ return
+
+def EditChange():
+ Name = NameField.QueryText ()
+ if len(Name)==0:
+ SaveButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ SaveButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def DeleteGameConfirm():
+ global Games
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Pos = TopIndex +GemRB.GetVar ("LoadIdx")
+ GemRB.DeleteSaveGame(Games[Pos])
+ if TopIndex>0:
+ GemRB.SetVar ("TopIndex",TopIndex-1)
+ del Games[pos]
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress()
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress():
+ global ConfirmWindow
+
+ SaveWindow.SetVisible (WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow (2)
+ Text=ConfirmWindow.GetControl (0)
+ Text.SetText (15305)
+ DeleteButton=ConfirmWindow.GetControl (1)
+ DeleteButton.SetText (13957)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ CancelButton=ConfirmWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CloseSaveWindow ():
+ if SaveWindow:
+ SaveWindow.Unload ()
+ if GemRB.GetVar ("QuitAfterSave"):
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("Start")
+ return
+
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE) #enabling the game control screen
+ GemRB.UnhideGUI () #enabling the other windows
+ return
diff --git a/gemrb/GUIScripts/iwd2/GUISPL.py b/gemrb/GUIScripts/iwd2/GUISPL.py
new file mode 100644
index 0000000..7325575
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUISPL.py
@@ -0,0 +1,395 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUISPL.py - scripts to control priest spells windows from GUIPR winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+from ie_action import ACT_CAST
+
+TopIndex = 0
+SpellBookWindow = None
+SpellBookSpellInfoWindow = None
+SpellBookSpellLevel = 0
+SpellBookSpellUnmemorizeWindow = None
+PortraitWindow = None
+OldPortraitWindow = None
+OptionsWindow = None
+OldOptionsWindow = None
+
+def OpenSpellBookWindow ():
+ global SpellBookWindow, OptionsWindow, PortraitWindow
+ global OldPortraitWindow, OldOptionsWindow, TopIndex
+
+ if GUICommon.CloseOtherWindow (OpenSpellBookWindow):
+ if SpellBookWindow:
+ SpellBookWindow.Unload ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+
+ SpellBookWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ OldPortraitWindow = None
+ GUICommonWindows.OptionsWindow = OldOptionsWindow
+ OldOptionsWindow = None
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ return
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE)
+
+ GemRB.LoadWindowPack ("GUISPL", 800, 600)
+ SpellBookWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", SpellBookWindow.ID)
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow ()
+ OldOptionsWindow = GUICommonWindows.OptionsWindow
+ OptionsWindow = GemRB.LoadWindow (0)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 0, OpenSpellBookWindow)
+ Window.SetFrame ()
+
+ Button = Window.GetControl (92)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SpellBookPrevPress)
+
+ Button = Window.GetControl (93)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SpellBookNextPress)
+
+ #setup level buttons
+ for i in range (9):
+ Button = Window.GetControl (55 + i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RefreshSpellBookLevel)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+
+ for i in range (9):
+ Button = Window.GetControl (55 + i)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+ Button.SetVarAssoc ("SpellBookSpellLevel", i)
+
+ # Setup memorized spells buttons
+ for i in range (24):
+ Button = Window.GetControl (6 + i)
+ Button.SetBorder (0,0,0,0,0,0,0,0,160,0,1)
+ #Button.SetBAM ("SPELFRAM",0,0,0)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Setup book spells buttons
+ for i in range (8):
+ Button = Window.GetControl (30 + i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetVarAssoc ("SpellIndex", i)
+
+ ScrollBar = Window.GetControl (54)
+ TopIndex = 0
+ GemRB.SetVar ("TopIndex",0)
+ ScrollBar.SetVarAssoc ("TopIndex", 0)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateSpellBookWindow)
+ UpdateSpellBookWindow ()
+ return
+
+def UpdateSpellBookWindow ():
+ global SpellBookMemorizedSpellList, PriestKnownSpellList, TopIndex
+
+ SpellBookMemorizedSpellList = []
+ SpellBookKnownSpellList = []
+
+ Window = SpellBookWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+ type = IE_SPELL_TYPE_PRIEST
+ level = SpellBookSpellLevel
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, type, level)
+
+ #Label = Window.GetControl (0x10000032)
+ #Label.SetText (GemRB.GetString(12137)+str(level+1) )
+
+ Name = GemRB.GetPlayerName (pc, 0)
+ #Label = Window.GetControl (0xffffffff)
+ #Label.SetText (Name)
+
+ Button = Window.GetControl (1)
+ Button.SetPicture (GemRB.GetPlayerPortrait (pc,0))
+
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+ for i in range (24):
+ Button = Window.GetControl (6 + i)
+ if i < mem_cnt:
+ ms = GemRB.GetMemorizedSpell (pc, type, level, i)
+ Button.SetSpellIcon (ms['SpellResRef'])
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if ms['Flags']:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSpellBookSpellUnmemorizeWindow)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnSpellBookUnmemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenSpellBookSpellInfoWindow)
+ spell = GemRB.GetSpell (ms['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ SpellBookMemorizedSpellList.append (ms['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", i)
+ Button.EnableBorder (0, ms['Flags'] == 0)
+ else:
+ if i < max_mem_cnt:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+
+ known_cnt = GemRB.GetKnownSpellsCount (pc, type, level)
+ for i in range (8):
+ Button = Window.GetControl (30 + i)
+ if i+TopIndex < known_cnt:
+ ks = GemRB.GetKnownSpell (pc, type, level, i+TopIndex)
+ Button.SetSpellIcon (ks['SpellResRef'])
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnSpellBookMemorizeSpell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenSpellBookSpellInfoWindow)
+ spell = GemRB.GetSpell (ks['SpellResRef'])
+ Button.SetTooltip (spell['SpellName'])
+ SpellBookKnownSpellList.append (ks['SpellResRef'])
+ Button.SetVarAssoc ("SpellButton", 100 + i)
+
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetTooltip ('')
+ Button.EnableBorder (0, 0)
+
+ #if actor is uncontrollable, make this grayed
+ CantCast = GemRB.GetPlayerStat(pc, IE_DISABLEDBUTTON)&(1<<ACT_CAST)
+ if CantCast or GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ OptionsWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+#TODO: spell type selector
+def SpellBookPrevPress ():
+ global SpellType
+
+ UpdateSpellBookWindow ()
+ return
+
+#TODO: spell type selector
+def SpellBookNextPress ():
+ global SpellBookSpellLevel
+
+ UpdateSpellBookWindow ()
+ return
+
+def RefreshSpellBookLevel ():
+ global SpellBookSpellLevel
+
+ SpellBookSpellLevel = GemRB.GetVar ("PriestSpellLevel")
+ UpdateSpellBookWindow ()
+ return
+
+def OpenSpellBookSpellInfoWindow ():
+ global SpellBookSpellInfoWindow
+
+ GemRB.HideGUI ()
+
+ if SpellBookSpellInfoWindow != None:
+ if SpellBookSpellInfoWindow:
+ SpellBookSpellInfoWindow.Unload ()
+ SpellBookSpellInfoWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ SpellBookSpellInfoWindow = Window = GemRB.LoadWindow (3)
+ GemRB.SetVar ("FloatWindow", SpellBookSpellInfoWindow.ID)
+
+ #back
+ Button = Window.GetControl (5)
+ Button.SetText (15416)
+ Button.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSpellBookSpellInfoWindow)
+
+ index = GemRB.GetVar ("SpellButton")
+ if index < 100:
+ ResRef = SpellBookMemorizedSpellList[index]
+ else:
+ ResRef = SpellBookKnownSpellList[index - 100]
+
+ spell = GemRB.GetSpell (ResRef)
+
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (spell['SpellName'])
+
+ Button = Window.GetControl (2)
+ Button.SetSpellIcon (ResRef)
+
+ Text = Window.GetControl (3)
+ Text.SetText (spell['SpellDesc'])
+
+ #IconResRef = 'SPL' + spell['SpellBookIcon'][2:]
+
+ #Button = Window.GetControl (5)
+ #Button.SetSprites (IconResRef, 0, 0, 0, 0, 0)
+
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+
+def OnSpellBookMemorizeSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = SpellBookSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton") - 100 + TopIndex
+
+ if GemRB.MemorizeSpell (pc, type, level, index):
+ UpdateSpellBookWindow ()
+ return
+
+def OpenSpellBookSpellRemoveWindow ():
+ global SpellBookSpellUnmemorizeWindow
+
+ GemRB.HideGUI ()
+
+ if SpellBookSpellUnmemorizeWindow != None:
+ if SpellBookSpellUnmemorizeWindow:
+ SpellBookSpellUnmemorizeWindow.Unload ()
+ SpellBookSpellUnmemorizeWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ SpellBookSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+ GemRB.SetVar ("FloatWindow", SpellBookSpellUnmemorizeWindow.ID)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (63745)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnSpellBookRemoveSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSpellBookSpellRemoveWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OpenSpellBookSpellUnmemorizeWindow ():
+ global SpellBookSpellUnmemorizeWindow
+
+ GemRB.HideGUI ()
+
+ if SpellBookSpellUnmemorizeWindow != None:
+ if SpellBookSpellUnmemorizeWindow:
+ SpellBookSpellUnmemorizeWindow.Unload ()
+ SpellBookSpellUnmemorizeWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ SpellBookSpellUnmemorizeWindow = Window = GemRB.LoadWindow (5)
+ GemRB.SetVar ("FloatWindow", SpellBookSpellUnmemorizeWindow.ID)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (11824)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (17507)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnSpellBookUnmemorizeSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (13727)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSpellBookSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def OnSpellBookUnmemorizeSpell ():
+ if SpellBookSpellUnmemorizeWindow:
+ OpenSpellBookSpellUnmemorizeWindow ()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = SpellBookSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton")
+
+ if GemRB.UnmemorizeSpell (pc, type, level, index):
+ UpdateSpellBookWindow ()
+ return
+
+
+def OnSpellBookRemoveSpell ():
+ if SpellBookSpellUnmemorizeWindow:
+ OpenSpellBookSpellRemoveWindow ()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = SpellBookSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton")
+
+ #remove spell from memory
+ #GemRB.UnmemorizeSpell (pc, type, level, index)
+ #remove spell from book
+ #GemRB.RemoveSpell (pc, type, level, index)
+ UpdateSpellBookWindow ()
+ return
+
+###################################################
+# End of file GUISPL.py
diff --git a/gemrb/GUIScripts/iwd2/GUIWORLD.py b/gemrb/GUIScripts/iwd2/GUIWORLD.py
new file mode 100644
index 0000000..885b989
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GUIWORLD.py
@@ -0,0 +1,184 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIW.py - scripts to control some windows from GUIWORLD winpack
+# except of Portrait, Options and Dialog windows
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+import GUICommon
+from GUIClasses import GWindow
+import GUICommonWindows
+import MessageWindow
+import CommonWindow
+
+FRAME_PC_SELECTED = 0
+FRAME_PC_TARGET = 1
+
+ContinueWindow = None
+ReformPartyWindow = None
+OldActionsWindow = None
+OldMessageWindow = None
+
+def DialogStarted ():
+ global ContinueWindow, OldActionsWindow
+
+ # try to force-close anything which is open
+ GUICommon.CloseOtherWindow(None)
+ CommonWindow.CloseContainerWindow()
+
+ # we need GUI for dialogs
+ GemRB.UnhideGUI()
+
+ # opening control size to maximum, enabling dialog window
+ GemRB.GameSetScreenFlags(GS_HIDEGUI, OP_NAND)
+ GemRB.GameSetScreenFlags(GS_DIALOG, OP_OR)
+
+ MessageWindow.UpdateControlStatus()
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ContinueWindow = Window = GemRB.LoadWindow (9)
+
+def DialogEnded ():
+ global ContinueWindow, OldActionsWindow
+
+ # TODO: why is this being called at game start?!
+ if not ContinueWindow:
+ return
+
+ ContinueWindow.Unload ()
+ ContinueWindow = None
+ OldActionsWindow = None
+
+def CloseContinueWindow ():
+ # don't close the actual window now to avoid flickering: we might still want it open
+ GemRB.SetVar ("DialogChoose", GemRB.GetVar ("DialogOption"))
+
+def NextDialogState ():
+ if not ContinueWindow:
+ return
+
+ ContinueWindow.SetVisible(WINDOW_INVISIBLE)
+
+ MessageWindow.TMessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenEndMessageWindow ():
+ ContinueWindow.SetVisible(WINDOW_VISIBLE)
+ Button = ContinueWindow.GetControl (0)
+ Button.SetText (9371)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenContinueMessageWindow ():
+ ContinueWindow.SetVisible(WINDOW_VISIBLE)
+ #continue
+ Button = ContinueWindow.GetControl (0)
+ Button.SetText (9372)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenReformPartyWindow ():
+ global ReformPartyWindow
+
+ hideflag = GemRB.HideGUI ()
+
+ if ReformPartyWindow:
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+ ReformPartyWindow = None
+
+ GemRB.SetVar ("OtherWindow", -1)
+ GemRB.LoadWindowPack ("GUIREC", 800, 600)
+ if hideflag:
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ReformPartyWindow = Window = GemRB.LoadWindow (24)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+
+ # Remove
+ Button = Window.GetControl (15)
+ Button.SetText (42514)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # Done
+ Button = Window.GetControl (8)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenReformPartyWindow)
+ if hideflag:
+ GemRB.UnhideGUI ()
+
+def DeathWindowPlot():
+ #no death movie, but music is changed
+ GemRB.LoadMusicPL ("Theme.mus",1)
+ GemRB.HideGUI ()
+ GemRB.SetVar("QuitGame1", 32848)
+ GemRB.SetTimedEvent (DeathWindowEnd, 10)
+ return
+
+def DeathWindow():
+ #no death movie, but music is changed
+ GemRB.LoadMusicPL ("Theme.mus",1)
+ GemRB.HideGUI ()
+ GemRB.SetVar("QuitGame1", 16498)
+ GemRB.SetTimedEvent (DeathWindowEnd, 10)
+ return
+
+def DeathWindowEnd ():
+ GemRB.GamePause (1,1)
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ Window = GemRB.LoadWindow (17)
+
+ #reason for death
+ Label = Window.GetControl (0x0fffffff)
+ strref = GemRB.GetVar ("QuitGame1")
+ Label.SetText (strref)
+
+ #load
+ Button = Window.GetControl (1)
+ Button.SetText (15590)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadPress)
+
+ #quit
+ Button = Window.GetControl (2)
+ Button.SetText (15417)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitPress)
+
+ GemRB.HideGUI ()
+ GemRB.SetVar ("MessageWindow", -1)
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def QuitPress():
+ GemRB.QuitGame()
+ GemRB.SetNextScript("Start")
+ return
+
+def LoadPress():
+ GemRB.QuitGame()
+ GemRB.SetNextScript("GUILOAD")
+ return
diff --git a/gemrb/GUIScripts/iwd2/GamePlay.py b/gemrb/GUIScripts/iwd2/GamePlay.py
new file mode 100644
index 0000000..959ae90
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/GamePlay.py
@@ -0,0 +1,189 @@
+# 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.
+#
+#
+#GamePlay Options Menu
+import GemRB
+from GUIDefines import *
+
+GamePlayWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global GamePlayWindow, TextAreaControl
+ GemRB.LoadWindowPack("GUIOPT", 800, 600)
+ GamePlayWindow = GemRB.LoadWindow(8)
+ GamePlayWindow.SetFrame( )
+ TextAreaControl = GamePlayWindow.GetControl(40)
+ DelayButton = GamePlayWindow.GetControl(21)
+ DelaySlider = GamePlayWindow.GetControl(1)
+ MouseSpdButton = GamePlayWindow.GetControl(22)
+ MouseSpdSlider = GamePlayWindow.GetControl(2)
+ KeySpdButton = GamePlayWindow.GetControl(23)
+ KeySpdSlider = GamePlayWindow.GetControl(3)
+ DifficultyButton = GamePlayWindow.GetControl(24)
+ DifficultySlider = GamePlayWindow.GetControl(12)
+ DitherButton = GamePlayWindow.GetControl(25)
+ DitherButtonB = GamePlayWindow.GetControl(14)
+ InfravisionButton = GamePlayWindow.GetControl(44)
+ InfravisionButtonB = GamePlayWindow.GetControl(42)
+ WeatherButton = GamePlayWindow.GetControl(46)
+ WeatherButtonB = GamePlayWindow.GetControl(47)
+ MaxHPButton = GamePlayWindow.GetControl(49)
+ MaxHPButtonB = GamePlayWindow.GetControl(50)
+ BloodButton = GamePlayWindow.GetControl(27)
+ BloodButtonB = GamePlayWindow.GetControl(19)
+ FeedbackButton = GamePlayWindow.GetControl(5)
+ AutoPauseButton = GamePlayWindow.GetControl(6)
+ OkButton = GamePlayWindow.GetControl(7)
+ CancelButton = GamePlayWindow.GetControl(20)
+ TextAreaControl.SetText(18042)
+
+ OkButton.SetText(11973)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton.SetText(13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ FeedbackButton.SetText(17163)
+ AutoPauseButton.SetText(17166)
+ DelayButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DelayPress)
+ DelaySlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, DelayPress)
+ DelaySlider.SetVarAssoc("Tooltips",TOOLTIP_DELAY_FACTOR)
+
+ KeySpdButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, KeySpdPress)
+ KeySpdSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, KeySpdPress)
+ KeySpdSlider.SetVarAssoc("Keyboard Scroll Speed",36)
+
+ MouseSpdButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MouseSpdPress)
+ MouseSpdSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, MouseSpdPress)
+ MouseSpdSlider.SetVarAssoc("Mouse Scroll Speed",36)
+
+ DifficultyButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DifficultyPress)
+ DifficultySlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, DifficultyPress)
+ DifficultySlider.SetVarAssoc("Difficulty Level",2)
+
+ BloodButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BloodPress)
+ BloodButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, BloodPress)
+ BloodButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ BloodButtonB.SetVarAssoc("Gore",1)
+ BloodButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ DitherButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DitherPress)
+ DitherButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, DitherPress)
+ DitherButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ DitherButtonB.SetVarAssoc("Always Dither",1)
+ DitherButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ InfravisionButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, InfravisionPress)
+ InfravisionButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, InfravisionPress)
+ InfravisionButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ InfravisionButtonB.SetVarAssoc("Darkvision",1)
+ InfravisionButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ WeatherButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, WeatherPress)
+ WeatherButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, WeatherPress)
+ WeatherButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ WeatherButtonB.SetVarAssoc("Weather",1)
+ WeatherButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ MaxHPButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MaxHPPress)
+ MaxHPButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, MaxHPPressB)
+ MaxHPButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ MaxHPButtonB.SetVarAssoc("Maximum HP",1)
+ MaxHPButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ FeedbackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, FeedbackPress)
+ AutoPauseButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, AutoPausePress)
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ GamePlayWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def DelayPress():
+ TextAreaControl.SetText(18017)
+ GemRB.SetTooltipDelay (GemRB.GetVar ("Tooltips") )
+ return
+
+def KeySpdPress():
+ TextAreaControl.SetText(18019)
+ return
+
+def MouseSpdPress():
+ TextAreaControl.SetText(18018)
+ GemRB.SetMouseScrollSpeed (GemRB.GetVar ("Mouse Scroll Speed") )
+ return
+
+def DifficultyPress():
+ TextAreaControl.SetText(18020)
+ return
+
+def BloodPress():
+ TextAreaControl.SetText(18023)
+ return
+
+def DitherPress():
+ TextAreaControl.SetText(18021)
+ return
+
+def InfravisionPress():
+ TextAreaControl.SetText(11797)
+ return
+
+def WeatherPress():
+ TextAreaControl.SetText(20619)
+ return
+
+def RestUntilHealPress():
+ TextAreaControl.SetText(2242)
+ return
+
+def MaxHPPress():
+ TextAreaControl.SetText(15136)
+ return
+
+def MaxHPPressB():
+ #TODO: TextAreaControl.SetText(15136)
+ return
+
+def FeedbackPress():
+ GamePlayWindow.SetVisible(WINDOW_INVISIBLE)
+ if GamePlayWindow:
+ GamePlayWindow.Unload()
+ GemRB.SetNextScript("Feedback")
+ return
+
+def AutoPausePress():
+ GamePlayWindow.SetVisible(WINDOW_INVISIBLE)
+ if GamePlayWindow:
+ GamePlayWindow.Unload()
+ GemRB.SetNextScript("AutoPause")
+ return
+
+def OkPress():
+ GamePlayWindow.SetVisible(WINDOW_INVISIBLE)
+ if GamePlayWindow:
+ GamePlayWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
+
+def CancelPress():
+ GamePlayWindow.SetVisible(WINDOW_INVISIBLE)
+ if GamePlayWindow:
+ GamePlayWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
diff --git a/gemrb/GUIScripts/iwd2/Gender.py b/gemrb/GUIScripts/iwd2/Gender.py
new file mode 100644
index 0000000..e03b054
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Gender.py
@@ -0,0 +1,114 @@
+# 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.
+#
+#
+#character generation, gender (GUICG1)
+import GemRB
+from GUIDefines import *
+
+GenderWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+
+def OnLoad():
+ global GenderWindow, TextAreaControl, DoneButton
+
+ GemRB.LoadWindowPack("GUICG", 800, 600)
+ #this hack will redraw the base CG window
+ GenderWindow = GemRB.LoadWindow(0)
+ GenderWindow.SetFrame( )
+ PortraitButton = GenderWindow.GetControl(12)
+ PortraitButton.SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ ImportButton = GenderWindow.GetControl(13)
+ ImportButton.SetText(13955)
+ ImportButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = GenderWindow.GetControl(15)
+ CancelButton.SetText(13727)
+ CancelButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ BiographyButton = GenderWindow.GetControl(16)
+ BiographyButton.SetText(18003)
+ BiographyButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ GenderWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.DrawWindows()
+ if GenderWindow:
+ GenderWindow.Unload()
+ GenderWindow = GemRB.LoadWindow(1)
+
+ BackButton = GenderWindow.GetControl(6)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = GenderWindow.GetControl(0)
+ DoneButton.SetText(36789)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = GenderWindow.GetControl(5)
+ TextAreaControl.SetText(17236)
+
+ MaleButton = GenderWindow.GetControl(7)
+ MaleButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClickedMale)
+ MaleButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ FemaleButton = GenderWindow.GetControl(8)
+ FemaleButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClickedFemale)
+ FemaleButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ MaleButton.SetVarAssoc("Gender",1)
+ FemaleButton.SetVarAssoc("Gender",2)
+
+
+ MaleButton = GenderWindow.GetControl(2)
+ MaleButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ MaleButton.SetText(1050)
+
+ FemaleButton = GenderWindow.GetControl(3)
+ FemaleButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ FemaleButton.SetText(1051)
+
+ MaleButton.SetVarAssoc("Gender",1)
+ FemaleButton.SetVarAssoc("Gender",2)
+ MaleButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClickedMale)
+ FemaleButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ClickedFemale)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ GenderWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ClickedMale():
+ TextAreaControl.SetText(13083)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def ClickedFemale():
+ TextAreaControl.SetText(13084)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
+
+def BackPress():
+ if GenderWindow:
+ GenderWindow.Unload()
+ GemRB.SetNextScript("CharGen")
+ GemRB.SetVar("Gender",0) #scrapping the gender value
+ return
+
+def NextPress():
+ if GenderWindow:
+ GenderWindow.Unload()
+ GemRB.SetNextScript("Portrait") #appearance
+ return
diff --git a/gemrb/GUIScripts/iwd2/Graphics.py b/gemrb/GUIScripts/iwd2/Graphics.py
new file mode 100644
index 0000000..b1fc93b
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Graphics.py
@@ -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.
+#
+#
+#Graphics Menu
+import GemRB
+from GUIDefines import *
+
+GraphicsWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global GraphicsWindow, TextAreaControl
+ GemRB.LoadWindowPack("GUIOPT", 800, 600)
+ GraphicsWindow = GemRB.LoadWindow(6)
+ GraphicsWindow.SetFrame( )
+
+ TextAreaControl = GraphicsWindow.GetControl(33)
+ BrightnessButton = GraphicsWindow.GetControl(35)
+ BrightnessSlider = GraphicsWindow.GetControl(3)
+ BrightnessSlider.SetVarAssoc("Brightness Correction",0)
+
+ ContrastButton = GraphicsWindow.GetControl(36)
+ ContrastSlider = GraphicsWindow.GetControl(22)
+ ContrastSlider.SetVarAssoc("Gamma Correction",1)
+
+ BppButton = GraphicsWindow.GetControl(37)
+ BppButtonB1 = GraphicsWindow.GetControl(5)
+ BppButtonB2 = GraphicsWindow.GetControl(6)
+ BppButtonB3 = GraphicsWindow.GetControl(7)
+ BppButtonB1.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ BppButtonB2.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ BppButtonB3.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ BppButtonB1.SetVarAssoc("BitsPerPixel",16)
+ BppButtonB2.SetVarAssoc("BitsPerPixel",24)
+ BppButtonB3.SetVarAssoc("BitsPerPixel",32)
+ BppButtonB1.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+ BppButtonB2.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+ BppButtonB3.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+ FullScreenButton = GraphicsWindow.GetControl(38)
+ FullScreenButtonB = GraphicsWindow.GetControl(9)
+ FullScreenButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ FullScreenButtonB.SetVarAssoc("Full Screen",1)
+ FullScreenButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ SoftMirrBltButton = GraphicsWindow.GetControl(44)
+ SoftMirrBltButtonB = GraphicsWindow.GetControl(40)
+ SoftMirrBltButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ SoftMirrBltButtonB.SetVarAssoc("SoftMirrorBlt",1)
+ SoftMirrBltButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ SoftTransBltButton = GraphicsWindow.GetControl(46)
+ SoftTransBltButtonB = GraphicsWindow.GetControl(41)
+ SoftTransBltButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ SoftTransBltButtonB.SetVarAssoc("SoftSrcKeyBlt",1)
+ SoftTransBltButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ SoftStandBltButton = GraphicsWindow.GetControl(48)
+ SoftStandBltButtonB = GraphicsWindow.GetControl(42)
+ SoftStandBltButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ SoftStandBltButtonB.SetVarAssoc("SoftBltFast",1)
+ SoftStandBltButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ TransShadowButton = GraphicsWindow.GetControl(50)
+ TransShadowButtonB = GraphicsWindow.GetControl(51)
+ TransShadowButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ TransShadowButtonB.SetVarAssoc("Translucent Shadows",1)
+ TransShadowButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ TransBlitButton = GraphicsWindow.GetControl(52)
+ TransBlitButtonB = GraphicsWindow.GetControl(56)
+ TransBlitButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ TransBlitButtonB.SetVarAssoc("Translucent Blts",1)
+ TransBlitButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ StaticAniButton = GraphicsWindow.GetControl(54)
+ StaticAniButtonB = GraphicsWindow.GetControl(57)
+ StaticAniButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ StaticAniButtonB.SetVarAssoc("Static Animations",1)
+ StaticAniButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ OkButton = GraphicsWindow.GetControl(21)
+ CancelButton = GraphicsWindow.GetControl(32)
+ TextAreaControl.SetText(18038)
+ OkButton.SetText(11973)
+ CancelButton.SetText(13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ BrightnessButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BrightnessPress)
+ BrightnessSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, BrightnessPress)
+ ContrastButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ContrastPress)
+ ContrastSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, ContrastPress)
+ BppButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BppPress)
+ BppButtonB1.SetEvent(IE_GUI_BUTTON_ON_PRESS, BppPress)
+ BppButtonB2.SetEvent(IE_GUI_BUTTON_ON_PRESS, BppPress)
+ BppButtonB3.SetEvent(IE_GUI_BUTTON_ON_PRESS, BppPress)
+ FullScreenButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, FullScreenPress)
+ FullScreenButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, FullScreenPress)
+ TransShadowButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, TransShadowPress)
+ TransShadowButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, TransShadowPress)
+ SoftMirrBltButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftMirrBltPress)
+ SoftMirrBltButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftMirrBltPress)
+ SoftTransBltButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftTransBltPress)
+ SoftTransBltButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftTransBltPress)
+ SoftStandBltButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftStandBltPress)
+ SoftStandBltButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoftStandBltPress)
+ TransBlitButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, TransBlitPress)
+ TransBlitButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, TransBlitPress)
+ StaticAniButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, StaticAniPress)
+ StaticAniButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, StaticAniPress)
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+
+ GraphicsWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def BrightnessPress():
+ TextAreaControl.SetText(17203)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction")/2)
+ return
+
+def ContrastPress():
+ TextAreaControl.SetText(17204)
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction"),GemRB.GetVar("Gamma Correction")/2)
+ return
+
+def BppPress():
+ TextAreaControl.SetText(17205)
+ return
+
+def FullScreenPress():
+ TextAreaControl.SetText(18000)
+ GemRB.SetFullScreen (GemRB.GetVar("Full Screen"))
+ return
+
+def TransShadowPress():
+ TextAreaControl.SetText(20620)
+ return
+
+def SoftMirrBltPress():
+ TextAreaControl.SetText(18004)
+ return
+
+def SoftTransBltPress():
+ TextAreaControl.SetText(18006)
+ return
+
+def SoftStandBltPress():
+ TextAreaControl.SetText(18007)
+ return
+
+def TransBlitPress():
+ TextAreaControl.SetText(15141)
+ return
+
+def StaticAniPress():
+ TextAreaControl.SetText(18004)
+ return
+
+def OkPress():
+ if GraphicsWindow:
+ GraphicsWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
+
+def CancelPress():
+ if GraphicsWindow:
+ GraphicsWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
diff --git a/gemrb/GUIScripts/iwd2/ImportFile.py b/gemrb/GUIScripts/iwd2/ImportFile.py
new file mode 100644
index 0000000..987ea70
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/ImportFile.py
@@ -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.
+#
+#
+#character generation, import (GUICG20)
+import GemRB
+from GUIDefines import *
+
+#import from a character sheet
+MainWindow = 0
+PortraitButton = 0
+ImportWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+
+def OnLoad():
+ global MainWindow, PortraitButton
+ global ImportWindow, TextAreaControl, DoneButton
+
+ GemRB.LoadWindowPack("GUICG", 800, 600)
+ MainWindow = GemRB.LoadWindow(0)
+ MainWindow.SetFrame()
+
+ PortraitButton = MainWindow.GetControl (12)
+ PortraitButton.SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+
+ ImportWindow = GemRB.LoadWindow(20)
+
+ TextAreaControl = ImportWindow.GetControl(4)
+ TextAreaControl.SetText(10963)
+
+ TextAreaControl = ImportWindow.GetControl(2)
+ TextAreaControl.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ TextAreaControl.GetCharacters()
+
+ DoneButton = ImportWindow.GetControl(0)
+ DoneButton.SetText(36789)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ CancelButton = ImportWindow.GetControl(1)
+ CancelButton.SetText(15416)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # disable the three extraneous buttons in the bottom row
+ for i in [16, 13, 15]:
+ TmpButton = MainWindow.GetControl(i)
+ TmpButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ TextAreaControl.SetEvent(IE_GUI_TEXTAREA_ON_CHANGE, SelectFile)
+ MainWindow.SetVisible(WINDOW_VISIBLE)
+ ImportWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def DonePress():
+ if ImportWindow:
+ ImportWindow.Unload()
+ if MainWindow:
+ MainWindow.Unload()
+ #this part is fuzzy
+ #we don't have the character as an object in the chargen
+ #but we just imported a complete object
+ #either we take the important stats and destroy the object
+ #or start with an object from the beginning
+ #or use a different script here???
+ GemRB.SetNextScript("CharGen7")
+ return
+
+def CancelPress():
+ if ImportWindow:
+ ImportWindow.Unload()
+ if MainWindow:
+ MainWindow.Unload()
+ GemRB.SetNextScript(GemRB.GetToken("NextScript"))
+ return
+
+def SelectFile():
+ FileName = TextAreaControl.QueryText()
+ Slot = GemRB.GetVar("Slot")
+ GemRB.CreatePlayer(FileName, Slot| 0x8000, 1)
+ Portrait = GemRB.GetPlayerPortrait (Slot,0)
+ PortraitButton.SetPicture (Portrait, "NOPORTLG")
+ ImportWindow.SetVisible(WINDOW_FRONT) #bring it to the front
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
diff --git a/gemrb/GUIScripts/iwd2/LoadScreen.py b/gemrb/GUIScripts/iwd2/LoadScreen.py
new file mode 100644
index 0000000..04dd038
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/LoadScreen.py
@@ -0,0 +1,72 @@
+# -*-python-*-
+# 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.
+#
+
+# LoadScreen.py - display Loading screen
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+
+LoadScreen = None
+Picture = None
+
+def SetLoadScreen ():
+ Table = GemRB.LoadTable ("areaload")
+ Area = GemRB.GetGameString (STR_AREANAME)
+ LoadPic = Table.GetValue (Area, Table.GetColumnName(0) )
+ if LoadPic!="*":
+ Picture.SetPicture(LoadPic)
+ return
+
+def StartLoadScreen ():
+ global LoadScreen, Picture
+
+ GemRB.LoadWindowPack ("guils", 800, 600)
+ LoadScreen = GemRB.LoadWindow (0)
+ LoadScreen.SetFrame( )
+
+ LoadPic = GemRB.GetGameString (STR_LOADMOS)
+ if LoadPic=="":
+ LoadPic = "GUILS0"+str(GemRB.Roll(1,9,0))
+ LoadScreen.SetPicture(LoadPic)
+ Progress = 0
+ GemRB.SetVar ("Progress", Progress)
+
+ Table = GemRB.LoadTable ("loadhint")
+ tmp = Table.GetRowCount ()
+ tmp = GemRB.Roll (1,tmp,0)
+ HintStr = Table.GetValue (tmp, 0)
+ TextArea = LoadScreen.GetControl (2)
+ TextArea.SetText (HintStr)
+
+ Picture = LoadScreen.GetControl (4)
+
+ Bar = LoadScreen.GetControl (0)
+ Bar.SetVarAssoc ("Progress", Progress)
+ Bar.SetEvent (IE_GUI_PROGRESS_END_REACHED, EndLoadScreen)
+ LoadScreen.SetVisible (WINDOW_VISIBLE)
+ return
+
+def EndLoadScreen ():
+ Skull = LoadScreen.GetControl (3)
+ Skull.SetMOS ("GTRBPSK2")
+ LoadScreen.SetVisible (WINDOW_VISIBLE)
+ LoadScreen.Unload()
+ return
diff --git a/gemrb/GUIScripts/iwd2/Makefile.am b/gemrb/GUIScripts/iwd2/Makefile.am
new file mode 100644
index 0000000..c44c46e
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Makefile.am
@@ -0,0 +1,4 @@
+iwd2script_DATA = *.py
+iwd2scriptdir = $(moddir)/GUIScripts/iwd2/
+EXTRA_DIST = *.py
+MOSTLYCLEANFILES = *.pyc
diff --git a/gemrb/GUIScripts/iwd2/MessageWindow.py b/gemrb/GUIScripts/iwd2/MessageWindow.py
new file mode 100644
index 0000000..b67a95e
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/MessageWindow.py
@@ -0,0 +1,124 @@
+# -*-python-*-
+# 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.
+#
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+import GUIClasses
+from GUIDefines import *
+
+MessageWindow = 0
+PortraitWindow = 0
+TMessageTA = 0 # for dialog code
+
+def OnLoad():
+ global MessageWindow, PortraitWindow
+
+ GemRB.GameSetPartySize(PARTY_SIZE)
+ GemRB.GameSetProtagonistMode(2)
+ GemRB.SetDefaultActions(1,14,16,17)
+ GemRB.LoadWindowPack(GUICommon.GetWindowPack())
+ OptionsWindow = MessageWindow = GemRB.LoadWindow(0)
+ ActionsWindow = PortraitWindow = GUICommonWindows.OpenPortraitWindow()
+
+ GemRB.SetVar ("MessageWindow", MessageWindow.ID)
+ GemRB.SetVar ("PortraitWindow", PortraitWindow.ID)
+ GemRB.SetVar ("TopWindow", -1)
+ GemRB.SetVar ("OtherWindow", -1)
+ GemRB.SetVar ("FloatWindow", -1)
+ GemRB.SetVar ("ActionsPosition", -1) #already handled in portraitwindow
+ GemRB.SetVar ("OptionsPosition", -1) #already handled in messagewindow
+ GemRB.SetVar ("MessagePosition", 4)
+ GemRB.SetVar ("PortraitPosition", 4)
+ GemRB.SetVar ("OtherPosition", 5) #Inactivating
+ GemRB.SetVar ("TopPosition", 5) #Inactivating
+
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow, 1, None)
+
+ MessageWindow.SetVisible(WINDOW_VISIBLE)
+ PortraitWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def UpdateControlStatus():
+ global MessageWindow,TMessageTA
+
+ TMessageWindow = 0
+ TMessageTA = 0
+ GSFlags = GemRB.GetMessageWindowSize()
+ Expand = GSFlags&GS_DIALOGMASK
+ Override = GSFlags&GS_DIALOG
+ GSFlags = GSFlags-Expand
+
+ #a dialogue is running, setting messagewindow size to maximum
+ if Override:
+ Expand = GS_LARGEDIALOG
+
+ MessageWindow = GemRB.GetVar ("MessageWindow")
+
+ GemRB.LoadWindowPack(GUICommon.GetWindowPack())
+ hideflag = GemRB.HideGUI()
+ if Expand == GS_LARGEDIALOG:
+ GemRB.SetVar ("PortraitWindow", -1)
+ TMessageWindow = GemRB.LoadWindow(7)
+ TMessageTA = TMessageWindow.GetControl (1)
+ else:
+ GemRB.SetVar ("PortraitWindow", PortraitWindow.ID)
+ TMessageWindow = GemRB.LoadWindow(0)
+ TMessageTA = TMessageWindow.GetControl (1)
+ GUICommonWindows.SetupMenuWindowControls (TMessageWindow, 1, None)
+
+
+ TMessageTA.SetFlags(IE_GUI_TEXTAREA_AUTOSCROLL)
+ TMessageTA.SetHistory(100)
+
+ MessageTA = GUIClasses.GTextArea(MessageWindow, GemRB.GetVar ("MessageTextArea"))
+ if MessageWindow>0 and MessageWindow!=TMessageWindow.ID:
+ MessageTA.MoveText (TMessageTA)
+ GUIClasses.GWindow(MessageWindow).Unload()
+ GemRB.SetVar ("MessageWindow", TMessageWindow.ID)
+ GemRB.SetVar ("MessageTextArea", TMessageTA.ID)
+
+ if Override:
+ TMessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ #gets PC currently talking
+ pc = GemRB.GameGetSelectedPCSingle (1)
+ if pc:
+ Portrait = GemRB.GetPlayerPortrait(pc,1)
+ else:
+ Portrait = None
+ Button = TMessageWindow.GetControl(11)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ if not Portrait:
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ else:
+ Button.SetPicture(Portrait, "NOPORTSM")
+ else:
+ GUICommon.GameControl.SetStatus(IE_GUI_CONTROL_FOCUSED)
+
+ if hideflag:
+ GemRB.UnhideGUI()
+ return
+
+#upgrade savegame to next version
+def GameExpansion():
+ #the original savegames got 0, but the engine upgrades all saves to 3
+ #this is a good place to perform one-time adjustments if needed
+ GemRB.GameSetExpansion(3)
+ return
+
diff --git a/gemrb/GUIScripts/iwd2/Movies.py b/gemrb/GUIScripts/iwd2/Movies.py
new file mode 100644
index 0000000..3fd337d
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Movies.py
@@ -0,0 +1,79 @@
+# 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.
+#
+#
+import GemRB
+from GUIDefines import *
+
+MovieWindow = 0
+TextAreaControl = 0
+MoviesTable = 0
+
+def OnLoad():
+ global MovieWindow, TextAreaControl, MoviesTable
+
+ GemRB.LoadWindowPack("GUIMOVIE", 800, 600)
+ MovieWindow = GemRB.LoadWindow(2)
+ MovieWindow.SetFrame ()
+ TextAreaControl = MovieWindow.GetControl(0)
+ TextAreaControl.SetFlags(IE_GUI_TEXTAREA_SELECTABLE)
+ PlayButton = MovieWindow.GetControl(2)
+ CreditsButton = MovieWindow.GetControl(3)
+ DoneButton = MovieWindow.GetControl(4)
+ MoviesTable = GemRB.LoadTable("MOVIDESC")
+ for i in range(0, MoviesTable.GetRowCount() ):
+ t = MoviesTable.GetRowName(i)
+ if GemRB.GetVar(t)==1:
+ s = MoviesTable.GetValue(i, 0)
+ TextAreaControl.Append(s,-1)
+ TextAreaControl.SetVarAssoc("MovieIndex",0)
+ PlayButton.SetText(17318)
+ CreditsButton.SetText(15591)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PlayButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ CreditsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CreditsPress)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+
+ MovieWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def PlayPress():
+ s = GemRB.GetVar("MovieIndex")
+ for i in range(0, MoviesTable.GetRowCount() ):
+ t = MoviesTable.GetRowName(i)
+ if GemRB.GetVar(t)==1:
+ if s==0:
+ s = MoviesTable.GetRowName(i)
+ GemRB.PlayMovie(s, 1)
+ MovieWindow.Invalidate()
+ return
+ s = s - 1
+ return
+
+def CreditsPress():
+ if MovieWindow:
+ MovieWindow.Unload()
+ GemRB.SetNextScript("Songs")
+ return
+
+def DonePress():
+ if MovieWindow:
+ MovieWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
diff --git a/gemrb/GUIScripts/iwd2/Name.py b/gemrb/GUIScripts/iwd2/Name.py
new file mode 100644
index 0000000..4cd33fa
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Name.py
@@ -0,0 +1,73 @@
+# 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.
+#
+#
+#character generation, name (GUICG5)
+import GemRB
+from GUIDefines import *
+
+NameWindow = 0
+NameField = 0
+DoneButton = 0
+
+def OnLoad():
+ global NameWindow, NameField, DoneButton
+
+ GemRB.LoadWindowPack("GUICG", 800, 600)
+ NameWindow = GemRB.LoadWindow(5)
+
+ BackButton = NameWindow.GetControl(3)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = NameWindow.GetControl(0)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+
+ NameField = NameWindow.GetControl(2)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ NameField.SetEvent(IE_GUI_EDIT_ON_CHANGE, EditChange)
+ NameWindow.SetVisible(WINDOW_VISIBLE)
+ NameField.SetStatus(IE_GUI_CONTROL_FOCUSED)
+ return
+
+def BackPress():
+ if NameWindow:
+ NameWindow.Unload()
+ GemRB.SetNextScript("CharGen8")
+ return
+
+def NextPress():
+ Name = NameField.QueryText()
+ #check length?
+ #seems like a good idea to store it here for the time being
+ GemRB.SetToken("CHARNAME",Name)
+ if NameWindow:
+ NameWindow.Unload()
+ GemRB.SetNextScript("CharGen9")
+ return
+
+def EditChange():
+ Name = NameField.QueryText()
+ if len(Name)==0:
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ else:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ return
diff --git a/gemrb/GUIScripts/iwd2/Options.py b/gemrb/GUIScripts/iwd2/Options.py
new file mode 100644
index 0000000..ccf7653
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Options.py
@@ -0,0 +1,112 @@
+# -*-python-*-
+# 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.
+#
+
+# Options.py - scripts to control options windows mostly from GUIOPT winpack
+
+import GemRB
+from GUIDefines import *
+
+OptionsWindow = 0
+
+def OnLoad():
+ global OptionsWindow
+ GemRB.LoadWindowPack("GUIOPT", 800, 600)
+
+ MessageBarWindow = GemRB.LoadWindow(0)
+ MessageBarWindow.SetVisible(WINDOW_VISIBLE) #This will startup the window as grayed
+
+ CharactersBarWindow = GemRB.LoadWindow(1)
+ CharactersBarWindow.SetVisible(WINDOW_VISIBLE)
+
+ GemRB.DrawWindows()
+
+ MessageBarWindow.SetVisible(WINDOW_INVISIBLE)
+ CharactersBarWindow.SetVisible(WINDOW_INVISIBLE)
+
+ if MessageBarWindow:
+ MessageBarWindow.Unload()
+ if CharactersBarWindow:
+ CharactersBarWindow.Unload()
+
+ OptionsWindow = GemRB.LoadWindow(13)
+ OptionsWindow.SetFrame ()
+
+ VersionLabel = OptionsWindow.GetControl(0x1000000B)
+ VersionLabel.SetText(GEMRB_VERSION)
+
+ GraphicsButton = OptionsWindow.GetControl(7)
+ SoundButton = OptionsWindow.GetControl(8)
+ GamePlayButton = OptionsWindow.GetControl(9)
+ MoviesButton = OptionsWindow.GetControl(14)
+ KeyboardButton = OptionsWindow.GetControl(13)
+ ReturnButton = OptionsWindow.GetControl(11)
+
+ GraphicsButton.SetText(17162)
+ GraphicsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, GraphicsPress)
+ SoundButton.SetText(17164)
+ SoundButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoundPress)
+ GamePlayButton.SetText(17165)
+ GamePlayButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, GamePlayPress)
+ MoviesButton.SetText(15415)
+ MoviesButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MoviePress)
+ KeyboardButton.SetText(33468)
+ KeyboardButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, None) #TODO: KeyboardPress
+
+ ReturnButton.SetText(10308)
+ ReturnButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ReturnPress)
+ ReturnButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ OptionsWindow.SetVisible(WINDOW_VISIBLE)
+
+ return
+
+def ReturnPress():
+ global OptionsWindow
+ if OptionsWindow:
+ OptionsWindow.Unload()
+ GemRB.SetNextScript("Start")
+ return
+
+def GraphicsPress():
+ global OptionsWindow
+ if OptionsWindow:
+ OptionsWindow.Unload()
+ GemRB.SetNextScript("Graphics")
+ return
+
+def SoundPress():
+ global OptionsWindow
+ if OptionsWindow:
+ OptionsWindow.Unload()
+ GemRB.SetNextScript("Sound")
+ return
+
+def GamePlayPress():
+ global OptionsWindow
+ if OptionsWindow:
+ OptionsWindow.Unload()
+ GemRB.SetNextScript("GamePlay")
+ return
+
+def MoviePress():
+ global OptionsWindow
+ if OptionsWindow:
+ OptionsWindow.Unload()
+ GemRB.SetNextScript("Movies")
+ return
diff --git a/gemrb/GUIScripts/iwd2/Portrait.py b/gemrb/GUIScripts/iwd2/Portrait.py
new file mode 100644
index 0000000..db1245d
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Portrait.py
@@ -0,0 +1,225 @@
+# 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.
+#
+#
+#character generation, appearance (GUICG12)
+import GemRB
+from GUIDefines import *
+
+AppearanceWindow = 0
+PortraitButton = 0
+PortraitsTable = 0
+LastPortrait = 0
+Gender = 0
+
+def SetPicture ():
+ global PortraitsTable, LastPortrait
+
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"L"
+ PortraitButton.SetPicture (PortraitName)
+ return
+
+def OnLoad ():
+ global AppearanceWindow, PortraitButton, PortraitsTable, LastPortrait
+ global Gender
+
+ Gender=GemRB.GetVar ("Gender")
+
+ GemRB.LoadWindowPack ("GUICG", 800, 600)
+ AppearanceWindow = GemRB.LoadWindow (11)
+ #AppearanceWindow.SetFrame ()
+
+ #Load the Portraits Table
+ PortraitsTable = GemRB.LoadTable ("PICTURES")
+ LastPortrait = 0
+
+ PortraitButton = AppearanceWindow.GetControl (1)
+ PortraitButton.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ PortraitButton.SetState (IE_GUI_BUTTON_LOCKED)
+
+ LeftButton = AppearanceWindow.GetControl (2)
+ RightButton = AppearanceWindow.GetControl (3)
+
+ BackButton = AppearanceWindow.GetControl (5)
+ BackButton.SetText (15416)
+ BackButton.SetFlags (IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ CustomButton = AppearanceWindow.GetControl (6)
+ CustomButton.SetText (17545)
+
+ DoneButton = AppearanceWindow.GetControl (0)
+ DoneButton.SetText (36789)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ RightButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, RightPress)
+ LeftButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, LeftPress)
+ BackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BackPress)
+ CustomButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomPress)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, NextPress)
+
+ while True:
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ SetPicture ()
+ break
+ LastPortrait = LastPortrait + 1
+ AppearanceWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def RightPress ():
+ global LastPortrait
+ while True:
+ LastPortrait = LastPortrait + 1
+ if LastPortrait >= PortraitsTable.GetRowCount ():
+ LastPortrait = 0
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ SetPicture ()
+ return
+
+def LeftPress ():
+ global LastPortrait
+ while True:
+ LastPortrait = LastPortrait - 1
+ if LastPortrait < 0:
+ LastPortrait = PortraitsTable.GetRowCount ()-1
+ if PortraitsTable.GetValue (LastPortrait, 0) == Gender:
+ SetPicture ()
+ return
+
+def BackPress ():
+ if AppearanceWindow:
+ AppearanceWindow.Unload ()
+ GemRB.SetNextScript ("CharGen")
+ GemRB.SetVar ("Gender",0) #scrapping the gender value
+ return
+
+def CustomDone ():
+ Window = CustomWindow
+
+ Portrait = PortraitList1.QueryText ()
+ GemRB.SetToken ("SmallPortrait", Portrait)
+ Portrait = PortraitList2.QueryText ()
+ GemRB.SetToken ("LargePortrait", Portrait)
+ if Window:
+ Window.Unload ()
+ if AppearanceWindow:
+ AppearanceWindow.Unload ()
+ GemRB.SetNextScript ("CharGen2")
+ return
+
+def CustomAbort ():
+ if CustomWindow:
+ CustomWindow.Unload ()
+ return
+
+def LargeCustomPortrait ():
+ Window = CustomWindow
+
+ Portrait = PortraitList1.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row1") == RowCount1:
+ return
+
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (6)
+ if Portrait=="":
+ Portrait = "NOPORTMD"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList2.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (0)
+ Button.SetPicture (Portrait, "NOPORTMD")
+ return
+
+def SmallCustomPortrait ():
+ Window = CustomWindow
+
+ Portrait = PortraitList2.QueryText ()
+ #small hack
+ if GemRB.GetVar ("Row2") == RowCount2:
+ return
+
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (Portrait)
+
+ Button = Window.GetControl (6)
+ if Portrait=="":
+ Portrait = "NOPORTSM"
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ if PortraitList1.QueryText ()!="":
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ Button = Window.GetControl (1)
+ Button.SetPicture (Portrait, "NOPORTSM")
+ return
+
+def CustomPress ():
+ global PortraitList1, PortraitList2
+ global RowCount1, RowCount2
+ global CustomWindow
+
+ CustomWindow = Window = GemRB.LoadWindow (18)
+ PortraitList1 = Window.GetControl (2)
+ RowCount1 = PortraitList1.GetPortraits (0)
+ PortraitList1.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, LargeCustomPortrait)
+ GemRB.SetVar ("Row1", RowCount1)
+ PortraitList1.SetVarAssoc ("Row1",RowCount1)
+
+ PortraitList2 = Window.GetControl (4)
+ RowCount2 = PortraitList2.GetPortraits (1)
+ PortraitList2.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, SmallCustomPortrait)
+ GemRB.SetVar ("Row2", RowCount2)
+ PortraitList2.SetVarAssoc ("Row2",RowCount2)
+
+ Button = Window.GetControl (6)
+ Button.SetText (11973)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomDone)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Button = Window.GetControl (7)
+ Button.SetText (15416)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL,OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CustomAbort)
+
+ Button = Window.GetControl (0)
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"L"
+ Button.SetPicture (PortraitName, "NOPORTMD")
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Button = Window.GetControl (1)
+ PortraitName = PortraitsTable.GetRowName (LastPortrait)+"S"
+ Button.SetPicture (PortraitName, "NOPORTSM")
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ Window.ShowModal (MODAL_SHADOW_NONE)
+ return
+
+def NextPress ():
+ if AppearanceWindow:
+ AppearanceWindow.Unload ()
+ PortraitTable = GemRB.LoadTable ("pictures")
+ PortraitName = PortraitTable.GetRowName (LastPortrait )
+ GemRB.SetToken ("SmallPortrait", PortraitName+"S")
+ GemRB.SetToken ("LargePortrait", PortraitName+"L")
+ #GemRB.SetVar ("PortraitIndex",LastPortrait)
+ GemRB.SetNextScript ("CharGen2") #Before race
+ return
diff --git a/gemrb/GUIScripts/iwd2/QuitGame.py b/gemrb/GUIScripts/iwd2/QuitGame.py
new file mode 100644
index 0000000..40616b1
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/QuitGame.py
@@ -0,0 +1,27 @@
+# -*-python-*-
+# 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.
+#
+
+# QuitGame.py - display EndGame sequence
+
+###################################################
+
+import GUIWORLD
+
+def OnLoad ():
+ GUIWORLD.DeathWindowEnd ()
diff --git a/gemrb/GUIScripts/iwd2/Race.py b/gemrb/GUIScripts/iwd2/Race.py
new file mode 100644
index 0000000..5cec35a
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Race.py
@@ -0,0 +1,94 @@
+# 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.
+#
+#
+#character generation, race (GUICG2)
+import GemRB
+from GUIDefines import *
+import CommonTables
+
+RaceWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+SubRacesTable = 0
+
+def OnLoad():
+ global RaceWindow, TextAreaControl, DoneButton
+ global SubRacesTable
+
+ GemRB.LoadWindowPack("GUICG", 800 ,600)
+ RaceWindow = GemRB.LoadWindow (8)
+
+ RaceCount = CommonTables.Races.GetRowCount ()
+
+ SubRacesTable = GemRB.LoadTable ("SUBRACES")
+
+ for i in range(2,9):
+ Button = RaceWindow.GetControl (i)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ for i in range(7):
+ Button = RaceWindow.GetControl (i+2)
+ Button.SetText (CommonTables.Races.GetValue (i,0) )
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RacePress)
+ Button.SetVarAssoc ("BaseRace", CommonTables.Races.GetValue (i, 3) )
+
+ BackButton = RaceWindow.GetControl (11)
+ BackButton.SetText (15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = RaceWindow.GetControl (0)
+ DoneButton.SetText (36789)
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = RaceWindow.GetControl (9)
+ TextAreaControl.SetText (17237)
+
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, BackPress)
+ RaceWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def RacePress():
+ global RaceWindow, SubRacesTable
+ Race = GemRB.GetVar ("BaseRace")
+ GemRB.SetVar ("Race", Race)
+ RaceID = CommonTables.Races.GetRowName (CommonTables.Races.FindValue (3, Race) )
+ HasSubRaces = SubRacesTable.GetValue (RaceID, "PURE_RACE")
+ if HasSubRaces == 0:
+ TextAreaControl.SetText (CommonTables.Races.GetValue (RaceID,"DESC_REF") )
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+ if RaceWindow:
+ RaceWindow.Unload ()
+ GemRB.SetNextScript ("SubRaces")
+ return
+
+def BackPress():
+ if RaceWindow:
+ RaceWindow.Unload ()
+ GemRB.SetNextScript ("CharGen2")
+ GemRB.SetVar ("BaseRace",0) #scrapping the race value
+ return
+
+def NextPress():
+ if RaceWindow:
+ RaceWindow.Unload ()
+ GemRB.SetNextScript ("CharGen3") #class
+ return
diff --git a/gemrb/GUIScripts/iwd2/SPParty.py b/gemrb/GUIScripts/iwd2/SPParty.py
new file mode 100644
index 0000000..a09fb40
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/SPParty.py
@@ -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.
+#
+#
+#Single Player Party Select
+import GemRB
+from GUIDefines import *
+
+PartySelectWindow = 0
+TextArea = 0
+PartyCount = 0
+ScrollBar = 0
+
+def OnLoad():
+ global PartySelectWindow, TextArea, PartyCount, ScrollBar
+ GemRB.LoadWindowPack("GUISP", 800, 600)
+
+ PartyCount = GemRB.GetINIPartyCount()
+
+ PartySelectWindow = GemRB.LoadWindow(10)
+ PartySelectWindow.SetFrame( )
+ TextArea = PartySelectWindow.GetControl(6)
+ ScrollBar = PartySelectWindow.GetControl(8)
+ ScrollBar.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ ScrollBar.SetVarAssoc("TopIndex", PartyCount)
+
+ ModifyButton = PartySelectWindow.GetControl(12)
+ ModifyButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ModifyPress)
+ ModifyButton.SetText(10316)
+
+ CancelButton = PartySelectWindow.GetControl(11)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetText(13727)
+ CancelButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = PartySelectWindow.GetControl(10)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ GemRB.SetVar("PartyIdx",0)
+ GemRB.SetVar("TopIndex",0)
+
+ for i in range(0,PARTY_SIZE):
+ Button = PartySelectWindow.GetControl(i)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, PartyButtonPress)
+
+ ScrollBarPress()
+ PartyButtonPress()
+
+ PartySelectWindow.SetVisible(WINDOW_VISIBLE)
+
+ return
+
+def ScrollBarPress():
+ global PartySelectWindow, PartyCount
+ Pos = GemRB.GetVar("TopIndex")
+ for i in range(0,PARTY_SIZE):
+ ActPos = Pos + i
+ Button = PartySelectWindow.GetControl(i)
+ Button.SetText("")
+ Button.SetVarAssoc("PartyIdx",-1)
+ if ActPos<PartyCount:
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+
+ for i in range(0,PARTY_SIZE):
+ ActPos = Pos + i
+ Button = PartySelectWindow.GetControl(i)
+ if ActPos<PartyCount:
+ Button.SetVarAssoc("PartyIdx",ActPos)
+ Tag = "Party " + str(ActPos)
+ PartyDesc = GemRB.GetINIPartyKey(Tag, "Name", "")
+ Button.SetText(PartyDesc)
+ return
+
+def ModifyPress():
+ Pos = GemRB.GetVar("PartyIdx")
+ if Pos == 0: # first entry - behaves same as pressing on done
+ if PartySelectWindow:
+ PartySelectWindow.Unload()
+ GemRB.LoadGame(None, 22)
+ GemRB.SetNextScript("SPPartyFormation")
+ #else: # here come the real modifications
+
+def DonePress():
+ global PartySelectWindow
+ Pos = GemRB.GetVar("PartyIdx")
+ if Pos == 0:
+ if PartySelectWindow:
+ PartySelectWindow.Unload()
+ GemRB.LoadGame(None, 22)
+ GemRB.SetNextScript("SPPartyFormation")
+ else:
+ if PartySelectWindow:
+ PartySelectWindow.Unload()
+ GemRB.LoadGame(None, 22)
+ #here we should load the party characters
+ #but gemrb engine limitations require us to
+ #return to the main engine (loadscreen)
+ GemRB.SetNextScript("SPParty2")
+ return
+
+def CancelPress():
+ global PartySelectWindow
+ if PartySelectWindow:
+ PartySelectWindow.Unload()
+ GemRB.SetNextScript("Start")
+ return
+
+def PartyButtonPress():
+ global PartySelectWindow, TextArea
+ i = GemRB.GetVar("PartyIdx")
+ Tag = "Party " + str(i)
+ PartyDesc = ""
+ for j in range(1, 9):
+ Key = "Descr" + str(j)
+ NewString = GemRB.GetINIPartyKey(Tag, Key, "")
+ if NewString != "":
+ NewString = NewString + "\n\n"
+ PartyDesc = PartyDesc + NewString
+
+ TextArea.SetText(PartyDesc)
+ return
+
+#loading characters from party.ini
+def LoadPartyCharacters():
+ i = GemRB.GetVar("PartyIdx")
+ Tag = "Party " + str(i)
+ for j in range(1, PARTY_SIZE+1):
+ Key = "Char"+str(j)
+ CharName = GemRB.GetINIPartyKey(Tag, Key, "")
+ print Tag, Key, CharName
+ if CharName !="":
+ GemRB.CreatePlayer(CharName, j, 1)
+ return
diff --git a/gemrb/GUIScripts/iwd2/SPParty2.py b/gemrb/GUIScripts/iwd2/SPParty2.py
new file mode 100644
index 0000000..02be81f
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/SPParty2.py
@@ -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.
+#
+#
+#Single Player Party Select
+import GemRB
+
+def OnLoad():
+ LoadPartyCharacters()
+ GemRB.SetNextScript("SPPartyFormation")
+ return
+
+#loading characters from party.ini
+def LoadPartyCharacters():
+ i = GemRB.GetVar("PartyIdx")
+ Tag = "Party " + str(i)
+ for j in range(1, PARTY_SIZE+1):
+ Key = "Char"+str(j)
+ CharName = GemRB.GetINIPartyKey(Tag, Key, "")
+ if CharName !="":
+ GemRB.CreatePlayer(CharName, j, 1)
+ return
diff --git a/gemrb/GUIScripts/iwd2/SPPartyFormation.py b/gemrb/GUIScripts/iwd2/SPPartyFormation.py
new file mode 100644
index 0000000..d5c79c8
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/SPPartyFormation.py
@@ -0,0 +1,137 @@
+# -*-python-*-
+# 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.
+#
+
+# Single Player Party Formation (GUISP)
+
+import GemRB
+from GUIDefines import *
+
+PartyFormationWindow = 0
+ExitWindow = 0
+
+def OnLoad ():
+ global PartyFormationWindow
+ GemRB.LoadWindowPack ("GUISP", 800, 600)
+
+ PartyFormationWindow = GemRB.LoadWindow (0)
+ PartyFormationWindow.SetFrame ( )
+ ExitButton = PartyFormationWindow.GetControl (30)
+ ExitButton.SetText (13906)
+ ExitButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitPress)
+ ExitButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ModifyCharactersButton = PartyFormationWindow.GetControl (43)
+ ModifyCharactersButton.SetText (18816)
+ ModifyCharactersButton.SetState (IE_GUI_BUTTON_DISABLED)
+ ModifyCharactersButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, None) #TODO: ModifyCharactersPress
+
+ DoneButton = PartyFormationWindow.GetControl (28)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ Portraits = 0
+
+ for i in range (18,24):
+ Label = PartyFormationWindow.GetControl (0x10000012+i)
+ #removing this label, it just disturbs us
+ Label.SetSize (0, 0)
+ Button = PartyFormationWindow.GetControl (i-12)
+ ResRef = GemRB.GetPlayerPortrait (i-17, 1)
+ if ResRef == "":
+ Button.SetFlags (IE_GUI_BUTTON_NORMAL,OP_SET)
+ else:
+ Button.SetPicture (ResRef)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Portraits = Portraits+1
+
+ Button.SetVarAssoc ("Slot",i-17)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GeneratePress)
+
+ Button = PartyFormationWindow.GetControl (i)
+ Button.SetVarAssoc ("Slot",i-17)
+ if ResRef == "":
+ Button.SetText (10264)
+ else:
+ Button.SetText (GemRB.GetPlayerName (i-17,0) )
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GeneratePress)
+
+ if Portraits == 0:
+ DoneButton.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ DoneButton.SetState (IE_GUI_BUTTON_ENABLED)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, EnterGamePress)
+
+ PartyFormationWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ExitPress ():
+ global PartyFormationWindow, ExitWindow
+ PartyFormationWindow.SetVisible (WINDOW_INVISIBLE)
+ ExitWindow = GemRB.LoadWindow (7)
+
+ TextArea = ExitWindow.GetControl (0)
+ TextArea.SetText (11329)
+
+ CancelButton = ExitWindow.GetControl (2)
+ CancelButton.SetText (13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitCancelPress)
+
+ DoneButton = ExitWindow.GetControl (1)
+ DoneButton.SetText (11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ DoneButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, ExitDonePress)
+
+ ExitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ExitDonePress ():
+ global PartyFormationWindow, ExitWindow
+ if ExitWindow:
+ ExitWindow.Unload ()
+ if PartyFormationWindow:
+ PartyFormationWindow.Unload ()
+ GemRB.SetNextScript ("Start")
+ return
+
+def ExitCancelPress ():
+ global PartyFormationWindow, ExitWindow
+ if ExitWindow:
+ ExitWindow.Unload ()
+ PartyFormationWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def GeneratePress ():
+ global PartyFormationWindow
+ slot = GemRB.GetVar ("Slot")
+ ResRef = GemRB.GetPlayerPortrait (slot, 0)
+ if ResRef:
+ print "Already existing slot, we should drop it"
+ if PartyFormationWindow:
+ PartyFormationWindow.Unload ()
+ GemRB.SetNextScript ("CharGen")
+ return
+
+def EnterGamePress ():
+ GemRB.SetVar ("SaveDir",1) #iwd2 is always using 'mpsave'
+ GemRB.EnterGame ()
+ return
+
diff --git a/gemrb/GUIScripts/iwd2/Skills.py b/gemrb/GUIScripts/iwd2/Skills.py
new file mode 100644
index 0000000..587e351
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Skills.py
@@ -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.
+#
+#
+#character generation, skills (GUICG6)
+import GemRB
+from GUIDefines import *
+import CommonTables
+
+SkillWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+SkillTable = 0
+CostTable = 0
+TopIndex = 0
+PointsLeft = 0
+Level = 0
+ClassColumn = 0
+
+def RedrawSkills():
+ global CostTable, TopIndex
+
+ SumLabel = SkillWindow.GetControl(0x1000000c)
+ if PointsLeft == 0:
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ SumLabel.SetTextColor(255, 255, 255)
+ else:
+ SumLabel.SetTextColor(255, 255, 0)
+ SumLabel.SetText(str(PointsLeft) )
+
+ for i in range(10):
+ Pos=TopIndex+i
+ Cost = CostTable.GetValue(Pos, ClassColumn)
+ # Skill cost is currently restricted to base classes. This means it is fairly easy
+ # to add a class kit as it will use its parents skill cost values however it makes
+ # it impossible to add a new class entirely. Support for whole new classes is
+ # probably not massively important but could eventually allow Prestige classes to
+ # be implemented
+ SkillName = SkillTable.GetValue(Pos, 1)
+ Label = SkillWindow.GetControl(0x10000001+i)
+ ActPoint = GemRB.GetVar("Skill "+str(Pos) )
+ if Cost>0:
+ #we use this function to retrieve the string
+ t=GemRB.StatComment(SkillName,0,0)
+ Label.SetText("%s (%d)"%(t,Cost) )
+ Label.SetTextColor(255, 255, 255)
+ if PointsLeft < 1 or ActPoint * Cost >= Level + 3:
+ Label.SetTextColor(150, 150, 150)
+ else:
+ Label.SetText(SkillName)
+ Label.SetTextColor(150, 150, 150) # Grey
+
+ Button1 = SkillWindow.GetControl(i*2+14)
+ Button2 = SkillWindow.GetControl(i*2+15)
+
+ Btn1State = Btn2State = IE_GUI_BUTTON_ENABLED
+ if Cost < 1:
+ Btn1State = Btn2State = IE_GUI_BUTTON_DISABLED
+ else:
+ if ActPoint * Cost >= Level + 3 or PointsLeft < 1:
+ Btn1State = IE_GUI_BUTTON_DISABLED
+ if ActPoint < 1:
+ Btn2State = IE_GUI_BUTTON_DISABLED
+ Button1.SetState(Btn1State)
+ Button2.SetState(Btn2State)
+
+ Label = SkillWindow.GetControl(0x10000069+i)
+ Label.SetText(str(ActPoint) )
+ if ActPoint>0:
+ Label.SetTextColor(0,255,255)
+ elif Cost < 1 or PointsLeft < 1:
+ Label.SetTextColor(150, 150, 150)
+ else:
+ Label.SetTextColor(255,255,255)
+
+ return
+
+def ScrollBarPress():
+ global TopIndex
+
+ TopIndex = GemRB.GetVar("TopIndex")
+ RedrawSkills()
+ return
+
+def OnLoad():
+ global SkillWindow, TextAreaControl, DoneButton, TopIndex
+ global SkillTable, CostTable, PointsLeft
+ global KitName, Level, ClassColumn
+
+ #enable repeated clicks
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_NAND)
+ GemRB.SetVar("Level",1) #for simplicity
+ Class = GemRB.GetVar("Class") - 1
+ KitName = CommonTables.Classes.GetRowName(Class)
+ #classcolumn is base class
+ ClassColumn=GemRB.GetVar("BaseClass") - 1
+ SkillPtsTable = GemRB.LoadTable("skillpts")
+ p = SkillPtsTable.GetValue(0, ClassColumn)
+ IntBonus = GemRB.GetVar("Ability 3")/2-5 #intelligence bonus
+ p = p + IntBonus
+ #at least 1 skillpoint / level advanced
+ if p <1:
+ p=1
+
+ Level = GemRB.GetVar("Level") #this is the level increase
+ PointsLeft = p
+
+ # Humans recieve +2 skill points at level 1 and +1 skill points each level thereafter
+ # Recommend creation of SKILRACE.2da with levels as rows and race names as columns
+
+ RaceName = CommonTables.Races.GetRowName(CommonTables.Races.FindValue(3, GemRB.GetVar('Race')))
+
+ ### Example code for implementation of SKILRACE.2da
+ # TmpTable = GemRB.LoadTable('skilrace')
+ # PointsLeft += TmpTable.GetValue(str(Level), RaceName)
+ ###
+
+ if Level < 2:
+ PointsLeft = p * 4 # 4-times skill points @ first level
+
+ ### Human skill bonus hack (ignores intelligence modifier):
+ if RaceName == 'HUMAN':
+ if Level < 2: PointsLeft += 2
+ else: PointsLeft += 1
+
+
+ SkillTable = GemRB.LoadTable("skills")
+ RowCount = SkillTable.GetRowCount()
+
+ CostTable = GemRB.LoadTable("skilcost")
+
+ SkillRacTable = GemRB.LoadTable("SKILLRAC")
+
+ for i in range(RowCount):
+ GemRB.SetVar("Skill "+str(i),0) # Racial/Class bonuses don't factor in char-gen or leveling
+ # so can be safely ignored
+
+ GemRB.SetToken("number",str(PointsLeft) )
+
+ GemRB.LoadWindowPack("GUICG", 800 ,600)
+ SkillWindow = GemRB.LoadWindow(6)
+
+ for i in range(10):
+ Button = SkillWindow.GetControl(i+93)
+ Button.SetVarAssoc("Skill",i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, JustPress)
+
+ Button = SkillWindow.GetControl(i*2+14)
+ Button.SetVarAssoc("Skill",i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, LeftPress)
+
+ Button = SkillWindow.GetControl(i*2+15)
+ Button.SetVarAssoc("Skill",i)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, RightPress)
+
+ BackButton = SkillWindow.GetControl(105)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = SkillWindow.GetControl(0)
+ DoneButton.SetText(36789)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = SkillWindow.GetControl(92)
+ TextAreaControl.SetText(17248)
+
+ ScrollBarControl = SkillWindow.GetControl(104)
+ ScrollBarControl.SetEvent(IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ ScrollBarControl.SetDefaultScrollBar ()
+ #decrease it with the number of controls on screen (list size)
+ TopIndex = 0
+ GemRB.SetVar("TopIndex",0)
+ ScrollBarControl.SetVarAssoc("TopIndex",RowCount-10+1)
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ DoneButton.SetState(IE_GUI_BUTTON_DISABLED)
+ RedrawSkills()
+ SkillWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+
+def JustPress():
+ Pos = GemRB.GetVar("Skill")+TopIndex
+ TextAreaControl.SetText(SkillTable.GetValue(Pos,2) )
+ return
+
+def RightPress():
+ global PointsLeft
+
+ Pos = GemRB.GetVar("Skill")+TopIndex
+ Cost = CostTable.GetValue(Pos, ClassColumn)
+ if Cost==0:
+ return
+
+ TextAreaControl.SetText(SkillTable.GetValue(Pos,2) )
+ ActPoint = GemRB.GetVar("Skill "+str(Pos) )
+ if ActPoint <= 0:
+ return
+ GemRB.SetVar("Skill "+str(Pos),ActPoint-1)
+ PointsLeft = PointsLeft + Cost
+ RedrawSkills()
+ return
+
+def LeftPress():
+ global PointsLeft
+
+ Pos = GemRB.GetVar("Skill")+TopIndex
+ Cost = CostTable.GetValue(Pos, ClassColumn)
+ if Cost==0:
+ return
+
+ TextAreaControl.SetText(SkillTable.GetValue(Pos,2) )
+ if PointsLeft < Cost:
+ return
+ ActPoint = GemRB.GetVar("Skill "+str(Pos) )
+ if Cost*ActPoint >= Level+3:
+ return
+ GemRB.SetVar("Skill "+str(Pos), ActPoint+1)
+ PointsLeft = PointsLeft - Cost
+ RedrawSkills()
+ return
+
+def BackPress():
+ MyChar = GemRB.GetVar("Slot")
+ TmpTable = GemRB.LoadTable ("skillsta")
+ for i in range(TmpTable.GetRowCount()):
+ GemRB.SetVar("Skill "+str(i),0)
+ StatID=TmpTable.GetValue (i, 2)
+ GemRB.SetPlayerStat (MyChar, StatID, 0)
+ if SkillWindow:
+ SkillWindow.Unload()
+ GemRB.SetNextScript("CharGen6")
+ return
+
+def NextPress():
+ MyChar = GemRB.GetVar("Slot")
+ #setting skills
+ TmpTable = GemRB.LoadTable ("skillsta")
+ SkillCount = TmpTable.GetRowCount ()
+ for i in range (SkillCount):
+ StatID=TmpTable.GetValue (i, 2)
+ GemRB.SetPlayerStat (MyChar, StatID, GemRB.GetVar ("Skill "+str(i) ) )
+ if SkillWindow:
+ SkillWindow.Unload()
+ GemRB.SetNextScript("Feats") #feats
+ return
diff --git a/gemrb/GUIScripts/iwd2/Songs.py b/gemrb/GUIScripts/iwd2/Songs.py
new file mode 100644
index 0000000..fd6689c
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Songs.py
@@ -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.
+#
+#
+#instead of credits, you can listen the songs of the game :)
+import GemRB
+from GUIDefines import *
+
+MovieWindow = 0
+TextAreaControl = 0
+MoviesTable = 0
+
+def OnLoad():
+ global MovieWindow, TextAreaControl, MoviesTable
+
+ GemRB.LoadWindowPack("GUIMOVIE", 800, 600)
+ MovieWindow = GemRB.LoadWindow(2)
+ MovieWindow.SetFrame ()
+ TextAreaControl = MovieWindow.GetControl(0)
+ TextAreaControl.SetFlags(IE_GUI_TEXTAREA_SELECTABLE)
+ PlayButton = MovieWindow.GetControl(2)
+ CreditsButton = MovieWindow.GetControl(3)
+ DoneButton = MovieWindow.GetControl(4)
+ MoviesTable = GemRB.LoadTable("MUSIC")
+ for i in range(0, MoviesTable.GetRowCount() ):
+ s = MoviesTable.GetRowName(i)
+ TextAreaControl.Append(s,-1)
+ TextAreaControl.SetVarAssoc("MovieIndex",0)
+ PlayButton.SetText(17318)
+ CreditsButton.SetText(15591)
+ DoneButton.SetText(11973)
+ DoneButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ PlayButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, PlayPress)
+ CreditsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CreditsPress)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DonePress)
+ MovieWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def PlayPress():
+ s = GemRB.GetVar("MovieIndex")
+ t = MoviesTable.GetValue(s, 0)
+ GemRB.LoadMusicPL(t,1)
+ return
+
+def CreditsPress():
+ GemRB.PlayMovie("CREDITS")
+ return
+
+def DonePress():
+ if MovieWindow:
+ MovieWindow.Unload()
+ GemRB.SetNextScript("Start")
+ return
diff --git a/gemrb/GUIScripts/iwd2/Sound.py b/gemrb/GUIScripts/iwd2/Sound.py
new file mode 100644
index 0000000..4dd0a6e
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Sound.py
@@ -0,0 +1,132 @@
+# 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.
+#
+#
+#sound options
+import GemRB
+from GUIDefines import *
+
+SoundWindow = 0
+TextAreaControl = 0
+
+def OnLoad():
+ global SoundWindow, TextAreaControl
+
+ GemRB.LoadWindowPack("GUIOPT", 800, 600)
+ SoundWindow = GemRB.LoadWindow(7)
+ SoundWindow.SetFrame( )
+
+ TextAreaControl = SoundWindow.GetControl(14)
+ AmbientButton = SoundWindow.GetControl(16)
+ AmbientSlider = SoundWindow.GetControl(1)
+ SoundEffectsButton = SoundWindow.GetControl(17)
+ SoundEffectsSlider = SoundWindow.GetControl(2)
+ DialogueButton = SoundWindow.GetControl(18)
+ DialogueSlider = SoundWindow.GetControl(3)
+ MusicButton = SoundWindow.GetControl(19)
+ MusicSlider = SoundWindow.GetControl(4)
+ MoviesButton = SoundWindow.GetControl(20)
+ MoviesSlider = SoundWindow.GetControl(22)
+ EnvironmentalButton = SoundWindow.GetControl(28)
+ EnvironmentalButtonB = SoundWindow.GetControl(26)
+ CharacterSoundButton = SoundWindow.GetControl(13)
+ OkButton = SoundWindow.GetControl(24)
+ CancelButton = SoundWindow.GetControl(25)
+ TextAreaControl.SetText(18040)
+ CharacterSoundButton.SetText(17778)
+
+ OkButton.SetText(11973)
+ OkButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton.SetText(13727)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ AmbientButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, AmbientPress)
+ AmbientSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, AmbientPress)
+ AmbientSlider.SetVarAssoc("Volume Ambients",10)
+
+ SoundEffectsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, SoundEffectsPress)
+ SoundEffectsSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, SoundEffectsPress)
+ SoundEffectsSlider.SetVarAssoc("Volume SFX",10)
+
+ DialogueButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, DialoguePress)
+ DialogueSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, DialoguePress)
+ DialogueSlider.SetVarAssoc("Volume Voices",10)
+
+ MusicButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MusicPress)
+ MusicSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, MusicPress)
+ MusicSlider.SetVarAssoc("Volume Music",10)
+
+ MoviesButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, MoviesPress)
+ MoviesSlider.SetEvent(IE_GUI_SLIDER_ON_CHANGE, MoviesPress)
+ MoviesSlider.SetVarAssoc("Volume Movie",10)
+
+ EnvironmentalButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, EnvironmentalPress)
+ EnvironmentalButtonB.SetEvent(IE_GUI_BUTTON_ON_PRESS, EnvironmentalPress)
+ EnvironmentalButtonB.SetFlags(IE_GUI_BUTTON_CHECKBOX,OP_OR)
+ EnvironmentalButtonB.SetVarAssoc("Environmental Audio",1)
+ EnvironmentalButtonB.SetSprites("GBTNOPT4", 0, 0, 1, 2, 3)
+
+ CharacterSoundButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CharacterSoundPress)
+ OkButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkPress)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ SoundWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def AmbientPress():
+ TextAreaControl.SetText(18008)
+ GemRB.UpdateAmbientsVolume ()
+ return
+
+def SoundEffectsPress():
+ TextAreaControl.SetText(18009)
+ return
+
+def DialoguePress():
+ TextAreaControl.SetText(18010)
+ return
+
+def MusicPress():
+ TextAreaControl.SetText(18011)
+ GemRB.UpdateMusicVolume ()
+ return
+
+def MoviesPress():
+ TextAreaControl.SetText(18012)
+ return
+
+def EnvironmentalPress():
+ TextAreaControl.SetText(18022)
+ return
+
+def CharacterSoundPress():
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("CharSound")
+ return
+
+def OkPress():
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
+
+def CancelPress():
+ if SoundWindow:
+ SoundWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
diff --git a/gemrb/GUIScripts/iwd2/Start.py b/gemrb/GUIScripts/iwd2/Start.py
new file mode 100644
index 0000000..faafcdc
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/Start.py
@@ -0,0 +1,217 @@
+# -*-python-*-
+# 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.
+#
+
+#
+import GemRB
+import LoadScreen
+from GUIDefines import *
+
+StartWindow = 0
+ProtocolWindow = 0
+QuitWindow = 0
+QuickLoadSlot = 0
+
+def OnLoad():
+ global StartWindow, QuickLoadSlot
+
+ screen_width = GemRB.GetSystemVariable (SV_WIDTH)
+ screen_height = GemRB.GetSystemVariable (SV_HEIGHT)
+ if screen_width == 1024:
+ GemRB.LoadWindowFrame("STON10L", "STON10R", "STON10T", "STON10B")
+ GemRB.LoadWindowPack("GUICONN", 800, 600)
+#main window
+ StartWindow = GemRB.LoadWindow(0)
+ StartWindow.SetFrame ()
+ ProtocolButton = StartWindow.GetControl(0x00)
+ NewGameButton = StartWindow.GetControl(0x02)
+ LoadGameButton = StartWindow.GetControl(0x07)
+ QuickLoadButton = StartWindow.GetControl(0x03)
+ JoinGameButton = StartWindow.GetControl(0x0B)
+ OptionsButton = StartWindow.GetControl(0x08)
+ QuitGameButton = StartWindow.GetControl(0x01)
+ StartWindow.CreateLabel(0x0fff0000, 0,0,800,30, "REALMS2", "", 1)
+ VersionLabel = StartWindow.GetControl(0x0fff0000)
+ VersionLabel.SetText(GEMRB_VERSION)
+ ProtocolButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ NewGameButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ LoadGameButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+
+ GemRB.SetVar("SaveDir",1)
+ Games=GemRB.GetSaveGames()
+
+ #looking for the quicksave
+ EnableQuickLoad = IE_GUI_BUTTON_DISABLED
+ for Game in Games:
+ Slotname = Game.GetSaveID()
+ # quick save is 1
+ if Slotname == 1:
+ EnableQuickLoad = IE_GUI_BUTTON_ENABLED
+ QuickLoadSlot = Game
+ break
+
+ QuickLoadButton.SetStatus(EnableQuickLoad)
+ JoinGameButton.SetStatus(IE_GUI_BUTTON_DISABLED)
+ OptionsButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ QuitGameButton.SetStatus(IE_GUI_BUTTON_ENABLED)
+ LastProtocol = GemRB.GetVar("Last Protocol Used")
+ if LastProtocol == 0:
+ ProtocolButton.SetText(15413)
+ elif LastProtocol == 1:
+ ProtocolButton.SetText(13967)
+ elif LastProtocol == 2:
+ ProtocolButton.SetText(13968)
+ NewGameButton.SetText(13963)
+ LoadGameButton.SetText(13729)
+ QuickLoadButton.SetText(33508)
+ JoinGameButton.SetText(13964)
+ OptionsButton.SetText(13905)
+ QuitGameButton.SetText(13731)
+ QuitGameButton.SetFlags(IE_GUI_BUTTON_CANCEL, OP_OR)
+ NewGameButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NewGamePress)
+ QuitGameButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, QuitPress)
+ ProtocolButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ProtocolPress)
+ OptionsButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, OptionsPress)
+ LoadGameButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, LoadPress)
+ QuickLoadButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, QuickLoadPress)
+ StartWindow.SetVisible(WINDOW_VISIBLE)
+ GemRB.LoadMusicPL("Theme.mus")
+
+ return
+
+def ProtocolPress():
+ global StartWindow, ProtocolWindow
+ #StartWindow.Unload()
+ StartWindow.SetVisible(WINDOW_INVISIBLE)
+ ProtocolWindow = GemRB.LoadWindow(1)
+
+ #Disabling Unused Buttons in this Window
+ Button = ProtocolWindow.GetControl(2)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button = ProtocolWindow.GetControl(3)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button = ProtocolWindow.GetControl(9)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+
+ SinglePlayerButton = ProtocolWindow.GetControl(10)
+ SinglePlayerButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ SinglePlayerButton.SetText(15413)
+
+ IPXButton = ProtocolWindow.GetControl(0)
+ IPXButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ IPXButton.SetText(13967)
+
+ TCPIPButton = ProtocolWindow.GetControl(1)
+ TCPIPButton.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ TCPIPButton.SetText(13968)
+
+ SinglePlayerButton.SetVarAssoc("Last Protocol Used", 0)
+ IPXButton.SetVarAssoc("Last Protocol Used", 1)
+ TCPIPButton.SetVarAssoc("Last Protocol Used", 2)
+
+ TextArea = ProtocolWindow.GetControl(7)
+ TextArea.SetText(11316)
+
+ DoneButton = ProtocolWindow.GetControl(6)
+ DoneButton.SetText(11973)
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ProtocolDonePress)
+ DoneButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ProtocolWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ProtocolDonePress():
+ global StartWindow, ProtocolWindow
+ if ProtocolWindow:
+ ProtocolWindow.Unload()
+
+ ProtocolButton = StartWindow.GetControl(0x00)
+
+ LastProtocol = GemRB.GetVar("Last Protocol Used")
+ if LastProtocol == 0:
+ ProtocolButton.SetText(15413)
+ elif LastProtocol == 1:
+ ProtocolButton.SetText(13967)
+ elif LastProtocol == 2:
+ ProtocolButton.SetText(13968)
+
+ StartWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def LoadPress():
+ global StartWindow
+
+ if StartWindow:
+ StartWindow.Unload()
+ GemRB.SetNextScript("GUILOAD")
+ return
+
+def QuickLoadPress():
+ global StartWindow, QuickLoadSlot
+
+ LoadScreen.StartLoadScreen()
+ GemRB.LoadGame(QuickLoadSlot) # load & start game
+ GemRB.EnterGame()
+ return
+
+def OptionsPress():
+ global StartWindow
+ if StartWindow:
+ StartWindow.Unload()
+ GemRB.SetNextScript("Options")
+ return
+
+def QuitPress():
+ global StartWindow, QuitWindow
+ StartWindow.SetVisible(WINDOW_INVISIBLE)
+ QuitWindow = GemRB.LoadWindow(22)
+ CancelButton = QuitWindow.GetControl(2)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, QuitCancelPress)
+ CancelButton.SetFlags(IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ QuitButton = QuitWindow.GetControl(1)
+ QuitButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, QuitQuitPress)
+ QuitButton.SetFlags(IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ TextArea = QuitWindow.GetControl(0)
+ CancelButton.SetText(13727)
+ QuitButton.SetText(15417)
+ TextArea.SetText(19532)
+ QuitWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def NewGamePress():
+ global StartWindow
+ if StartWindow:
+ StartWindow.Unload()
+ GemRB.SetNextScript("SPParty")
+ return
+
+def QuitCancelPress():
+ global StartWindow, QuitWindow
+ if QuitWindow:
+ QuitWindow.Unload()
+ StartWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def QuitQuitPress():
+ GemRB.Quit()
+ return
diff --git a/gemrb/GUIScripts/iwd2/SubRaces.py b/gemrb/GUIScripts/iwd2/SubRaces.py
new file mode 100644
index 0000000..3b6054c
--- /dev/null
+++ b/gemrb/GUIScripts/iwd2/SubRaces.py
@@ -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.
+#
+#
+#character generation, SubRaces (GUICG54)
+import GemRB
+from GUIDefines import *
+import CommonTables
+
+RaceWindow = 0
+TextAreaControl = 0
+DoneButton = 0
+SubRacesTable = 0
+
+def OnLoad():
+ global RaceWindow, TextAreaControl, DoneButton
+ global SubRacesTable
+
+ GemRB.LoadWindowPack("GUICG", 800, 600)
+ RaceWindow = GemRB.LoadWindow(54)
+
+ RaceCount = CommonTables.Races.GetRowCount()
+
+ SubRacesTable = GemRB.LoadTable("SUBRACES")
+
+ for i in range(1, 5):
+ Button = RaceWindow.GetControl(i)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+
+ Race = GemRB.GetVar("BaseRace")
+ RaceName = CommonTables.Races.GetRowName(CommonTables.Races.FindValue(3, Race) )
+
+ PureRace = SubRacesTable.GetValue(RaceName , "PURE_RACE")
+ Button = RaceWindow.GetControl(1)
+ RaceStrRef = CommonTables.Races.GetValue(PureRace, "CAP_REF")
+ Button.SetText(RaceStrRef )
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, SubRacePress)
+ RaceID = CommonTables.Races.GetValue(PureRace, "ID")
+ Button.SetVarAssoc("Race",RaceID)
+
+ #if you want a fourth subrace you can safely add a control id #5 to
+ #the appropriate window (guicg#54), and increase 4 to 5
+ for i in range(1,4):
+ Label = "SUBRACE"+str(i)
+ HasSubRace = SubRacesTable.GetValue(RaceName, Label)
+ Button = RaceWindow.GetControl(i+1)
+ if HasSubRace == 0:
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetText("")
+ else:
+ HasSubRace = PureRace+"_"+HasSubRace
+ RaceStrRef = CommonTables.Races.GetValue(HasSubRace, "CAP_REF")
+ Button.SetText(RaceStrRef )
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, SubRacePress)
+ RaceID = CommonTables.Races.GetValue(HasSubRace, "ID")
+ Button.SetVarAssoc("Race",RaceID)
+
+ BackButton = RaceWindow.GetControl(8)
+ BackButton.SetText(15416)
+ BackButton.SetFlags(IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ DoneButton = RaceWindow.GetControl(0)
+ DoneButton.SetText(36789)
+ DoneButton.SetState(IE_GUI_BUTTON_ENABLED)
+ DoneButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ TextAreaControl = RaceWindow.GetControl(6)
+ TextAreaControl.SetText(CommonTables.Races.GetValue(RaceName, "DESC_REF"))
+
+ DoneButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NextPress)
+ BackButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, BackPress)
+ RaceWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def SubRacePress():
+ global RaceWindow, TextAreaControl
+ Race = CommonTables.Races.FindValue(3, GemRB.GetVar("Race") )
+ TextAreaControl.SetText(CommonTables.Races.GetValue(Race, 1))
+ return
+
+def BackPress():
+ if RaceWindow:
+ RaceWindow.Unload()
+ GemRB.SetNextScript("Race")
+ GemRB.SetVar("Race",0) #scrapping the subrace value
+ GemRB.SetVar("BaseRace",0) #scrapping the race value
+ return
+
+def NextPress():
+ if RaceWindow:
+ RaceWindow.Unload()
+ GemRB.SetNextScript("CharGen3") #class
+ return
diff --git a/gemrb/GUIScripts/maze_defs.py b/gemrb/GUIScripts/maze_defs.py
new file mode 100644
index 0000000..860f67f
--- /dev/null
+++ b/gemrb/GUIScripts/maze_defs.py
@@ -0,0 +1,34 @@
+# !!! Keep these synchronized with maze definitions in Game.h !!!
+
+
+#maze header indices
+MH_POS1X = 0
+MH_POS1Y = 1
+MH_POS2X = 2
+MH_POS2Y = 3
+MH_POS3X = 4
+MH_POS3Y = 5
+MH_POS4X = 6
+MH_POS4Y = 7
+MH_TRAPCOUNT = 8
+MH_INITED = 9
+MH_UNKNOWN28 = 10
+MH_UNKNOWN2C = 11
+
+#maze entry indices
+ME_OVERRIDE = 0
+ME_VALID = 1
+ME_ACCESSIBLE = 2
+ME_TRAP = 3
+ME_WALLS = 4
+ME_VISITED = 5
+
+# Wall directions
+WALL_SOUTH = 1
+WALL_NORTH = 2
+WALL_EAST = 4
+WALL_WEST = 8
+
+#maximum maze entry
+MAZE_ENTRY_COUNT = 64
+MAZE_MAX_DIM = 8
diff --git a/gemrb/GUIScripts/pst/Autodetect.py b/gemrb/GUIScripts/pst/Autodetect.py
new file mode 100644
index 0000000..ec00515
--- /dev/null
+++ b/gemrb/GUIScripts/pst/Autodetect.py
@@ -0,0 +1,20 @@
+# -*-python-*-
+# vim: set ts=4 sw=4 expandtab:
+
+import GemRB
+from ie_restype import *
+from AutodetectCommon import CheckFiles
+
+files = (
+ ("START", "CHU", RES_CHU),
+ ("STARTPOS", "2DA", RES_2DA),
+ ("STARTARE", "2DA", RES_2DA),
+
+ ("VAR", "VAR", RES_VAR),
+ ("RESDATA", "INI", RES_INI),
+ ("BEAST", "INI", RES_INI),
+)
+
+
+if CheckFiles(files):
+ GemRB.AddGameTypeHint ("pst", 100)
diff --git a/gemrb/GUIScripts/pst/CMakeLists.txt b/gemrb/GUIScripts/pst/CMakeLists.txt
new file mode 100644
index 0000000..111981a
--- /dev/null
+++ b/gemrb/GUIScripts/pst/CMakeLists.txt
@@ -0,0 +1,3 @@
+FILE( GLOB FILES_TO_INSTALL *.py )
+
+INSTALL( FILES ${FILES_TO_INSTALL} DESTINATION ${DATA_DIR}/GUIScripts/pst )
diff --git a/gemrb/GUIScripts/pst/FloatMenuWindow.py b/gemrb/GUIScripts/pst/FloatMenuWindow.py
new file mode 100644
index 0000000..98e5eee
--- /dev/null
+++ b/gemrb/GUIScripts/pst/FloatMenuWindow.py
@@ -0,0 +1,507 @@
+# -*-python-*-
+# 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.
+#
+
+# FloatMenuWindow.py - display PST's floating menu window from GUIWORLD winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+from ie_action import *
+
+FloatMenuWindow = None
+
+MENU_MODE_SINGLE = 0
+MENU_MODE_GROUP = 1
+MENU_MODE_WEAPONS = 2
+MENU_MODE_ITEMS = 3
+MENU_MODE_SPELLS = 4
+MENU_MODE_ABILITIES = 5
+
+float_menu_mode = MENU_MODE_SINGLE
+float_menu_index = 0
+float_menu_selected = None
+type = None
+
+def UseSpell ():
+ pc = GemRB.GameGetFirstSelectedPC ()
+ slot = float_menu_selected+float_menu_index
+ print "spell", type, slot
+ GemRB.SpellCast (pc, 1<<type, slot)
+ return
+
+def UseItem ():
+ pc = GemRB.GameGetFirstSelectedPC ()
+ GemRB.UseItem (pc, float_menu_selected+21, 0)
+ return
+
+def UseWeapon ():
+ pc = GemRB.GameGetFirstSelectedPC ()
+ GemRB.SetEquippedQuickSlot (pc, float_menu_selected)
+ return
+
+def OpenFloatMenuWindow ():
+ global FloatMenuWindow
+ global float_menu_mode, float_menu_index
+
+ GemRB.HideGUI ()
+
+ if FloatMenuWindow:
+ if FloatMenuWindow:
+ FloatMenuWindow.Unload ()
+ FloatMenuWindow = None
+
+ #FIXME: UnpauseGameTimer
+ GemRB.GamePause (False, 0)
+ GemRB.SetVar ("FloatWindow", -1)
+ GUICommonWindows.SetSelectionChangeMultiHandler (None)
+ GemRB.UnhideGUI ()
+
+ if float_menu_selected==None:
+ GemRB.GameControlSetTargetMode (TARGET_MODE_NONE)
+ return
+
+ if float_menu_mode == MENU_MODE_ITEMS:
+ UseItem()
+ elif float_menu_mode == MENU_MODE_WEAPONS:
+ UseWeapon()
+ elif float_menu_mode == MENU_MODE_SPELLS:
+ UseSpell()
+ elif float_menu_mode == MENU_MODE_ABILITIES:
+ UseSpell()
+ return
+
+ ActionLevel = 0
+ if not GemRB.GameGetFirstSelectedPC ():
+ GemRB.UnhideGUI ()
+ return
+ # FIXME: remember current selection
+ #FIXME: PauseGameTimer
+ GemRB.GamePause (True, 0)
+
+ GemRB.LoadWindowPack ("GUIWORLD")
+ FloatMenuWindow = Window = GemRB.LoadWindow (3)
+ GemRB.SetVar ("FloatWindow", Window.ID)
+
+ x, y = GemRB.GetVar ("MenuX"), GemRB.GetVar ("MenuY")
+
+ # FIXME: keep the menu inside the viewport!!!
+ Window.SetPos (x, y, WINDOW_CENTER | WINDOW_BOUNDED)
+
+ # portrait button
+ Button = Window.GetControl (0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, FloatMenuSelectNextPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, GUICommon.OpenFloatMenuWindow)
+
+ # Initiate Dialogue
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, FloatMenuSelectDialog)
+ Button.SetTooltip (8191)
+
+ # Attack/Select Weapon
+ Button = Window.GetControl (2)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, FloatMenuSelectWeapons)
+ Button.SetTooltip (8192)
+
+ # Cast spell
+ Button = Window.GetControl (3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, FloatMenuSelectSpells)
+ Button.SetTooltip (8193)
+
+ # Use Item
+ Button = Window.GetControl (4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, FloatMenuSelectItems)
+ Button.SetTooltip (8194)
+
+ # Use Special Ability
+ Button = Window.GetControl (5)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, FloatMenuSelectAbilities)
+ Button.SetTooltip (8195)
+
+ # Menu Anchors/Handles
+ Button = Window.GetControl (11)
+ Button.SetTooltip (8199)
+ Button.SetFlags (IE_GUI_BUTTON_DRAGGABLE, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG, FloatMenuDrag)
+
+ Button = Window.GetControl (12)
+ Button.SetTooltip (8199)
+ Button.SetFlags (IE_GUI_BUTTON_DRAGGABLE, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG, FloatMenuDrag)
+
+ # Rotate Items left (to begin)
+ Button = Window.GetControl (13)
+ Button.SetTooltip (8197)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, FloatMenuPreviousItem)
+
+ # Rotate Items right (to end)
+ Button = Window.GetControl (14)
+ Button.SetTooltip (8198)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, FloatMenuNextItem)
+
+ # 6 - 10 - items/spells/other
+ for i in range (6, 11):
+ Button = Window.GetControl (i)
+ Button.SetPos (65535, 65535)
+
+ # 15 - 19 - empty item slot
+ for i in range (15, 20):
+ Button = Window.GetControl (i)
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT | IE_GUI_BUTTON_ALIGN_BOTTOM, OP_OR)
+
+ # BAMs:
+ # AMALLSTP - 41655
+ # AMATTCK - 41654
+ #AMGENB1
+ #AMGENS
+ #AMGUARD - 31657, 32431, 41652, 48887
+ #AMHILITE - highlight frame
+ #AMINVNT - 41601, 41709
+ #AMJRNL - 41623, 41714
+ #AMMAP - 41625, 41710
+ #AMSPLL - 4709
+ #AMSTAT - 4707
+ #AMTLK - 41653
+
+ #AMPANN
+ #AMPDKK
+ #AMPFFG
+ #AMPIGY
+ #AMPMRT
+ #AMPNDM
+ #AMPNM1
+ #AMPVHA
+
+ num = 0
+ for i in range (GemRB.GetPartySize ()):
+ if GemRB.GameIsPCSelected (i + 1):
+ num = num + 1
+
+ # if more than one is selected, start in group menu mode (mode 1)
+ if num == 1:
+ float_menu_mode = MENU_MODE_SINGLE
+ else:
+ float_menu_mode = MENU_MODE_GROUP
+
+ float_menu_index = 0
+
+ GUICommonWindows.SetSelectionChangeMultiHandler (FloatMenuSelectAnotherPC)
+ UpdateFloatMenuWindow ()
+
+ GemRB.UnhideGUI ()
+ return
+
+def UpdateFloatMenuWindow ():
+ Window = FloatMenuWindow
+
+ pc = GemRB.GameGetFirstSelectedPC ()
+
+ Button = Window.GetControl (0)
+ Button.SetSprites (GUICommonWindows.GetActorPortrait (pc, 'FMENU'), 0, 0, 1, 2, 3)
+ Button = Window.GetControl (13)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button = Window.GetControl (14)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ if float_menu_mode == MENU_MODE_SINGLE:
+ for i in range (5):
+ UpdateFloatMenuSingleAction (i)
+ elif float_menu_mode == MENU_MODE_GROUP:
+ for i in range (5):
+ UpdateFloatMenuGroupAction (i)
+ elif float_menu_mode == MENU_MODE_WEAPONS:
+ # weapons
+ for i in range (5):
+ UpdateFloatMenuItem (pc, i, 1)
+ elif float_menu_mode == MENU_MODE_ITEMS:
+ # items
+ for i in range (5):
+ UpdateFloatMenuItem (pc, i, 0)
+ elif float_menu_mode == MENU_MODE_SPELLS:
+ # spells
+ RefreshSpellList(pc, False)
+ if float_menu_index:
+ Button = Window.GetControl (13)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ if float_menu_index+3<len(spell_list):
+ Button = Window.GetControl (14)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ for i in range (5):
+ UpdateFloatMenuSpell (pc, i)
+ elif float_menu_mode == MENU_MODE_ABILITIES:
+ # abilities
+ RefreshSpellList(pc, True)
+ if float_menu_index:
+ Button = Window.GetControl (13)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ if float_menu_index+3<len(spell_list):
+ Button = Window.GetControl (14)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ for i in range (5):
+ UpdateFloatMenuSpell (pc, i)
+ return
+
+def UpdateFloatMenuSingleAction (i):
+ Window = FloatMenuWindow
+ butts = [ ('AMMAP', 41625, ''),
+ ('AMJRNL', 41623, ''),
+ ('AMINVNT', 41601, ''),
+ ('AMSTAT', 4707, ''),
+ ('AMSPLL', 4709, '') ] # or 41624
+ # Map Screen
+ # Journal Screen
+ # Inventory screen
+ # Statistics screen
+ # Mage Spell Book / Priest Scroll
+
+ Button = Window.GetControl (15 + i)
+
+ Button.SetSprites (butts[i][0], 0, 0, 1, 2, 3)
+ Button.SetTooltip (butts[i][1])
+ Button.SetText ('')
+ return
+
+def UpdateFloatMenuGroupAction (i):
+ Window = FloatMenuWindow
+ #butts = [ ('AMGUARD', 31657, ''),
+ # ('AMTLK', 41653, ''),
+ # ('AMATTCK', 41654, ''),
+ # ('AMALLSTP', 41655, ''),
+ # ('AMGENS', '', '') ]
+
+ butts = [ ACT_DEFEND, ACT_TALK, ACT_ATTACK, ACT_STOP, ACT_SEARCH ]
+ # Guard
+ # Initiate dialogue
+ # Attack
+ # Abort Current Action
+ Button = Window.GetControl (15 + i)
+
+ #Button.SetSprites (butts[i][0], 0, 0, 1, 2, 3)
+ #Button.SetTooltip (butts[i][1])
+ #Button.SetText ('')
+ Button.SetActionIcon (globals(), butts[i], i+1 )
+ return
+
+def RefreshSpellList(pc, innate):
+ global spell_hash, spell_list, type
+
+ if innate:
+ type = IE_SPELL_TYPE_INNATE
+ else:
+ if (CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat( pc, IE_CLASS), 1)=="*"):
+ type = IE_SPELL_TYPE_WIZARD
+ else:
+ type = IE_SPELL_TYPE_PRIEST
+
+ # FIXME: ugly, should be in Spellbook.cpp
+ spell_hash = {}
+ spell_list = []
+ #level==0 is level #1
+ for level in range (9):
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+ for j in range (mem_cnt):
+ ms = GemRB.GetMemorizedSpell (pc, type, level, j)
+
+ # Spell was already used up
+ if not ms['Flags']: continue
+
+ if ms['SpellResRef'] in spell_hash:
+ spell_hash[ms['SpellResRef']] = spell_hash[ms['SpellResRef']] + 1
+ else:
+ spell_hash[ms['SpellResRef']] = 1
+ spell_list.append( ms['SpellResRef'] )
+ return
+
+def UpdateFloatMenuItem (pc, i, weapons):
+ Window = FloatMenuWindow
+ #no float menu index needed for weapons or quick items
+ if weapons:
+ slot_item = GemRB.GetSlotItem (pc, 10 + i)
+ else:
+ slot_item = GemRB.GetSlotItem (pc, 21 + i)
+ Button = Window.GetControl (15 + i)
+
+ #the selected state is in another bam, sucks, we have to do everything manually
+ if i == float_menu_selected:
+ Button.SetSprites ('AMHILITE', 0, 0, 1, 0, 0)
+ else:
+ Button.SetSprites ('AMGENS', 0, 0, 1, 0, 0)
+
+ # Weapons - the last action is 'Guard'
+ # TODO: use SetActionIcon whenever possible
+ if weapons and i==4:
+ Button.SetActionIcon (globals(), ACT_DEFEND)
+ return
+
+ #if slot_item and slottype:
+ if slot_item:
+ item = GemRB.GetItem (slot_item['ItemResRef'])
+ identified = slot_item['Flags'] & IE_INV_ITEM_IDENTIFIED
+ if not identified:
+ Button.SetItemIcon ('')
+ Button.SetText ('')
+ Button.SetTooltip ('')
+ return
+
+ Button.SetItemIcon (slot_item['ItemResRef'], 6)
+ if item['StackAmount'] > 1:
+ Button.SetText (str (slot_item['Usages0']))
+ else:
+ Button.SetText ('')
+ if not identified or item['ItemNameIdentified'] == -1:
+ Button.SetTooltip (item['ItemName'])
+ else:
+ Button.SetTooltip (item['ItemNameIdentified'])
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectItem)
+ Button.SetVarAssoc ('ItemButton', i)
+ return
+
+ Button.SetItemIcon ('')
+ Button.SetText ('')
+ Button.SetTooltip ('')
+ return
+
+def SelectItem ():
+ global float_menu_selected
+
+ Window = FloatMenuWindow
+ Button = GemRB.GetVar ('ItemButton')
+ #simulating radiobutton+checkbox hybrid
+ if float_menu_selected == Button:
+ float_menu_selected = None
+ else:
+ float_menu_selected = Button
+ UpdateFloatMenuWindow()
+ return
+
+def UpdateFloatMenuSpell (pc, i):
+ Window = FloatMenuWindow
+
+ Button = Window.GetControl (15 + i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ if i == float_menu_selected:
+ Button.SetBAM ('AMHILITE', 0, 0)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+
+ if i + float_menu_index < len (spell_list):
+ SpellResRef = spell_list[i + float_menu_index]
+ Button.SetSpellIcon (SpellResRef)
+ Button.SetText ("%d" %spell_hash[SpellResRef])
+
+ spell = GemRB.GetSpell (SpellResRef)
+ Button.SetTooltip (spell['SpellName'])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectItem)
+ Button.SetVarAssoc ('ItemButton', i)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetSpellIcon ('')
+ Button.SetText ('')
+ Button.SetTooltip ('')
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def FloatMenuSelectNextPC ():
+ sel = GemRB.GameGetFirstSelectedPC ()
+ if sel == 0:
+ GUICommon.OpenFloatMenuWindow ()
+ return
+
+ GemRB.GameSelectPC (sel % GemRB.GetPartySize () + 1, 1, SELECT_REPLACE)
+ # NOTE: it invokes FloatMenuSelectAnotherPC() through selection change handler
+ return
+
+def FloatMenuSelectAnotherPC ():
+ global float_menu_mode, float_menu_index, float_menu_selected
+ float_menu_mode = MENU_MODE_SINGLE
+ float_menu_index = 0
+ float_menu_selected = None
+ UpdateFloatMenuWindow ()
+ return
+
+def FloatMenuSelectDialog ():
+ global float_menu_selected
+ GemRB.GameControlSetTargetMode (TARGET_MODE_TALK)
+ float_menu_selected = None
+ UpdateFloatMenuWindow ()
+ return
+
+def FloatMenuSelectWeapons ():
+ global float_menu_mode, float_menu_index, float_menu_selected
+ float_menu_mode = MENU_MODE_WEAPONS
+ float_menu_index = 0
+ float_menu_selected = None
+ # FIXME: Force attack mode
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK)
+ UpdateFloatMenuWindow ()
+ return
+
+def FloatMenuSelectItems ():
+ global float_menu_mode, float_menu_index, float_menu_selected
+ float_menu_mode = MENU_MODE_ITEMS
+ float_menu_index = 0
+ float_menu_selected = None
+ UpdateFloatMenuWindow ()
+ return
+
+def FloatMenuSelectSpells ():
+ global float_menu_mode, float_menu_index, float_menu_selected
+ float_menu_mode = MENU_MODE_SPELLS
+ float_menu_index = 0
+ float_menu_selected = None
+ GemRB.GameControlSetTargetMode (TARGET_MODE_CAST)
+ UpdateFloatMenuWindow ()
+ return
+
+def FloatMenuSelectAbilities ():
+ global float_menu_mode, float_menu_index, float_menu_selected
+ float_menu_mode = MENU_MODE_ABILITIES
+ float_menu_index = 0
+ float_menu_selected = None
+ UpdateFloatMenuWindow ()
+ return
+
+def FloatMenuPreviousItem ():
+ global float_menu_index, float_menu_selected
+ if float_menu_index > 0:
+ float_menu_index = float_menu_index - 1
+ if float_menu_selected!=None:
+ float_menu_selected = float_menu_selected + 1
+ UpdateFloatMenuWindow ()
+ return
+
+def FloatMenuNextItem ():
+ global float_menu_index, float_menu_selected
+ float_menu_index = float_menu_index + 1
+ if float_menu_selected!=None:
+ float_menu_selected = float_menu_selected - 1
+ UpdateFloatMenuWindow ()
+ return
+
+def FloatMenuDrag ():
+ dx, dy = GemRB.GetVar ("DragX"), GemRB.GetVar ("DragY")
+ FloatMenuWindow.SetPos (dx, dy, WINDOW_RELATIVE | WINDOW_BOUNDED)
+ return
+
+#############################
diff --git a/gemrb/GUIScripts/pst/GUICommonWindows.py b/gemrb/GUIScripts/pst/GUICommonWindows.py
new file mode 100644
index 0000000..2503a08
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUICommonWindows.py
@@ -0,0 +1,653 @@
+# -*-python-*-
+# 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.
+#
+
+# GUICommonWindows.py - functions to open common
+# windows in lower part of the screen
+###################################################
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+from ie_action import *
+import GUIClasses
+import GUICommon
+import CommonTables
+import LUCommon
+import InventoryCommon
+
+# needed for all the Open*Window callbacks in the OptionsWindow
+import GUIJRNL
+import GUIMA
+import GUIMG
+import GUIINV
+import GUIOPT
+import GUIPR
+import GUIREC
+import Maze
+
+FRAME_PC_SELECTED = 0
+FRAME_PC_TARGET = 1
+
+TimeWindow = None
+PortWindow = None
+MenuWindow = None
+MainWindow = None
+global PortraitWindow
+PortraitWindow = None
+ActionsWindow = None
+OptionsWindow = None
+DraggedPortrait = None
+DiscWindow = None
+
+# Buttons:
+# 0 CNTREACH
+# 1 INVNT
+# 2 MAP
+# 3 MAGE
+# 4 AI
+# 5 STATS
+# 6 JRNL
+# 7 PRIEST
+# 8 OPTION
+# 9 REST
+# 10 TXTE
+
+def OpenCommonWindows ():
+ global TimeWindow, PortWindow, MenuWindow, MainWindow
+
+ TimeWindow = GemRB.LoadWindow (0)
+ PortWindow = GemRB.LoadWindow (1)
+ MenuWindow = GemRB.LoadWindow (2)
+ MainWindow = GemRB.LoadWindow (3)
+
+ Window = MenuWindow
+
+ # Can't Reach ???
+ Button = Window.GetControl (0)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None) #TODO: CntReachPress
+
+ # AI
+ Button = Window.GetControl (4)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, AIPress)
+
+ # Message popup
+ Button = Window.GetControl (10)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, TxtePress)
+
+
+ SetupMenuWindowControls (Window)
+
+
+ TimeWindow.SetVisible (WINDOW_VISIBLE)
+ PortWindow.SetVisible (WINDOW_VISIBLE)
+ MenuWindow.SetVisible (WINDOW_VISIBLE)
+ MainWindow.SetVisible (WINDOW_VISIBLE)
+
+def CloseCommonWindows ():
+ global MainWindow
+
+ if MainWindow == None:
+ return
+ #if TimeWindow == None:
+ # return
+
+ if MainWindow:
+ MainWindow.Unload ()
+ if TimeWindow:
+ TimeWindow.Unload ()
+ if PortWindow:
+ PortWindow.Unload ()
+ if MenuWindow:
+ MenuWindow.Unload ()
+
+ MainWindow = None
+
+def SetupMenuWindowControls (Window):
+ # Inventory
+ Button = Window.GetControl (1)
+ Button.SetTooltip (41601)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIINV.OpenInventoryWindow)
+
+ # Map
+ Button = Window.GetControl (2)
+ Button.SetTooltip (41625)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIMA.OpenMapWindow)
+
+ # Mage
+ Button = Window.GetControl (3)
+ Button.SetTooltip (41624)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIMG.OpenMageWindow)
+ # Stats
+ Button = Window.GetControl (5)
+ Button.SetTooltip (4707)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIREC.OpenRecordsWindow)
+
+ # Journal
+ Button = Window.GetControl (6)
+ Button.SetTooltip (41623)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIJRNL.OpenJournalWindow)
+
+ # Priest
+ Button = Window.GetControl (7)
+ Button.SetTooltip (4709)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIPR.OpenPriestWindow)
+
+ # Options
+ Button = Window.GetControl (8)
+ Button.SetTooltip (41626)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIOPT.OpenOptionsWindow)
+
+ # Rest
+ Button = Window.GetControl (9)
+ Button.SetTooltip (41628)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.RestPress)
+
+
+ # AI
+ Button = Window.GetControl (4)
+ Button.SetTooltip (41631) # or 41646 Activate ...
+ #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.OpenFloatMenuWindow)
+
+ # (Un)Lock view on character
+ Button = Window.GetControl (0)
+ Button.SetTooltip (41647) # or 41648 Unlock ...
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnLockViewPress)
+
+ # Message popup
+ Button = Window.GetControl (10)
+ Button.SetTooltip (41660) # or 41661 Close ...
+
+
+def OnLockViewPress ():
+ GemRB.GameControlSetScreenFlags (SF_CENTERONACTOR | SF_ALWAYSCENTER, OP_OR)
+ print "OnLockViewPress"
+
+def AIPress ():
+ print "AIPress"
+
+def TxtePress ():
+ print "TxtePress"
+
+def SetupActionsWindowControls (Window):
+ # time button
+ Button = Window.GetControl (0)
+ Button.SetAnimation ("WMTIME")
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_ANIMATED, OP_SET)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, GUICommon.GearsClicked)
+ Button.SetEvent(IE_GUI_MOUSE_ENTER_BUTTON, UpdateClock)
+
+ # 41627 - Return to the Game World
+ Button = Window.GetControl (2)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetTooltip (41627)
+
+ # Select all characters
+ Button = Window.GetControl (1)
+ Button.SetTooltip (41659)
+
+ # Abort current action
+ Button = Window.GetControl (3)
+ Button.SetTooltip (41655)
+
+ # Formations
+ Button = Window.GetControl (4)
+ Button.SetTooltip (44945)
+
+
+
+# which=INVENTORY|STATS|FMENU
+def GetActorPortrait (actor, which):
+ #return GemRB.GetPlayerPortrait( actor, which)
+
+ # only the lowest byte is meaningful here (OneByteAnimID)
+ anim_id = GemRB.GetPlayerStat (actor, IE_ANIMATION_ID) & 255
+ row = "0x%02X" %anim_id
+
+ return CommonTables.Pdolls.GetValue (row, which)
+
+
+def UpdateAnimation ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ disguise = GemRB.GetGameVar ("APPEARANCE")
+ if disguise == 2: #dustman
+ animid = "DR"
+ elif disguise == 1: #zombie
+ animid = "ZO"
+ else:
+ slot = GemRB.GetEquippedQuickSlot (pc)
+ item = GemRB.GetSlotItem (pc, slot )
+ animid = ""
+ if item:
+ item = GemRB.GetItem(item["ItemResRef"])
+ if item:
+ animid = item["AnimationType"]
+
+ BioTable = GemRB.LoadTable ("BIOS")
+ Specific = "%d"%GemRB.GetPlayerStat (pc, IE_SPECIFIC)
+ AvatarName = BioTable.GetValue (Specific, "PC")
+ AnimTable = GemRB.LoadTable ("ANIMS")
+ if animid=="":
+ animid="*"
+ value = AnimTable.GetValue (animid, AvatarName)
+ if value<0:
+ return
+ GemRB.SetPlayerStat (pc, IE_ANIMATION_ID, value)
+ return
+
+
+SelectionChangeHandler = None
+SelectionChangeMultiHandler = None
+
+def SetSelectionChangeHandler (handler):
+ """Updates the selection handler."""
+
+ global SelectionChangeHandler
+
+ # Switching from walking to non-walking environment:
+ # set the first selected PC in walking env as a selected
+ # in nonwalking env
+ #if (not SelectionChangeHandler) and handler and (not GUICommon.NextWindowFn):
+ if (not SelectionChangeHandler) and handler:
+ sel = GemRB.GameGetFirstSelectedPC ()
+ if not sel:
+ sel = 1
+ GemRB.GameSelectPCSingle (sel)
+
+ SelectionChangeHandler = handler
+
+ # redraw selection on change main selection | single selection
+ SelectionChanged ()
+
+def SetSelectionChangeMultiHandler (handler):
+ global SelectionChangeMultiHandler
+ SelectionChangeMultiHandler = handler
+ #SelectionChanged ()
+
+def RunSelectionChangeHandler ():
+ if SelectionChangeHandler:
+ SelectionChangeHandler ()
+
+portrait_hp_numeric = [0, 0, 0, 0, 0, 0]
+
+
+def OpenPortraitWindow (needcontrols):
+ global PortraitWindow
+
+ PortraitWindow = Window = GemRB.LoadWindow (1)
+
+ for i in range (PARTY_SIZE):
+ Button = Window.GetControl (i)
+ Button.SetVarAssoc ('PressedPortrait', i+1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitButtonOnPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, PortraitButtonOnShiftPress)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDropItemToPC)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG, PortraitButtonOnDrag)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, PortraitButtonOnMouseEnter)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, PortraitButtonOnMouseLeave)
+
+ Button.SetBorder (FRAME_PC_SELECTED, 1, 1, 2, 2, 0, 255, 0, 255)
+ Button.SetBorder (FRAME_PC_TARGET, 3, 3, 4, 4, 255, 255, 0, 255)
+
+ ButtonHP = Window.GetControl (6 + i)
+ ButtonHP.SetVarAssoc ('PressedPortraitHP', i+1)
+ ButtonHP.SetEvent (IE_GUI_BUTTON_ON_PRESS, PortraitButtonHPOnPress)
+
+ portrait_hp_numeric[i] = 0
+
+ UpdatePortraitWindow ()
+ SelectionChanged ()
+ return Window
+
+def UpdatePortraitWindow ():
+ """Updates all of the portraits."""
+
+ Window = PortraitWindow
+
+ for i in range (PARTY_SIZE):
+ Button = Window.GetControl (i)
+ ButtonHP = Window.GetControl (6 + i)
+
+ pic = GemRB.GetPlayerPortrait (i+1, 0)
+ if not pic:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ ButtonHP.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ continue
+
+ #sel = GemRB.GameGetSelectedPCSingle () == i + 1
+ Button.SetBAM (pic, 0, 0, -1)
+
+ state = GemRB.GetPlayerStat (i+1, IE_STATE_ID)
+ hp = GemRB.GetPlayerStat (i+1, IE_HITPOINTS)
+ hp_max = GemRB.GetPlayerStat (i+1, IE_MAXHITPOINTS)
+
+ if state & STATE_DEAD:
+ cycle = 9
+ elif state & STATE_HELPLESS:
+ cycle = 8
+ elif state & STATE_PETRIFIED:
+ cycle = 7
+ elif state & STATE_PANIC:
+ cycle = 6
+ elif state & STATE_POISONED:
+ cycle = 2
+ elif hp<hp_max/5:
+ cycle = 4
+ else:
+ cycle = 0
+
+ if cycle<6:
+ Button.SetFlags(IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_ANIMATED | IE_GUI_BUTTON_PLAYRANDOM|IE_GUI_BUTTON_DRAGGABLE|IE_GUI_BUTTON_MULTILINE, OP_SET)
+ else:
+ Button.SetFlags(IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_ANIMATED | IE_GUI_BUTTON_DRAGGABLE|IE_GUI_BUTTON_MULTILINE, OP_SET)
+ Button.SetAnimation (pic, cycle)
+
+
+ ButtonHP.SetFlags(IE_GUI_BUTTON_PICTURE, OP_SET)
+
+ if hp_max<1:
+ ratio = 0.0
+ else:
+ ratio = (hp + 0.0) / hp_max
+ if ratio > 1.0: ratio = 1.0
+ r = int (255 * (1.0 - ratio))
+ g = int (255 * ratio)
+
+ ButtonHP.SetText ("%d / %d" %(hp, hp_max))
+ ButtonHP.SetTextColor (r, g, 0, False)
+ ButtonHP.SetBAM ('FILLBAR', 0, 0, -1)
+ ButtonHP.SetPictureClipping (ratio)
+
+ if portrait_hp_numeric[i-1]:
+ op = OP_NAND
+ else:
+ op = OP_OR
+
+ ButtonHP.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_NO_TEXT, op)
+
+
+ #if sel:
+ # Button.EnableBorder(FRAME_PC_SELECTED, 1)
+ #else:
+ # Button.EnableBorder(FRAME_PC_SELECTED, 0)
+ return
+
+def PortraitButtonOnDrag ():
+ global DraggedPortrait
+
+ #they start from 1
+ DraggedPortrait = GemRB.GetVar ("PressedPortrait")
+ GemRB.DragItem (DraggedPortrait, -1, "")
+ return
+
+def PortraitButtonOnPress ():
+ """Selects the portrait individually."""
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if GemRB.GameControlGetTargetMode() != TARGET_MODE_NONE:
+ GemRB.ActOnPC (i)
+ return
+
+ if (not SelectionChangeHandler):
+ if GemRB.GameIsPCSelected (i):
+ GemRB.GameControlSetScreenFlags (SF_CENTERONACTOR, OP_OR)
+ GemRB.GameSelectPC (i, True, SELECT_REPLACE)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def PortraitButtonOnShiftPress ():
+ i = GemRB.GetVar ('PressedPortrait')
+
+ if (not SelectionChangeHandler):
+ sel = GemRB.GameIsPCSelected (i)
+ sel = not sel
+ GemRB.GameSelectPC (i, sel)
+ else:
+ GemRB.GameSelectPCSingle (i)
+ SelectionChanged ()
+ RunSelectionChangeHandler ()
+ return
+
+def PortraitButtonHPOnPress ():
+ Window = PortraitWindow
+
+ i = GemRB.GetVar ('PressedPortraitHP')
+
+ portrait_hp_numeric[i-1] = not portrait_hp_numeric[i-1]
+ ButtonHP = Window.GetControl (5 + i)
+
+ if portrait_hp_numeric[i-1]:
+ op = OP_NAND
+ else:
+ op = OP_OR
+
+ ButtonHP.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_NO_TEXT, op)
+ return
+
+def StopAllOnPress ():
+ for i in GemRB.GetSelectedActors():
+ GemRB.ClearActions (i, 1)
+ return
+
+# Run by Game class when selection was changed
+def SelectionChanged ():
+ # FIXME: hack. If defined, display single selection
+ if (not SelectionChangeHandler):
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, GemRB.GameIsPCSelected (i + 1))
+ if SelectionChangeMultiHandler:
+ SelectionChangeMultiHandler ()
+ else:
+ sel = GemRB.GameGetSelectedPCSingle ()
+
+ for i in range (PARTY_SIZE):
+ Button = PortraitWindow.GetControl (i)
+ Button.EnableBorder (FRAME_PC_SELECTED, i + 1 == sel)
+ import CommonWindow
+ CommonWindow.CloseContainerWindow()
+ return
+
+def PortraitButtonOnMouseEnter ():
+ global DraggedPortrait
+
+ i = GemRB.GetVar ("PressedPortrait")
+
+ if not i:
+ return
+
+ if DraggedPortrait != None:
+ GemRB.DragItem (0, -1, "")
+ #this might not work
+ GemRB.SwapPCs (DraggedPortrait, i)
+ DraggedPortrait = None
+
+ if GemRB.IsDraggingItem ():
+ Button = PortraitWindow.GetControl (i-1)
+ Button.EnableBorder (FRAME_PC_TARGET, 1)
+
+def PortraitButtonOnMouseLeave ():
+ i = GemRB.GetVar ("PressedPortrait")
+ if not i:
+ return
+
+ if GemRB.IsDraggingItem ():
+ Button = PortraitWindow.GetControl (i-1)
+ Button.EnableBorder (FRAME_PC_TARGET, 0)
+ return
+
+def DisableAnimatedWindows ():
+ global ActionsWindow, OptionsWindow
+ GemRB.SetVar ("PortraitWindow", -1)
+ ActionsWindow = GUIClasses.GWindow( GemRB.GetVar ("ActionsWindow") )
+ GemRB.SetVar ("ActionsWindow", -1)
+ OptionsWindow = GUIClasses.GWindow( GemRB.GetVar ("OptionsWindow") )
+ GemRB.SetVar ("OptionsWindow", -1)
+ GemRB.GamePause (1,1)
+
+def EnableAnimatedWindows ():
+ GemRB.SetVar ("PortraitWindow", PortraitWindow.ID)
+ GemRB.SetVar ("ActionsWindow", ActionsWindow.ID)
+ GemRB.SetVar ("OptionsWindow", OptionsWindow.ID)
+ GemRB.GamePause (0,1)
+
+def SetItemButton (Window, Button, Slot, PressHandler, RightPressHandler):
+ if Slot != None:
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ identified = Slot['Flags'] & IE_INV_ITEM_IDENTIFIED
+ #Button.SetVarAssoc ("LeftIndex", LeftTopIndex+i)
+ #Button.SetSprites ('IVSLOT', 0, 0, 0, 0, 0)
+ Button.SetItemIcon (Slot['ItemResRef'],0)
+
+ if Item['StackAmount'] > 1:
+ Button.SetText (str (Slot['Usages0']))
+ else:
+ Button.SetText ('')
+
+
+ if not identified or Item['ItemNameIdentified'] == -1:
+ Button.SetTooltip (Item['ItemName'])
+ else:
+ Button.SetTooltip (Item['ItemNameIdentified'])
+
+ #Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ #Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PressHandler)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, RightPressHandler)
+ #Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, ShiftPressHandler)
+ #Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, DragDropHandler)
+
+ else:
+ #Button.SetVarAssoc ("LeftIndex", -1)
+ Button.SetItemIcon ('')
+ Button.SetTooltip (4273) # Ground Item
+ Button.SetText ('')
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ #Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None)
+ #Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, None)
+
+def OpenWaitForDiscWindow ():
+ global DiscWindow
+ #print "OpenWaitForDiscWindow"
+
+ if DiscWindow:
+ GemRB.HideGUI ()
+ if DiscWindow:
+ DiscWindow.Unload ()
+ GemRB.SetVar ("OtherWindow", -1)
+ # ...LoadWindowPack()
+ EnableAnimatedWindows ()
+ DiscWindow = None
+ GemRB.UnhideGUI ()
+ return
+
+ try:
+ GemRB.HideGUI ()
+ except:
+ pass
+
+ GemRB.LoadWindowPack ("GUIID")
+ DiscWindow = Window = GemRB.LoadWindow (0)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+ label = DiscWindow.GetControl (0)
+
+ disc_num = GemRB.GetVar ("WaitForDisc")
+ #disc_path = GemRB.GetVar ("WaitForDiscPath")
+ disc_path = 'XX:'
+
+ text = GemRB.GetString (31483) + " " + str (disc_num) + " " + GemRB.GetString (31569) + " " + disc_path + "\n" + GemRB.GetString (49152)
+ label.SetText (text)
+ DisableAnimatedWindows ()
+
+ # 31483 - Please place PS:T disc number
+ # 31568 - Please place the PS:T DVD
+ # 31569 - in drive
+ # 31570 - Wrong disc in drive
+ # 31571 - There is no disc in drive
+ # 31578 - No disc could be found in drive. Please place Disc 1 in drive.
+ # 49152 - To quit the game, press Alt-F4
+
+
+ try:
+ GemRB.UnhideGUI ()
+ except:
+ DiscWindow.SetVisible (WINDOW_VISIBLE)
+
+def SetPSTGamedaysAndHourToken ():
+ currentTime = GemRB.GetGameTime()
+ hours = (currentTime % 7200) / 300
+ if hours < 12:
+ ampm = "AM"
+ else:
+ ampm = "PM"
+ hours -= 12
+ minutes = (currentTime % 300) / 60
+
+ GemRB.SetToken ('CLOCK_HOUR', str (hours))
+ GemRB.SetToken ('CLOCK_MINUTE', '%02d' %minutes)
+ GemRB.SetToken ('CLOCK_AMPM', ampm)
+
+def UpdateClock():
+ ActionsWindow = GemRB.LoadWindow(0)
+ Button = ActionsWindow.GetControl (0)
+ SetPSTGamedaysAndHourToken ()
+ Button.SetTooltip (65027)
+
+def UpdateActionsWindow():
+ # pst doesn't need this, but it is one of the core callbacks, so it has to be defined
+ return
+
+def ActionStopPressed ():
+ for i in range (PARTY_SIZE):
+ if GemRB.GameIsPCSelected (i + 1):
+ GemRB.ClearActions (i + 1)
+ return
+
+def ActionTalkPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_TALK,GA_NO_DEAD|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionAttackPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_ATTACK,GA_NO_DEAD|GA_NO_SELF|GA_NO_HIDDEN)
+
+def ActionDefendPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_DEFEND,GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+def ActionThievingPressed ():
+ GemRB.GameControlSetTargetMode (TARGET_MODE_PICK, GA_NO_DEAD|GA_NO_SELF|GA_NO_ENEMY|GA_NO_HIDDEN)
+
+#this is an unused callback in PST
+def EmptyControls ():
+ return
+
+def CheckLevelUp(pc):
+ GemRB.SetVar ("CheckLevelUp"+str(pc), LUCommon.CanLevelUp (pc))
diff --git a/gemrb/GUIScripts/pst/GUIINV.py b/gemrb/GUIScripts/pst/GUIINV.py
new file mode 100644
index 0000000..c724584
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUIINV.py
@@ -0,0 +1,517 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIINV.py - scripts to control inventory windows from GUIINV winpack
+
+###################################################
+
+from GUIDefines import *
+from ie_stats import *
+from ie_slots import *
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+import InventoryCommon
+
+InventoryWindow = None
+ItemAmountWindow = None
+OverSlot = None
+
+ItemHash = {}
+
+# Control ID's:
+# 0 - 9 eye, rings, earrings, tattoos, etc
+# 10-13 Quick Weapon
+# 14-18 Quick Item
+# 19-23 Quiver
+# 24-43 Personal Item
+# 47-56 Ground Item
+
+# 44 - portrait
+# 45 - ground scrollbar?
+# 46 - encumbrance
+# 0x10000000 + 57 - name
+# 0x10000000 + 58 - AC
+# 0x10000000 + 59 - hp
+# 0x10000000 + 60 - hp max
+# 0x10000000 + 61 - msg
+# 0x10000000 + 62 - party gold
+# 0x10000000 + 63 - class
+
+
+def OpenInventoryWindow ():
+ global AvSlotsTable
+ global InventoryWindow
+
+ AvSlotsTable = GemRB.LoadTable ('avslots')
+
+ if GUICommon.CloseOtherWindow (OpenInventoryWindow):
+ GemRB.HideGUI ()
+ if InventoryWindow:
+ InventoryWindow.Unload ()
+ InventoryWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GemRB.SetVar ("MessageLabel", -1)
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack ("GUIINV")
+ InventoryWindow = Window = GemRB.LoadWindow (3)
+ GemRB.SetVar ("OtherWindow", InventoryWindow.ID)
+ GemRB.SetVar ("MessageLabel", Window.GetControl(0x1000003d).ID)
+
+ # inventory slots
+ for i in range (44):
+ Button = Window.GetControl (i)
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT , OP_OR)
+ Button.SetFont ("NUMBER2")
+ Button.SetVarAssoc ("ItemButton", i)
+ Button.SetBorder (0,0,0,0,0,128,128,255,64,0,1)
+ Button.SetBorder (1,0,0,0,0,255,128,128,64,0,1)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, MouseEnterSlot)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, MouseLeaveSlot)
+
+ # Ground Item
+ for i in range (10):
+ Button = Window.GetControl (i+47)
+ Button.SetVarAssoc ("GroundItemButton", i)
+ Button.SetFlags (IE_GUI_BUTTON_ALIGN_RIGHT , OP_OR)
+ Button.SetFont ("NUMBER2")
+ Button.SetBorder (0,0,0,0,0,128,128,255,64,0,1)
+ Button.SetBorder (1,0,0,0,0,255,128,128,64,0,1)
+ Button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, InventoryCommon.MouseEnterGround)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, InventoryCommon.MouseLeaveGround)
+
+ ScrollBar = Window.GetControl (45)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RefreshInventoryWindow)
+
+ for i in range (57, 64):
+ Label = Window.GetControl (0x10000000 + i)
+ Label.SetText (str (i))
+
+ # portrait
+ Button = Window.GetControl (44)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, OnAutoEquip)
+
+ # encumbrance
+ Button = Window.GetControl (46)
+ Button.SetFont ("NUMBER")
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+
+ # armor class
+ Label = Window.GetControl (0x1000003a)
+ Label.SetTooltip (4197)
+
+ # hp current
+ Label = Window.GetControl (0x1000003b)
+ Label.SetTooltip (4198)
+
+ # hp max
+ Label = Window.GetControl (0x1000003c)
+ Label.SetTooltip (4199)
+
+ # info label, game paused, etc
+ Label = Window.GetControl (0x1000003d)
+ Label.SetTextColor(255, 255, 255)
+ Label.SetText ("")
+ GemRB.SetVar ("TopIndex", 0)
+ GUICommonWindows.SetSelectionChangeHandler (UpdateInventoryWindow)
+ UpdateInventoryWindow ()
+
+ GemRB.UnhideGUI()
+
+
+def UpdateInventoryWindow ():
+ global ItemHash
+ global slot_list
+
+ Window = InventoryWindow
+
+ GUICommonWindows.UpdateAnimation ()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ Container = GemRB.GetContainer (pc, 1)
+ ScrollBar = Window.GetControl (45)
+ Count = Container['ItemCount']
+ if Count<1:
+ Count=1
+ ScrollBar.SetVarAssoc ("TopIndex", Count)
+ RefreshInventoryWindow ()
+ # And now for the items ....
+
+ ItemHash = {}
+
+ # get a list which maps slot number to slot type/icon/tooltip
+ row = GemRB.GetPlayerStat (pc, IE_SPECIFIC)
+ slot_list = map (int, AvSlotsTable.GetValue (row, 1, 0).split( ','))
+
+ # populate inventory slot controls
+ for i in range (46):
+ UpdateSlot (pc, i)
+
+def RefreshInventoryWindow ():
+ Window = InventoryWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # name
+ Label = Window.GetControl (0x10000039)
+ Label.SetText (GemRB.GetPlayerName (pc, 1))
+
+
+ # portrait
+ Button = Window.GetControl (44)
+ Button.SetPicture (GUICommonWindows.GetActorPortrait (pc, 'INVENTORY'))
+
+ GUICommon.SetEncumbranceLabels (Window, 46, None, pc, True)
+
+ # armor class
+ ac = GemRB.GetPlayerStat (pc, IE_ARMORCLASS)
+ ac += GemRB.GetAbilityBonus (IE_DEX, 2, GemRB.GetPlayerStat (pc, IE_DEX) )
+ Label = Window.GetControl (0x1000003a)
+ Label.SetText (str (ac))
+
+ # hp current
+ hp = GemRB.GetPlayerStat (pc, IE_HITPOINTS)
+ Label = Window.GetControl (0x1000003b)
+ Label.SetText (str (hp))
+
+ # hp max
+ hpmax = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)
+ Label = Window.GetControl (0x1000003c)
+ Label.SetText (str (hpmax))
+
+ # party gold
+ Label = Window.GetControl (0x1000003e)
+ Label.SetText (str (GemRB.GameGetPartyGold ()))
+
+ # class
+ text = CommonTables.Classes.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS) - 1, 0)
+
+ Label = Window.GetControl (0x1000003f)
+ Label.SetText (text)
+
+ held = GemRB.GetPlayerStat (pc, IE_HELD) + GemRB.GetPlayerStat (pc, IE_CASTERHOLD)
+ if held == 0 and GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD == 0:
+ Window.SetVisible (WINDOW_VISIBLE)
+ else:
+ Window.SetVisible (WINDOW_GRAYED)
+
+ # update ground inventory slots
+ Container = GemRB.GetContainer(pc, 1)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ for i in range (10):
+ Button = Window.GetControl (i+47)
+ if GemRB.IsDraggingItem ():
+ Button.SetState (IE_GUI_BUTTON_SECOND)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, InventoryCommon.OnDragItemGround)
+ Slot = GemRB.GetContainerItem (pc, i+TopIndex)
+ if Slot != None:
+ item = GemRB.GetItem (Slot['ItemResRef'])
+ identified = Slot["Flags"] & IE_INV_ITEM_IDENTIFIED
+
+ Button.SetItemIcon (Slot['ItemResRef'])
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if not identified or item["ItemNameIdentified"] == -1:
+ Button.SetTooltip (item["ItemName"])
+ Button.EnableBorder (0, 1)
+ else:
+ Button.SetTooltip (item["ItemNameIdentified"])
+ Button.EnableBorder (0, 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, InventoryCommon.OnDragItemGround)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InventoryCommon.OpenGroundItemInfoWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None) #TODO: implement OpenGroundItemAmountWindow
+
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Button.SetTooltip (4273)
+ Button.EnableBorder (0, 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None)
+ return
+
+def UpdateSlot (pc, i):
+ Window = InventoryWindow
+
+ # NOTE: there are invisible items (e.g. MORTEP) in inaccessible slots
+ # used to assign powers and protections
+
+ if slot_list[i]<0:
+ slot = i+1
+ SlotType = GemRB.GetSlotType (slot)
+ slot_item = 0
+ else:
+ slot = slot_list[i]+1
+ SlotType = GemRB.GetSlotType (slot)
+ slot_item = GemRB.GetSlotItem (pc, slot)
+
+ ControlID = SlotType["ID"]
+ if ControlID<0:
+ return
+
+ if GemRB.IsDraggingItem ():
+ #get dragged item
+ drag_item = GemRB.GetSlotItem (0,0)
+ itemname = drag_item["ItemResRef"]
+ drag_item = GemRB.GetItem (itemname)
+ else:
+ itemname = ""
+
+ Button = Window.GetControl (ControlID)
+ ItemHash[ControlID] = [slot, slot_item]
+ Button.SetSprites ('IVSLOT', 0, 0, 1, 2, 3)
+ if slot_item:
+ item = GemRB.GetItem (slot_item['ItemResRef'])
+ identified = slot_item['Flags'] & IE_INV_ITEM_IDENTIFIED
+
+ Button.SetItemIcon (slot_item['ItemResRef'])
+ if item['StackAmount'] > 1:
+ Button.SetText (str (slot_item['Usages0']))
+ else:
+ Button.SetText ('')
+
+ if not identified or item['ItemNameIdentified'] == -1:
+ Button.SetTooltip (item['ItemName'])
+ Button.EnableBorder (0, 1)
+ else:
+ Button.SetTooltip (item['ItemNameIdentified'])
+ Button.EnableBorder (0, 0)
+
+ if GemRB.CanUseItemType (SLOT_ANY, slot_item['ItemResRef'], pc):
+ Button.EnableBorder (1, 0)
+ else:
+ Button.EnableBorder (1, 1)
+
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnDragItem)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InventoryCommon.OpenItemInfoWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, OpenItemAmountWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, OnDragItem)
+ else:
+ Button.SetItemIcon ('')
+ Button.SetText ('')
+
+ if slot_list[i]>=0:
+ Button.SetSprites (SlotType["ResRef"], 0, 0, 1, 2, 3)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, OnDragItem)
+ Button.SetTooltip (SlotType["Tip"])
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_DRAG_DROP, None)
+ Button.SetTooltip ('')
+ itemname = ""
+
+ Button.EnableBorder (0, 0)
+ Button.EnableBorder (1, 0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Button.SetEvent (IE_GUI_BUTTON_ON_SHIFT_PRESS, None)
+
+ if OverSlot == slot-1:
+ if GemRB.CanUseItemType (SlotType["Type"], itemname):
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ if (SlotType["Type"]&SLOT_INVENTORY) or not GemRB.CanUseItemType (SlotType["Type"], itemname):
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_SECOND)
+
+ if slot_item and (GemRB.GetEquippedQuickSlot (pc)==slot or GemRB.GetEquippedAmmunition (pc)==slot):
+ Button.SetState (IE_GUI_BUTTON_THIRD)
+ return
+
+def OnAutoEquip ():
+ if not GemRB.IsDraggingItem ():
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ item = GemRB.GetSlotItem (0,0)
+ ret = GemRB.DropDraggedItem (pc, -1)
+ #this is exactly the same hack as the blackisle guys did it
+ #quite lame, but we should copy their efforts the best
+ if ret == 2 and item and (item['ItemResRef'] == "dustrobe"):
+ GemRB.SetGlobal("APPEARANCE","GLOBAL",2)
+
+ UpdateInventoryWindow ()
+ return
+
+def OnDragItem ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ slot, slot_item = ItemHash[GemRB.GetVar ('ItemButton')]
+
+ if not GemRB.IsDraggingItem ():
+ ResRef = slot_item['ItemResRef']
+ item = GemRB.GetItem (ResRef)
+ GemRB.DragItem (pc, slot, item["ItemIcon"], 0, 0)
+ if slot == 2:
+ GemRB.SetGlobal("APPEARANCE","GLOBAL",0)
+ else:
+ item = GemRB.GetSlotItem (0,0)
+ itemdata = GemRB.GetItem(item['ItemResRef'])
+ GemRB.DropDraggedItem (pc, slot)
+ if slot == 2 and item['ItemResRef'] == 'dustrobe':
+ GemRB.SetGlobal("APPEARANCE","GLOBAL",2)
+ elif slot < 21 and itemdata['AnimationType'] != '':
+ GemRB.SetGlobal("APPEARANCE","GLOBAL",0)
+
+ UpdateInventoryWindow ()
+
+def DragItemAmount ():
+ """Drag a split item."""
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot, slot_item = ItemHash[GemRB.GetVar ('ItemButton')]
+ Text = ItemAmountWindow.GetControl (6)
+ Amount = Text.QueryText ()
+ item = GemRB.GetItem (slot_item["ItemResRef"])
+ GemRB.DragItem (pc, slot, item["ItemIcon"], int ("0"+Amount), 0)
+ OpenItemAmountWindow ()
+ return
+
+def OpenItemAmountWindow ():
+ global ItemAmountWindow
+
+ GemRB.HideGUI ()
+
+ if ItemAmountWindow != None:
+ if ItemAmountWindow:
+ ItemAmountWindow.Unload ()
+ ItemAmountWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ ItemAmountWindow = Window = GemRB.LoadWindow (4)
+ GemRB.SetVar ("FloatWindow", ItemAmountWindow.ID)
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot, slot_item = ItemHash[GemRB.GetVar ('ItemButton')]
+ ResRef = slot_item['ItemResRef']
+ item = GemRB.GetItem (ResRef)
+
+ # item icon
+ Icon = Window.GetControl (0)
+ Icon.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Icon.SetItemIcon (ResRef)
+
+ # item amount
+ Text = Window.GetControl (6)
+ Text.SetSize (40, 40)
+ Text.SetText ("1")
+ Text.SetStatus (IE_GUI_EDIT_NUMBER|IE_GUI_CONTROL_FOCUSED)
+
+ # Done
+ Button = Window.GetControl (2)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DragItemAmount)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (4196)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenItemAmountWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # 0 bmp
+ # 1,2 done/cancel?
+ # 3 +
+ # 4 -
+ # 6 text
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def LeftItemScroll ():
+ tmp = GemRB.GetVar ('ItemButton') - 1
+ if tmp < 0:
+ tmp = 43
+
+ while tmp not in ItemHash or not ItemHash[tmp][1]:
+ tmp = tmp - 1
+ if tmp < 0:
+ tmp = 43
+
+ GemRB.SetVar ('ItemButton', tmp)
+ InventoryCommon.CloseItemInfoWindow ()
+ InventoryCommon.OpenItemInfoWindow ()
+ return
+
+def RightItemScroll ():
+ tmp = GemRB.GetVar ('ItemButton') + 1
+ if tmp > 43:
+ tmp = 0
+
+ while tmp not in ItemHash or not ItemHash[tmp][1]:
+ tmp = tmp + 1
+ if tmp > 43:
+ tmp = 0
+
+ GemRB.SetVar ('ItemButton', tmp)
+ InventoryCommon.CloseItemInfoWindow ()
+ InventoryCommon.OpenItemInfoWindow ()
+ return
+
+def MouseEnterSlot ():
+ global OverSlot
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ('ItemButton')
+ if slot not in ItemHash:
+ return
+ OverSlot = ItemHash[slot][0]-1
+ if GemRB.IsDraggingItem ():
+ for i in range(len(slot_list)):
+ if slot_list[i]==OverSlot:
+ UpdateSlot (pc, i)
+ return
+
+def MouseLeaveSlot ():
+ global OverSlot
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ slot = GemRB.GetVar ('ItemButton')
+ if slot not in ItemHash:
+ return
+ slot = ItemHash[slot][0]-1
+ if slot == OverSlot or not GemRB.IsDraggingItem ():
+ OverSlot = None
+
+ for i in range(len(slot_list)):
+ if slot_list[i]==slot:
+ UpdateSlot (pc, i)
+ return
+
+###################################################
+# End of file GUIINV.py
diff --git a/gemrb/GUIScripts/pst/GUIJRNL.py b/gemrb/GUIScripts/pst/GUIJRNL.py
new file mode 100644
index 0000000..5e5d3f3
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUIJRNL.py
@@ -0,0 +1,455 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIJRNL.py - scripts to control journal/diary windows from GUIJRNL winpack
+
+# GUIJRNL:
+# 0 - main journal window
+# 1 - quests window
+# 2 - beasts window
+# 3 - log/diary window
+
+###################################################
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+
+###################################################
+JournalWindow = None
+LogWindow = None
+BeastsWindow = None
+QuestsWindow = None
+
+# list of all assigned (0) or completed (1) quests
+global quests
+quests = [ [], [] ]
+
+# whether user has chosen assigned (0) or completed (1) quests
+global selected_quest_class
+selected_quest_class = 0
+
+# list of all PC (0) or NPC (1) beasts/creatures
+global beasts
+beasts = [ [], [] ]
+
+# whether user has chosen PC (0) or NPC (1) beasts
+global selected_beast_class
+selected_beast_class = 0
+
+global BeastImage
+BeastImage = None
+StartTime = 0
+
+###################################################
+def OpenJournalWindow ():
+ global JournalWindow, PortraitWindow, ActionsWindow
+ global StartTime
+
+ Table = GemRB.LoadTable("YEARS")
+ StartTime = Table.GetValue("STARTTIME", "VALUE")
+
+ if GUICommon.CloseOtherWindow (OpenJournalWindow):
+ if LogWindow: OpenLogWindow()
+ if BeastsWindow: OpenBeastsWindow()
+ if QuestsWindow: OpenQuestsWindow()
+
+ GemRB.HideGUI ()
+
+ if JournalWindow:
+ JournalWindow.Unload()
+ #making the portraitwindow visible again
+ GUICommonWindows.EnableAnimatedWindows ()
+ GemRB.SetVar ("OtherWindow", -1)
+ PortraitWindow = None
+ ActionsWindow = None
+ JournalWindow = None
+
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack ("GUIJRNL")
+ JournalWindow = GemRB.LoadWindow (0)
+ GemRB.SetVar ("OtherWindow", JournalWindow.ID)
+ GUICommonWindows.DisableAnimatedWindows ()
+
+ # Quests
+ Button = JournalWindow.GetControl (0)
+ Button.SetText (20430)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenQuestsWindow)
+
+ # Beasts
+ Button = JournalWindow.GetControl (1)
+ Button.SetText (20634)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBeastsWindow)
+
+ # Journal
+ Button = JournalWindow.GetControl (2)
+ Button.SetText (20635)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenLogWindow)
+
+ # Done
+ Button = JournalWindow.GetControl (3)
+ Button.SetText (20636)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenJournalWindow)
+
+ #JournalWindow.SetVisible (WINDOW_VISIBLE)
+ GemRB.UnhideGUI()
+
+
+
+###################################################
+def OpenQuestsWindow ():
+ global JournalWindow, QuestsWindow, QuestsList, QuestDesc
+
+ GemRB.HideGUI()
+
+ if QuestsWindow:
+ if QuestsWindow:
+ QuestsWindow.Unload()
+ QuestsWindow = None
+
+ GemRB.SetVar ("OtherWindow", JournalWindow.ID)
+
+ GemRB.UnhideGUI()
+ return
+
+ QuestsWindow = Window = GemRB.LoadWindow (1)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+
+ # Assigned
+ Button = Window.GetControl (8)
+ Button.SetText (39433)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnJournalAssignedPress)
+
+ # Completed
+ Button = Window.GetControl (9)
+ Button.SetText (39434)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnJournalCompletedPress)
+
+ # Back
+ Button = Window.GetControl (5)
+ Button.SetText (46677)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenQuestsWindow)
+
+ # Done
+ Button = Window.GetControl (0)
+ Button.SetText (20636)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenJournalWindow)
+
+ QuestsList = List = Window.GetControl (1)
+ List.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ List.SetVarAssoc ('SelectedQuest', -1)
+ List.SetEvent (IE_GUI_TEXTAREA_ON_CHANGE, OnJournalQuestSelect)
+
+ QuestDesc = Window.GetControl (3)
+
+ EvaluateAllQuests ()
+ PopulateQuestsList ()
+
+ #QuestsWindow.SetVisible (WINDOW_VISIBLE)
+ GemRB.UnhideGUI()
+
+
+def OnJournalQuestSelect ():
+ row = GemRB.GetVar ('SelectedQuest')
+ q = quests[selected_quest_class][row]
+ QuestDesc.SetText (int (q[1]))
+
+def OnJournalAssignedPress ():
+ global selected_quest_class
+
+ # Assigned Quests
+ Label = QuestsWindow.GetControl (0x10000005)
+ Label.SetText (38585)
+
+ selected_quest_class = 0
+ PopulateQuestsList ()
+
+def OnJournalCompletedPress ():
+ global selected_quest_class
+
+ # Completed Quests
+ Label = QuestsWindow.GetControl (0x10000005)
+ Label.SetText (39527)
+
+ selected_quest_class = 1
+ PopulateQuestsList ()
+
+
+def PopulateQuestsList ():
+ GemRB.SetVar ('SelectedQuest', -1)
+ QuestsList.Clear ()
+ QuestDesc.Clear ()
+
+ j = 0
+ for q in quests[selected_quest_class]:
+ title = GemRB.GetINIQuestsKey (str (q[0]), 'title', '0')
+ QuestsList.Append ('- ', j)
+ QuestsList.Append (int (title), j)
+ j = j + 1
+
+def EvaluateCondition (var, value, condition):
+ cur_value = int (GemRB.GetGameVar (var))
+
+ #print cur_value, condition, value
+ if condition == 'EQ':
+ return cur_value == int (value)
+ if condition == 'NE':
+ return cur_value != int (value)
+ elif condition == 'GT':
+ return cur_value > int (value)
+ elif condition == 'LT':
+ return cur_value < int (value)
+ else:
+ print 'Unknown condition in quests.ini:', condition
+ return None
+
+def EvaluateQuest (index):
+ tag = str (index)
+
+ endings = int (GemRB.GetINIQuestsKey (tag, 'possibleEndings', '1'))
+
+ for e in range (endings):
+ if e == 0:
+ suff = ''
+ else:
+ suff = chr (ord ('A') + e)
+
+ completed = 1
+ cc = int (GemRB.GetINIQuestsKey (tag, 'completeChecks' + suff, '0'))
+ for i in range (1, cc + 1):
+ var = GemRB.GetINIQuestsKey (tag, 'cVar' + suff + str (i), '')
+ value = GemRB.GetINIQuestsKey (tag, 'cValue' + suff + str (i), '0')
+ condition = GemRB.GetINIQuestsKey (tag, 'cCondition' + suff + str (i), 'EQ')
+
+ completed = completed and EvaluateCondition (var, value, condition)
+
+ #print 'CC:', var, int (GemRB.GetGameVar (var)), condition, value, ': ', completed
+ if not completed: break
+
+ if completed:
+ #print "COMPLETED", suff
+ desc = GemRB.GetINIQuestsKey (tag, 'descCompleted' + suff, '0')
+ return (1, desc)
+ break
+
+
+ assigned = 1
+ ac = int (GemRB.GetINIQuestsKey (tag, 'assignedChecks', '0'))
+ for i in range (1, ac + 1):
+ var = GemRB.GetINIQuestsKey (tag, 'aVar' + str (i), '')
+ value = GemRB.GetINIQuestsKey (tag, 'aValue' + str (i), '0')
+ condition = GemRB.GetINIQuestsKey (tag, 'aCondition' + str (i), 'EQ')
+
+ assigned = assigned and EvaluateCondition (var, value, condition)
+
+ #print 'AC:', var, condition, value, ': ', assigned
+ if not assigned: break
+
+ if assigned:
+ #print "ASSIGNED"
+ desc = GemRB.GetINIQuestsKey (tag, 'descAssigned', '0')
+ return (0, desc)
+
+ return None
+
+
+def EvaluateAllQuests ():
+ del quests[0][:]
+ del quests[1][:]
+
+ count = int (GemRB.GetINIQuestsKey ('init', 'questcount', '0'))
+ for i in range (count):
+ res = EvaluateQuest (i)
+ if res:
+ quests[res[0]].append ((i, res[1]))
+
+
+###################################################
+
+def OpenBeastsWindow ():
+ global JournalWindow, BeastsWindow, BeastsList, BeastImage, BeastDesc
+
+ GemRB.HideGUI()
+
+ if BeastsWindow:
+ if BeastsWindow:
+ BeastsWindow.Unload()
+ BeastsWindow = None
+
+ GemRB.SetVar ("OtherWindow", JournalWindow.ID)
+
+ GemRB.UnhideGUI()
+ return
+
+ BeastsWindow = Window = GemRB.LoadWindow (2)
+ GemRB.SetVar ("OtherWindow", BeastsWindow.ID)
+
+ # PC
+ Button = BeastsWindow.GetControl (5)
+ Button.SetText (20637)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnJournalPCPress)
+
+ # NPC
+ Button = BeastsWindow.GetControl (6)
+ Button.SetText (20638)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnJournalNPCPress)
+
+ # Back
+ Button = BeastsWindow.GetControl (7)
+ Button.SetText (46677)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBeastsWindow)
+
+ # Done
+ Button = BeastsWindow.GetControl (4)
+ Button.SetText (20636)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenJournalWindow)
+
+ BeastsList = List = Window.GetControl (0)
+ List.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ List.SetVarAssoc ('SelectedBeast', -1)
+ List.SetEvent(IE_GUI_TEXTAREA_ON_CHANGE, OnJournalBeastSelect)
+
+ Window.CreateButton (8, 19, 19, 281, 441)
+ BeastImage = Window.GetControl (8)
+ BeastImage.SetFlags (IE_GUI_BUTTON_PICTURE | IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+
+ BeastDesc = Window.GetControl (2)
+
+ EvaluateAllBeasts ()
+ PopulateBeastsList ()
+
+ GemRB.UnhideGUI()
+ return
+
+def OnJournalBeastSelect ():
+ row = GemRB.GetVar ('SelectedBeast')
+ b = beasts[selected_beast_class][row]
+
+ desc = GemRB.GetINIBeastsKey (str (b), 'desc0', '0')
+ BeastDesc.SetText (int (desc))
+
+ image = GemRB.GetINIBeastsKey (str (b), 'imageKnown', '')
+ BeastImage.SetPicture (image)
+
+def OnJournalPCPress ():
+ global selected_beast_class
+
+ selected_beast_class = 0
+ PopulateBeastsList ()
+
+def OnJournalNPCPress ():
+ global selected_beast_class
+
+ selected_beast_class = 1
+ PopulateBeastsList ()
+
+
+def PopulateBeastsList ():
+ GemRB.SetVar ('SelectedBeast', -1)
+ BeastsList.Clear ()
+ BeastDesc.Clear ()
+ BeastImage.SetPicture ('default')
+
+ j = 0
+ for b in beasts[selected_beast_class]:
+ name = GemRB.GetINIBeastsKey (str (b), 'name', '0')
+ BeastsList.Append (int (name), j)
+ j = j + 1
+
+def EvaluateAllBeasts ():
+ del beasts[0][:]
+ del beasts[1][:]
+
+ count = int (GemRB.GetINIBeastsKey ('init', 'beastcount', '0'))
+
+ j = 0
+ for i in range (count):
+ if not GemRB.GameIsBeastKnown (i):
+ continue
+
+ klass = int (GemRB.GetINIBeastsKey (str (i), 'class', '0'))
+ beasts[klass].append (i)
+
+ j = j + 1
+
+
+###################################################
+
+def OpenLogWindow ():
+ global JournalWindow, LogWindow
+
+ GemRB.HideGUI()
+
+ if LogWindow:
+ if LogWindow:
+ LogWindow.Unload()
+ LogWindow = None
+
+ GemRB.SetVar ("OtherWindow", JournalWindow.ID)
+
+ GemRB.UnhideGUI()
+ return
+
+ LogWindow = Window = GemRB.LoadWindow (3)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+
+ # Back
+ Button = Window.GetControl (1)
+ Button.SetText (46677)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenLogWindow)
+
+ # Done
+ Button = Window.GetControl (0)
+ Button.SetText (20636)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenJournalWindow)
+
+ # text area
+ Text = Window.GetControl (2)
+
+ for i in range (GemRB.GetJournalSize (0)):
+ je = GemRB.GetJournalEntry (0, i)
+
+ if je == None:
+ continue
+
+ # FIXME: the date computed here is wrong by approx. time
+ # of the first journal entry compared to journal in
+ # orig. game. So it's probably computed since "awakening"
+ # there instead of start of the day.
+ # FIXME: use strref 19310 or 64192
+
+ gt = StartTime + je["GameTime"]
+ dt = int (gt/86400)
+ date = str (1 + dt)
+ #time = str (gt - dt*86400)
+
+ Text.Append ("[color=FFFF00]"+GemRB.GetString(19310)+" "+date+"[/color]", 3*i)
+ Text.Append (je['Text'], 3*i + 1)
+ Text.Append ("", 3*i + 2)
+
+ Window.SetVisible (WINDOW_VISIBLE)
+
+ GemRB.UnhideGUI()
+
+###################################################
+# End of file GUIJRNL.py
diff --git a/gemrb/GUIScripts/pst/GUILOAD.py b/gemrb/GUIScripts/pst/GUILOAD.py
new file mode 100644
index 0000000..1fb9bf2
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUILOAD.py
@@ -0,0 +1,180 @@
+# -*-python-*-
+# 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.
+#
+
+# GUILOAD.py - Load game window from GUILOAD winpack
+
+###################################################
+
+import GemRB
+import LoadScreen
+from GUIDefines import *
+
+LoadWindow = 0
+TextAreaControl = 0
+Games = ()
+ScrollBar = 0
+
+def OnLoad ():
+ global LoadWindow, TextAreaControl, Games, ScrollBar
+
+ GemRB.LoadWindowPack ("GUILOAD")
+ LoadWindow = GemRB.LoadWindow (0)
+ CancelButton=LoadWindow.GetControl (46)
+ CancelButton.SetText (4196)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL,OP_OR)
+
+ GemRB.SetVar ("LoadIdx",0)
+
+ for i in range (4):
+ Button = LoadWindow.GetControl (14+i)
+ Button.SetText (28648)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ Button = LoadWindow.GetControl (18+i)
+ Button.SetText (28640)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("LoadIdx",i)
+
+ #area previews
+ Button = LoadWindow.GetControl (1+i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ #PC portraits
+ for j in range (PARTY_SIZE):
+ Button = LoadWindow.GetControl (22+i*PARTY_SIZE+j)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE,OP_SET)
+
+ ScrollBar=LoadWindow.GetControl (13)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games=GemRB.GetSaveGames () #count of games in save folder?
+ if len(Games)>3:
+ TopIndex = len(Games)-4
+ else:
+ TopIndex = 0
+ GemRB.SetVar ("TopIndex",TopIndex)
+ ScrollBar.SetVarAssoc ("TopIndex", TopIndex+1)
+ ScrollBarPress ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def ScrollBarPress ():
+ #draw load game portraits
+ Pos = GemRB.GetVar ("TopIndex")
+ for i in range (4):
+ ActPos = Pos + i
+
+ Button1 = LoadWindow.GetControl (14+i)
+ Button2 = LoadWindow.GetControl (18+i)
+ if ActPos<len(Games):
+ Button1.SetState (IE_GUI_BUTTON_ENABLED)
+ Button2.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState (IE_GUI_BUTTON_DISABLED)
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetName()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl (0x10000004+i)
+ Label.SetText (Slotname)
+
+ if ActPos<len(Games):
+ Slotname = Games[ActPos].GetDate()
+ else:
+ Slotname = ""
+ Label = LoadWindow.GetControl (0x10000008+i)
+ Label.SetText (Slotname)
+
+ Button=LoadWindow.GetControl (1+i)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture ("")
+ for j in range (PARTY_SIZE):
+ Button=LoadWindow.GetControl (22+i*PARTY_SIZE+j)
+ if ActPos<len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture ("")
+ return
+
+def LoadGamePress ():
+ if LoadWindow:
+ LoadWindow.Unload ()
+ Pos = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("LoadIdx")
+ LoadScreen.StartLoadScreen ()
+ GemRB.LoadGame(Games[Pos]) # load & start game
+ GemRB.EnterGame ()
+ return
+
+def DeleteGameConfirm ():
+ global Games
+
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Pos = TopIndex +GemRB.GetVar ("LoadIdx")
+ GemRB.DeleteSaveGame(Games[Pos])
+ if TopIndex>0:
+ GemRB.SetVar ("TopIndex",TopIndex-1)
+ Games=GemRB.GetSaveGames ()
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress ()
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel ():
+ if ConfirmWindow:
+ ConfirmWindow.Unload ()
+ LoadWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress ():
+ global ConfirmWindow
+
+ LoadWindow.SetVisible (WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow (1)
+
+ Text=ConfirmWindow.GetControl (0)
+ Text.SetText (28639)
+
+ DeleteButton=ConfirmWindow.GetControl (1)
+ DeleteButton.SetText (28640)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ DeleteButton.SetFlags (IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ CancelButton=ConfirmWindow.GetControl (2)
+ CancelButton.SetText (4196)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL,OP_OR)
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CancelPress ():
+ if LoadWindow:
+ LoadWindow.Unload ()
+ GemRB.SetNextScript ("Start")
+ return
diff --git a/gemrb/GUIScripts/pst/GUIMA.py b/gemrb/GUIScripts/pst/GUIMA.py
new file mode 100644
index 0000000..33b4beb
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUIMA.py
@@ -0,0 +1,192 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMA.py - scripts to control map windows from GUIMA and GUIWMAP winpacks
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+
+MapWindow = None
+WorldMapWindow = None
+PosX = 0
+PosY = 0
+
+###################################################
+def OpenMapWindow ():
+ global MapWindow
+
+ if GUICommon.CloseOtherWindow (OpenMapWindow):
+ GemRB.HideGUI ()
+ if WorldMapWindow: OpenWorldMapWindowInside ()
+
+ if MapWindow:
+ MapWindow.Unload ()
+ MapWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack ("GUIMA")
+ MapWindow = Window = GemRB.LoadWindow (3)
+ GemRB.SetVar ("OtherWindow", MapWindow.ID)
+
+ # World Map
+ Button = Window.GetControl (0)
+ Button.SetText (20429)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenWorldMapWindow)
+
+ # Add Note
+ Button = Window.GetControl (1)
+ Button.SetText (4182)
+ Button.SetVarAssoc ("x", IE_GUI_MAP_SET_NOTE)
+
+ # Note text
+ Text = Window.GetControl (4)
+ Text.SetText ("")
+
+ Edit = Window.CreateTextEdit (6, 54, 353, 416, 25, "FONTDLG", "")
+
+ # Map Control
+ # ronote and usernote are the pins for the notes
+ # 4 is the Label's control ID
+ Window.CreateMapControl (3, 24, 23, 480, 360, 4, "USERNOTE","RONOTE")
+ Map = Window.GetControl (3)
+ GemRB.SetVar ("x", IE_GUI_MAP_VIEW_NOTES)
+ Map.SetVarAssoc ("x", IE_GUI_MAP_VIEW_NOTES)
+
+ Map.SetEvent (IE_GUI_MAP_ON_PRESS, SetMapNote)
+ Map.SetEvent (IE_GUI_MAP_ON_DOUBLE_PRESS, LeftDoublePressMap)
+
+ MapTable = GemRB.LoadTable( "MAPNAME" )
+ MapName = MapTable.GetValue (GemRB.GetCurrentArea (), "STRING")
+
+ Label = Window.GetControl (0x10000005)
+ Label.SetText (MapName)
+ #Label.SetTextColor (255, 0, 0)
+ # 2 - map name?
+ # 3 - map bitmap?
+ # 4 - ???
+
+ # Done
+ Button = Window.GetControl (5)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMapWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+ GemRB.UnhideGUI ()
+
+def LeftDoublePressMap ():
+ OpenMapWindow()
+ return
+
+def NoteChanged ():
+ #shift focus to the static label
+ Label = MapWindow.GetControl (4)
+ Label.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+ Edit = MapWindow.GetControl (6)
+ Edit.SetEvent (IE_GUI_EDIT_ON_DONE, None)
+ Text = Edit.QueryText ()
+ Edit.SetText ("")
+ GemRB.SetMapnote (PosX, PosY, 0, Text)
+ #the mapcontrol is a bit sluggish, update the label now
+ Label.SetText (Text)
+ return
+
+def SetMapNote ():
+ global PosX, PosY
+
+ if GemRB.GetVar ("x")!=IE_GUI_MAP_SET_NOTE:
+ return
+
+ Label = MapWindow.GetControl (4)
+
+ Edit = MapWindow.GetControl (6)
+ Edit.SetEvent (IE_GUI_EDIT_ON_DONE, NoteChanged)
+ Edit.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+ #copy the text from the static label into the editbox
+ Edit.SetText (Label.QueryText())
+ Label.SetText ("")
+
+ PosX = GemRB.GetVar("MapControlX")
+ PosY = GemRB.GetVar("MapControlY")
+ Map = MapWindow.GetControl (3)
+ GemRB.SetVar ("x", IE_GUI_MAP_VIEW_NOTES)
+ Map.SetVarAssoc ("x", IE_GUI_MAP_VIEW_NOTES)
+ return
+
+def OpenWorldMapWindowInside ():
+ WorldMapWindowCommon (-1)
+ return
+
+def OpenWorldMapWindow ():
+ WorldMapWindowCommon (GemRB.GetVar ("Travel"))
+ return
+
+def WorldMapWindowCommon (Travel):
+ global WorldMapWindow
+
+ GemRB.HideGUI()
+
+ if WorldMapWindow:
+ if WorldMapWindow:
+ WorldMapWindow.Unload ()
+ WorldMapWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommonWindows.EnableAnimatedWindows ()
+ GemRB.UnhideGUI ()
+ return
+
+ GUICommonWindows.DisableAnimatedWindows ()
+ GemRB.LoadWindowPack ("GUIWMAP")
+ WorldMapWindow = Window = GemRB.LoadWindow (0)
+ MapWindow = None
+ GemRB.SetVar ("OtherWindow", WorldMapWindow.ID)
+
+ Window.CreateWorldMapControl (4, 0, 62, 640, 418, Travel, "FONTDLG")
+ WMap = Window.GetControl (4)
+ WMap.SetTextColor (IE_GUI_WMAP_COLOR_BACKGROUND, 0x84, 0x4a, 0x2c, 0x00)
+ WMap.SetTextColor (IE_GUI_WMAP_COLOR_NORMAL, 0x20, 0x20, 0x00, 0xff)
+ WMap.SetTextColor (IE_GUI_WMAP_COLOR_SELECTED, 0x20, 0x20, 0x00, 0xff)
+ WMap.SetTextColor (IE_GUI_WMAP_COLOR_NOTVISITED, 0x20, 0x20, 0x00, 0xa0)
+ WMap.SetAnimation ("WMPTY")
+ #center on current area
+ WMap.AdjustScrolling (0,0)
+
+ # Done
+ Button = Window.GetControl (0)
+ Button.SetText (1403)
+ if Travel>=0:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenWorldMapWindow)
+ else:
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMapWindow)
+ GemRB.UnhideGUI ()
+
+###################################################
+# End of file GUIMA.py
+
diff --git a/gemrb/GUIScripts/pst/GUIMG.py b/gemrb/GUIScripts/pst/GUIMG.py
new file mode 100644
index 0000000..b434116
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUIMG.py
@@ -0,0 +1,272 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIMG.py - scripts to control mage spells windows from GUIMG winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+
+MageWindow = None
+MageSpellInfoWindow = None
+MageSpellLevel = 0
+MageSpellUnmemorizeWindow = None
+
+def OpenMageWindow ():
+ global MageWindow
+
+ if GUICommon.CloseOtherWindow (OpenMageWindow):
+ GemRB.HideGUI ()
+ if MageWindow:
+ MageWindow.Unload ()
+ MageWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack ("GUIMG")
+ MageWindow = Window = GemRB.LoadWindow (3)
+ GemRB.SetVar ("OtherWindow", MageWindow.ID)
+
+ Button = Window.GetControl (0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MagePrevLevelPress)
+
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, MageNextLevelPress)
+
+ # Setup memorized spells buttons
+ for i in range (12):
+ Icon = Window.GetControl (2 + i)
+ Icon.SetBorder (0, 0, 0, 0, 0, 0, 0, 0, 160, 0, 1)
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateMageWindow)
+ GemRB.UnhideGUI ()
+ UpdateMageWindow ()
+
+def UpdateMageWindow ():
+ global MageMemorizedSpellList, MageKnownSpellList
+
+ MageMemorizedSpellList = []
+ MageKnownSpellList = []
+
+ Window = MageWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+ type = IE_SPELL_TYPE_WIZARD
+ level = MageSpellLevel
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, type, level)
+
+ Name = GemRB.GetPlayerName (pc, 1)
+ Label = Window.GetControl (0x10000027)
+ Label.SetText (Name)
+
+ Label = Window.GetControl (0x10000026)
+ GemRB.SetToken ('LEVEL', str (level + 1))
+ Label.SetText (19672)
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+ for i in range (12):
+ Icon = Window.GetControl (2 + i)
+ Icon.SetBorder (0, 0, 0, 0, 0, 0, 0, 0, 160, 0, 1)
+ if i < mem_cnt:
+ ms = GemRB.GetMemorizedSpell (pc, type, level, i)
+ Icon.SetSpellIcon (ms['SpellResRef'])
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ if ms['Flags']:
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellUnmemorizeWindow)
+ else:
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageUnmemorizeSpell)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenMageSpellInfoWindow)
+ spell = GemRB.GetSpell (ms['SpellResRef'])
+ Icon.SetTooltip (spell['SpellName'])
+ MageMemorizedSpellList.append (ms['SpellResRef'])
+ Icon.SetVarAssoc ("SpellButton", i)
+ Icon.EnableBorder (0, ms['Flags'] == 0)
+ else:
+ if i < max_mem_cnt:
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Icon.SetSprites ("IVSLOT", 0, 0, 0, 0, 0)
+ else:
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Icon.SetTooltip ('')
+ Icon.EnableBorder (0, 0)
+
+ #--------------------------test-----------------------------#
+ print "max_mem_cnt is: ", max_mem_cnt
+ print "mem_cnt is: ", mem_cnt
+ known_cnt = GemRB.GetKnownSpellsCount (pc, type, level)
+ for i in range (20):
+ Icon = Window.GetControl (14 + i)
+ if i < known_cnt:
+ ks = GemRB.GetKnownSpell (pc, type, level, i)
+ Icon.SetSpellIcon (ks['SpellResRef'])
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageMemorizeSpell)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenMageSpellInfoWindow)
+ spell = GemRB.GetSpell (ks['SpellResRef'])
+ Icon.SetTooltip (spell['SpellName'])
+ MageKnownSpellList.append (ks['SpellResRef'])
+ Icon.SetVarAssoc ("SpellButton", 100 + i)
+ else:
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Icon.SetTooltip ('')
+ if (CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS), 2)=="*") or \
+ GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+
+def MagePrevLevelPress ():
+ global MageSpellLevel
+
+ if MageSpellLevel > 0:
+ MageSpellLevel = MageSpellLevel - 1
+ UpdateMageWindow ()
+
+
+def MageNextLevelPress ():
+ global MageSpellLevel
+
+ if MageSpellLevel < 8:
+ MageSpellLevel = MageSpellLevel + 1
+ UpdateMageWindow ()
+
+def OpenMageSpellInfoWindow ():
+ global MageSpellInfoWindow
+
+ GemRB.HideGUI ()
+
+ if MageSpellInfoWindow != None:
+ if MageSpellInfoWindow:
+ MageSpellInfoWindow.Unload ()
+ MageSpellInfoWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ MageSpellInfoWindow = Window = GemRB.LoadWindow (4)
+ GemRB.SetVar ("FloatWindow", MageSpellInfoWindow.ID)
+
+ Button = Window.GetControl (4)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellInfoWindow)
+
+ index = GemRB.GetVar ("SpellButton")
+ if index < 100:
+ ResRef = MageMemorizedSpellList[index]
+ else:
+ ResRef = MageKnownSpellList[index - 100]
+
+ spell = GemRB.GetSpell (ResRef)
+
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (spell['SpellName'])
+
+ Icon = Window.GetControl (1)
+ Icon.SetSpellIcon (ResRef)
+
+ Text = Window.GetControl (2)
+ Text.SetText (spell['SpellDesc'])
+
+ IconResRef = 'SPL' + spell['SpellbookIcon'][2:]
+
+ Icon = Window.GetControl (5)
+ Icon.SetSprites (IconResRef, 0, 0, 0, 0, 0)
+
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+
+def OnMageMemorizeSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ if GemRB.MemorizeSpell (pc, type, level, index):
+ UpdateMageWindow ()
+
+ # FIXME: use FLASH.bam
+
+
+def OpenMageSpellUnmemorizeWindow ():
+ global MageSpellUnmemorizeWindow
+
+ GemRB.HideGUI ()
+
+ if MageSpellUnmemorizeWindow != None:
+ if MageSpellUnmemorizeWindow:
+ MageSpellUnmemorizeWindow.Unload ()
+ MageSpellUnmemorizeWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ MageSpellUnmemorizeWindow = Window = GemRB.LoadWindow (6)
+ GemRB.SetVar ("FloatWindow", MageSpellUnmemorizeWindow.ID)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (2)
+ TextArea.SetText (50450)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (42514)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnMageUnmemorizeSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ Button = Window.GetControl (1)
+ Button.SetText (4196)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMageSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+
+def OnMageUnmemorizeSpell ():
+ if MageSpellUnmemorizeWindow:
+ OpenMageSpellUnmemorizeWindow ()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = MageSpellLevel
+ type = IE_SPELL_TYPE_WIZARD
+
+ index = GemRB.GetVar ("SpellButton")
+
+ if GemRB.UnmemorizeSpell (pc, type, level, index):
+ UpdateMageWindow ()
+
+###################################################
+# End of file GUIMG.py
diff --git a/gemrb/GUIScripts/pst/GUIOPT.py b/gemrb/GUIScripts/pst/GUIOPT.py
new file mode 100644
index 0000000..a91d060
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUIOPT.py
@@ -0,0 +1,940 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIOPT.py - scripts to control options windows mostly from GUIOPT winpack
+
+# GUIOPT:
+# 0 - Main options window (peacock tail)
+# 1 - Video options window
+# 2 - msg win with 1 button
+# 3 - msg win with 2 buttons
+# 4 - msg win with 3 buttons
+# 5 - Audio options window
+# 6 - Gameplay options window
+# 8 - Feedback options window
+# 9 - Autopause options window
+
+
+###################################################
+import GemRB
+import GUICommon
+import GUICommonWindows
+import GUISAVE
+from GUIDefines import *
+
+###################################################
+OptionsWindow = None
+VideoOptionsWindow = None
+AudioOptionsWindow = None
+GameplayOptionsWindow = None
+FeedbackOptionsWindow = None
+AutopauseOptionsWindow = None
+LoadMsgWindow = None
+QuitMsgWindow = None
+MoviesWindow = None
+KeysWindow = None
+
+###################################################
+def OpenOptionsWindow ():
+ """Open main options window (peacock tail)"""
+ global OptionsWindow
+
+ if GUICommon.CloseOtherWindow (OpenOptionsWindow):
+ if VideoOptionsWindow: OpenVideoOptionsWindow ()
+ if AudioOptionsWindow: OpenAudioOptionsWindow ()
+ if GameplayOptionsWindow: OpenGameplayOptionsWindow ()
+ if FeedbackOptionsWindow: OpenFeedbackOptionsWindow ()
+ if AutopauseOptionsWindow: OpenAutopauseOptionsWindow ()
+ if LoadMsgWindow: OpenLoadMsgWindow ()
+ if QuitMsgWindow: OpenQuitMsgWindow ()
+ if KeysWindow: OpenKeysWindow ()
+ if MoviesWindow: OpenMoviesWindow ()
+
+ GemRB.HideGUI ()
+ if OptionsWindow:
+ OptionsWindow.Unload ()
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommonWindows.EnableAnimatedWindows ()
+ OptionsWindow = None
+
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack ("GUIOPT")
+ OptionsWindow = Window = GemRB.LoadWindow (0)
+ GemRB.SetVar ("OtherWindow", OptionsWindow.ID)
+ GUICommonWindows.DisableAnimatedWindows ()
+
+ # Return to Game
+ Button = Window.GetControl (0)
+ Button.SetText (28638)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenOptionsWindow)
+
+ # Quit Game
+ Button = Window.GetControl (1)
+ Button.SetText (2595)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenQuitMsgWindow)
+
+ # Load Game
+ Button = Window.GetControl (2)
+ Button.SetText (2592)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenLoadMsgWindow)
+
+ # Save Game
+ Button = Window.GetControl (3)
+ Button.SetText (20639)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUISAVE.OpenSaveWindow)
+
+ # Video Options
+ Button = Window.GetControl (4)
+ Button.SetText (28781)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenVideoOptionsWindow)
+
+ # Audio Options
+ Button = Window.GetControl (5)
+ Button.SetText (29720)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenAudioOptionsWindow)
+
+ # Gameplay Options
+ Button = Window.GetControl (6)
+ Button.SetText (29722)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenGameplayOptionsWindow)
+
+ # Keyboard Mappings
+ Button = Window.GetControl (7)
+ Button.SetText (29723)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenKeyboardMappingsWindow)
+
+ # Movies
+ Button = Window.GetControl (9)
+ Button.SetText (38156) # or 2594
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMoviesWindow)
+
+ # game version, e.g. v1.1.0000
+ Label = Window.GetControl (0x10000007)
+ Label.SetText (GEMRB_VERSION)
+
+ #Window.SetVisible (WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+
+
+
+###################################################
+
+def OpenVideoOptionsWindow ():
+ """Open video options window"""
+ global VideoOptionsWindow, VideoHelpText
+
+ GemRB.HideGUI ()
+
+ if VideoOptionsWindow:
+ if VideoOptionsWindow:
+ VideoOptionsWindow.Unload ()
+ VideoOptionsWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+
+ VideoOptionsWindow = Window = GemRB.LoadWindow (1)
+ GemRB.SetVar ("FloatWindow", VideoOptionsWindow.ID)
+
+
+ VideoHelpText = OptHelpText ('VideoOptions', Window, 9, 31052)
+
+ OptDone ('VideoOptions', Window, 7)
+ OptCancel ('VideoOptions', Window, 8)
+
+ OptSlider ('VideoOptions', 'Brightness', Window, 1, 10, 31234, "Brightness Correction", GammaFeedback, 1)
+ OptSlider ('VideoOptions', 'Contrast', Window, 2, 11, 31429, "Gamma Correction", GammaFeedback, 1)
+
+ OptCheckbox ('VideoOptions', 'SoftwareBlitting', Window, 6, 15, 30898, None) #TODO: SoftBlt
+ OptCheckbox ('VideoOptions', 'SoftwareMirroring', Window, 4, 13, 30896, None) #TODO: SoftMirrorBlt
+ OptCheckbox ('VideoOptions', 'SoftwareTransparency', Window, 5, 14, 30897, None) #TODO: SoftSrcKeyBlt
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def GammaFeedback ():
+ GemRB.SetGamma (GemRB.GetVar("Brightness Correction")/5,GemRB.GetVar("Gamma Correction")/5)
+ return
+
+def DisplayHelpVideoOptions ():
+ VideoHelpText.SetText (31052)
+
+def DisplayHelpBrightness ():
+ VideoHelpText.SetText (31431)
+
+def DisplayHelpContrast ():
+ VideoHelpText.SetText (31459)
+
+def DisplayHelpSoftwareBlitting ():
+ VideoHelpText.SetText (31221)
+
+def DisplayHelpSoftwareMirroring ():
+ VideoHelpText.SetText (31216)
+
+def DisplayHelpSoftwareTransparency ():
+ VideoHelpText.SetText (31220)
+
+
+
+###################################################
+
+saved_audio_options = {}
+
+def OpenAudioOptionsWindow ():
+ """Open audio options window"""
+ global AudioOptionsWindow, AudioHelpText
+
+ GemRB.HideGUI ()
+
+ if AudioOptionsWindow:
+ if AudioOptionsWindow:
+ AudioOptionsWindow.Unload ()
+ AudioOptionsWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ # Restore values in case of cancel
+ if GemRB.GetVar ("Cancel") == 1:
+ for k, v in saved_audio_options.items ():
+ GemRB.SetVar (k, v)
+ UpdateVolume ()
+
+ GemRB.UnhideGUI ()
+ return
+
+
+ AudioOptionsWindow = Window = GemRB.LoadWindow (5)
+ GemRB.SetVar ("FloatWindow", AudioOptionsWindow.ID)
+
+
+ # save values, so we can restore them on cancel
+ for v in "Volume Ambients", "Volume SFX", "Volume Voices", "Volume Music", "Volume Movie", "Sound Processing", "Music Processing":
+ saved_audio_options[v] = GemRB.GetVar (v)
+
+
+ AudioHelpText = OptHelpText ('AudioOptions', Window, 9, 31210)
+
+ OptDone ('AudioOptions', Window, 7)
+ OptCancel ('AudioOptions', Window, 8)
+
+
+ OptSlider ('AudioOptions', 'AmbientVolume', Window, 1, 10, 31460, "Volume Ambients", UpdateVolume)
+ OptSlider ('AudioOptions', 'SoundFXVolume', Window, 2, 11, 31466, "Volume SFX", UpdateVolume)
+ OptSlider ('AudioOptions', 'VoiceVolume', Window, 3, 12, 31467, "Volume Voices", UpdateVolume)
+ OptSlider ('AudioOptions', 'MusicVolume', Window, 4, 13, 31468, "Volume Music", UpdateVolume)
+ OptSlider ('AudioOptions', 'MovieVolume', Window, 5, 14, 31469, "Volume Movie", UpdateVolume)
+
+ OptCheckbox ('AudioOptions', 'CreativeEAX', Window, 6, 15, 30900, "Environmental Audio")
+ OptCheckbox ('AudioOptions', 'SoundProcessing', Window, 16, 17, 63242, "Sound Processing")
+ OptCheckbox ('AudioOptions', 'MusicProcessing', Window, 18, 19, 63243, "Music Processing")
+
+
+ #AudioOptionsWindow.SetVisible (WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def UpdateVolume ():
+ GemRB.UpdateAmbientsVolume ()
+ GemRB.UpdateMusicVolume ()
+
+def DisplayHelpAudioOptions ():
+ AudioHelpText.SetText (31210)
+
+def DisplayHelpAmbientVolume ():
+ AudioHelpText.SetText (31227)
+
+def DisplayHelpSoundFXVolume ():
+ AudioHelpText.SetText (31228)
+
+def DisplayHelpVoiceVolume ():
+ AudioHelpText.SetText (31226)
+
+def DisplayHelpMusicVolume ():
+ AudioHelpText.SetText (31225)
+
+def DisplayHelpMovieVolume ():
+ AudioHelpText.SetText (31229)
+
+def DisplayHelpCreativeEAX ():
+ AudioHelpText.SetText (31224)
+
+def DisplayHelpSoundProcessing ():
+ AudioHelpText.SetText (63244)
+
+def DisplayHelpMusicProcessing ():
+ AudioHelpText.SetText (63247)
+
+
+
+###################################################
+
+def OpenGameplayOptionsWindow ():
+ """Open gameplay options window"""
+ global GameplayOptionsWindow, GameplayHelpText
+
+ GemRB.HideGUI ()
+
+ if GameplayOptionsWindow:
+ if FeedbackOptionsWindow: OpenFeedbackOptionsWindow()
+ if AutopauseOptionsWindow: OpenAutopauseOptionsWindow()
+
+ if GameplayOptionsWindow:
+ GameplayOptionsWindow.Unload ()
+ GameplayOptionsWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+
+ GameplayOptionsWindow = Window = GemRB.LoadWindow (6)
+ GemRB.SetVar ("FloatWindow", GameplayOptionsWindow.ID)
+
+
+ GameplayHelpText = OptHelpText ('GameplayOptions', Window, 12, 31212)
+
+ OptDone ('GameplayOptions', Window, 10)
+ OptCancel ('GameplayOptions', Window, 11)
+
+ OptSlider ('GameplayOptions', 'TooltipDelay', Window, 1, 13, 31481, "Tooltips", UpdateTooltips, TOOLTIP_DELAY_FACTOR)
+ OptSlider ('GameplayOptions', 'MouseScrollingSpeed', Window, 2, 14, 31482, "Mouse Scroll Speed", UpdateMouseSpeed)
+ OptSlider ('GameplayOptions', 'KeyboardScrollingSpeed', Window, 3, 15, 31480, "Keyboard Scroll Speed", UpdateKeyboardSpeed)
+ OptSlider ('GameplayOptions', 'Difficulty', Window, 4, 16, 31479, "Difficulty Level")
+
+ OptCheckbox ('GameplayOptions', 'DitherAlways', Window, 5, 17, 31217, "Always Dither")
+ OptCheckbox ('GameplayOptions', 'Gore', Window, 6, 18, 31218, "Gore???")
+ OptCheckbox ('GameplayOptions', 'AlwaysRun', Window, 22, 23, 62418, "Always Run")
+
+ OptButton ('GameplayOptions', 'FeedbackOptions', Window, 8, 20, 31478)
+ OptButton ('GameplayOptions', 'AutopauseOptions', Window, 9, 21, 31470)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def DisplayHelpGameplayOptions ():
+ GameplayHelpText.SetText (31212)
+
+def UpdateTooltips ():
+ GemRB.SetTooltipDelay (GemRB.GetVar ("Tooltips") )
+
+def DisplayHelpTooltipDelay ():
+ GameplayHelpText.SetText (31232)
+
+def UpdateMouseSpeed ():
+ GemRB.SetMouseScrollSpeed (GemRB.GetVar ("Mouse Scroll Speed") )
+
+def DisplayHelpMouseScrollingSpeed ():
+ GameplayHelpText.SetText (31230)
+
+def UpdateKeyboardSpeed ():
+ #GemRB.SetKeyboardScrollSpeed (GemRB.GetVar ("Keyboard Scroll Speed") )
+ return
+
+def DisplayHelpKeyboardScrollingSpeed ():
+ GameplayHelpText.SetText (31231)
+
+def DisplayHelpDifficulty ():
+ GameplayHelpText.SetText (31233)
+
+def DisplayHelpDitherAlways ():
+ GameplayHelpText.SetText (31222)
+
+def DisplayHelpGore ():
+ GameplayHelpText.SetText (31223)
+
+def DisplayHelpAlwaysRun ():
+ GameplayHelpText.SetText (62419)
+
+def DisplayHelpFeedbackOptions ():
+ GameplayHelpText.SetText (31213)
+
+def DisplayHelpAutopauseOptions ():
+ GameplayHelpText.SetText (31214)
+
+
+
+###################################################
+
+def OpenFeedbackOptionsWindow ():
+ """Open feedback options window"""
+ global FeedbackOptionsWindow, FeedbackHelpText
+
+ GemRB.HideGUI ()
+
+ if FeedbackOptionsWindow:
+ if FeedbackOptionsWindow:
+ FeedbackOptionsWindow.Unload ()
+ FeedbackOptionsWindow = None
+ GemRB.SetVar ("FloatWindow", GameplayOptionsWindow.ID)
+
+ GemRB.UnhideGUI ()
+ GameplayOptionsWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+
+ FeedbackOptionsWindow = Window = GemRB.LoadWindow (8)
+ GemRB.SetVar ("FloatWindow", FeedbackOptionsWindow.ID)
+
+
+ FeedbackHelpText = OptHelpText ('FeedbackOptions', Window, 9, 37410)
+
+ OptDone ('FeedbackOptions', Window, 7)
+ OptCancel ('FeedbackOptions', Window, 8)
+
+ OptSlider ('FeedbackOptions', 'MarkerFeedback', Window, 1, 10, 37463, "GUI Feedback Level")
+ OptSlider ('FeedbackOptions', 'LocatorFeedback', Window, 2, 11, 37586, "Locator Feedback Level")
+ OptSlider ('FeedbackOptions', 'SelectionFeedbackLevel', Window, 20, 21, 54879, "Selection Sounds Frequency")
+ OptSlider ('FeedbackOptions', 'CommandFeedbackLevel', Window, 22, 23, 55012, "Command Sounds Frequency")
+
+ OptCheckbox ('FeedbackOptions', 'CharacterStates', Window, 6, 15, 37594, "")
+ OptCheckbox ('FeedbackOptions', 'MiscellaneousMessages', Window, 17, 19, 37596, "")
+ OptCheckbox ('FeedbackOptions', 'ToHitRolls', Window, 3, 12, 37588, "")
+ OptCheckbox ('FeedbackOptions', 'CombatInfo', Window, 4, 13, 37590, "")
+ OptCheckbox ('FeedbackOptions', 'SpellCasting', Window, 5, 14, 37592, "")
+
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def DisplayHelpMarkerFeedback ():
+ FeedbackHelpText.SetText (37411)
+
+def DisplayHelpLocatorFeedback ():
+ FeedbackHelpText.SetText (37447)
+
+def DisplayHelpSelectionFeedbackLevel ():
+ FeedbackHelpText.SetText (54878)
+
+def DisplayHelpCommandFeedbackLevel ():
+ FeedbackHelpText.SetText (54880)
+
+def DisplayHelpCharacterStates ():
+ FeedbackHelpText.SetText (37460)
+
+def DisplayHelpMiscellaneousMessages ():
+ FeedbackHelpText.SetText (37462)
+
+def DisplayHelpToHitRolls ():
+ FeedbackHelpText.SetText (37453)
+
+def DisplayHelpCombatInfo ():
+ FeedbackHelpText.SetText (37457)
+
+def DisplayHelpSpellCasting ():
+ FeedbackHelpText.SetText (37458)
+
+
+###################################################
+
+def OpenAutopauseOptionsWindow ():
+ """Open autopause options window"""
+ global AutopauseOptionsWindow, AutopauseHelpText
+
+ GemRB.HideGUI ()
+
+ if AutopauseOptionsWindow:
+ if AutopauseOptionsWindow:
+ AutopauseOptionsWindow.Unload ()
+ AutopauseOptionsWindow = None
+ GemRB.SetVar ("FloatWindow", GameplayOptionsWindow.ID)
+
+ GemRB.UnhideGUI ()
+ GameplayOptionsWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+
+ AutopauseOptionsWindow = Window = GemRB.LoadWindow (9)
+ GemRB.SetVar ("FloatWindow", AutopauseOptionsWindow.ID)
+
+
+ AutopauseHelpText = OptHelpText ('AutopauseOptions', Window, 1, 31214)
+
+ OptDone ('AutopauseOptions', Window, 16)
+ OptCancel ('AutopauseOptions', Window, 17)
+
+ # Set variable for each checkbox according to a particular bit of
+ # AutoPauseState
+ state = GemRB.GetVar ("Auto Pause State")
+ GemRB.SetVar("AutoPauseState_Unusable", (state & 0x01) != 0 )
+ GemRB.SetVar("AutoPauseState_Attacked", (state & 0x02) != 0 )
+ GemRB.SetVar("AutoPauseState_Hit", (state & 0x04) != 0 )
+ GemRB.SetVar("AutoPauseState_Wounded", (state & 0x08) != 0 )
+ GemRB.SetVar("AutoPauseState_Dead", (state & 0x10) != 0 )
+ GemRB.SetVar("AutoPauseState_NoTarget", (state & 0x20) != 0 )
+ GemRB.SetVar("AutoPauseState_EndRound", (state & 0x40) != 0 )
+
+
+ OptCheckbox ('AutopauseOptions', 'CharacterHit', Window, 2, 9, 37598, "AutoPauseState_Hit", OnAutoPauseClicked)
+ OptCheckbox ('AutopauseOptions', 'CharacterInjured', Window, 3, 10, 37681, "AutoPauseState_Wounded", OnAutoPauseClicked)
+ OptCheckbox ('AutopauseOptions', 'CharacterDead', Window, 4, 11, 37682, "AutoPauseState_Dead", OnAutoPauseClicked)
+ OptCheckbox ('AutopauseOptions', 'CharacterAttacked', Window, 5, 12, 37683, "AutoPauseState_Attacked", OnAutoPauseClicked)
+ OptCheckbox ('AutopauseOptions', 'WeaponUnusable', Window, 6, 13, 37684, "AutoPauseState_Unusable", OnAutoPauseClicked)
+ OptCheckbox ('AutopauseOptions', 'TargetGone', Window, 7, 14, 37685, "AutoPauseState_NoTarget", OnAutoPauseClicked)
+ OptCheckbox ('AutopauseOptions', 'EndOfRound', Window, 8, 15, 37686, "AutoPauseState_EndRound", OnAutoPauseClicked)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+
+
+def OnAutoPauseClicked ():
+ state = (0x01 * GemRB.GetVar("AutoPauseState_Unusable") +
+ 0x02 * GemRB.GetVar("AutoPauseState_Attacked") +
+ 0x04 * GemRB.GetVar("AutoPauseState_Hit") +
+ 0x08 * GemRB.GetVar("AutoPauseState_Wounded") +
+ 0x10 * GemRB.GetVar("AutoPauseState_Dead") +
+ 0x20 * GemRB.GetVar("AutoPauseState_NoTarget") +
+ 0x40 * GemRB.GetVar("AutoPauseState_EndRound"))
+
+ GemRB.SetVar("Auto Pause State", state)
+
+def DisplayHelpCharacterHit ():
+ AutopauseHelpText.SetText (37688)
+
+def DisplayHelpCharacterInjured ():
+ AutopauseHelpText.SetText (37689)
+
+def DisplayHelpCharacterDead ():
+ AutopauseHelpText.SetText (37690)
+
+def DisplayHelpCharacterAttacked ():
+ AutopauseHelpText.SetText (37691)
+
+def DisplayHelpWeaponUnusable ():
+ AutopauseHelpText.SetText (37692)
+
+def DisplayHelpTargetGone ():
+ AutopauseHelpText.SetText (37693)
+
+def DisplayHelpEndOfRound ():
+ AutopauseHelpText.SetText (37694)
+
+
+###################################################
+###################################################
+
+def OpenLoadMsgWindow ():
+ global LoadMsgWindow
+
+ GemRB.HideGUI()
+
+ if LoadMsgWindow:
+ if LoadMsgWindow:
+ LoadMsgWindow.Unload ()
+ LoadMsgWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ LoadMsgWindow = Window = GemRB.LoadWindow (3)
+ GemRB.SetVar ("FloatWindow", LoadMsgWindow.ID)
+
+ # Load
+ Button = Window.GetControl (0)
+ Button.SetText (28648)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, LoadGame)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (4196)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenLoadMsgWindow)
+
+ # Loading a game will destroy ...
+ Text = Window.GetControl (3)
+ Text.SetText (39432)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+
+def LoadGame ():
+ OpenOptionsWindow ()
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ('GUILOAD')
+
+
+
+###################################################
+
+def OpenQuitMsgWindow ():
+ global QuitMsgWindow
+
+ #GemRB.HideGUI()
+
+ if QuitMsgWindow:
+ if QuitMsgWindow:
+ QuitMsgWindow.Unload ()
+ QuitMsgWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ #GemRB.UnhideGUI ()
+ return
+
+ QuitMsgWindow = Window = GemRB.LoadWindow (4)
+ GemRB.SetVar ("FloatWindow", QuitMsgWindow.ID)
+
+ # Save
+ Button = Window.GetControl (0)
+ Button.SetText (28645)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SaveGame)
+
+ # Quit Game
+ Button = Window.GetControl (1)
+ Button.SetText (2595)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, QuitGame)
+
+ # Cancel
+ Button = Window.GetControl (2)
+ Button.SetText (4196)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenQuitMsgWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # The game has not been saved ....
+ Text = Window.GetControl (3)
+ Text.SetText (39430) # or 39431 - cannot be saved atm
+
+ #GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def QuitGame ():
+ OpenOptionsWindow ()
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ('Start')
+
+def SaveGame ():
+ OpenOptionsWindow ()
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ('GUISAVE')
+
+
+###################################################
+
+key_list = [
+ ('GemRB', None),
+ ('Grab pointer', '^G'),
+ ('Toggle fullscreen', '^F'),
+ ('Enable cheats', '^T'),
+ ('', None),
+
+ ('IE', None),
+ ('Open Inventory', 'I'),
+ ('Open Priest Spells', 'P'),
+ ('Open Mage Spells', 'S'),
+ ('Pause Game', 'SPC'),
+ ('Select Weapon', ''),
+ ('', None),
+ ]
+
+
+KEYS_PAGE_SIZE = 60
+KEYS_PAGE_COUNT = ((len (key_list) - 1) / KEYS_PAGE_SIZE)+ 1
+
+def OpenKeyboardMappingsWindow ():
+ global KeysWindow
+ global last_key_action
+
+ last_key_action = None
+
+ GemRB.HideGUI()
+
+ if KeysWindow:
+ if KeysWindow:
+ KeysWindow.Unload ()
+ KeysWindow = None
+ GemRB.SetVar ("OtherWindow", OptionsWindow.ID)
+
+ GemRB.LoadWindowPack ("GUIOPT")
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.LoadWindowPack ("GUIKEYS")
+ KeysWindow = Window = GemRB.LoadWindow (0)
+ GemRB.SetVar ("OtherWindow", KeysWindow.ID)
+
+
+ # Default
+ Button = Window.GetControl (3)
+ Button.SetText (49051)
+ #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+
+ # Done
+ Button = Window.GetControl (4)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenKeyboardMappingsWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (5)
+ Button.SetText (4196)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenKeyboardMappingsWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ keys_setup_page (0)
+
+
+ #KeysWindow.SetVisible (WINDOW_VISIBLE)
+ GemRB.UnhideGUI ()
+
+
+def keys_setup_page (pageno):
+ Window = KeysWindow
+
+
+ # Page n of n
+ Label = Window.GetControl (0x10000001)
+ #txt = GemRB.ReplaceVarsInText (49053, {'PAGE': str (pageno + 1), 'NUMPAGES': str (KEYS_PAGE_COUNT)})
+ GemRB.SetToken ('PAGE', str (pageno + 1))
+ GemRB.SetToken ('NUMPAGES', str (KEYS_PAGE_COUNT))
+ Label.SetText (49053)
+
+
+ for i in range (KEYS_PAGE_SIZE):
+ try:
+ label, key = key_list[pageno * KEYS_PAGE_SIZE + i]
+ except:
+ label = ''
+ key = None
+
+
+ if key == None:
+ # Section header
+ Label = Window.GetControl (0x10000005 + i)
+ Label.SetText ('')
+
+ Label = Window.GetControl (0x10000041 + i)
+ Label.SetText (label)
+ Label.SetTextColor (0, 255, 255)
+
+ else:
+ Label = Window.GetControl (0x10000005 + i)
+ Label.SetText (key)
+ Label.SetEvent (IE_GUI_LABEL_ON_PRESS, OnActionLabelPress)
+ Label.SetVarAssoc ("KeyAction", i)
+
+ Label = Window.GetControl (0x10000041 + i)
+ Label.SetText (label)
+ Label.SetEvent (IE_GUI_LABEL_ON_PRESS, OnActionLabelPress)
+ Label.SetVarAssoc ("KeyAction", i)
+
+
+
+last_key_action = None
+def OnActionLabelPress ():
+ global last_key_action
+
+ Window = KeysWindow
+ i = GemRB.GetVar ("KeyAction")
+
+ if last_key_action != None:
+ Label = Window.GetControl (0x10000005 + last_key_action)
+ Label.SetTextColor (255, 255, 255)
+ Label = Window.GetControl (0x10000041 + last_key_action)
+ Label.SetTextColor (255, 255, 255)
+
+ Label = Window.GetControl (0x10000005 + i)
+ Label.SetTextColor (255, 255, 0)
+ Label = Window.GetControl (0x10000041 + i)
+ Label.SetTextColor (255, 255, 0)
+
+ last_key_action = i
+
+ # 49155
+
+###################################################
+
+def OpenMoviesWindow ():
+ global MoviesWindow
+
+ GemRB.HideGUI()
+
+ if MoviesWindow:
+ if MoviesWindow:
+ MoviesWindow.Unload ()
+ MoviesWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.LoadWindowPack ("GUIOPT")
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.LoadWindowPack ("GUIMOVIE")
+ # FIXME: clean the window to black
+ MoviesWindow = Window = GemRB.LoadWindow (0)
+ GemRB.SetVar ("FloatWindow", MoviesWindow.ID)
+
+
+ # Play Movie
+ Button = Window.GetControl (2)
+ Button.SetText (33034)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPlayMoviePress)
+
+ # Credits
+ Button = Window.GetControl (3)
+ Button.SetText (33078)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnCreditsPress)
+
+ # Done
+ Button = Window.GetControl (4)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMoviesWindow)
+
+ # movie list
+ List = Window.GetControl (0)
+ List.SetFlags (IE_GUI_TEXTAREA_SELECTABLE)
+ List.SetVarAssoc ('SelectedMovie', -1)
+
+ #Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenMoviesWindow)
+
+
+ MovieTable = GemRB.LoadTable ("MOVIDESC")
+
+ for i in range (MovieTable.GetRowCount ()):
+ #key = MovieTable.GetRowName (i)
+ desc = MovieTable.GetValue (i, 0)
+ List.Append (desc, i)
+
+
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_BLACK)
+
+
+###################################################
+def OnPlayMoviePress ():
+ selected = GemRB.GetVar ('SelectedMovie')
+
+ # FIXME: This should not happen, when the PlayMovie button gets
+ # properly disabled/enabled, but it does not now
+ if selected == -1:
+ return
+
+ MovieTable = GemRB.LoadTable ("MOVIDESC")
+ key = MovieTable.GetRowName (selected)
+
+ GemRB.PlayMovie (key, 1)
+
+###################################################
+def OnCreditsPress ():
+ GemRB.PlayMovie ("CREDITS")
+
+###################################################
+###################################################
+
+# These functions help to setup controls found
+# in Video, Audio, Gameplay, Feedback and Autopause
+# options windows
+
+# These controls are usually made from an active
+# control (button, slider ...) and a label
+
+
+def OptSlider (winname, ctlname, window, slider_id, label_id, label_strref, assoc_var, fn = None, scale = 1):
+ """Standard slider for option windows"""
+ slider = window.GetControl (slider_id)
+ #slider.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, eval("DisplayHelp" + ctlname))
+ #slider.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, eval("DisplayHelp" + winname))
+ if fn: slider.SetEvent (IE_GUI_SLIDER_ON_CHANGE, fn)
+
+ slider.SetVarAssoc (assoc_var, scale)
+
+ label = window.GetControl (label_id)
+ label.SetText (label_strref)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+ #label.SetEvent (IE_GUI_MOUSE_OVER_BUTTON, eval("DisplayHelp" + ctlname))
+ label.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, eval("DisplayHelp" + ctlname))
+ label.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, eval("DisplayHelp" + winname))
+
+ return slider
+
+
+def OptCheckbox (winname, ctlname, window, button_id, label_id, label_strref, assoc_var = None, handler = None):
+ """Standard checkbox for option windows"""
+
+ button = window.GetControl (button_id)
+ button.SetFlags (IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, eval("DisplayHelp" + ctlname))
+ button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, eval("DisplayHelp" + winname))
+ if assoc_var:
+ button.SetVarAssoc (assoc_var, 1)
+ if GemRB.GetVar (assoc_var):
+ button.SetState (IE_GUI_BUTTON_PRESSED)
+ else:
+ button.SetState (IE_GUI_BUTTON_UNPRESSED)
+ else:
+ button.SetState (IE_GUI_BUTTON_UNPRESSED)
+
+ if handler:
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, handler)
+
+ label = window.GetControl (label_id)
+ label.SetText (label_strref)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+ label.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, eval("DisplayHelp" + ctlname))
+ label.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, eval("DisplayHelp" + winname))
+
+ return button
+
+def OptButton (winname, ctlname, window, button_id, label_id, label_strref):
+ """Standard subwindow button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Open%sWindow" %ctlname))
+ button.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, eval("DisplayHelp" + ctlname))
+ button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, eval("DisplayHelp" + winname))
+
+ label = window.GetControl (label_id)
+ label.SetText (label_strref)
+ label.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ label.SetState (IE_GUI_BUTTON_LOCKED)
+ label.SetEvent (IE_GUI_MOUSE_ENTER_BUTTON, eval("DisplayHelp" + ctlname))
+ label.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, eval("DisplayHelp" + winname))
+
+def OptDone (winname, window, button_id):
+ """Standard `Done' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (1403) # Done
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Open%sWindow" %winname))
+ button.SetVarAssoc ("Cancel", 0)
+
+def OptCancel (winname, window, button_id):
+ """Standard `Cancel' button for option windows"""
+ button = window.GetControl (button_id)
+ button.SetText (4196) # Cancel
+ button.SetEvent (IE_GUI_BUTTON_ON_PRESS, eval("Open%sWindow" %winname))
+ button.SetVarAssoc ("Cancel", 1)
+
+def OptHelpText (winname, window, text_id, text_strref):
+ """Standard textarea with context help for option windows"""
+ text = window.GetControl (text_id)
+ text.SetText (text_strref)
+ return text
+
+
+###################################################
+# End of file GUIOPT.py
diff --git a/gemrb/GUIScripts/pst/GUIPR.py b/gemrb/GUIScripts/pst/GUIPR.py
new file mode 100644
index 0000000..81c42d3
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUIPR.py
@@ -0,0 +1,281 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIPR.py - scripts to control priest spells windows from GUIPR winpack
+
+###################################################
+
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+from ie_action import ACT_CAST
+
+PriestWindow = None
+PriestSpellInfoWindow = None
+PriestSpellLevel = 0
+PriestSpellUnmemorizeWindow = None
+
+
+def OpenPriestWindow ():
+ global PriestWindow
+
+ if GUICommon.CloseOtherWindow (OpenPriestWindow):
+ GemRB.HideGUI ()
+ if PriestWindow:
+ PriestWindow.Unload ()
+ PriestWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+
+ GUICommonWindows.SetSelectionChangeHandler (None)
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack ("GUIPR")
+ PriestWindow = Window = GemRB.LoadWindow (3)
+ GemRB.SetVar ("OtherWindow", PriestWindow.ID)
+
+ Button = Window.GetControl (0)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestPrevLevelPress)
+
+ Button = Window.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, PriestNextLevelPress)
+
+ # Setup memorized spells buttons
+ for i in range (12):
+ Icon = Window.GetControl (2 + i)
+ Icon.SetBorder (0, 0, 0, 0, 0, 0, 0, 0, 160, 0, 1)
+
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdatePriestWindow)
+ GemRB.UnhideGUI ()
+ UpdatePriestWindow ()
+
+def UpdatePriestWindow ():
+ global PriestMemorizedSpellList, PriestKnownSpellList
+
+ PriestMemorizedSpellList = []
+ PriestKnownSpellList = []
+
+ Window = PriestWindow
+ pc = GemRB.GameGetSelectedPCSingle ()
+ type = IE_SPELL_TYPE_PRIEST
+ level = PriestSpellLevel
+ max_mem_cnt = GemRB.GetMemorizableSpellsCount (pc, type, level)
+
+ Name = GemRB.GetPlayerName (pc, 1)
+ Label = Window.GetControl (0x10000027)
+ Label.SetText (Name)
+
+ Label = Window.GetControl (0x10000026)
+ GemRB.SetToken ('LEVEL', str (level + 1))
+ Label.SetText (19672)
+
+
+ mem_cnt = GemRB.GetMemorizedSpellsCount (pc, type, level)
+ for i in range (12):
+ Icon = Window.GetControl (2 + i)
+ Icon.SetBorder (0, 0, 0, 0, 0, 0, 0, 0, 160, 0, 1)
+ if i < mem_cnt:
+ ms = GemRB.GetMemorizedSpell (pc, type, level, i)
+ Icon.SetSpellIcon (ms['SpellResRef'])
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ if ms['Flags']:
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellUnmemorizeWindow)
+ else:
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestUnmemorizeSpell)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenPriestSpellInfoWindow)
+ spell = GemRB.GetSpell (ms['SpellResRef'])
+ Icon.SetTooltip (spell['SpellName'])
+ PriestMemorizedSpellList.append (ms['SpellResRef'])
+ Icon.SetVarAssoc ("SpellButton", i)
+ Icon.EnableBorder (0, ms['Flags'] == 0)
+ else:
+ if i < max_mem_cnt:
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Icon.SetSprites ("IVSLOT", 0, 0, 0, 0, 0)
+ else:
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Icon.SetTooltip ('')
+ Icon.EnableBorder (0, 0)
+
+
+ known_cnt = GemRB.GetKnownSpellsCount (pc, type, level)
+ for i in range (20):
+ Icon = Window.GetControl (14 + i)
+ if i < known_cnt:
+ ks = GemRB.GetKnownSpell (pc, type, level, i)
+ Icon.SetSpellIcon (ks['SpellResRef'])
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestMemorizeSpell)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, OpenPriestSpellInfoWindow)
+ spell = GemRB.GetSpell (ks['SpellResRef'])
+ Icon.SetTooltip (spell['SpellName'])
+ PriestKnownSpellList.append (ks['SpellResRef'])
+ Icon.SetVarAssoc ("SpellButton", 100 + i)
+
+ else:
+ Icon.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Icon.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, None)
+ Icon.SetTooltip ('')
+ CantCast = CommonTables.ClassSkills.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS), 1) == "*"
+ CantCast += GemRB.GetPlayerStat(pc, IE_DISABLEDBUTTON)&(1<<ACT_CAST)
+ if CantCast or GemRB.GetPlayerStat (pc, IE_STATE_ID) & STATE_DEAD:
+ Window.SetVisible (WINDOW_GRAYED)
+ else:
+ Window.SetVisible (WINDOW_VISIBLE)
+
+
+def PriestPrevLevelPress ():
+ global PriestSpellLevel
+
+ if PriestSpellLevel > 0:
+ PriestSpellLevel = PriestSpellLevel - 1
+ UpdatePriestWindow ()
+
+
+def PriestNextLevelPress ():
+ global PriestSpellLevel
+
+ if PriestSpellLevel < 5:
+ PriestSpellLevel = PriestSpellLevel + 1
+ UpdatePriestWindow ()
+
+
+def OpenPriestSpellInfoWindow ():
+ global PriestSpellInfoWindow
+
+ GemRB.HideGUI ()
+
+ if PriestSpellInfoWindow != None:
+ if PriestSpellInfoWindow:
+ PriestSpellInfoWindow.Unload ()
+ PriestSpellInfoWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ PriestSpellInfoWindow = Window = GemRB.LoadWindow (4)
+ GemRB.SetVar ("FloatWindow", PriestSpellInfoWindow.ID)
+
+ Button = Window.GetControl (4)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellInfoWindow)
+
+ index = GemRB.GetVar ("SpellButton")
+ if index < 100:
+ ResRef = PriestMemorizedSpellList[index]
+ else:
+ ResRef = PriestKnownSpellList[index - 100]
+
+ spell = GemRB.GetSpell (ResRef)
+
+ Label = Window.GetControl (0x0fffffff)
+ Label.SetText (spell['SpellName'])
+
+ Icon = Window.GetControl (1)
+ Icon.SetSpellIcon (ResRef)
+
+ Text = Window.GetControl (2)
+ Text.SetText (spell['SpellDesc'])
+
+ IconResRef = 'SPL' + spell['SpellbookIcon'][2:]
+
+ Icon = Window.GetControl (5)
+ Icon.SetSprites (IconResRef, 0, 0, 0, 0, 0)
+
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+
+def OnPriestMemorizeSpell ():
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton") - 100
+
+ if GemRB.MemorizeSpell (pc, type, level, index):
+ UpdatePriestWindow ()
+
+ # FIXME: use FLASH.bam
+
+
+def OpenPriestSpellUnmemorizeWindow ():
+ global PriestSpellUnmemorizeWindow
+
+ GemRB.HideGUI ()
+
+ if PriestSpellUnmemorizeWindow != None:
+ if PriestSpellUnmemorizeWindow:
+ PriestSpellUnmemorizeWindow.Unload ()
+ PriestSpellUnmemorizeWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ PriestSpellUnmemorizeWindow = Window = GemRB.LoadWindow (6)
+ GemRB.SetVar ("FloatWindow", PriestSpellUnmemorizeWindow.ID)
+
+ # "Are you sure you want to ....?"
+ TextArea = Window.GetControl (2)
+ TextArea.SetText (50450)
+
+ # Remove
+ Button = Window.GetControl (0)
+ Button.SetText (42514)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OnPriestUnmemorizeSpell)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (1)
+ Button.SetText (4196)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenPriestSpellUnmemorizeWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+
+def OnPriestUnmemorizeSpell ():
+ if PriestSpellUnmemorizeWindow:
+ OpenPriestSpellUnmemorizeWindow ()
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ level = PriestSpellLevel
+ type = IE_SPELL_TYPE_PRIEST
+
+ index = GemRB.GetVar ("SpellButton")
+
+ if GemRB.UnmemorizeSpell (pc, type, level, index):
+ UpdatePriestWindow ()
+
+
+###################################################
+# End of file GUIPR.py
diff --git a/gemrb/GUIScripts/pst/GUIREC.py b/gemrb/GUIScripts/pst/GUIREC.py
new file mode 100644
index 0000000..a1597e9
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUIREC.py
@@ -0,0 +1,1301 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIREC.py - scripts to control stats/records windows from GUIREC winpack
+
+# GUIREC:
+# 0,1,2 - common windows (time, message, menu)
+# 3 - main statistics window
+# 4 - level up win
+# 5 - info - kills, weapons ...
+# 6 - dual class list ???
+# 7 - class panel
+# 8 - skills panel
+# 9 - choose mage spells panel
+# 10 - some small win, 1 button
+# 11 - some small win, 2 buttons
+# 12 - biography?
+# 13 - specialist mage panel
+# 14 - proficiencies
+# 15 - some 2 panel window
+# 16 - some 2 panel window
+# 17 - some 2 panel window
+
+
+# MainWindow:
+# 0 - main textarea
+# 1 - its scrollbar
+# 2 - WMCHRP character portrait
+# 5 - STALIGN alignment
+# 6 - STFCTION faction
+# 7,8,9 - STCSTM (info, reform party, level up)
+# 0x1000000a - name
+#0x1000000b - ac
+#0x1000000c, 0x1000000d hp now, hp max
+#0x1000000e str
+#0x1000000f int
+#0x10000010 wis
+#0x10000011 dex
+#0x10000012 con
+#0x10000013 chr
+#x10000014 race
+#x10000015 sex
+#0x10000016 class
+
+#31-36 stat buts
+#37 ac but
+#38 hp but?
+
+###################################################
+import GemRB
+import GUICommon
+import CommonTables
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+import GUIWORLD
+
+###################################################
+LevelUpWindow = None
+RecordsWindow = None
+InformationWindow = None
+BiographyWindow = None
+
+###################################################
+def OpenRecordsWindow ():
+ global RecordsWindow
+ global StatTable
+
+ StatTable = GemRB.LoadTable("abcomm")
+
+ if GUICommon.CloseOtherWindow (OpenRecordsWindow):
+ GemRB.HideGUI ()
+ if InformationWindow: OpenInformationWindow ()
+
+ if RecordsWindow:
+ RecordsWindow.Unload ()
+ RecordsWindow = None
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommonWindows.SetSelectionChangeHandler (None)
+
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack ("GUIREC")
+ RecordsWindow = Window = GemRB.LoadWindow (3)
+ GemRB.SetVar ("OtherWindow", RecordsWindow.ID)
+
+
+ # Information
+ Button = Window.GetControl (7)
+ Button.SetText (4245)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenInformationWindow)
+
+ # Reform Party
+ Button = Window.GetControl (8)
+ Button.SetText (4244)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIWORLD.OpenReformPartyWindow)
+
+ # Level Up
+ Button = Window.GetControl (9)
+ Button.SetText (4246)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenLevelUpWindow)
+
+ statevents = (OnRecordsHelpStrength, OnRecordsHelpIntelligence, OnRecordsHelpWisdom, OnRecordsHelpDexterity, OnRecordsHelpConstitution, OnRecordsHelpCharisma)
+ # stat buttons
+ for i in range (6):
+ Button = Window.GetControl (31 + i)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetSprites("", 0, 0, 0, 0, 0)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetEvent (IE_GUI_MOUSE_OVER_BUTTON, statevents[i])
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, OnRecordsButtonLeave)
+
+ # AC button
+ Button = Window.GetControl (37)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetSprites("", 0, 0, 0, 0, 0)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetEvent (IE_GUI_MOUSE_OVER_BUTTON, OnRecordsHelpArmorClass)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, OnRecordsButtonLeave)
+
+
+ # HP button
+ Button = Window.GetControl (38)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetSprites ("", 0, 0, 0, 0, 0)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetEvent (IE_GUI_MOUSE_OVER_BUTTON, OnRecordsHelpHitPoints)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, OnRecordsButtonLeave)
+
+
+ GUICommonWindows.SetSelectionChangeHandler (UpdateRecordsWindow)
+ UpdateRecordsWindow ()
+
+ GemRB.UnhideGUI ()
+
+
+stats_overview = None
+faction_help = ''
+alignment_help = ''
+avatar_header = {'PrimClass': "", 'SecoClass': "", 'PrimLevel': 0, 'SecoLevel': 0, 'XP': 0, 'PrimNextLevXP': 0, 'SecoNextLevXP': 0}
+
+def UpdateRecordsWindow ():
+ global stats_overview, faction_help, alignment_help
+
+ Window = RecordsWindow
+ if not RecordsWindow:
+ print "SelectionChange handler points to non existing window\n"
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # Setting up the character information
+ GetCharacterHeader (pc)
+
+ # Checking whether character has leveled up.
+ Button = Window.GetControl (9)
+ if avatar_header['SecoLevel'] == 0:
+ if avatar_header['XP'] >= avatar_header['PrimNextLevXP']:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ # Character is Multi-Class
+ if avatar_header['XP'] >= avatar_header['PrimNextLevXP'] or avatar_header['XP'] >= avatar_header['SecoNextLevXP']:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # name
+ Label = Window.GetControl (0x1000000a)
+ Label.SetText (GemRB.GetPlayerName (pc, 1))
+
+ # portrait
+ Image = Window.GetControl (2)
+ Image.SetState (IE_GUI_BUTTON_LOCKED)
+ Image.SetFlags(IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ Image.SetPicture (GUICommonWindows.GetActorPortrait (pc, 'STATS'))
+
+ # armorclass
+ Label = Window.GetControl (0x1000000b)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_ARMORCLASS)))
+ Label.SetTooltip (4197)
+
+ # hp now
+ Label = Window.GetControl (0x1000000c)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_HITPOINTS)))
+ Label.SetTooltip (4198)
+
+ # hp max
+ Label = Window.GetControl (0x1000000d)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)))
+ Label.SetTooltip (4199)
+
+ # stats
+
+ sstr = GemRB.GetPlayerStat (pc, IE_STR)
+ bstr = GemRB.GetPlayerStat (pc, IE_STR,1)
+ sstrx = GemRB.GetPlayerStat (pc, IE_STREXTRA)
+ bstrx = GemRB.GetPlayerStat (pc, IE_STREXTRA,1)
+ if (sstrx > 0) and (sstr==18):
+ sstr = "%d/%02d" %(sstr, sstrx % 100)
+ if (bstrx > 0) and (bstr==18):
+ bstr = "%d/%02d" %(bstr, bstrx % 100)
+ sint = GemRB.GetPlayerStat (pc, IE_INT)
+ bint = GemRB.GetPlayerStat (pc, IE_INT,1)
+ swis = GemRB.GetPlayerStat (pc, IE_WIS)
+ bwis = GemRB.GetPlayerStat (pc, IE_WIS,1)
+ sdex = GemRB.GetPlayerStat (pc, IE_DEX)
+ bdex = GemRB.GetPlayerStat (pc, IE_DEX,1)
+ scon = GemRB.GetPlayerStat (pc, IE_CON)
+ bcon = GemRB.GetPlayerStat (pc, IE_CON,1)
+ schr = GemRB.GetPlayerStat (pc, IE_CHR)
+ bchr = GemRB.GetPlayerStat (pc, IE_CHR,1)
+
+ stats = (sstr, sint, swis, sdex, scon, schr)
+ basestats = (bstr, bint, bwis, bdex, bcon, bchr)
+ for i in range (6):
+ Label = Window.GetControl (0x1000000e + i)
+ if stats[i]!=basestats[i]:
+ Label.SetTextColor (255, 0, 0)
+ else:
+ Label.SetTextColor (255, 255, 255)
+ Label.SetText (str (stats[i]))
+
+ # race
+ # HACK: for some strange reason, Morte's race is 1 (Human), instead of 45 (Morte)
+ print "species: %d race: %d" %(GemRB.GetPlayerStat (pc, IE_SPECIES), GemRB.GetPlayerStat (pc, IE_RACE))
+ #be careful, some saves got this field corrupted
+ race = GemRB.GetPlayerStat (pc, IE_SPECIES) - 1
+
+ text = CommonTables.Races.GetValue (race, 0)
+
+ Label = Window.GetControl (0x10000014)
+ Label.SetText (text)
+
+
+ # sex
+ GenderTable = GemRB.LoadTable ("GENDERS")
+ text = GenderTable.GetValue (GemRB.GetPlayerStat (pc, IE_SEX) - 1, 0)
+
+ Label = Window.GetControl (0x10000015)
+ Label.SetText (text)
+
+
+ # class
+ text = CommonTables.Classes.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS) - 1, 0)
+
+ Label = Window.GetControl (0x10000016)
+ Label.SetText (text)
+
+ # alignment
+ align = GemRB.GetPlayerStat (pc, IE_ALIGNMENT)
+ ss = GemRB.LoadSymbol ("ALIGN")
+ sym = ss.GetValue (align)
+
+ AlignmentTable = GemRB.LoadTable ("ALIGNS")
+ alignment_help = GemRB.GetString (AlignmentTable.GetValue (sym, 'DESC_REF'))
+ frame = (3 * int (align / 16) + align % 16) - 4
+
+ Button = Window.GetControl (5)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetSprites ('STALIGN', 0, frame, 0, 0, 0)
+ Button.SetEvent (IE_GUI_MOUSE_OVER_BUTTON, OnRecordsHelpAlignment)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, OnRecordsButtonLeave)
+
+
+ # faction
+ faction = GemRB.GetPlayerStat (pc, IE_FACTION)
+ FactionTable = GemRB.LoadTable ("FACTIONS")
+ faction_help = GemRB.GetString (FactionTable.GetValue (faction, 0))
+ frame = FactionTable.GetValue (faction, 1)
+
+ Button = Window.GetControl (6)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetSprites ('STFCTION', 0, frame, 0, 0, 0)
+ Button.SetEvent (IE_GUI_MOUSE_OVER_BUTTON, OnRecordsHelpFaction)
+ Button.SetEvent (IE_GUI_MOUSE_LEAVE_BUTTON, OnRecordsButtonLeave)
+
+ # help, info textarea
+ stats_overview = GetStatOverview (pc)
+ Text = Window.GetControl (0)
+ Text.SetText (stats_overview)
+ return
+
+# puts default info to textarea (overview of PC's bonuses, saves, etc.
+def OnRecordsButtonLeave ():
+ Window = RecordsWindow
+ # help, info textarea
+ Text = Window.GetControl (0)
+ Text.SetText (stats_overview)
+ return
+
+def OnRecordsHelpFaction ():
+ Window = RecordsWindow
+ Help = GemRB.GetString (20106) + "\n\n" + faction_help
+ TextArea = Window.GetControl (0)
+ TextArea.SetText (Help)
+ return
+
+def OnRecordsHelpArmorClass ():
+ Window = RecordsWindow
+ Help = GemRB.GetString (18493)
+ TextArea = Window.GetControl (0)
+ TextArea.SetText (Help)
+ return
+
+def OnRecordsHelpHitPoints ():
+ Window = RecordsWindow
+ Help = GemRB.GetString (18494)
+ TextArea = Window.GetControl (0)
+ TextArea.SetText (Help)
+ return
+
+def OnRecordsHelpAlignment ():
+ Window = RecordsWindow
+ Help = GemRB.GetString (20105) + "\n\n" + alignment_help
+ TextArea = Window.GetControl (0)
+ TextArea.SetText (Help)
+ return
+
+#Bio:
+# 38787 no
+# 39423 morte
+# 39424 annah
+# 39425 dakkon
+# 39426 ffg
+# 39427 ignus
+# 39428 nordom
+# 39429 vhailor
+
+def OnRecordsHelpStrength ():
+ Window = RecordsWindow
+ TextArea = Window.GetControl (0)
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # Getting the character's strength
+ s = GemRB.GetPlayerStat (pc, IE_STR)
+ e = GemRB.GetPlayerStat (pc, IE_STREXTRA)
+
+ x = CommonTables.StrMod.GetValue(s, 0) + CommonTables.StrModEx.GetValue(e, 0)
+ y = CommonTables.StrMod.GetValue(s, 1) + CommonTables.StrModEx.GetValue(e, 1)
+ if x==0:
+ x=y
+ y=0
+ if e>60:
+ s=19
+ TextArea.SetText(18489)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(s,0),x,y) )
+
+ return
+
+def OnRecordsHelpDexterity ():
+ Window = RecordsWindow
+ TextArea = Window.GetControl (0)
+
+ # Loading table of modifications
+ Table = GemRB.LoadTable("dexmod")
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # Getting the character's dexterity
+ Dex = GemRB.GetPlayerStat (pc, IE_DEX)
+
+ # Getting the dexterity description
+ x = -Table.GetValue(Dex,2)
+
+ TextArea.SetText(18487)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Dex,3),x,0) )
+ return
+
+def OnRecordsHelpIntelligence ():
+ Window = RecordsWindow
+ TextArea = Window.GetControl (0)
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # Getting the character's intelligence
+ Int = GemRB.GetPlayerStat (pc, IE_INT)
+
+ TextArea.SetText(18488)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Int,1),0,0) )
+ return
+
+def OnRecordsHelpWisdom ():
+ Window = RecordsWindow
+ TextArea = Window.GetControl (0)
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # Getting the character's wisdom
+ Wis = GemRB.GetPlayerStat (pc, IE_WIS)
+
+ TextArea.SetText(18490)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Wis,2),0,0) )
+ return
+
+def OnRecordsHelpConstitution ():
+ Window = RecordsWindow
+ TextArea = Window.GetControl (0)
+
+ # Loading table of modifications
+ Table = GemRB.LoadTable("hpconbon")
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # Getting the character's constitution
+ Con = GemRB.GetPlayerStat (pc, IE_CON)
+
+ # Getting the constitution description
+ x = Table.GetValue(Con-1,1)
+
+ TextArea.SetText(18491)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Con,4),x,0) )
+ return
+
+def OnRecordsHelpCharisma ():
+ Window = RecordsWindow
+ TextArea = Window.GetControl (0)
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # Getting the character's charisma
+ Cha = GemRB.GetPlayerStat (pc, IE_CHR)
+
+ TextArea.SetText(1903)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Cha,5),0,0) )
+ return
+
+def GetCharacterHeader (pc):
+ global avatar_header
+
+ BioTable = GemRB.LoadTable ("bios")
+
+ Class = GemRB.GetPlayerStat (pc, IE_CLASS) - 1
+ Multi = CommonTables.Classes.GetValue (Class, 4)
+ Specific = "%d"%GemRB.GetPlayerStat (pc, IE_SPECIFIC)
+
+ #Nameless is Specific == 1
+ avatar_header['Specific'] = Specific
+
+ # Nameless is a special case (dual class)
+ if Specific == 1:
+ avatar_header['PrimClass'] = CommonTables.Classes.GetRowName (Class)
+ avatar_header['SecoClass'] = "*"
+
+ avatar_header['SecoLevel'] = 0
+
+ if avatar_header['PrimClass'] == "FIGHTER":
+ avatar_header['PrimLevel'] = GemRB.GetPlayerStat (pc, IE_LEVEL)
+ avatar_header['XP'] = GemRB.GetPlayerStat (pc, IE_XP)
+ elif avatar_header['PrimClass'] == "MAGE":
+ avatar_header['PrimLevel'] = GemRB.GetPlayerStat (pc, IE_LEVEL2)
+ avatar_header['XP'] = GemRB.GetPlayerStat (pc, IE_XP_MAGE)
+ else:
+ avatar_header['PrimLevel'] = GemRB.GetPlayerStat (pc, IE_LEVEL3)
+ avatar_header['XP'] = GemRB.GetPlayerStat (pc, IE_XP_THIEF)
+
+ avatar_header['PrimNextLevXP'] = GetNextLevelExp (avatar_header['PrimLevel'], avatar_header['PrimClass'])
+ avatar_header['SecoNextLevXP'] = 0
+ else:
+ # PC is not NAMELESS_ONE
+ avatar_header['PrimLevel'] = GemRB.GetPlayerStat (pc, IE_LEVEL)
+ avatar_header['XP'] = GemRB.GetPlayerStat (pc, IE_XP)
+ if Multi:
+ avatar_header['XP'] = avatar_header['XP'] / 2
+ avatar_header['SecoLevel'] = GemRB.GetPlayerStat (pc, IE_LEVEL2)
+
+ avatar_header['PrimClass'] = "FIGHTER"
+ if Multi == 3:
+ #fighter/mage
+ Class = 0
+ else:
+ #fighter/thief
+ Class = 3
+ avatar_header['SecoClass'] = CommonTables.Classes.GetRowName (Class)
+
+ avatar_header['PrimNextLevXP'] = GetNextLevelExp (avatar_header['PrimLevel'], avatar_header['PrimClass'])
+ avatar_header['SecoNextLevXP'] = GetNextLevelExp (avatar_header['SecoLevel'], avatar_header['SecoClass'])
+
+ # Converting to the displayable format
+ avatar_header['SecoClass'] = GemRB.GetString (CommonTables.Classes.GetValue (avatar_header['SecoClass'], "NAME_REF"))
+ else:
+ avatar_header['SecoLevel'] = 0
+ avatar_header['PrimClass'] = CommonTables.Classes.GetRowName (Class)
+ avatar_header['SecoClass'] = "*"
+ avatar_header['PrimNextLevXP'] = GetNextLevelExp (avatar_header['PrimLevel'], avatar_header['PrimClass'])
+ avatar_header['SecoNextLevXP'] = 0
+
+ # Converting to the displayable format
+ avatar_header['PrimClass'] = GemRB.GetString (CommonTables.Classes.GetValue (avatar_header['PrimClass'], "NAME_REF"))
+
+
+
+def GetNextLevelExp (Level, Class):
+ if (Level < 20):
+ NextLevel = CommonTables.NextLevel.GetValue (Class, str (Level + 1))
+ else:
+ After21ExpTable = GemRB.LoadTable ("LVL21PLS")
+ ExpGap = After21ExpTable.GetValue (Class, 'XPGAP')
+ LevDiff = Level - 19
+ Lev20Exp = CommonTables.NextLevel.GetValue (Class, "20")
+ NextLevel = Lev20Exp + (LevDiff * ExpGap)
+
+ return NextLevel
+
+
+def GetStatOverview (pc):
+ won = "[color=FFFFFF]"
+ woff = "[/color]"
+ str_None = GemRB.GetString (41275)
+
+ GS = lambda s, pc=pc: GemRB.GetPlayerStat (pc, s)
+
+ stats = []
+
+ # Displaying Class, Level, Experience and Next Level Experience
+ if (avatar_header['SecoLevel'] == 0):
+ stats.append ((avatar_header['PrimClass'], "", 'd'))
+ stats.append ((48156, avatar_header['PrimLevel'], ''))
+ stats.append ((19673, avatar_header['XP'], ''))
+ stats.append ((19674, avatar_header['PrimNextLevXP'], ''))
+ else:
+ stats.append ((19414, "", ''))
+ stats.append (None)
+ stats.append ((avatar_header['PrimClass'], "", 'd'))
+ stats.append ((48156, avatar_header['PrimLevel'], ''))
+ stats.append ((19673, avatar_header['XP'], ''))
+ stats.append ((19674, avatar_header['PrimNextLevXP'], ''))
+ stats.append (None)
+ stats.append ((avatar_header['SecoClass'], "", 'd'))
+ stats.append ((48156, avatar_header['SecoLevel'], ''))
+ stats.append ((19673, avatar_header['XP'], ''))
+ stats.append ((19674, avatar_header['SecoNextLevXP'], ''))
+
+ # 59856 Current State
+ stats.append (None)
+ StatesTable = GemRB.LoadTable ("states")
+ StateID = GS (IE_STATE_ID)
+ State = GemRB.GetString (StatesTable.GetValue (str (StateID), "NAME_REF"))
+ stats.append ((won + GemRB.GetString (59856) + woff, "", 'd'))
+ stats.append ((State, "", 'd'))
+ stats.append (None)
+
+ # 67049 AC Bonuses
+ stats.append (67049)
+ # 67204 AC vs. Slashing
+ stats.append ((67204, GS (IE_ACSLASHINGMOD), ''))
+ # 67205 AC vs. Piercing
+ stats.append ((67205, GS (IE_ACPIERCINGMOD), ''))
+ # 67206 AC vs. Crushing
+ stats.append ((67206, GS (IE_ACCRUSHINGMOD), ''))
+ # 67207 AC vs. Missile
+ stats.append ((67207, GS (IE_ACMISSILEMOD), ''))
+ stats.append (None)
+
+
+ # 67208 Resistances
+ stats.append (67208)
+ # 67209 Normal Fire
+ stats.append ((67209, GS (IE_RESISTFIRE), '%'))
+ # 67210 Magic Fire
+ stats.append ((67210, GS (IE_RESISTMAGICFIRE), '%'))
+ # 67211 Normal Cold
+ stats.append ((67211, GS (IE_RESISTCOLD), '%'))
+ # 67212 Magic Cold
+ stats.append ((67212, GS (IE_RESISTMAGICCOLD), '%'))
+ # 67213 Electricity
+ stats.append ((67213, GS (IE_RESISTELECTRICITY), '%'))
+ # 67214 Acid
+ stats.append ((67214, GS (IE_RESISTACID), '%'))
+ # 67215 Magic
+ stats.append ((67215, GS (IE_RESISTMAGIC), '%'))
+ # 67216 Slashing Attacks
+ stats.append ((67216, GS (IE_RESISTSLASHING), '%'))
+ # 67217 Piercing Attacks
+ stats.append ((67217, GS (IE_RESISTPIERCING), '%'))
+ # 67218 Crushing Attacks
+ stats.append ((67218, GS (IE_RESISTCRUSHING), '%'))
+ # 67219 Missile Attacks
+ stats.append ((67219, GS (IE_RESISTMISSILE), '%'))
+ stats.append (None)
+
+ # 4220 Proficiencies
+ stats.append (4220)
+ # 4208 THAC0
+ stats.append ((4208, GS (IE_TOHIT), ''))
+ # 4209 Number of Attacks
+ tmp = GS (IE_NUMBEROFATTACKS)
+ if (tmp&1):
+ tmp2 = str(tmp/2) + chr(188)
+ else:
+ tmp2 = str(tmp/2)
+
+ stats.append ((4209, tmp2, ''))
+ # 4210 Lore
+ stats.append ((4210, GS (IE_LORE), ''))
+ # 4211 Open Locks
+ stats.append ((4211, GS (IE_LOCKPICKING), '%'))
+ # 4212 Stealth
+ stats.append ((4212, GS (IE_STEALTH), '%'))
+ # 4213 Find/Remove Traps
+ stats.append ((4213, GS (IE_TRAPS), '%'))
+ # 4214 Pick Pockets
+ stats.append ((4214, GS (IE_PICKPOCKET), '%'))
+ # 4215 Tracking
+ stats.append ((4215, GS (IE_TRACKING), ''))
+ # 4216 Reputation
+ stats.append ((4216, GS (IE_REPUTATION), ''))
+ # 4217 Turn Undead Level
+ stats.append ((4217, GS (IE_TURNUNDEADLEVEL), ''))
+ # 4218 Lay on Hands Amount
+ stats.append ((4218, GS (IE_LAYONHANDSAMOUNT), ''))
+ # 4219 Backstab Damage
+ stats.append ((4219, GS (IE_BACKSTABDAMAGEMULTIPLIER), 'x'))
+ stats.append (None)
+
+ # 4221 Saving Throws
+ stats.append (4221)
+ # 4222 Paralyze/Poison/Death
+ stats.append ((4222, GS (IE_SAVEVSDEATH), ''))
+ # 4223 Rod/Staff/Wand
+ stats.append ((4223, GS (IE_SAVEVSWANDS), ''))
+ # 4224 Petrify/Polymorph
+ stats.append ((4224, GS (IE_SAVEVSPOLY), ''))
+ # 4225 Breath Weapon
+ stats.append ((4225, GS (IE_SAVEVSBREATH), ''))
+ # 4226 Spells
+ stats.append ((4226, GS (IE_SAVEVSSPELL), ''))
+ stats.append (None)
+
+ # 4227 Weapon Proficiencies
+ stats.append (4227)
+ # 55011 Unused Slots
+ stats.append ((55011, GS (IE_FREESLOTS), ''))
+ # 33642 Fist
+ stats.append ((33642, GS (IE_PROFICIENCYBASTARDSWORD), '+'))
+ # 33649 Edged Weapon
+ stats.append ((33649, GS (IE_PROFICIENCYLONGSWORD), '+'))
+ # 33651 Hammer
+ stats.append ((33651, GS (IE_PROFICIENCYSHORTSWORD), '+'))
+ # 44990 Axe
+ stats.append ((44990, GS (IE_PROFICIENCYAXE), '+'))
+ # 33653 Club
+ stats.append ((33653, GS (IE_PROFICIENCYTWOHANDEDSWORD), '+'))
+ # 33655 Bow
+ stats.append ((33655, GS (IE_PROFICIENCYKATANA), '+'))
+ stats.append (None)
+
+ # 4228 Ability Bonuses
+ stats.append (4228)
+ # 4229 To Hit
+ # 4230 Damage
+ # 4231 Open Doors
+ # 4232 Weight Allowance
+ # 4233 Armor Class Bonus
+ # 4234 Missile Adjustment
+ stats.append ((4234, GS (IE_ACMISSILEMOD), ''))
+ # 4236 CON HP Bonus/Level
+ # 4240 Reaction
+ stats.append (None)
+
+ # 4238 Magical Defense Adjustment
+ stats.append (4238)
+ # 4239 Bonus Priest Spells
+ stats.append ((4239, GS (IE_CASTINGLEVELBONUSCLERIC), ''))
+ stats.append (None)
+
+ # 4237 Chance to learn spell
+ #SpellLearnChance = won + GemRB.GetString (4237) + woff
+
+ # ??? 4235 Reaction Adjustment
+
+ res = []
+ lines = 0
+ for s in stats:
+ try:
+ strref, val, type = s
+ if val == 0 and type != '0':
+ continue
+ if type == '+':
+ res.append (GemRB.GetString (strref) + ' '+ '+' * val)
+ elif type == 'd': #strref is an already resolved string
+ res.append (strref)
+ elif type == 'x':
+ res.append (GemRB.GetString (strref) + ': x' + str (val))
+ else:
+ res.append (GemRB.GetString (strref) + ': ' + str (val) + type)
+
+ lines = 1
+ except:
+ if s != None:
+ res.append (won + GemRB.GetString (s) + woff)
+ lines = 0
+ else:
+ if not lines:
+ res.append (str_None)
+ res.append ("")
+ lines = 0
+
+ return "\n".join (res)
+ pass
+
+
+def OpenInformationWindow ():
+ global InformationWindow
+
+ GemRB.HideGUI ()
+
+ if InformationWindow != None:
+ if BiographyWindow: OpenBiographyWindow ()
+
+ if InformationWindow:
+ InformationWindow.Unload ()
+ InformationWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI()
+ return
+
+ InformationWindow = Window = GemRB.LoadWindow (5)
+ GemRB.SetVar ("FloatWindow", InformationWindow.ID)
+
+
+ TotalPartyExp = 0
+ TotalPartyKills = 0
+ for i in range (1, GemRB.GetPartySize() + 1):
+ stat = GemRB.GetPCStats(i)
+ TotalPartyExp = TotalPartyExp + stat['KillsChapterXP']
+ TotalPartyKills = TotalPartyKills + stat['KillsChapterCount']
+
+ # These are used to get the stats
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ stat = GemRB.GetPCStats (pc)
+
+ Label = Window.GetControl (0x10000001)
+ Label.SetText (GemRB.GetPlayerName (pc, 1))
+
+
+ # class
+ text = CommonTables.Classes.GetValue (GemRB.GetPlayerStat (pc, IE_CLASS) - 1, 0)
+
+ Label = Window.GetControl (0x1000000A)
+ Label.SetText (text)
+
+
+
+ Label = Window.GetControl (0x10000002)
+ if stat['BestKilledName'] == -1:
+ Label.SetText (GemRB.GetString (41275))
+ else:
+ Label.SetText (GemRB.GetString (stat['BestKilledName']))
+
+ # NOTE: currentTime is in seconds, joinTime is in seconds * 15
+ # (script updates???). In each case, there are 60 seconds
+ # in a minute, 24 hours in a day, but ONLY 5 minutes in an hour!!
+ # Hence currentTime (and joinTime after div by 15) has
+ # 7200 secs a day (60 * 5 * 24)
+ currentTime = GemRB.GetGameTime()
+ joinTime = stat['JoinDate'] - stat['AwayTime']
+
+ party_time = currentTime - (joinTime / 15)
+ days = party_time / 7200
+ hours = (party_time % 7200) / 300
+
+ GemRB.SetToken ('GAMEDAYS', str (days))
+ GemRB.SetToken ('HOUR', str (hours))
+
+ Label = Window.GetControl (0x10000003)
+ Label.SetText (41277)
+
+ Label = Window.GetControl (0x10000004)
+ Label.SetText (stat['FavouriteSpell'])
+
+ Label = Window.GetControl (0x10000005)
+ Label.SetText (stat['FavouriteWeapon'])
+
+ Label = Window.GetControl (0x10000006)
+ if TotalPartyExp != 0:
+ PartyExp = int ((stat['KillsChapterXP'] * 100) / TotalPartyExp)
+ Label.SetText (str (PartyExp) + '%')
+ else:
+ Label.SetText ("0%")
+
+ Label = Window.GetControl (0x10000007)
+ if TotalPartyKills != 0:
+ PartyKills = int ((stat['KillsChapterCount'] * 100) / TotalPartyKills)
+ Label.SetText (str (PartyKills) + '%')
+ else:
+ Label.SetText ('0%')
+
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (str (stat['KillsTotalXP']))
+
+ Label = Window.GetControl (0x10000009)
+ Label.SetText (str (stat['KillsTotalCount']))
+
+
+ Label = Window.GetControl (0x1000000B)
+ Label.SetTextColor (255, 255, 255)
+
+ Label = Window.GetControl (0x1000000C)
+ Label.SetTextColor (255, 255, 255)
+
+ Label = Window.GetControl (0x1000000D)
+ Label.SetTextColor (255, 255, 255)
+
+ Label = Window.GetControl (0x1000000E)
+ Label.SetTextColor (255, 255, 255)
+
+ Label = Window.GetControl (0x1000000F)
+ Label.SetTextColor (255, 255, 255)
+
+ Label = Window.GetControl (0x10000010)
+ Label.SetTextColor (255, 255, 255)
+
+ Label = Window.GetControl (0x10000011)
+ Label.SetTextColor (255, 255, 255)
+
+ Label = Window.GetControl (0x10000012)
+ Label.SetTextColor (255, 255, 255)
+
+
+
+ # Biography
+ Button = Window.GetControl (1)
+ Button.SetText (4247)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyWindow)
+
+ # Done
+ Button = Window.GetControl (0)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenInformationWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+
+
+def OpenBiographyWindow ():
+ global BiographyWindow
+
+ GemRB.HideGUI ()
+
+ if BiographyWindow != None:
+ if BiographyWindow:
+ BiographyWindow.Unload ()
+ BiographyWindow = None
+ GemRB.SetVar ("FloatWindow", InformationWindow.ID)
+
+ GemRB.UnhideGUI()
+ InformationWindow.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+ BiographyWindow = Window = GemRB.LoadWindow (12)
+ GemRB.SetVar ("FloatWindow", BiographyWindow.ID)
+
+ # These are used to get the bio
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ BioTable = GemRB.LoadTable ("bios")
+ Specific = "%d"%GemRB.GetPlayerStat (pc, IE_SPECIFIC)
+ BioText = int (BioTable.GetValue (Specific, 'BIO'))
+
+ TextArea = Window.GetControl (0)
+ TextArea.SetText (BioText)
+
+
+ # Done
+ Button = Window.GetControl (2)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenBiographyWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+
+def AcceptLevelUp():
+ OpenLevelUpWindow()
+ #do level up
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SetPlayerStat (pc, IE_SAVEVSDEATH, SavThrows[0])
+ GemRB.SetPlayerStat (pc, IE_SAVEVSWANDS, SavThrows[1])
+ GemRB.SetPlayerStat (pc, IE_SAVEVSPOLY, SavThrows[2])
+ GemRB.SetPlayerStat (pc, IE_SAVEVSBREATH, SavThrows[3])
+ GemRB.SetPlayerStat (pc, IE_SAVEVSSPELL, SavThrows[4])
+ oldhp = GemRB.GetPlayerStat (pc, IE_HITPOINTS)
+ GemRB.SetPlayerStat (pc, IE_HITPOINTS, HPGained+oldhp)
+ oldhp = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS)
+ GemRB.SetPlayerStat (pc, IE_MAXHITPOINTS, HPGained+oldhp)
+ #increase weapon proficiency if needed
+ if WeapProfType!=-1:
+ GemRB.SetPlayerStat (pc, WeapProfType, CurrWeapProf + WeapProfGained )
+ Specific = GemRB.GetPlayerStat (pc, IE_SPECIFIC)
+ if Specific == 1:
+ #TODO:
+ #the nameless one is dual classed
+ #so we have to determine which level to increase
+ GemRB.SetPlayerStat (pc, IE_LEVEL, GemRB.GetPlayerStat (pc, IE_LEVEL)+NumOfPrimLevUp)
+ else:
+ GemRB.SetPlayerStat (pc, IE_LEVEL, GemRB.GetPlayerStat (pc, IE_LEVEL)+NumOfPrimLevUp)
+ if avatar_header['SecoLevel'] != 0:
+ GemRB.SetPlayerStat (pc, IE_LEVEL2, GemRB.GetPlayerStat (pc, IE_LEVEL2)+NumOfSecoLevUp)
+ UpdateRecordsWindow ()
+
+
+def OpenLevelUpWindow ():
+ global LevelUpWindow
+ global SavThrows
+ global HPGained
+ global WeapProfType, CurrWeapProf, WeapProfGained
+ global NumOfPrimLevUp, NumOfSecoLevUp
+
+ GemRB.HideGUI ()
+
+ if LevelUpWindow != None:
+ if LevelUpWindow:
+ LevelUpWindow.Unload ()
+ LevelUpWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI()
+ return
+
+ LevelUpWindow = Window = GemRB.LoadWindow (4)
+ GemRB.SetVar ("FloatWindow", LevelUpWindow.ID)
+
+ # Accept
+ Button = Window.GetControl (0)
+ Button.SetText (4192)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, AcceptLevelUp)
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ # These are used to identify Nameless One
+ BioTable = GemRB.LoadTable ("bios")
+ Specific = "%d"%GemRB.GetPlayerStat (pc, IE_SPECIFIC)
+ AvatarName = BioTable.GetValue (Specific, "PC")
+
+ # These will be used for saving throws
+ SavThrUpdated = False
+ SavThrows = [0,0,0,0,0]
+ SavThrows[0] = GemRB.GetPlayerStat (pc, IE_SAVEVSDEATH)
+ SavThrows[1] = GemRB.GetPlayerStat (pc, IE_SAVEVSWANDS)
+ SavThrows[2] = GemRB.GetPlayerStat (pc, IE_SAVEVSPOLY)
+ SavThrows[3] = GemRB.GetPlayerStat (pc, IE_SAVEVSBREATH)
+ SavThrows[4] = GemRB.GetPlayerStat (pc, IE_SAVEVSSPELL)
+
+ HPGained = 0
+ ConHPBon = 0
+ Thac0Updated = False
+ Thac0 = 0
+ WeapProfGained = 0
+
+ ClasWeapTable = GemRB.LoadTable ("weapprof")
+ WeapProfType = -1
+ CurrWeapProf = -1
+ #This does not apply to Nameless since he uses unused slots system
+ #Nameless is Specific == 1
+ if Specific != "1":
+ # Searching for the column name where value is 1
+ for i in range (5):
+ WeapProfName = ClasWeapTable.GetRowName (i)
+ value = ClasWeapTable.GetValue (AvatarName, WeapProfName)
+ if value == 1:
+ WeapProfType = i
+ break
+
+ if WeapProfType!=-1:
+ CurrWeapProf = GemRB.GetPlayerStat (pc, IE_WEAPPROF+WeapProfType)
+
+ # Recording this avatar's current proficiency level
+ # Since Nameless one is not covered, hammer and club can't occur
+ # What is the avatar's class (Which we can use to lookup XP)
+ ClassRow = GemRB.GetPlayerStat (pc, IE_CLASS)-1
+ Class = CommonTables.Classes.GetRowName (ClassRow)
+
+ # name
+ Label = Window.GetControl (0x10000000)
+ Label.SetText (GemRB.GetPlayerName (pc, 1))
+
+ # class
+ Label = Window.GetControl (0x10000001)
+ Label.SetText (CommonTables.Classes.GetValue (ClassRow, 0))
+
+ # Armor Class
+ Label = Window.GetControl (0x10000023)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_ARMORCLASS)))
+
+ # Thief Skills
+ # For now we shall set them to the current levels and disable the increment buttons
+ # Points Left
+ Label = Window.GetControl (0x10000006)
+ Label.SetText ('0')
+ # Stealth
+ Label = Window.GetControl (0x10000008)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_STEALTH)) + '%')
+ # Detect Traps
+ Label = Window.GetControl (0x1000000A)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_TRAPS)) + '%')
+ # Pick Pockets
+ Label = Window.GetControl (0x1000000C)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_PICKPOCKET)) + '%')
+ # Open Doors
+ Label = Window.GetControl (0x1000000E)
+ Label.SetText (str (GemRB.GetPlayerStat (pc, IE_LOCKPICKING)) + '%')
+ # Plus and Minus buttons
+ for i in range (8):
+ Button = Window.GetControl (16 + i)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Is avatar multi-class?
+ if avatar_header['SecoLevel'] == 0:
+ # avatar is single class
+ # What will be avatar's next level?
+ NextLevel = avatar_header['PrimLevel'] + 1
+ while avatar_header['XP'] >= GetNextLevelExp (NextLevel, Class):
+ NextLevel = NextLevel + 1
+ NumOfPrimLevUp = NextLevel - avatar_header['PrimLevel'] # How many levels did we go up?
+
+ # Is avatar Nameless One?
+ if Specific == "1":
+ # Saving Throws
+ # Nameless One gets the best possible throws from all the classes except Priest
+ FigSavThrTable = GemRB.LoadTable ("SAVEWAR")
+ MagSavThrTable = GemRB.LoadTable ("SAVEWIZ")
+ ThiSavThrTable = GemRB.LoadTable ("SAVEROG")
+ # For Nameless, we also need to know his levels in every class, so that we pick
+ # the right values from the corresponding tables. Also we substract one, so
+ # that we may use them as column indices in tables.
+ FighterLevel = GemRB.GetPlayerStat (pc, IE_LEVEL) - 1
+ MageLevel = GemRB.GetPlayerStat (pc, IE_LEVEL2) - 1
+ ThiefLevel = GemRB.GetPlayerStat (pc, IE_LEVEL3) - 1
+ # this is the constitution bonus type for this level
+ CONType = 1
+ # We are leveling up one of those levels. Therefore, one of them has to be updated.
+ if avatar_header['PrimClass'] == "FIGHTER":
+ FighterLevel = NextLevel - 1
+ CONType = 0
+ elif avatar_header['PrimClass'] == "MAGE":
+ MageLevel = NextLevel - 1
+ else:
+ ThiefLevel = NextLevel - 1
+
+ ConHPBon = GetConHPBonus (pc, NumOfPrimLevUp, 0, CONType)
+ # Now we need to update the saving throws with the best values from those tables.
+ # The smaller the number, the better saving throw it is.
+ # We also need to check if any of the levels are larger than 21, since
+ # after that point the table runs out, and the throws remain the
+ # same
+ if FighterLevel < 21:
+ for i in range (5):
+ Throw = FigSavThrTable.GetValue (i, FighterLevel)
+ if Throw < SavThrows[i]:
+ SavThrows[i] = Throw
+ SavThrUpdated = True
+ if MageLevel < 21:
+ for i in range (5):
+ Throw = MagSavThrTable.GetValue (i, MageLevel)
+ if Throw < SavThrows[i]:
+ SavThrows[i] = Throw
+ SavThrUpdated = True
+ if ThiefLevel < 21:
+ for i in range (5):
+ Throw = ThiSavThrTable.GetValue (i, ThiefLevel)
+ if Throw < SavThrows[i]:
+ SavThrows[i] = Throw
+ SavThrUpdated = True
+ # Cleaning up
+ else:
+ #How many weapon procifiencies we get
+ for i in range (NumOfPrimLevUp):
+ if HasGainedWeapProf (pc, CurrWeapProf + WeapProfGained, avatar_header['PrimLevel'] + i, avatar_header['PrimClass']):
+ WeapProfGained += 1
+
+ # Saving Throws
+ # Loading the right saving throw table
+ SavThrTable = GemRB.LoadTable (CommonTables.Classes.GetValue (Class, "SAVE"))
+ # Updating the current saving throws. They are changed only if the
+ # new ones are better than current. The smaller the number, the better.
+ # We need to substract one from the NextLevel, so that we get right values.
+ # We also need to check if NextLevel is larger than 21, since after that point
+ # the table runs out, and the throws remain the same
+ if NextLevel < 22:
+ for i in range (5):
+ Throw = SavThrTable.GetValue (i, NextLevel-1)
+ if Throw < SavThrows[i]:
+ SavThrows[i] = Throw
+ SavThrUpdated = True
+ # Cleaning Up
+
+ # Hit Points Gained and Hit Points from Constitution Bonus
+ for i in range (NumOfPrimLevUp):
+ HPGained = HPGained + GetSingleClassHP (Class, avatar_header['PrimLevel'])
+
+ if avatar_header['PrimClass'] == "FIGHTER":
+ CONType = 0
+ else:
+ CONType = 1
+ ConHPBon = GetConHPBonus (pc, NumOfPrimLevUp, 0, CONType)
+
+ # Thac0
+ Thac0 = GetThac0 (Class, NextLevel)
+ # Is the new thac0 better than old? (The smaller, the better)
+ if Thac0 < GemRB.GetPlayerStat (pc, IE_TOHIT):
+ Thac0Updated = True
+
+ else:
+ # avatar is multi class
+ # we have only fighter/X multiclasses, so this
+ # part is a bit hardcoded
+ PrimNextLevel = 0
+ SecoNextLevel = 0
+ NumOfPrimLevUp = 0
+ NumOfSecoLevUp = 0
+
+ # What will be avatar's next levels?
+ PrimNextLevel = avatar_header['PrimLevel']
+ while avatar_header['XP'] >= GetNextLevelExp (PrimNextLevel, "FIGHTER"):
+ PrimNextLevel = PrimNextLevel + 1
+ # How many primary levels did we go up?
+ NumOfPrimLevUp = PrimNextLevel - avatar_header['PrimLevel']
+
+ for i in range (NumOfPrimLevUp):
+ if HasGainedWeapProf (pc, CurrWeapProf + WeapProfGained, avatar_header['PrimLevel'] + i, avatar_header['PrimClass']):
+ WeapProfGained += 1
+
+ # Saving Throws
+ FigSavThrTable = GemRB.LoadTable ("SAVEWAR")
+ if PrimNextLevel < 22:
+ for i in range (5):
+ Throw = FigSavThrTable.GetValue (i, PrimNextLevel - 1)
+ if Throw < SavThrows[i]:
+ SavThrows[i] = Throw
+ SavThrUpdated = True
+ # Which multi class is it?
+ if GemRB.GetPlayerStat (pc, IE_CLASS) == 7:
+ # avatar is Fighter/Mage (Dak'kon)
+ Class = "MAGE"
+ SavThrTable = GemRB.LoadTable ("SAVEWIZ")
+ else:
+ # avatar is Fighter/Thief (Annah)
+ Class = "THIEF"
+ SavThrTable = GemRB.LoadTable ("SAVEROG")
+
+ SecoNextLevel = avatar_header['SecoLevel']
+ while avatar_header['XP'] >= GetNextLevelExp (SecoNextLevel, Class):
+ SecoNextLevel = SecoNextLevel + 1
+ # How many secondary levels did we go up?
+ NumOfSecoLevUp = SecoNextLevel - avatar_header['SecoLevel']
+ if SecoNextLevel < 22:
+ for i in range (5):
+ Throw = SavThrTable.GetValue (i, SecoNextLevel - 1)
+ if Throw < SavThrows[i]:
+ SavThrows[i] = Throw
+ SavThrUpdated = True
+
+ # Hit Points Gained and Hit Points from Constitution Bonus (multiclass)
+ for i in range (NumOfPrimLevUp):
+ HPGained = HPGained + GetSingleClassHP ("FIGHTER", avatar_header['PrimLevel'])/2
+
+ for i in range (NumOfSecoLevUp):
+ HPGained = HPGained + GetSingleClassHP (Class, avatar_header['SecoLevel'])/2
+ ConHPBon = GetConHPBonus (pc, NumOfPrimLevUp, NumOfSecoLevUp, 2)
+
+ # Thac0
+ # Multi class use the primary class level to determine Thac0
+ Thac0 = GetThac0 (Class, PrimNextLevel)
+ # Is the new thac0 better than old? (The smaller the better)
+ if Thac0 < GemRB.GetPlayerStat (pc, IE_TOHIT):
+ Thac0Updated = True
+
+
+ # Displaying the saving throws
+ # Death
+ Label = Window.GetControl (0x10000019)
+ Label.SetText (str (SavThrows[0]))
+ # Wand
+ Label = Window.GetControl (0x1000001B)
+ Label.SetText (str (SavThrows[1]))
+ # Polymorph
+ Label = Window.GetControl (0x1000001D)
+ Label.SetText (str (SavThrows[2]))
+ # Breath
+ Label = Window.GetControl (0x1000001F)
+ Label.SetText (str (SavThrows[3]))
+ # Spell
+ Label = Window.GetControl (0x10000021)
+ Label.SetText (str (SavThrows[4]))
+
+ FinalCurHP = GemRB.GetPlayerStat (pc, IE_HITPOINTS) + HPGained
+ FinalMaxHP = GemRB.GetPlayerStat (pc, IE_MAXHITPOINTS) + HPGained
+
+ # Current HP
+ Label = Window.GetControl (0x10000025)
+ Label.SetText (str (FinalCurHP))
+
+ # Max HP
+ Label = Window.GetControl (0x10000027)
+ Label.SetText (str (FinalMaxHP))
+
+ # Displaying level up info
+ overview = ""
+ if CurrWeapProf!=-1 and WeapProfGained>0:
+ overview = overview + '+' + str (WeapProfGained) + ' ' + GemRB.GetString (WeapProfDispStr) + '\n'
+
+ overview = overview + str (HPGained) + " " + GemRB.GetString (38713) + '\n'
+ overview = overview + str (ConHPBon) + " " + GemRB.GetString (38727) + '\n'
+
+ if SavThrUpdated:
+ overview = overview + GemRB.GetString (38719) + '\n'
+ if Thac0Updated:
+ overview = overview + GemRB.GetString (38718) + '\n'
+
+ Text = Window.GetControl (3)
+ Text.SetText (overview)
+
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+
+def GetSingleClassHP (Class, Level):
+ HPTable = GemRB.LoadTable (CommonTables.Classes.GetValue (Class, "HP"))
+
+ # We need to check if Level is larger than 20, since after that point
+ # the table runs out, and the formula remain the same.
+ if Level > 20:
+ Level = 20
+
+ # We need the Level as a string, so that we can use the column names
+ Level = str (Level)
+
+ Sides = HPTable.GetValue (Level, "SIDES")
+ Rolls = HPTable.GetValue (Level, "ROLLS")
+ Modif = HPTable.GetValue (Level, "MODIFIER")
+
+
+ return GemRB.Roll (Rolls, Sides, Modif)
+
+def GetConHPBonus (pc, numPrimLevels, numSecoLevels, levelUpType):
+ ConHPBonTable = GemRB.LoadTable ("HPCONBON")
+
+ con = str (GemRB.GetPlayerStat (pc, IE_CON))
+ if levelUpType == 0:
+ # Pure fighter
+ return ConHPBonTable.GetValue (con, "WARRIOR") * numPrimLevels
+ if levelUpType == 1:
+ # Mage, Priest or Thief
+ return ConHPBonTable.GetValue (con, "OTHER") * numPrimLevels
+ return ConHPBonTable.GetValue (con, "WARRIOR") * numPrimLevels / 2 + ConHPBonTable.GetValue (con, "OTHER") * numSecoLevels / 2
+
+def GetThac0 (Class, Level):
+ Thac0Table = GemRB.LoadTable ("THAC0")
+ # We need to check if Level is larger than 60, since after that point
+ # the table runs out, and the value remains the same.
+ if Level > 60:
+ Level = 60
+
+ return Thac0Table.GetValue (Class, str (Level))
+
+#apparently the original code doesn't follow profsmax/profs tables
+def HasGainedWeapProf (pc, currProf, currLevel, Class):
+ #only fighters gain weapon proficiencies
+ if Class!="FIGHTER":
+ return False
+ #hardcoded limit is 4
+ if currProf>3:
+ return False
+ if CurrProf>(currLevel-1)/3:
+ return False
+ return True
+
+###################################################
+# End of file GUIREC.py
diff --git a/gemrb/GUIScripts/pst/GUISAVE.py b/gemrb/GUIScripts/pst/GUISAVE.py
new file mode 100644
index 0000000..07c6fd8
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUISAVE.py
@@ -0,0 +1,317 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUISAVE.py - Save game screen from GUISAVE winpack
+
+###################################################
+
+import GemRB
+import GUIClasses
+import LoadScreen
+import GUIOPT
+from GUIDefines import *
+
+SaveWindow = None
+SaveDetailWindow = None
+OptionsWindow = None
+Games = ()
+ScrollBar = 0
+
+
+def OpenSaveWindow ():
+ global SaveWindow, OptionsWindow, Games, ScrollBar
+
+ if SaveWindow:
+ GemRB.HideGUI ()
+
+ if SaveDetailWindow: OpenSaveDetailWindow ()
+
+ if SaveWindow:
+ SaveWindow.Unload ()
+ SaveWindow = None
+ # FIXME: LOAD GUIOPT?
+ GemRB.SetVar ("OtherWindow", OptionsWindow.ID)
+
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack ("GUISAVE", 640, 480)
+ SaveWindow = Window = GemRB.LoadWindow (0)
+ OptionsWindow = GUIClasses.GWindow ( GemRB.GetVar ("OtherWindow") )
+ GemRB.SetVar ("OtherWindow", SaveWindow.ID)
+
+ # Cancel button
+ CancelButton = Window.GetControl (46)
+ CancelButton.SetText (4196)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CancelPress)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+ GemRB.SetVar ("SaveIdx", 0)
+
+ for i in range (4):
+ Button = Window.GetControl (14 + i)
+ Button.SetText (28645)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SaveGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("SaveIdx", i)
+
+ Button = Window.GetControl (18 + i)
+ Button.SetText (28640)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGamePress)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetVarAssoc ("SaveIdx", i)
+
+ # area previews
+ Button = Window.GetControl (1 + i)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE, OP_SET)
+
+ # PC portraits
+ for j in range (6):
+ Button = Window.GetControl (22 + i*6 + j)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE, OP_SET)
+
+ ScrollBar = Window.GetControl (13)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, ScrollBarPress)
+ Games = GemRB.GetSaveGames()
+ TopIndex = max (0, len(Games) - 4 + 1) #one more for the 'new game'
+
+ GemRB.SetVar ("TopIndex",TopIndex)
+ ScrollBar.SetVarAssoc ("TopIndex", len(Games))
+ ScrollBarPress ()
+
+ GemRB.UnhideGUI ()
+
+
+def ScrollBarPress():
+ Window = SaveWindow
+ # draw load game portraits
+ Pos = GemRB.GetVar ("TopIndex")
+ for i in range (4):
+ ActPos = Pos + i
+
+ Button1 = Window.GetControl (14 + i)
+ Button2 = Window.GetControl (18 + i)
+ if ActPos <= len(Games):
+ Button1.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button1.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if ActPos < len(Games):
+ Slotname = Games[ActPos].GetName ()
+ Slottime = Games[ActPos].GetDate ()
+ Button2.SetState (IE_GUI_BUTTON_ENABLED)
+ elif ActPos == len(Games):
+ Slotname = 28647 # "Empty"
+ Slottime = ""
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Slotname = ""
+ Slottime = ""
+ Button2.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Label = Window.GetControl (0x10000004+i)
+ Label.SetText (Slotname)
+
+ Label = Window.GetControl (0x10000008 + i)
+ Label.SetText (Slottime)
+
+ Button = Window.GetControl (1 + i)
+ if ActPos < len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPreview())
+ else:
+ Button.SetPicture ("")
+
+ for j in range (6):
+ Button = Window.GetControl (22 + i*6 + j)
+ if ActPos < len(Games):
+ Button.SetSprite2D(Games[ActPos].GetPortrait(j))
+ else:
+ Button.SetPicture ("")
+
+
+def SaveGamePress ():
+ OpenSaveDetailWindow ()
+ return
+
+def DeleteGameConfirm():
+ global Games
+
+ TopIndex = GemRB.GetVar("TopIndex")
+ Pos = TopIndex +GemRB.GetVar("SaveIdx")
+ GemRB.DeleteSaveGame (Games[Pos])
+ if TopIndex>0:
+ GemRB.SetVar("TopIndex",TopIndex-1)
+ Games = GemRB.GetSaveGames()
+ #del Games[pos]
+ ScrollBar.SetVarAssoc("TopIndex", len(Games))
+ ScrollBarPress()
+ if ConfirmWindow:
+ ConfirmWindow.Unload()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGameCancel():
+ if ConfirmWindow:
+ ConfirmWindow.Unload()
+ SaveWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def DeleteGamePress():
+ global ConfirmWindow
+
+ SaveWindow.SetVisible (WINDOW_INVISIBLE)
+ ConfirmWindow=GemRB.LoadWindow (3)
+
+ Text=ConfirmWindow.GetControl (0)
+ Text.SetText (28639)
+
+ DeleteButton=ConfirmWindow.GetControl (1)
+ DeleteButton.SetText (28640)
+ DeleteButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameConfirm)
+ DeleteButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ CancelButton=ConfirmWindow.GetControl (2)
+ CancelButton.SetText (4196)
+ CancelButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, DeleteGameCancel)
+ CancelButton.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ ConfirmWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def CancelPress():
+ OpenSaveWindow ()
+ return
+
+def OpenSaveDetailWindow ():
+ global SaveDetailWindow
+
+ GemRB.HideGUI ()
+
+ if SaveDetailWindow != None:
+ if SaveDetailWindow:
+ SaveDetailWindow.Unload ()
+ SaveDetailWindow = None
+ GemRB.SetVar ("FloatWindow", -1)
+
+ GemRB.UnhideGUI ()
+ return
+
+ SaveDetailWindow = Window = GemRB.LoadWindow (1)
+ GemRB.SetVar ("FloatWindow", SaveDetailWindow.ID)
+
+
+ Pos = GemRB.GetVar ("TopIndex") + GemRB.GetVar ("SaveIdx")
+
+
+ # Save/Overwrite
+ Button = Window.GetControl (4)
+ if Pos < len(Games):
+ Button.SetText (28644) # Overwrite
+ else:
+ Button.SetText (28645) # Save
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ConfirmedSaveGame)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Cancel
+ Button = Window.GetControl (5)
+ Button.SetText (4196)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenSaveDetailWindow)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ # Slot name and time
+ if Pos < len(Games):
+ Slotname = Games[Pos].GetName ()
+ Slottime = Games[Pos].GetGameDate ()
+ else:
+ Slotname = ""
+ Slottime = ""
+
+ Edit = Window.GetControl (1)
+ Edit.SetText (Slotname)
+ Edit.SetEvent (IE_GUI_EDIT_ON_CHANGE, CheckSaveName)
+ Edit.SetEvent (IE_GUI_EDIT_ON_DONE, ConfirmedSaveGame)
+
+ Label = Window.GetControl (0x10000002)
+ Label.SetText (Slottime)
+
+
+ # Areapreview
+ Button = Window.GetControl (0)
+ Button.SetSprite2D(GemRB.GetGamePreview())
+
+ # PC portraits
+ for j in range (PARTY_SIZE):
+ Button = Window.GetControl (6 + j)
+ Button.SetSprite2D(GemRB.GetGamePortraitPreview(j))
+
+
+ CheckSaveName ()
+ GemRB.UnhideGUI ()
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ Edit.SetStatus (IE_GUI_CONTROL_FOCUSED) # ShowModal will happily reset this..
+
+
+# Disable Save/Overwrite button if the save slotname is empty,
+# else enable it
+def CheckSaveName ():
+ Window = SaveDetailWindow
+ Button = Window.GetControl (4)
+ Edit = Window.GetControl (1)
+ Name = Edit.QueryText ()
+
+ if Name == "":
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+
+
+# User entered save name and pressed save/overwrite.
+# Display progress bar screen and save the game, close the save windows
+def ConfirmedSaveGame ():
+ Window = SaveDetailWindow
+
+ Pos = GemRB.GetVar ("TopIndex") + GemRB.GetVar ("SaveIdx")
+ Label = Window.GetControl (1)
+ Slotname = Label.QueryText ()
+
+ # Empty save name. We can get here if user presses Enter key
+ if Slotname == "":
+ return
+
+ # We have to close floating window first
+ OpenSaveDetailWindow ()
+ LoadScreen.StartLoadScreen (LoadScreen.LS_TYPE_SAVING)
+ if Pos < len(Games):
+ GemRB.SaveGame (Games[Pos], Slotname)
+ else:
+ GemRB.SaveGame (None, Slotname)
+ CloseSaveWindow ()
+
+
+# Exit either back to game or to the Start window
+def CloseSaveWindow ():
+ OpenSaveWindow ()
+ if GemRB.GetVar ("QuitAfterSave"):
+ GemRB.QuitGame ()
+ GemRB.SetNextScript ("Start")
+ return
+
+ GUIOPT.OpenOptionsWindow ()
diff --git a/gemrb/GUIScripts/pst/GUISTORE.py b/gemrb/GUIScripts/pst/GUISTORE.py
new file mode 100644
index 0000000..df07b2c
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUISTORE.py
@@ -0,0 +1,1373 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUISTORE.py - script to open store/inn/temple windows
+
+###################################################
+
+import GemRB
+import GUICommon
+import GUICommonWindows
+from GUIDefines import *
+from ie_stats import *
+from ie_slots import *
+
+StoreWindow = None
+
+MessageWindow = None
+ActionWindow = None
+PortraitWindow = None
+StoreShoppingWindow = None
+StoreIdentifyWindow = None
+StoreStealWindow = None
+StoreDonateWindow = None
+StoreHealWindow = None
+StoreRumourWindow = None
+StoreRentWindow = None
+OldPortraitWindow = None
+RentConfirmWindow = None
+LeftButton = None
+RightButton = None
+CureTable = None
+
+ITEM_PC = 0
+ITEM_STORE = 1
+
+Inventory = None
+RentIndex = -1
+Store = None
+Buttons = [-1,-1,-1,-1]
+inventory_slots = ()
+total_price = 0
+total_income = 0
+
+# 0 - Store
+# 1 - Tavern
+# 2 - Inn
+# 3 - Temple
+# 4 - Container
+# 5 - Container
+
+# 0 - buy/sell
+# 1 - identify
+# 2 - steal
+# 3 - heal
+# 4 - donate
+# 5 - drink
+# 6 - rent
+
+storebams = ("STORSTOR","STORTVRN","STORINN","STORTMPL","STORBAG","STORBAG")
+storetips = (44970, 44971, 44972, 45118, 45121, 45119, 45120)
+roomtypes = (66865, 66866, 66867, 66868)
+store_funcs = None
+
+def CloseWindows ():
+ global StoreShoppingWindow, StoreIdentifyWindow, StoreStealWindow
+ global StoreHealWindow, StoreDonateWindow, StoreRumourWindow, StoreRentWindow
+
+ for win in StoreShoppingWindow, StoreIdentifyWindow, StoreStealWindow, StoreHealWindow, StoreDonateWindow, StoreRumourWindow, StoreRentWindow:
+ if win:
+ win.Unload ()
+
+ StoreShoppingWindow = StoreIdentifyWindow = StoreStealWindow = StoreHealWindow = StoreDonateWindow = StoreRumourWindow = StoreRentWindow = None
+ return
+
+def CloseStoreWindow ():
+ import GUIINV
+ global StoreWindow, ActionWindow, PortraitWindow
+ global OldPortraitWindow
+
+ GemRB.SetVar ("Inventory", 0)
+ CloseWindows ()
+ if StoreWindow:
+ StoreWindow.Unload ()
+ if ActionWindow:
+ ActionWindow.Unload ()
+ if PortraitWindow:
+ PortraitWindow.Unload ()
+ StoreWindow = None
+ GemRB.LeaveStore ()
+ GUICommonWindows.PortraitWindow = OldPortraitWindow
+ if Inventory: # broken if available
+ GUIINV.OpenInventoryWindow ()
+ else:
+ GUICommon.GameWindow.SetVisible(WINDOW_VISIBLE) #enabling the game control screen
+ GemRB.UnhideGUI () #enabling the other windows
+ GUICommonWindows.SetSelectionChangeHandler( None )
+
+ CureTable = None
+ return
+
+def OpenStoreWindow ():
+ global Store
+ global StoreWindow, ActionWindow, PortraitWindow
+ global OldPortraitWindow
+ global store_funcs
+ global Inventory
+ global CureTable
+
+ CureTable = GemRB.LoadTable("speldesc") #additional info not supported by core
+
+ #these are function pointers, not strings
+ #can't put this in global init, doh!
+ store_funcs = (OpenStoreShoppingWindow,
+ OpenStoreIdentifyWindow,OpenStoreStealWindow,
+ OpenStoreHealWindow, OpenStoreDonateWindow,
+ OpenStoreRumourWindow,OpenStoreRentWindow )
+
+ GemRB.HideGUI ()
+ GUICommon.GameWindow.SetVisible(WINDOW_INVISIBLE) #removing the game control screen
+
+ if GemRB.GetVar ("Inventory"):
+ Inventory = 1
+ else:
+ Inventory = None
+
+ GemRB.SetVar ("Action", 0)
+ GemRB.LoadWindowPack ("GUISTORE", 640, 480)
+ StoreWindow = Window = GemRB.LoadWindow (3)
+ #saving the original portrait window
+ OldPortraitWindow = GUICommonWindows.PortraitWindow
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (0)
+ ActionWindow = GemRB.LoadWindow (0)
+ #this window is static and grey, but good to stick the frame onto
+ ActionWindow.SetFrame ()
+
+ Store = GemRB.GetStore ()
+
+ # Done
+ Button = Window.GetControl (0)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseStoreWindow)
+
+ #Store type icon
+ Button = Window.GetControl (5)
+ #Button.SetSprites (storebams[Store['StoreType']],0,0,0,0,0)
+
+ #based on shop type, these buttons will change
+ store_type = Store['StoreType']
+ store_buttons = Store['StoreButtons']
+ for i in range (4):
+ Buttons[i] = Button = Window.GetControl (i+1)
+ Action = store_buttons[i]
+ Button.SetVarAssoc ("Action", i)
+ if Action>=0:
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ #this is different from IWD???
+ #Button.SetSprites ("SSBBS", Action, 0,1,2,0)
+ Button.SetTooltip (storetips[Action])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, store_funcs[Action])
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetTooltip ("")
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, None)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ActionWindow.SetVisible (WINDOW_VISIBLE)
+ Window.SetVisible (WINDOW_VISIBLE)
+ store_funcs[store_buttons[0]] ()
+ PortraitWindow.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreShoppingWindow ():
+ global StoreShoppingWindow
+ global LeftButton, RightButton
+
+ CloseWindows()
+
+ StoreShoppingWindow = Window = GemRB.LoadWindow (4)
+
+ if Inventory:
+ # Title
+ Label = Window.GetControl (0xfffffff)
+ Label.SetText (51881)
+ # buy price ...
+ Label = Window.GetControl (0x1000002b)
+ Label.SetText ("")
+ # sell price ...
+ Label = Window.GetControl (0x1000002c)
+ Label.SetText ("")
+ # buy price ...
+ Label = Window.GetControl (0x1000002f)
+ Label.SetText ("")
+ # sell price ...
+ Label = Window.GetControl (0x10000030)
+ Label.SetText ("")
+ else:
+ # buy price ...
+ Label = Window.GetControl (0x10000003)
+ Label.SetText ("0")
+
+ # sell price ...
+ Label = Window.GetControl (0x10000004)
+ Label.SetText ("0")
+
+ for i in range (4):
+ Button = Window.GetControl (i+8)
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectBuy)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoLeftWindow)
+
+ Button = Window.GetControl (i+17)
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ if Store['StoreType'] != 3: # can't sell to temples
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectSell)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoRightWindow)
+
+ # Buy
+ LeftButton = Button = Window.GetControl (0)
+ if Inventory:
+ Button.SetText (51882)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ToBackpackPressed)
+ else:
+ Button.SetText (45303)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, BuyPressed)
+
+ # Sell
+ RightButton = Button = Window.GetControl (1)
+ if Inventory:
+ Button.SetText (51883)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ToBagPressed)
+ else:
+ Button.SetText (45304)
+ if Store['StoreType'] != 3: # can't sell to temples
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SellPressed)
+
+ ## inactive button
+ #Button = Window.GetControl (50)
+ #Button.SetState (IE_GUI_BUTTON_LOCKED)
+ #Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+
+ ##backpack
+ #Button = Window.GetControl (44)
+ #Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ ## encumbrance
+ Button = Window.GetControl (25)
+ GUICommon.SetEncumbranceLabels (Window, 25, None, GemRB.GameGetSelectedPCSingle ())
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ ##Label = Window.CreateLabel (0x10000019, 15,325,60,15,"NUMBER","0:",IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_TOP)
+ ##Label = Window.CreateLabel (0x10000044, 15,365,80,15,"NUMBER","0:",IE_FONT_ALIGN_RIGHT|IE_FONT_ALIGN_TOP)
+
+ # left scrollbar
+ ScrollBar = Window.GetControl (7)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreShoppingWindow)
+
+ # right scrollbar
+ ScrollBar = Window.GetControl (16)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreShoppingWindow)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreShoppingWindow )
+ UpdateStoreShoppingWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreIdentifyWindow ():
+ global StoreIdentifyWindow
+
+ GemRB.SetVar ("Index", -1)
+ GemRB.SetVar ("TopIndex", 0)
+ CloseWindows()
+
+ StoreIdentifyWindow = Window = GemRB.LoadWindow (5)
+
+ # Identify
+ Button = Window.GetControl (4)
+ Button.SetText (44971)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, IdentifyPressed)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoIdentifyWindow)
+
+ # price ...
+ Label = Window.GetControl (0x10000001)
+ Label.SetText ("0")
+
+ # 8-11 item slots, 0x1000000c-f labels
+ for i in range (4):
+ Button = Window.GetControl (i+6)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RedrawStoreIdentifyWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoIdentifyWindow)
+
+ ScrollBar = Window.GetControl (5)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreIdentifyWindow)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreIdentifyWindow )
+ UpdateStoreIdentifyWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreStealWindow ():
+ global StoreStealWindow
+ global LeftButton
+
+ GemRB.SetVar ("RightIndex", 0)
+ GemRB.SetVar ("LeftIndex", 0)
+ CloseWindows()
+
+ StoreStealWindow = Window = GemRB.LoadWindow (7)
+
+ for i in range (4):
+ Button = Window.GetControl (i+5)
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RedrawStoreStealWindow)
+
+ Button = Window.GetControl (i+14)
+ Button.SetBorder (0,0,0,0,0,0,0,128,160,0,1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoRightWindow)
+
+ # Steal
+ LeftButton = Button = Window.GetControl (0)
+ Button.SetText (45305)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, StealPressed)
+
+ # encumbrance
+ Button = Window.GetControl (22)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ GUICommon.SetEncumbranceLabels (Window, 22, None, GemRB.GameGetSelectedPCSingle ())
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ #Label = Window.CreateLabel (0x10000043, 15,325,60,15,"NUMBER","0:",IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_TOP)
+ #Label = Window.CreateLabel (0x10000044, 15,365,80,15,"NUMBER","0:",IE_FONT_ALIGN_RIGHT|IE_FONT_ALIGN_TOP)
+
+ # left scrollbar
+ ScrollBar = Window.GetControl (4)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreStealWindow)
+
+ # right scrollbar
+ ScrollBar = Window.GetControl (13)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, RedrawStoreStealWindow)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreStealWindow )
+ UpdateStoreStealWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreDonateWindow ():
+ global StoreDonateWindow
+
+ CloseWindows ()
+
+ StoreDonateWindow = Window = GemRB.LoadWindow (10)
+
+ ## graphics
+ #Button = Window.GetControl (10)
+ #Button.SetFlags (IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ANIMATED|IE_GUI_BUTTON_PLAYONCE, OP_OR)
+ #Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ # Donate
+ Button = Window.GetControl (2)
+ Button.SetText (45307)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonateGold)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ # Entry
+ Field = Window.GetControl (3)
+ Field.SetText ("0")
+ Field.SetEvent (IE_GUI_EDIT_ON_CHANGE, UpdateStoreDonateWindow)
+ Field.SetStatus (IE_GUI_EDIT_NUMBER|IE_GUI_CONTROL_FOCUSED)
+
+ # +
+ Button = Window.GetControl (4)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, IncrementDonation)
+ # -
+ Button = Window.GetControl (5)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DecrementDonation)
+
+ GUICommonWindows.SetSelectionChangeHandler( UpdateStoreDonateWindow )
+ UpdateStoreDonateWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreHealWindow ():
+ global StoreHealWindow
+
+ GemRB.SetVar ("Index", -1)
+ GemRB.SetVar ("TopIndex", 0)
+ CloseWindows()
+
+ StoreHealWindow = Window = GemRB.LoadWindow (6)
+
+ #spell buttons
+ for i in range (4):
+ Button = Window.GetControl (i+5)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateStoreHealWindow)
+ Button.SetEvent (IE_GUI_BUTTON_ON_RIGHT_PRESS, InfoHealWindow)
+
+ # price tag
+ Label = Window.GetControl (0x10000001)
+ Label.SetText ("0")
+
+ # Heal
+ Button = Window.GetControl (3)
+ Button.SetText (8836)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, BuyHeal)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ ScrollBar = Window.GetControl (4)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, UpdateStoreHealWindow)
+ Count = Store['StoreCureCount']
+ if Count>4:
+ Count = Count-4
+ else:
+ Count = 0
+ ScrollBar.SetVarAssoc ("TopIndex", Count+1)
+
+ UpdateStoreHealWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreRumourWindow ():
+ global StoreRumourWindow
+
+ GemRB.SetVar ("TopIndex", 0)
+ CloseWindows()
+
+ StoreRumourWindow = Window = GemRB.LoadWindow (9)
+
+ #removing those pesky labels
+ for i in range (5):
+ Window.DeleteControl (0x10000005+i)
+
+ TextArea = Window.GetControl (13)
+ TextArea.SetText (0)
+
+ ##tavern quality image
+ #BAM = "TVRNQUL%d"% ((Store['StoreFlags']>>9)&3)
+ #Button = Window.GetControl (12)
+ #Button.SetSprites (BAM, 0, 0, 0, 0, 0)
+ #Button.SetState (IE_GUI_BUTTON_LOCKED)
+
+ ScrollBar = Window.GetControl (5)
+ ScrollBar.SetEvent (IE_GUI_SCROLLBAR_ON_CHANGE, UpdateStoreRumourWindow)
+ Count = Store['StoreDrinkCount']
+ if Count>4:
+ Count = Count-4
+ else:
+ Count = 0
+ ScrollBar.SetVarAssoc ("TopIndex", Count+1)
+
+ UpdateStoreRumourWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def OpenStoreRentWindow ():
+ global StoreRentWindow, RentIndex
+
+ CloseWindows()
+
+ StoreRentWindow = Window = GemRB.LoadWindow (8)
+
+ # room types
+ RentIndex = -1
+ room_type = (45308, 45310, 45313, 45316)
+ for i in range (4):
+ ok = Store['StoreRoomPrices'][i]
+ Button = Window.GetControl (i)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateStoreRentWindow)
+ if ok<0:
+ Button.SetState (IE_GUI_BUTTON_DISABLED) #disabled room icons are selected, not disabled
+ else:
+ Button.SetVarAssoc ("RentIndex", i)
+ if RentIndex==-1:
+ RentIndex = i
+
+ Button = Window.GetControl (i+4)
+ Button.SetText (room_type[i])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, UpdateStoreRentWindow)
+ Button.SetFlags (IE_GUI_BUTTON_RADIOBUTTON, OP_OR)
+ Button.SetVarAssoc ("RentIndex", i)
+ if ok<0:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # Rent
+ Button = Window.GetControl (8)
+ Button.SetText (45306)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RentRoom)
+
+ GemRB.SetVar ("RentIndex", RentIndex)
+
+ UpdateStoreRentWindow ()
+ Window.SetVisible (WINDOW_VISIBLE)
+ return
+
+def UpdateStoreCommon (Window, title, name, gold):
+
+ Label = Window.GetControl (title)
+ Label.SetText (GemRB.GetString (Store['StoreName']).upper ())
+
+ if name:
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Label = Window.GetControl (name)
+ Label.SetText (GemRB.GetPlayerName (pc, 0) )
+
+ Label = Window.GetControl (gold)
+ Label.SetText (str(GemRB.GameGetPartyGold ()))
+ return
+
+def UpdateStoreShoppingWindow ():
+ global Store, inventory_slots
+
+ Window = StoreShoppingWindow
+ #reget store in case of a change
+ Store = GemRB.GetStore ()
+ LeftCount = Store['StoreItemCount']-3
+ if LeftCount<0:
+ LeftCount=0
+ ScrollBar = Window.GetControl (7)
+ ScrollBar.SetVarAssoc ("LeftTopIndex", LeftCount)
+ LeftTopIndex = GemRB.GetVar ("LeftTopIndex")
+ if LeftTopIndex>LeftCount:
+ GemRB.SetVar ("LeftTopIndex", LeftCount)
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ inventory_slots = GemRB.GetSlots (pc, SLOT_INVENTORY)
+ RightCount = len(inventory_slots)-3
+ if RightCount<0:
+ RightCount=0
+ ScrollBar = Window.GetControl (16)
+ ScrollBar.SetVarAssoc ("RightTopIndex", RightCount)
+ RightTopIndex = GemRB.GetVar ("RightTopIndex")
+ if RightTopIndex>RightCount:
+ GemRB.SetVar ("RightTopIndex", RightCount)
+
+ RedrawStoreShoppingWindow ()
+ return
+
+def SelectBuy ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ GemRB.ChangeStoreItem (pc, LeftIndex, SHOP_BUY|SHOP_SELECT)
+ RedrawStoreShoppingWindow ()
+ return
+
+def ToBackpackPressed ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ LeftCount = Store['StoreItemCount']
+ #going backwards because removed items shift the slots
+ for i in range (LeftCount, 0, -1):
+ Flags = GemRB.IsValidStoreItem (pc, i-1, ITEM_STORE)&SHOP_SELECT
+ if Flags:
+ GemRB.ChangeStoreItem (pc, i-1, SHOP_BUY)
+
+ UpdateStoreShoppingWindow ()
+ return
+
+def BuyPressed ():
+ Window = StoreShoppingWindow
+
+ if (BuySum>GemRB.GameGetPartyGold ()):
+ ErrorWindow (50081)
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ LeftCount = Store['StoreItemCount']
+ #going backwards because removed items shift the slots
+ for i in range (LeftCount, 0, -1):
+ Flags = GemRB.IsValidStoreItem (pc, i-1, ITEM_STORE)&SHOP_SELECT
+ if Flags:
+ Slot = GemRB.GetStoreItem (i-1)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ Price = Item['Price'] * Store['SellMarkup'] / 100
+ if Price <= 0:
+ Price = 1
+
+ if GemRB.ChangeStoreItem (pc, i-1, SHOP_BUY):
+ GemRB.GameSetPartyGold (GemRB.GameGetPartyGold ()-Price)
+ UpdateStoreShoppingWindow ()
+ return
+
+def SelectSell ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ RightIndex = GemRB.GetVar ("RightIndex")
+ GemRB.ChangeStoreItem (pc, inventory_slots[RightIndex], SHOP_SELL|SHOP_SELECT)
+ RedrawStoreShoppingWindow ()
+ return
+
+def ToBagPressed ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ RightCount = len (inventory_slots)
+ #no need to go reverse
+ for Slot in range (RightCount):
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[Slot], ITEM_PC)
+ if Flags & SHOP_SELECT:
+ GemRB.ChangeStoreItem (pc, inventory_slots[Slot], SHOP_SELL)
+ UpdateStoreShoppingWindow ()
+ return
+
+def SellPressed ():
+ Window = StoreShoppingWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ RightCount = len (inventory_slots)
+ #no need to go reverse
+ for Slot in range (RightCount):
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[Slot], ITEM_PC) & SHOP_SELECT
+ if Flags:
+ GemRB.ChangeStoreItem (pc, inventory_slots[Slot], SHOP_SELL)
+
+ GemRB.GameSetPartyGold (GemRB.GameGetPartyGold ()+SellSum)
+ UpdateStoreShoppingWindow ()
+ return
+
+def RedrawStoreShoppingWindow ():
+ global BuySum, SellSum
+
+ Window = StoreShoppingWindow
+
+ UpdateStoreCommon (Window, 0x10000001, 0x10000005, 0x10000002)
+ pc = GemRB.GameGetSelectedPCSingle ()
+
+ LeftTopIndex = GemRB.GetVar ("LeftTopIndex")
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ RightTopIndex = GemRB.GetVar ("RightTopIndex")
+ RightIndex = GemRB.GetVar ("RightIndex")
+ LeftCount = Store['StoreItemCount']
+ BuySum = 0
+ for i in range (LeftCount):
+ if GemRB.IsValidStoreItem (pc, i, ITEM_STORE) & SHOP_SELECT:
+ Slot = GemRB.GetStoreItem (i)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ if Inventory:
+ Price = 1
+ else:
+ Price = Item['Price'] * Store['SellMarkup'] / 100
+ if Price <= 0:
+ Price = 1
+ BuySum = BuySum + Price
+
+ RightCount = len(inventory_slots)
+ SellSum = 0
+ for i in range (RightCount):
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[i], ITEM_PC)
+ if Flags & SHOP_SELECT:
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[i])
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ if Inventory:
+ Price = 1
+ else:
+ Price = Item['Price'] * Store['BuyMarkup'] / 100
+ if Flags & SHOP_ID:
+ Price = 1
+ SellSum = SellSum + Price
+
+ Label = Window.GetControl (0x10000003)
+ if Inventory:
+ Label.SetText ("")
+ else:
+ Label.SetText (str(BuySum) )
+ if BuySum:
+ LeftButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ LeftButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ Label = Window.GetControl (0x10000004)
+ if Inventory:
+ Label.SetText ("")
+ else:
+ Label.SetText (str(SellSum) )
+ if SellSum:
+ RightButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ RightButton.SetState (IE_GUI_BUTTON_DISABLED)
+
+ for i in range (4):
+ if i+LeftTopIndex<LeftCount:
+ Slot = GemRB.GetStoreItem (i+LeftTopIndex)
+ else:
+ Slot = None
+ Button = Window.GetControl (i+8)
+ Label = Window.GetControl (0x1000000b+i)
+ Button.SetVarAssoc ("LeftIndex", LeftTopIndex+i)
+ if Slot != None:
+ Flags = GemRB.IsValidStoreItem (pc, i+LeftTopIndex, ITEM_STORE)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if Flags & SHOP_BUY:
+ if Flags & SHOP_SELECT:
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if Flags & SHOP_ID:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemName']))
+ Button.EnableBorder (0, 1)
+ else:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemNameIdentified']))
+ Button.EnableBorder (0, 0)
+
+ if Inventory:
+ Label.SetText (28337)
+ else:
+ Price = Item['Price'] * Store['SellMarkup'] / 100
+ if Price <= 0:
+ Price = 1
+ GemRB.SetToken ("ITEMCOST", str(Price) )
+ Label.SetText (45374)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Label.SetText ("")
+
+ if i+RightTopIndex<RightCount:
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[i+RightTopIndex])
+ else:
+ Slot = None
+ Button = Window.GetControl (i+17)
+ Label = Window.GetControl (0x10000014+i)
+ Button.SetVarAssoc ("RightIndex", RightTopIndex+i)
+ if Slot != None:
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[i+RightTopIndex], ITEM_PC)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if Inventory:
+ Price = 1
+ else:
+ Price = Item['Price'] * Store['BuyMarkup'] / 100
+
+ if (Price>0) and (Flags & SHOP_SELL):
+ if Flags & SHOP_SELECT:
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if Flags & SHOP_ID:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemName']))
+ Price = 1
+ Button.EnableBorder (0, 1)
+ else:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemNameIdentified']))
+ Button.EnableBorder (0, 0)
+
+ if Inventory:
+ Label.SetText (28337)
+ else:
+ GemRB.SetToken ("ITEMCOST", str(Price) )
+ Label.SetText (45374)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Label.SetText ("")
+ return
+
+def UpdateStoreIdentifyWindow ():
+ global inventory_slots
+
+ Window = StoreIdentifyWindow
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ inventory_slots = GemRB.GetSlots (pc, SLOT_INVENTORY)
+ Count = len(inventory_slots)
+ ScrollBar = Window.GetControl (5)
+ ScrollBar.SetVarAssoc ("TopIndex", Count-3)
+ GemRB.SetVar ("Index", -1)
+ RedrawStoreIdentifyWindow ()
+ return
+
+def RedrawStoreIdentifyWindow ():
+ Window = StoreIdentifyWindow
+
+ UpdateStoreCommon (Window, 0x0fffffff, 0x10000002, 0x10000000)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Index = GemRB.GetVar ("Index")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Count = len(inventory_slots)
+ IDPrice = Store['IDPrice']
+
+ TextArea = Window.GetControl (14)
+ TextArea.SetText ("")
+ Selected = 0
+ for i in range (4):
+ if TopIndex+i<Count:
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[TopIndex+i])
+ else:
+ Slot = None
+ Button = Window.GetControl (i+6)
+ Label = Window.GetControl (0x10000009+i)
+ Button.SetVarAssoc ("Index", TopIndex+i)
+ if Slot != None:
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[TopIndex+i], ITEM_PC)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if Flags & SHOP_ID:
+ if Index == TopIndex+i:
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ Text = Item['ItemDesc']
+ TextArea.SetText (Text)
+ Selected = 1
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemName']))
+ GemRB.SetToken ("ITEMCOST", str(IDPrice) )
+ Button.EnableBorder (0, 1)
+ else:
+ if Index == TopIndex+i:
+ Text = Item['ItemDescIdentified']
+ TextArea.SetText (Text)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemNameIdentified']))
+ GemRB.SetToken ("ITEMCOST", str(0) )
+ Button.EnableBorder (0, 0)
+
+ Label.SetText (45374)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Label.SetText ("")
+
+ Button = Window.GetControl (4)
+ Label = Window.GetControl (0x10000001)
+ if Selected:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Label.SetText (str(IDPrice) )
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Label.SetText (str(0) )
+ return
+
+def IdentifyPressed ():
+ IDPrice = Store['IDPrice']
+ if (GemRB.GameGetPartyGold ()<IDPrice):
+ return
+
+ Index = GemRB.GetVar ("Index")
+ if (Index<0):
+ return
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Count = len(inventory_slots)
+ if Index >= Count:
+ return
+
+ GemRB.ChangeStoreItem (pc, inventory_slots[Index], SHOP_ID)
+ GemRB.GameSetPartyGold (GemRB.GameGetPartyGold ()-IDPrice)
+ UpdateStoreIdentifyWindow ()
+ return
+
+def InfoIdentifyWindow ():
+ Index = GemRB.GetVar ("Index")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Count = len(inventory_slots)
+ if Index >= Count:
+ return
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[Index])
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ InfoWindow (Slot, Item)
+ return
+
+def InfoLeftWindow ():
+ Index = GemRB.GetVar ("LeftIndex")
+ Slot = GemRB.GetStoreItem (Index)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ InfoWindow (Slot, Item)
+ return
+
+def InfoRightWindow ():
+ Index = GemRB.GetVar ("RightIndex")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ Count = len(inventory_slots)
+ if Index >= Count:
+ return
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[Index])
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ InfoWindow (Slot, Item)
+ return
+
+def InfoWindow (Slot, Item):
+ global MessageWindow
+
+ Identify = Slot['Flags'] & IE_INV_ITEM_IDENTIFIED
+
+ MessageWindow = Window = GemRB.LoadWindow (13)
+
+ #fake label
+ Label = Window.GetControl (0x10000000)
+ Label.SetText ("")
+
+ #description bam
+ Button = Window.GetControl (6)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetItemIcon (Slot['ItemResRef'], 2)
+
+ #slot bam
+ Button = Window.GetControl (2)
+ Button.SetState (IE_GUI_BUTTON_LOCKED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+
+ Label = Window.GetControl (0x0fffffff)
+ TextArea = Window.GetControl (4)
+ if Identify:
+ Label.SetText (Item['ItemNameIdentified'])
+ TextArea.SetText (Item['ItemDescIdentified'])
+ else:
+ Label.SetText (Item['ItemName'])
+ TextArea.SetText (Item['ItemDesc'])
+
+ #Done
+ Button = Window.GetControl (3)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ErrorDone)
+
+ # hide the empty button
+ #Window.DeleteControl (9)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def UpdateStoreStealWindow ():
+ global Store, inventory_slots
+
+ Window = StoreStealWindow
+
+ #reget store in case of a change
+ Store = GemRB.GetStore ()
+ LeftCount = Store['StoreItemCount']
+ ScrollBar = Window.GetControl (4)
+ ScrollBar.SetVarAssoc ("LeftTopIndex", LeftCount-3)
+
+ pc = GemRB.GameGetSelectedPCSingle ()
+ inventory_slots = GemRB.GetSlots (pc, SLOT_INVENTORY)
+ RightCount = len(inventory_slots)
+ ScrollBar = Window.GetControl (13)
+ ScrollBar.SetVarAssoc ("RightTopIndex", RightCount-3)
+ GemRB.SetVar ("LeftIndex", -1)
+ LeftButton.SetState (IE_GUI_BUTTON_DISABLED)
+ RedrawStoreStealWindow ()
+ return
+
+def StealPressed ():
+ Window = StoreShoppingWindow
+
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ pc = GemRB.GameGetSelectedPCSingle ()
+ #percentage skill check, if fails, trigger StealFailed
+ #if difficulty = 0 and skill=100, automatic success
+ #if difficulty = 0 and skill=50, 50% success
+ #if difficulty = 50 and skill=50, 0% success
+ #if skill>random(100)+difficulty - success
+ if GUICommon.CheckStat100 (pc, IE_PICKPOCKET, Store['StealFailure']):
+ GemRB.ChangeStoreItem (pc, LeftIndex, SHOP_STEAL)
+ UpdateStoreStealWindow ()
+ else:
+ GemRB.StealFailed ()
+ CloseStoreWindow ()
+ return
+
+def RedrawStoreStealWindow ():
+ Window = StoreStealWindow
+
+ UpdateStoreCommon (Window, 0x10000000, 0x10000002, 0x10000001)
+ LeftTopIndex = GemRB.GetVar ("LeftTopIndex")
+ LeftIndex = GemRB.GetVar ("LeftIndex")
+ RightTopIndex = GemRB.GetVar ("RightTopIndex")
+ RightIndex = GemRB.GetVar ("RightIndex")
+ LeftCount = Store['StoreItemCount']
+ pc = GemRB.GameGetSelectedPCSingle ()
+ RightCount = len(inventory_slots)
+ for i in range (4):
+ Slot = GemRB.GetStoreItem (i+LeftTopIndex)
+ Button = Window.GetControl (i+5)
+ Label = Window.GetControl (0x10000008+i)
+ Button.SetVarAssoc ("LeftIndex", LeftTopIndex+i)
+ if Slot != None:
+ Flags = GemRB.IsValidStoreItem (pc, i+LeftTopIndex, ITEM_STORE)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ if Flags & SHOP_STEAL:
+ if LeftIndex == LeftTopIndex + i:
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ if Flags & SHOP_ID:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemName']))
+ Button.EnableBorder (0, 1)
+ else:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemNameIdentified']))
+ Button.EnableBorder (0, 0)
+
+ GemRB.SetToken ("ITEMCOST", str(Slot['Price']) )
+ Label.SetText (45374)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Label.SetText ("")
+
+ if i+RightTopIndex<RightCount:
+ Slot = GemRB.GetSlotItem (pc, inventory_slots[i+RightTopIndex])
+ else:
+ Slot = None
+ Button = Window.GetControl (i+14)
+ Label = Window.GetControl (0x10000011+i)
+ Button.SetVarAssoc ("RightIndex", RightTopIndex+i)
+ if Slot != None:
+ Flags = GemRB.IsValidStoreItem (pc, inventory_slots[i+RightTopIndex], ITEM_PC)
+ Item = GemRB.GetItem (Slot['ItemResRef'])
+ Button.SetItemIcon (Slot['ItemResRef'], 0)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+ Price = Item['Price'] * Store['BuyMarkup'] / 100
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ if Flags & SHOP_ID:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemName']))
+ Button.EnableBorder (0, 1)
+ else:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Item['ItemNameIdentified']))
+ Button.EnableBorder (0, 0)
+
+ GemRB.SetToken ("ITEMCOST", str(Price) )
+ Label.SetText (45374)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Label.SetText ("")
+ if LeftIndex>=0:
+ LeftButton.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ LeftButton.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def UpdateStoreDonateWindow ():
+ Window = StoreDonateWindow
+
+ UpdateStoreCommon (Window, 0x10000005, 0, 0x10000006)
+ Field = Window.GetControl (3)
+ donation = int("0"+Field.QueryText ())
+ gold = GemRB.GameGetPartyGold ()
+ if donation>gold:
+ donation = gold
+ Field.SetText (str(gold) )
+
+ Button = Window.GetControl (2)
+ if donation:
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def IncrementDonation ():
+ Window = StoreDonateWindow
+
+ Field = Window.GetControl (3)
+ donation = int("0"+Field.QueryText ())
+ if donation<GemRB.GameGetPartyGold ():
+ Field.SetText (str(donation+1) )
+ else:
+ Field.SetText (str(GemRB.GameGetPartyGold ()) )
+ UpdateStoreDonateWindow ()
+ return
+
+def DecrementDonation ():
+ Window = StoreDonateWindow
+
+ Field = Window.GetControl (3)
+ donation = int("0"+Field.QueryText ())
+ if donation>0:
+ Field.SetText (str(donation-1) )
+ else:
+ Field.SetText (str(0) )
+ UpdateStoreDonateWindow ()
+ return
+
+def DonateGold ():
+ Window = StoreDonateWindow
+
+ TextArea = Window.GetControl (0)
+ TextArea.SetFlags (IE_GUI_TEXTAREA_AUTOSCROLL)
+
+ #Button = Window.GetControl (10)
+ #Button.SetAnimation ("DONATE")
+
+ Field = Window.GetControl (3)
+ donation = int("0"+Field.QueryText ())
+ GemRB.GameSetPartyGold (GemRB.GameGetPartyGold ()-donation)
+ if GemRB.IncreaseReputation (donation):
+ TextArea.Append (26914, -1)
+ GemRB.PlaySound ("act_03")
+ UpdateStoreDonateWindow ()
+ return
+
+ TextArea.Append (26914, -1)
+ GemRB.PlaySound ("act_03e")
+ UpdateStoreDonateWindow ()
+ return
+
+def UpdateStoreHealWindow ():
+ Window = StoreHealWindow
+
+ UpdateStoreCommon (Window, 0x0fffffff, 0x1000000e, 0x10000000)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ Index = GemRB.GetVar ("Index")
+ for i in range (4):
+ Cure = GemRB.GetStoreCure (TopIndex+i)
+
+ Button = Window.GetControl (i+5)
+ Label = Window.GetControl (0x10000008+i)
+ Button.SetVarAssoc ("Index", TopIndex+i)
+ if Cure != None:
+ Spell = GemRB.GetSpell (Cure['CureResRef'])
+ Button.SetSpellIcon (Cure['CureResRef'], 1)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_OR)
+
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Spell['SpellName']))
+ GemRB.SetToken ("ITEMCOST", str(Cure['Price']) )
+ Label.SetText (45374)
+
+ else:
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ Button.SetFlags (IE_GUI_BUTTON_NO_IMAGE, OP_OR)
+ Button.SetFlags (IE_GUI_BUTTON_PICTURE, OP_NAND)
+ Label.SetText ("")
+ if TopIndex+i==Index:
+ TextArea = Window.GetControl (13)
+ TextArea.SetText (Cure['Description'])
+ Label = Window.GetControl (0x10000001)
+ Label.SetText (str(Cure['Price']) )
+ Button = Window.GetControl (3)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ return
+
+def InfoHealWindow ():
+ global MessageWindow
+
+ UpdateStoreHealWindow ()
+ Index = GemRB.GetVar ("Index")
+ Cure = GemRB.GetStoreCure (Index)
+ Spell = GemRB.GetSpell (Cure['CureResRef'])
+
+ MessageWindow = Window = GemRB.LoadWindow (13)
+
+ Label = Window.GetControl (0x10000000)
+ Label.SetText (Spell['SpellName'])
+
+ Button = Window.GetControl (2)
+ Button.SetSpellIcon (Cure['CureResRef'], 1)
+ Button = Window.GetControl (6)
+ Button.SetSpellIcon (Cure['CureResRef'], 2)
+
+ TextArea = Window.GetControl (5)
+ TextArea.SetText (Spell['SpellDesc'])
+
+ #Done
+ Button = Window.GetControl (3)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ErrorDone)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def PlayCureSoundEffect (spell):
+ GemRB.PlaySound (CureTable.GetValue(spell, "SOUND_EFFECT") )
+ return
+
+def BuyHeal ():
+ Index = GemRB.GetVar ("Index")
+ Cure = GemRB.GetStoreCure (Index)
+ gold = GemRB.GameGetPartyGold ()
+ if gold < Cure['Price']:
+ ErrorWindow (50082)
+ return
+
+ GemRB.GameSetPartyGold (gold-Cure['Price'])
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.ApplySpell (pc, Cure['CureResRef'])
+ PlayCureSoundEffect (Cure['CureResRef'])
+ UpdateStoreHealWindow ()
+ return
+
+def UpdateStoreRumourWindow ():
+ Window = StoreRumourWindow
+
+ UpdateStoreCommon (Window, 0x1000000a, 0, 0x1000000b)
+ TopIndex = GemRB.GetVar ("TopIndex")
+ for i in range (5):
+ Drink = GemRB.GetStoreDrink (TopIndex+i)
+ Button = Window.GetControl (i)
+ Button.SetVarAssoc ("Index", i)
+ if Drink != None:
+ GemRB.SetToken ("ITEMNAME", GemRB.GetString (Drink['DrinkName']))
+ GemRB.SetToken ("ITEMCOST", str(Drink['Price']) )
+ Button.SetText (45374)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GulpDrink)
+ else:
+ Button.SetText ("")
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def GulpDrink ():
+ Window = StoreRumourWindow
+
+ TextArea = Window.GetControl (13)
+ TextArea.SetFlags (IE_GUI_TEXTAREA_AUTOSCROLL)
+ pc = GemRB.GameGetSelectedPCSingle ()
+ intox = GemRB.GetPlayerStat (pc, IE_INTOXICATION)
+ intox = 0
+ if intox > 80:
+ TextArea.Append (0, -1)
+ return
+
+ gold = GemRB.GameGetPartyGold ()
+ Index = GemRB.GetVar ("TopIndex")+GemRB.GetVar ("Index")
+ Drink = GemRB.GetStoreDrink (Index)
+ if gold < Drink['Price']:
+ ErrorWindow (50083)
+ return
+
+ GemRB.GameSetPartyGold (gold-Drink['Price'])
+ GemRB.SetPlayerStat (pc, IE_INTOXICATION, intox+Drink['Strength'])
+ text = GemRB.GetRumour (Drink['Strength'], Store['TavernRumour'])
+ TextArea.Append (text, -1)
+ GemRB.PlaySound ("gam_07")
+ UpdateStoreRumourWindow ()
+ return
+
+def UpdateStoreRentWindow ():
+ global RentIndex
+
+ Window = StoreRentWindow
+ UpdateStoreCommon (Window, 0x1000000a, 0, 0x1000000b)
+ RentIndex = GemRB.GetVar ("RentIndex")
+ Button = Window.GetControl (8)
+ Label = Window.GetControl (0x1000000c)
+ if RentIndex>=0:
+ TextArea = Window.GetControl (9)
+ TextArea.SetText (roomtypes[RentIndex] )
+ price = Store['StoreRoomPrices'][RentIndex]
+ Label.SetText (str(price) )
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ else:
+ Label.SetText ("0" )
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+ return
+
+def RentConfirm ():
+ RentIndex = GemRB.GetVar ("RentIndex")
+ price = Store['StoreRoomPrices'][RentIndex]
+ Gold = GemRB.GameGetPartyGold ()
+ GemRB.GameSetPartyGold (Gold-price)
+ GemRB.RestParty (13, 1, RentIndex+1)
+ if RentConfirmWindow:
+ RentConfirmWindow.Unload ()
+ Window = StoreRentWindow
+ TextArea = Window.GetControl (9)
+ #is there any way to change this???
+ GemRB.SetToken ("HOUR", "8")
+ GemRB.SetToken ("DURATION", "hours")
+ GemRB.SetToken ("HP", "%d"%(RentIndex+1))
+ TextArea.SetText (19262)
+ GemRB.SetVar ("RentIndex", -1)
+ Button = Window.GetControl (RentIndex+4)
+ Button.SetState (IE_GUI_BUTTON_ENABLED)
+ UpdateStoreRentWindow ()
+ return
+
+def RentDeny () :
+ if RentConfirmWindow:
+ RentConfirmWindow.Unload ()
+ UpdateStoreRentWindow ()
+ return
+
+def RentRoom ():
+ global RentIndex, RentConfirmWindow
+
+ RentIndex = GemRB.GetVar ("RentIndex")
+ price = Store['StoreRoomPrices'][RentIndex]
+ Gold = GemRB.GameGetPartyGold ()
+ if Gold<price:
+ ErrorWindow (50085)
+ return
+
+ RentConfirmWindow = Window = GemRB.LoadWindow (12)
+ #confirm
+ Button = Window.GetControl (0)
+ Button.SetText (4242)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RentConfirm)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ #deny
+ Button = Window.GetControl (1)
+ Button.SetText (4196)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, RentDeny)
+ Button.SetFlags (IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ #textarea
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (4241)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ErrorWindow (strref):
+ global MessageWindow
+
+ MessageWindow = Window = GemRB.LoadWindow (11)
+
+ TextArea = Window.GetControl (3)
+ TextArea.SetText (strref)
+
+ #done
+ Button = Window.GetControl (0)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, ErrorDone)
+
+ Window.ShowModal (MODAL_SHADOW_GRAY)
+ return
+
+def ErrorDone ():
+ if MessageWindow:
+ MessageWindow.Unload ()
+ return
+
+###################################################
+# End of file GUISTORE.py
diff --git a/gemrb/GUIScripts/pst/GUIWORLD.py b/gemrb/GUIScripts/pst/GUIWORLD.py
new file mode 100644
index 0000000..081b852
--- /dev/null
+++ b/gemrb/GUIScripts/pst/GUIWORLD.py
@@ -0,0 +1,187 @@
+# -*-python-*-
+# 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.
+#
+
+
+# GUIWORLD.py - scripts to control some windows from GUIWORLD winpack
+# except of Actions, Portrait, Options and Dialog windows
+
+###################################################
+
+import GemRB
+import GUIClasses
+import GUICommon
+import GUICommonWindows
+import CommonWindow
+from GUIDefines import *
+import MessageWindow
+
+FormationWindow = None
+ReformPartyWindow = None
+
+def DialogStarted ():
+ global ContinueWindow, OldActionsWindow
+
+ # try to force-close anything which is open
+ GUICommon.CloseOtherWindow(None)
+ CommonWindow.CloseContainerWindow()
+
+ # we need GUI for dialogs
+ GemRB.UnhideGUI()
+
+ # opening control size to maximum, enabling dialog window
+ GemRB.GameSetScreenFlags(GS_HIDEGUI, OP_NAND)
+ GemRB.GameSetScreenFlags(GS_DIALOG, OP_OR)
+
+ MessageWindow.UpdateControlStatus()
+
+def DialogEnded ():
+ pass
+
+def CloseContinueWindow ():
+ # don't close the actual window now to avoid flickering: we might still want it open
+ GemRB.SetVar ("DialogChoose", GemRB.GetVar ("DialogOption"))
+
+def NextDialogState ():
+ if not MessageWindow.MessageWindow:
+ return
+
+ Button = MessageWindow.MessageWindow.GetControl (0)
+ Button.SetText(28082)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnDecreaseSize)
+
+ MessageWindow.MessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenEndMessageWindow ():
+ Button = MessageWindow.MessageWindow.GetControl (0)
+ Button.SetText (34602)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenContinueMessageWindow ():
+ #continue
+ Button = MessageWindow.MessageWindow.GetControl (0)
+ Button.SetText (34603)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, CloseContinueWindow)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+ Button.SetStatus (IE_GUI_CONTROL_FOCUSED)
+
+def OpenReformPartyWindow ():
+ global ReformPartyWindow
+
+ if GUICommon.CloseOtherWindow(OpenReformPartyWindow):
+ GemRB.HideGUI ()
+ if ReformPartyWindow:
+ ReformPartyWindow.Unload ()
+ ReformPartyWindow = None
+
+ GemRB.SetVar ("OtherWindow", -1)
+ GUICommonWindows.EnableAnimatedWindows ()
+ GemRB.LoadWindowPack ("GUIREC")
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ ReformPartyWindow = Window = GemRB.LoadWindow (24)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+ GUICommonWindows.DisableAnimatedWindows ()
+
+ # Remove
+ Button = Window.GetControl (15)
+ Button.SetText (42514)
+ Button.SetState (IE_GUI_BUTTON_DISABLED)
+
+ # Done
+ Button = Window.GetControl (8)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenReformPartyWindow)
+
+ GemRB.UnhideGUI ()
+
+
+last_formation = None
+
+def OpenFormationWindow ():
+ global FormationWindow
+
+ if GUICommon.CloseOtherWindow(OpenFormationWindow):
+ GemRB.HideGUI ()
+ if FormationWindow:
+ FormationWindow.Unload ()
+ FormationWindow = None
+
+ GemRB.GameSetFormation (last_formation, 0)
+ GUICommonWindows.EnableAnimatedWindows ()
+ GemRB.SetVar ("OtherWindow", -1)
+ GemRB.UnhideGUI ()
+ return
+
+ GemRB.HideGUI ()
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ FormationWindow = Window = GemRB.LoadWindow (27)
+ GemRB.SetVar ("OtherWindow", Window.ID)
+ GUICommonWindows.DisableAnimatedWindows ()
+
+ # Done
+ Button = Window.GetControl (13)
+ Button.SetText (1403)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, OpenFormationWindow)
+
+ tooltips = (
+ 44957, # Follow
+ 44958, # T
+ 44959, # Gather
+ 44960, # 4 and 2
+ 44961, # 3 by 2
+ 44962, # Protect
+ 48152, # 2 by 3
+ 44964, # Rank
+ 44965, # V
+ 44966, # Wedge
+ 44967, # S
+ 44968, # Line
+ 44969, # None
+ )
+
+ for i in range (13):
+ Button = Window.GetControl (i)
+ Button.SetVarAssoc ("SelectedFormation", i)
+ Button.SetTooltip (tooltips[i])
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, SelectFormation)
+
+ GemRB.SetVar ("SelectedFormation", GemRB.GameGetFormation (0))
+ SelectFormation ()
+
+ GemRB.UnhideGUI ()
+
+def SelectFormation ():
+ global last_formation
+ Window = FormationWindow
+
+ formation = GemRB.GetVar ("SelectedFormation")
+ print "FORMATION:", formation
+ if last_formation != None and last_formation != formation:
+ Button = Window.GetControl (last_formation)
+ Button.SetState (IE_GUI_BUTTON_UNPRESSED)
+
+ Button = Window.GetControl (formation)
+ Button.SetState (IE_GUI_BUTTON_SELECTED)
+
+ last_formation = formation
diff --git a/gemrb/GUIScripts/pst/LoadScreen.py b/gemrb/GUIScripts/pst/LoadScreen.py
new file mode 100644
index 0000000..2302adf
--- /dev/null
+++ b/gemrb/GUIScripts/pst/LoadScreen.py
@@ -0,0 +1,74 @@
+# -*-python-*-
+# 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.
+#
+
+# LoadScreen.py - display Loading screen
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+
+LoadScreen = None
+
+LS_TYPE_LOADING = 0
+LS_TYPE_SAVING = 1
+LS_TYPE_UNLOADING = 2
+
+def StartLoadScreen (screen_type = LS_TYPE_LOADING):
+ global LoadScreen
+
+ # While (un)loading, there are no other windows
+ if screen_type == LS_TYPE_SAVING:
+ GemRB.HideGUI ()
+
+ GemRB.LoadWindowPack ("guils")
+ LoadScreen = Window = GemRB.LoadWindow (0)
+
+ LoadPic = GemRB.GetGameString (STR_LOADMOS)
+
+ if LoadPic == "":
+ if screen_type == LS_TYPE_LOADING:
+ LoadPic = "GUILS%02d" %GemRB.Roll (1, 20, 0)
+ elif screen_type == LS_TYPE_SAVING:
+ LoadPic = "GUISG%02d" %GemRB.Roll (1, 10, 0)
+ else:
+ LoadPic = "GUIDS10"
+
+ Window.SetPicture (LoadPic)
+
+ Bar = Window.GetControl (0)
+ Progress = 0
+ GemRB.SetVar ("Progress", Progress)
+ Bar.SetVarAssoc ("Progress", Progress)
+ Bar.SetEvent (IE_GUI_PROGRESS_END_REACHED, EndLoadScreen)
+ Skull = Window.GetControl (1)
+ Skull.SetMOS ("GSKULOFF")
+
+ if screen_type == LS_TYPE_SAVING:
+ GemRB.UnhideGUI ()
+
+ Window.SetVisible (WINDOW_VISIBLE)
+
+
+def EndLoadScreen ():
+ Window = LoadScreen
+ Skull = Window.GetControl (1)
+ Skull.SetMOS ("GSKULON")
+ Window.SetVisible (WINDOW_VISIBLE)
+ Window.Unload ()
diff --git a/gemrb/GUIScripts/pst/Makefile.am b/gemrb/GUIScripts/pst/Makefile.am
new file mode 100644
index 0000000..2930c88
--- /dev/null
+++ b/gemrb/GUIScripts/pst/Makefile.am
@@ -0,0 +1,4 @@
+pstscript_DATA = *.py
+pstscriptdir = $(moddir)/GUIScripts/pst/
+EXTRA_DIST = *.py
+MOSTLYCLEANFILES = *.pyc
diff --git a/gemrb/GUIScripts/pst/Maze.py b/gemrb/GUIScripts/pst/Maze.py
new file mode 100644
index 0000000..b22ae48
--- /dev/null
+++ b/gemrb/GUIScripts/pst/Maze.py
@@ -0,0 +1,455 @@
+# -*-python-*-
+# 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.
+#
+
+# Maze.py - script to generate the modron maze in PST
+
+###################################################
+
+import GemRB
+from maze_defs import *
+from GUIDefines import STR_AREANAME
+
+rooms = None
+max = 0
+dims = 0
+entries = None
+offx = (0,0,1,0,-1)
+offy = (0,-1,0,1,0)
+#wallbits = (0, WALL_WEST, WALL_NORTH, WALL_EAST, WALL_SOUTH)
+wallbits = (0, WALL_NORTH, WALL_EAST, WALL_SOUTH, WALL_WEST)
+entrances = ("", "Entry3", "Entry4", "Entry1", "Entry2")
+doors = ("", "northdoor", "eastdoor", "southdoor", "westdoor")
+anims = ("", "a13xxdn", "a13xxde", "a13xxds", "a13xxdw")
+aposx=(0,1012,1005,505,380)
+aposy=(0,524,958,996,562)
+cposx=(686,886,497)
+cposy=(498,722,726)
+
+def Possible(posx, posy):
+ global entries
+
+ pos = posx*MAZE_MAX_DIM+posy
+
+ if entries[pos]:
+ return -1
+ return pos
+
+def GetPossible (pos):
+ posx = pos/MAZE_MAX_DIM
+ posy = pos-posx*MAZE_MAX_DIM
+ possible = []
+
+ if posx>0:
+ newpos = Possible(posx-1, posy)
+ if newpos!=-1:
+ possible[:0] = [newpos]
+ if posy>0:
+ newpos = Possible(posx, posy-1)
+ if newpos!=-1:
+ possible[:0] = [newpos]
+ if posx<dims-1:
+ newpos = Possible(posx+1, posy)
+ if newpos!=-1:
+ possible[:0] = [newpos]
+ if posy<dims-1:
+ newpos = Possible(posx, posy+1)
+ if newpos!=-1:
+ possible[:0] = [newpos]
+
+ return possible
+
+#loads a 2da and sets it up as maze
+def LoadMazeFrom2da(tablename):
+ MazeTable = GemRB.LoadTable(tablename)
+ if MazeTable == None:
+ return
+ size = MazeTable.GetValue(-1,-1)
+ GemRB.SetupMaze(size, size)
+ traps = 0
+ for i in range(MazeTable.GetRowCount()):
+ Area = MazeTable.GetRowName(i)
+ OVERRIDE = MazeTable.GetValue(Area,"OVERRIDE")
+ TRAPTYPE = MazeTable.GetValue(Area,"TRAPTYPE")
+ WALLS = MazeTable.GetValue(Area,"WALLS")
+ VISITED = MazeTable.GetValue(Area,"VISITED")
+ pos = ConvertPos(int(Area[4:])-1)
+ GemRB.SetMazeEntry(pos, ME_OVERRIDE, OVERRIDE)
+ GemRB.SetMazeEntry(pos, ME_TRAP, TRAPTYPE)
+ GemRB.SetMazeEntry(pos, ME_WALLS, WALLS)
+ GemRB.SetMazeEntry(pos, ME_VISITED, VISITED)
+ if TRAPTYPE>=0:
+ traps = traps+1
+
+ #disabling special rooms
+ GemRB.SetMazeData(MH_POS1X, -1)
+ GemRB.SetMazeData(MH_POS1Y, -1)
+ GemRB.SetMazeData(MH_POS2X, -1)
+ GemRB.SetMazeData(MH_POS2Y, -1)
+ #adding foyer coordinates (middle of bottom)
+ GemRB.SetMazeData(MH_POS3X, size/2)
+ GemRB.SetMazeData(MH_POS3Y, size-1)
+ #adding engine room coordinates (bottom right)
+ GemRB.SetMazeData(MH_POS4X, size-1)
+ GemRB.SetMazeData(MH_POS4Y, size-1)
+ #adding trap
+ GemRB.SetMazeData(MH_TRAPCOUNT, traps)
+ #finish
+ GemRB.SetMazeData(MH_INITED, 1)
+ return
+
+def AddRoom (pos):
+ global rooms
+ global entries
+
+ rooms[len(rooms):]=[pos]
+ entries[pos] = 1
+ return
+
+def MainRoomFits (pos1x, pos1y, pos):
+ global entries
+
+ room = pos1x*MAZE_MAX_DIM+pos1y
+ if room==pos:
+ return False
+
+ south = pos1x*MAZE_MAX_DIM+pos1y+1
+ if south==pos:
+ return False
+
+ north = pos1x*MAZE_MAX_DIM+pos1y-1
+ if north==pos:
+ return False
+
+ GemRB.SetMazeEntry(room, ME_WALLS, WALL_SOUTH)
+ entries[room] = 1
+ entries[north] = 1
+ return True
+
+def zeros (size):
+ return size*[0]
+
+def PrintMaze():
+ header = GemRB.GetMazeHeader()
+ if header==None or header["Inited"]==0:
+ print "There is maze or it is not initialized!"
+ return
+
+ MazeX = header["MazeX"]
+ MazeY = header["MazeY"]
+ MainX = header["Pos1X"]
+ MainY = header["Pos1Y"]
+ NordomX = header["Pos2X"]
+ NordomY = header["Pos2Y"]
+ FoyerX = header["Pos3X"]
+
+ print "Maze size is "+str(MazeX)+"X"+str(MazeY)
+ for y in range (MazeY):
+ line = ""
+ for x in range (MazeX):
+ pos = MAZE_MAX_DIM*x+y
+ entry = GemRB.GetMazeEntry(pos)
+ if entry["Walls"]&WALL_NORTH:
+ line = line + "+ "
+ else:
+ line = line + "+-"
+ print line+"+"
+ line = ""
+ for x in range (MazeX):
+ pos = MAZE_MAX_DIM*x+y
+ entry = GemRB.GetMazeEntry(pos)
+ if entry["Walls"]&WALL_WEST:
+ line = line + " "
+ else:
+ line = line + "|"
+ if x == NordomX and y == NordomY:
+ line = line + "N"
+ elif x == MainX and y == MainY:
+ line = line + "W"
+ elif entry["Trapped"]>=0:
+ line = line + chr(entry["Trapped"]+65)
+ else:
+ line = line + " "
+ print line+"|"
+ line = ""
+ for x in range (MazeX):
+ if FoyerX==x:
+ line = line + "+ "
+ else:
+ line = line + "+-"
+ print line+"+"
+ return
+
+def ConvertPos (pos):
+ return ((pos&7)<<3)|(pos>>3)
+
+###################################################
+def CreateMaze ():
+ global max
+ global dims
+ global entries
+ global rooms
+
+ if GemRB.GetGameVar("EnginInMaze")>0:
+ LoadMazeFrom2da("easymaze")
+ return
+
+ mazedifficulty = GemRB.GetGameVar("MazeDifficulty")
+
+ #make sure there are no more traps than rooms
+ #make sure dimensions don't exceed maximum possible
+ if mazedifficulty==0:
+ dims = 4
+ traps = 5
+ elif mazedifficulty==1:
+ dims = 6
+ traps = 12
+ else:
+ dims = 8
+ traps = 20
+
+ entries = zeros(MAZE_ENTRY_COUNT)
+ rooms = []
+
+ GemRB.SetupMaze(dims, dims)
+ for x in range(dims, MAZE_MAX_DIM):
+ for y in range(dims, MAZE_MAX_DIM):
+ pos = x*MAZE_MAX_DIM+y
+ entries[pos] = 1
+
+ nordomx = GemRB.Roll(1, dims-1, -1)
+ nordomy = GemRB.Roll(1, dims, -1)
+ pos = nordomx*MAZE_MAX_DIM+nordomy
+ entries[pos] = 1
+ GemRB.SetMazeEntry(pos, ME_WALLS, WALL_EAST)
+ pos = nordomx*MAZE_MAX_DIM+nordomy+MAZE_MAX_DIM
+ AddRoom(pos)
+ if (mazedifficulty>1):
+ GemRB.SetMazeData(MH_POS2X, nordomx)
+ GemRB.SetMazeData(MH_POS2Y, nordomy)
+ pos1x = GemRB.Roll(1, dims, -1)
+ pos1y = GemRB.Roll(1, dims-2, 0)
+ while not MainRoomFits(pos1x, pos1y, pos):
+ pos1x = GemRB.Roll(1, dims, -1)
+ pos1y = GemRB.Roll(1, dims-2, 0)
+ GemRB.SetMazeData(MH_POS1X, pos1x)
+ GemRB.SetMazeData(MH_POS1Y, pos1y)
+ else:
+ GemRB.SetMazeData(MH_POS1X, -1)
+ GemRB.SetMazeData(MH_POS1Y, -1)
+ GemRB.SetMazeData(MH_POS2X, -1)
+ GemRB.SetMazeData(MH_POS2Y, -1)
+
+ oldentries = entries
+ for i in range(traps):
+ posx = GemRB.Roll(1, dims, -1)
+ posy = GemRB.Roll(1, dims, -1)
+ pos = posx*MAZE_MAX_DIM+posy
+ while entries[pos]:
+ pos = pos + 1
+ if pos>=MAZE_ENTRY_COUNT:
+ posx = 0
+ posy = 0
+ pos = 0
+ else:
+ posx = pos/MAZE_MAX_DIM
+ posy = pos-posx*MAZE_MAX_DIM
+ GemRB.SetMazeEntry(pos, ME_TRAP, GemRB.Roll(1, 3, -1) )
+
+ entries = oldentries
+ while len(rooms)>0:
+ pos = rooms.pop(0)
+ posx = pos/MAZE_MAX_DIM
+ posy = pos-posx*MAZE_MAX_DIM
+ possible = GetPossible(pos)
+ plen = len(possible)
+ if plen>0:
+ if plen==1:
+ newpos = possible[0]
+ else:
+ #adding item back if we got room to grow
+ AddRoom(pos)
+ newpos = possible[GemRB.Roll(1, plen, -1) ]
+ if entries[newpos]==0:
+ if newpos+1 == pos:
+ GemRB.SetMazeEntry(pos, ME_WALLS, WALL_NORTH)
+ elif pos+1 == newpos:
+ GemRB.SetMazeEntry(pos, ME_WALLS, WALL_SOUTH)
+ elif pos+MAZE_MAX_DIM == newpos:
+ GemRB.SetMazeEntry(pos, ME_WALLS, WALL_EAST)
+ elif newpos+MAZE_MAX_DIM == pos:
+ GemRB.SetMazeEntry(pos, ME_WALLS, WALL_WEST)
+ else:
+ print "Something went wrong at pos: ", pos, " newpos: ", newpos
+ AddRoom(newpos)
+
+ #adding foyer coordinates
+ x = GemRB.Roll(1,dims,-1)
+ while x!=nordomx and dims-1!=nordomy:
+ x=GemRB.Roll(1,dims,-1)
+
+ GemRB.SetMazeData(MH_POS3X, GemRB.Roll(1,dims,-1) )
+ GemRB.SetMazeData(MH_POS3Y, dims-1)
+
+ #setting engine room coordinates to hidden (accessible from foyer)
+ GemRB.SetMazeData(MH_POS4X, -1)
+ GemRB.SetMazeData(MH_POS4Y, -1)
+ #adding traps
+ GemRB.SetMazeData(MH_TRAPCOUNT, traps)
+ #finish
+ GemRB.SetMazeData(MH_INITED, 1)
+ return
+
+def FormatAreaName(pos):
+ if pos<9:
+ return "AR130"+str(pos+1)
+ return "AR13"+str(pos+1)
+
+def CustomizeMaze(AreaName):
+
+ header = GemRB.GetMazeHeader()
+
+ mainX = header['Pos1X']
+ mainY = header['Pos1Y']
+ nordomX = header['Pos2X']
+ nordomY = header['Pos2Y']
+ foyerX = header['Pos3X']
+ foyerY = header['Pos3Y']
+ engineX = header['Pos4X']
+ engineY = header['Pos4Y']
+ #modron foyer
+ if AreaName == "fy":
+ #TODO modron foyer, only one entrance if EnginInMaze = 1
+ tmp = foyerX+foyerY*MAZE_MAX_DIM
+ GemRB.SetMapExit ("exit1", FormatAreaName(tmp), "Entry3" )
+
+ #disable engine room
+ if GemRB.GetGameVar("EnginInMaze")==1:
+ GemRB.SetMapExit ("exit3" )
+ GemRB.SetMapDoor (doors[3], 0)
+ GemRB.SetMapAnimation(aposx[3], aposy[3], anims[3])
+ else:
+ GemRB.SetMapExit ("exit3", "AR13EN")
+
+ GemRB.SetMapExit ("exit4" )
+ GemRB.SetMapAnimation(aposx[4], aposy[4], anims[4])
+ return
+
+ if AreaName == "en":
+ if GemRB.GetGameVar("EnginInMaze")==1:
+ tmp = engineX+(engineY-1)*MAZE_MAX_DIM
+ GemRB.SetMapExit ("exit1", FormatAreaName(tmp), "Entry3" )
+ else:
+ GemRB.SetMapExit ("exit1", "AR13FY" )
+ return
+
+ if AreaName == "wz":
+ #TODO wizard's lair
+ tmp = mainY+mainX*MAZE_MAX_DIM
+ entry = GemRB.GetMazeEntry(tmp)
+ tmp = mainX+mainY*MAZE_MAX_DIM
+ GemRB.SetMapExit ("exit3", FormatAreaName(tmp), "Entry1" )
+ GemRB.SetMapDoor (doors[3], 1)
+ return
+
+ if AreaName == "fd":
+ #TODO nordom
+ tmp = nordomY+nordomX*MAZE_MAX_DIM
+ entry = GemRB.GetMazeEntry(tmp)
+ tmp = nordomX+nordomY*MAZE_MAX_DIM
+ GemRB.SetMapExit ("exit2", FormatAreaName(tmp), "Entry4" )
+ GemRB.SetMapDoor (doors[2], 1)
+ return
+
+ tmp = int(AreaName)-1
+ if tmp<0 or tmp>63:
+ return
+
+ pos = ConvertPos(tmp)
+ entry = GemRB.GetMazeEntry(pos)
+ #TODO: customize maze area based on entry (walls, traps)
+
+ if entry['Visited']:
+ #already customized
+ return
+
+ difficulty = GemRB.GetGameVar("MazeDifficulty")
+ if difficulty == 0:
+ name = "CLOW"
+ elif difficulty == 1:
+ name = "CMOD"
+ else:
+ name = "CHIGH"
+
+ ccount = GemRB.Roll(1,3,0)
+
+ for i in range(ccount):
+ GemRB.CreateCreature(0, name, cposx[i], cposy[i])
+
+ trapped = entry['Trapped']
+ if trapped>=0:
+ type = GemRB.Roll(1,4,0)
+ GemRB.SetMapRegion('Trap'+chr(trapped+65), '1300trp'+str(type) )
+
+ GemRB.SetMazeEntry(pos, ME_VISITED, 1)
+ walls = entry['Walls']
+ y = tmp / MAZE_MAX_DIM
+ x = tmp - y*MAZE_MAX_DIM
+ for i in range(1,5):
+ if wallbits[i]&walls:
+ x2 = x+offx[i]
+ y2 = y+offy[i]
+ set = 0
+ if x2 == nordomX and y2 == nordomY:
+ NewArea = "AR13FD"
+ elif x2 == mainX and y2 == mainY:
+ NewArea = "AR13WZ"
+ elif x2 == foyerX and y2 == foyerY+1:
+ NewArea = "AR13FY"
+ elif x2 == engineX and y2 == engineY:
+ NewArea = "AR13EN"
+ else:
+ if x2>=0 and x2<MAZE_MAX_DIM and y2>=0 and y2<MAZE_MAX_DIM:
+ #reversed coordinates
+ NewArea = FormatAreaName (x2+y2*MAZE_MAX_DIM)
+ else:
+ #maximum dimensions
+ set = 1
+ else:
+ set = 1
+
+ if set:
+ #remove exit
+ GemRB.SetMapExit ("exit"+str(i) )
+ GemRB.SetMapDoor (doors[i], 0)
+ GemRB.SetMapAnimation(aposx[i], aposy[i], anims[i])
+ else:
+ #set exit
+ GemRB.SetMapExit ("exit"+str(i), NewArea, entrances[i] )
+ GemRB.SetMapDoor (doors[i], 1)
+ GemRB.SetMapAnimation(-1, -1, "", 0, 0)
+ return
+
+def CustomizeArea():
+ Area = GemRB.GetGameString (STR_AREANAME)
+ if Area[0:4] == "ar13":
+ CustomizeMaze(Area[4:])
+ return
+
+ #TODO insert non maze area customization here (set own area scripts for special areas)
+ return
diff --git a/gemrb/GUIScripts/pst/MessageWindow.py b/gemrb/GUIScripts/pst/MessageWindow.py
new file mode 100644
index 0000000..5806656
--- /dev/null
+++ b/gemrb/GUIScripts/pst/MessageWindow.py
@@ -0,0 +1,112 @@
+# -*-python-*-
+# 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.
+#
+
+
+# MessageWindow.py - scripts and GUI for main (walk) window
+
+###################################################
+
+import GemRB
+import GUIClasses
+import GUICommon
+import GUICommonWindows
+import CommonWindow
+import GUIWORLD
+from GUIDefines import *
+
+MessageWindow = 0
+ActionsWindow = 0
+PortraitWindow = 0
+OptionsWindow = 0
+MessageTA = 0
+
+def OnLoad():
+ global MessageWindow, ActionsWindow, PortraitWindow, OptionsWindow
+
+ GemRB.GameSetPartySize(PARTY_SIZE)
+ GemRB.GameSetProtagonistMode(0)
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ GemRB.SetInfoTextColor(0,255,0,255)
+ ActionsWindow = GemRB.LoadWindow(0)
+ OptionsWindow = GemRB.LoadWindow(2)
+ MessageWindow = GemRB.LoadWindow(7)
+ PortraitWindow = GUICommonWindows.OpenPortraitWindow (1)
+
+ MessageTA = MessageWindow.GetControl (1)
+ MessageTA.SetFlags (IE_GUI_TEXTAREA_AUTOSCROLL)
+ MessageTA.SetHistory (100)
+ GemRB.SetVar ("MessageTextArea", MessageTA.ID)
+ GemRB.SetVar ("ActionsWindow", ActionsWindow.ID)
+ GemRB.SetVar ("OptionsWindow", OptionsWindow.ID)
+ GemRB.SetVar ("MessageWindow", -1)
+ GemRB.SetVar ("OtherWindow", -1)
+ GemRB.SetVar ("ActionsPosition", 1) #Bottom
+ GemRB.SetVar ("OptionsPosition", 1) #Bottom
+ GemRB.SetVar ("MessagePosition", 1) #Bottom
+ GemRB.SetVar ("OtherPosition", 0) #Left
+
+ GemRB.GameSetScreenFlags (0, OP_SET)
+
+ CloseButton= MessageWindow.GetControl (0)
+ CloseButton.SetText(28082)
+ CloseButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnDecreaseSize)
+ CloseButton.SetFlags (IE_GUI_BUTTON_DEFAULT, OP_OR)
+
+ OpenButton = OptionsWindow.GetControl (10)
+ OpenButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, CommonWindow.OnIncreaseSize)
+
+ # Select all
+ Button = ActionsWindow.GetControl (1)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommon.SelectAllOnPress)
+
+ # Select all
+ Button = ActionsWindow.GetControl (3)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUICommonWindows.StopAllOnPress)
+
+ FormationButton = ActionsWindow.GetControl (4)
+ FormationButton.SetEvent (IE_GUI_BUTTON_ON_PRESS, GUIWORLD.OpenFormationWindow)
+
+ GUICommonWindows.SetupActionsWindowControls (ActionsWindow)
+ GUICommonWindows.SetupMenuWindowControls (OptionsWindow)
+
+ UpdateControlStatus ()
+
+def UpdateControlStatus ():
+ global MessageWindow, PortraitWindow, ActionsWindow, OptionsWindow, MessageTA
+
+ Expand = GemRB.GetMessageWindowSize() & (GS_DIALOGMASK|GS_DIALOG)
+
+ hideflags = GemRB.HideGUI ()
+ if Expand:
+ GemRB.SetVar ("MessageWindow", MessageWindow.ID)
+ GemRB.SetVar ("PortraitWindow", -1)
+ GemRB.SetVar ("ActionsWindow", -1)
+ GemRB.SetVar ("OptionsWindow", -1)
+ MessageTA = GUIClasses.GTextArea(MessageWindow.ID, GemRB.GetVar ("MessageTextArea"))
+ MessageTA.SetStatus (IE_GUI_CONTROL_FOCUSED)
+ else:
+ GemRB.SetVar ("MessageWindow", -1)
+ GemRB.SetVar ("PortraitWindow", PortraitWindow.ID)
+ GemRB.SetVar ("ActionsWindow", ActionsWindow.ID)
+ GemRB.SetVar ("OptionsWindow", OptionsWindow.ID)
+ GUICommon.GameControl.SetStatus(IE_GUI_CONTROL_FOCUSED)
+
+ if hideflags:
+ GemRB.UnhideGUI ()
+
diff --git a/gemrb/GUIScripts/pst/NewLife.py b/gemrb/GUIScripts/pst/NewLife.py
new file mode 100644
index 0000000..4b1677a
--- /dev/null
+++ b/gemrb/GUIScripts/pst/NewLife.py
@@ -0,0 +1,506 @@
+# -*-python-*-
+# 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.
+#
+
+# NewLife.py - Character generation screen
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+from ie_stats import *
+import CommonTables
+
+CommonTables.Load()
+
+NewLifeWindow = 0
+QuitWindow = 0
+TextArea = 0
+
+StrLabel = 0
+DexLabel = 0
+ConLabel = 0
+WisLabel = 0
+IntLabel = 0
+ChaLabel = 0
+TotLabel = 0
+AcLabel = 0
+HpLabel = 0
+
+StatTable = 0
+Str = 0
+Dex = 0
+Con = 0
+Wis = 0
+Int = 0
+Cha = 0
+TotPoints = 0
+AcPoints = 0
+HpPoints = 0
+strings = ("30","60","90","99","00")
+extras = (30,60,90,99,100)
+
+def OnLoad():
+ global NewLifeWindow, QuitWindow, StatTable
+ global TotPoints, AcPoints, HpPoints
+ global Str, Dex, Con, Wis, Int, Cha
+ global TotLabel, AcLabel, HpLabel
+ global StrLabel, DexLabel, ConLabel, WisLabel, IntLabel, ChaLabel
+ global TextArea
+
+ GemRB.SetRepeatClickFlags(GEM_RK_DOUBLESPEED, OP_SET)
+ GemRB.LoadGame(None) #loading the base game
+ StatTable = GemRB.LoadTable("abcomm")
+ GemRB.LoadWindowPack("GUICG")
+ #setting up confirmation window
+ QuitWindow = GemRB.LoadWindow(1)
+ QuitWindow.SetVisible(WINDOW_INVISIBLE)
+
+ #setting up CG window
+ NewLifeWindow = GemRB.LoadWindow(0)
+
+ Str = 9
+ Dex = 9
+ Con = 9
+ Wis = 9
+ Int = 9
+ Cha = 9
+ TotPoints = 21
+
+ StrLabel = NewLifeWindow.GetControl(0x10000018)
+ DexLabel = NewLifeWindow.GetControl(0x1000001B)
+ ConLabel = NewLifeWindow.GetControl(0x1000001C)
+ WisLabel = NewLifeWindow.GetControl(0x1000001A)
+ IntLabel = NewLifeWindow.GetControl(0x10000019)
+ ChaLabel = NewLifeWindow.GetControl(0x1000001D)
+
+ Button = NewLifeWindow.GetControl(2)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, StrPress)
+
+ Button = NewLifeWindow.GetControl(3)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, IntPress)
+
+ Button = NewLifeWindow.GetControl(4)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, WisPress)
+
+ Button = NewLifeWindow.GetControl(5)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, DexPress)
+
+ Button = NewLifeWindow.GetControl(6)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, ConPress)
+
+ Button = NewLifeWindow.GetControl(7)
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE, OP_SET)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, ChaPress)
+
+ Button = NewLifeWindow.GetControl(8)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+ Button.SetState(IE_GUI_BUTTON_LOCKED)
+ Button.SetSprites("", 0, 0, 0, 0, 0)
+ Button.SetText(5025)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, AcPress)
+
+ Button = NewLifeWindow.GetControl(9)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+ Button.SetState(IE_GUI_BUTTON_LOCKED)
+ Button.SetSprites("", 0, 0, 0, 0, 0)
+ Button.SetText(5026)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, HpPress)
+
+ Button = NewLifeWindow.GetControl(10)
+ Button.SetFlags(IE_GUI_BUTTON_RADIOBUTTON, OP_SET)
+ Button.SetState(IE_GUI_BUTTON_LOCKED)
+ Button.SetSprites("", 0, 0, 0, 0, 0)
+ Button.SetText(5027)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, PointPress)
+
+ Button = NewLifeWindow.GetControl(11) #str +
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, IncreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, StrPress)
+ Button.SetVarAssoc("Pressed", 0)
+
+ Button = NewLifeWindow.GetControl(13) #int +
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, IncreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, IntPress)
+ Button.SetVarAssoc("Pressed", 1)
+
+ Button = NewLifeWindow.GetControl(15) #wis +
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, IncreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, WisPress)
+ Button.SetVarAssoc("Pressed", 2)
+
+ Button = NewLifeWindow.GetControl(17) #dex +
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, IncreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, DexPress)
+ Button.SetVarAssoc("Pressed", 3)
+
+ Button = NewLifeWindow.GetControl(19) #con +
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, IncreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, ConPress)
+ Button.SetVarAssoc("Pressed", 4)
+
+ Button = NewLifeWindow.GetControl(21) #chr +
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, IncreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, ChaPress)
+ Button.SetVarAssoc("Pressed", 5)
+
+ Button = NewLifeWindow.GetControl(12) #str -
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DecreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, StrPress)
+ Button.SetVarAssoc("Pressed", 0)
+
+ Button = NewLifeWindow.GetControl(14) #int -
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DecreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, IntPress)
+ Button.SetVarAssoc("Pressed", 1)
+
+ Button = NewLifeWindow.GetControl(16) #wis -
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DecreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, WisPress)
+ Button.SetVarAssoc("Pressed", 2)
+
+ Button = NewLifeWindow.GetControl(18) #dex -
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DecreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, DexPress)
+ Button.SetVarAssoc("Pressed", 3)
+
+ Button = NewLifeWindow.GetControl( 20) #con -
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DecreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, ConPress)
+ Button.SetVarAssoc("Pressed", 4)
+
+ Button = NewLifeWindow.GetControl( 22) #chr -
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, DecreasePress)
+ Button.SetEvent(IE_GUI_MOUSE_OVER_BUTTON, ChaPress)
+ Button.SetVarAssoc("Pressed", 5)
+
+ NewLifeLabel = NewLifeWindow.GetControl(0x10000023)
+ NewLifeLabel.SetText(1899)
+
+ TextArea = NewLifeWindow.GetControl(23)
+ TextArea.SetText(18495)
+
+ TotLabel = NewLifeWindow.GetControl(0x10000020)
+ AcLabel = NewLifeWindow.GetControl(0x1000001E)
+ HpLabel = NewLifeWindow.GetControl(0x1000001F)
+
+ Label = NewLifeWindow.GetControl(0x10000021)
+ Label.SetText(254)
+
+ PhotoButton = NewLifeWindow.GetControl(35)
+ PhotoButton.SetState(IE_GUI_BUTTON_DISABLED)
+ PhotoButton.SetFlags(IE_GUI_BUTTON_NO_IMAGE | IE_GUI_BUTTON_PICTURE, OP_SET)
+ PhotoButton.SetPicture("STPNOC")
+
+ AcceptButton = NewLifeWindow.GetControl(0)
+ AcceptButton.SetText(4192)
+ AcceptButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, AcceptPress)
+ AcceptButton.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+
+ CancelButton = NewLifeWindow.GetControl(1)
+ CancelButton.SetText(4196)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, CancelPress)
+
+ UpdateLabels()
+
+ NewLifeWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def UpdateLabels():
+ global AcPoints, HpPoints
+
+ if Str<=18:
+ StrLabel.SetText(str(Str))
+ else:
+ StrLabel.SetText("18/"+strings[Str-19])
+ DexLabel.SetText(str(Dex))
+ ConLabel.SetText(str(Con))
+ WisLabel.SetText(str(Wis))
+ IntLabel.SetText(str(Int))
+ ChaLabel.SetText(str(Cha))
+ TotLabel.SetText(str(TotPoints))
+ AcPoints = 10
+ if Dex>14:
+ AcPoints = AcPoints - (Dex-14)
+
+ HpPoints = 20
+ if Con>14:
+ HpPoints = HpPoints + (Con-9)*2 + (Con-14)
+ else:
+ HpPoints = HpPoints + (Con-9)*2
+
+ AcLabel.SetText(str(AcPoints))
+ HpLabel.SetText(str(HpPoints))
+ return
+
+
+def OkButton():
+ QuitWindow.SetVisible(WINDOW_INVISIBLE)
+ NewLifeWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def AcceptPress():
+ if TotPoints:
+ # Setting up the error window
+ TextArea = QuitWindow.GetControl(0)
+ TextArea.SetText(46782)
+
+ Button = QuitWindow.GetControl(1)
+ Button.SetText("")
+ Button.SetFlags(IE_GUI_BUTTON_NO_IMAGE,OP_SET)
+ Button.SetState(IE_GUI_BUTTON_DISABLED)
+ Button = QuitWindow.GetControl(2)
+ Button.SetText(46783)
+ Button.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkButton)
+ NewLifeWindow.SetVisible(WINDOW_GRAYED) #go dark
+ QuitWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+ if NewLifeWindow:
+ NewLifeWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ #set my character up
+ MyChar = GemRB.CreatePlayer("charbase", 1 )
+
+ if Str<=18:
+ GemRB.SetPlayerStat(1, IE_STR, Str)
+ GemRB.SetPlayerStat(1, IE_STREXTRA,0)
+ else:
+ GemRB.SetPlayerStat(1, IE_STR, 18)
+ GemRB.SetPlayerStat(1, IE_STREXTRA,extras[Str-19])
+
+ GemRB.SetPlayerStat(1, IE_INT, Int)
+ GemRB.SetPlayerStat(1, IE_WIS, Wis)
+ GemRB.SetPlayerStat(1, IE_DEX, Dex)
+ GemRB.SetPlayerStat(1, IE_CON, Con)
+ GemRB.SetPlayerStat(1, IE_CHR, Cha)
+
+ #don't add con bonus, it will be calculated by the game
+ #interestingly enough, the game adds only one level's con bonus
+ if Con>14:
+ x = 30
+ else:
+ x = 20+(Con-9)*2
+
+ print "Setting max hp to: ",x
+ GemRB.SetPlayerStat(1, IE_MAXHITPOINTS, x)
+ #adding the remaining constitution bonus to the current hp
+ #if Con>14:
+ # x = x+(Con-14)*3
+ print "Setting current hp to: ",x
+ GemRB.SetPlayerStat(1, IE_HITPOINTS, x)
+
+ GemRB.FillPlayerInfo(1) #does all the rest
+ #alter this if needed
+ GemRB.SetRepeatClickFlags(GEM_RK_DISABLE, OP_SET)
+ #LETS PLAY!!
+ GemRB.EnterGame()
+ return
+
+def CancelPress():
+ # Setting up the confirmation window
+ TextArea = QuitWindow.GetControl(0)
+ TextArea.SetText(19406)
+
+ Button = QuitWindow.GetControl(1)
+ Button.SetText(23787)
+ Button.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_SET)
+ Button.SetState(IE_GUI_BUTTON_ENABLED)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, YesButton)
+
+ Button = QuitWindow.GetControl(2)
+ Button.SetText(23789)
+ Button.SetFlags(IE_GUI_BUTTON_DEFAULT,OP_OR)
+ Button.SetEvent(IE_GUI_BUTTON_ON_PRESS, OkButton)
+
+ NewLifeWindow.SetVisible(WINDOW_GRAYED) #go dark
+ QuitWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def YesButton():
+ if NewLifeWindow:
+ NewLifeWindow.Unload()
+ if QuitWindow:
+ QuitWindow.Unload()
+ GemRB.SetNextScript("Start")
+ return
+
+def StrPress():
+ TextArea.SetText(18489)
+ s = Str
+ if s>18:
+ e=extras[s-19]
+ s=18
+ else:
+ e=0
+
+ x = CommonTables.StrMod.GetValue(s,0) + CommonTables.StrModEx.GetValue(e,0)
+ y = CommonTables.StrMod.GetValue(s,1) + CommonTables.StrModEx.GetValue(e,1)
+ if x==0:
+ x=y
+ y=0
+
+ if e>60:
+ s=19
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(s,0),x,y) )
+ return
+
+def IntPress():
+ TextArea.SetText(18488)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Int,1),0,0) )
+ return
+
+def WisPress():
+ TextArea.SetText(18490)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Wis,2),0,0) )
+ return
+
+def DexPress():
+ Table = GemRB.LoadTable("dexmod")
+ x = -Table.GetValue(Dex,2)
+ TextArea.SetText(18487)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Dex,3),x,0) )
+ return
+
+def ConPress():
+ Table = GemRB.LoadTable("hpconbon")
+ x = Table.GetValue(Con-1,1)
+ TextArea.SetText(18491)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Con,4),x,0) )
+ return
+
+def ChaPress():
+ TextArea.SetText(1903)
+ TextArea.Append("\n\n"+GemRB.StatComment(StatTable.GetValue(Cha,5),0,0) )
+ return
+
+def PointPress():
+ TextArea.SetText(18492)
+ return
+
+def AcPress():
+ TextArea.SetText(18493)
+ return
+
+def HpPress():
+ TextArea.SetText(18494)
+ return
+
+def DecreasePress():
+ global TotPoints
+ global Sum, Str, Int, Wis, Dex, Con, Cha
+
+ Pressed = GemRB.GetVar("Pressed")
+ if Pressed == 0:
+ Sum = Str
+ if Pressed == 1:
+ Sum = Int
+ if Pressed == 2:
+ Sum = Wis
+ if Pressed == 3:
+ Sum = Dex
+ if Pressed == 4:
+ Sum = Con
+ if Pressed == 5:
+ Sum = Cha
+ if Sum<=9:
+ return
+ TotPoints = TotPoints+1
+ Sum = Sum-1
+ if Pressed == 0:
+ Str = Sum
+ StrPress()
+ if Pressed == 1:
+ Int = Sum
+ IntPress()
+ if Pressed == 2:
+ Wis = Sum
+ WisPress()
+ if Pressed == 3:
+ Dex = Sum
+ DexPress()
+ if Pressed == 4:
+ Con = Sum
+ ConPress()
+ if Pressed == 5:
+ Cha = Sum
+ ChaPress()
+ UpdateLabels()
+ return
+
+def IncreasePress():
+ global TotPoints
+ global Sum, Str, Int, Wis, Dex, Con, Cha
+
+ if TotPoints<=0:
+ return
+ Pressed = GemRB.GetVar("Pressed")
+ if Pressed == 0:
+ Sum = Str
+ if Sum>=23:
+ return
+ if Pressed == 1:
+ Sum = Int
+ if Sum>=18:
+ return
+ if Pressed == 2:
+ Sum = Wis
+ if Sum>=18:
+ return
+ if Pressed == 3:
+ Sum = Dex
+ if Sum>=18:
+ return
+ if Pressed == 4:
+ Sum = Con
+ if Sum>=18:
+ return
+ if Pressed == 5:
+ Sum = Cha
+ if Sum>=18:
+ return
+ TotPoints = TotPoints-1
+ Sum = Sum+1
+ if Pressed == 0:
+ Str = Sum
+ StrPress()
+ if Pressed == 1:
+ Int = Sum
+ IntPress()
+ if Pressed == 2:
+ Wis = Sum
+ WisPress()
+ if Pressed == 3:
+ Dex = Sum
+ DexPress()
+ if Pressed == 4:
+ Con = Sum
+ ConPress()
+ if Pressed == 5:
+ Cha = Sum
+ ChaPress()
+ UpdateLabels()
+ return
+
diff --git a/gemrb/GUIScripts/pst/QuitGame.py b/gemrb/GUIScripts/pst/QuitGame.py
new file mode 100644
index 0000000..29361f2
--- /dev/null
+++ b/gemrb/GUIScripts/pst/QuitGame.py
@@ -0,0 +1,72 @@
+# -*-python-*-
+# 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.
+#
+
+# QuitGame.py - display EndGame sequence
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+import GUICommon
+
+movies = [None,"T1DEATH","T1ABSORB","FINALE"]
+
+def OnLoad ():
+ GemRB.HideGUI ()
+ which = movies[GemRB.GetVar ("QuitGame1")]
+ if which!=None:
+ GemRB.PlayMovie (which,1)
+ which = movies[GemRB.GetVar ("QuitGame2")]
+ if which!=None:
+ GemRB.PlayMovie (which,1)
+ which = GemRB.GetVar ("QuitGame3")
+ if which:
+ DeathWindowEnd ()
+ else:
+ GemRB.QuitGame ()
+ GemRB.SetNextScript("Start")
+
+def DonePress ():
+ GemRB.QuitGame ()
+ GemRB.SetNextScript("Start")
+
+def DeathWindowEnd ():
+ GemRB.GamePause (1,1)
+
+ GemRB.LoadWindowPack (GUICommon.GetWindowPack())
+ Window = GemRB.LoadWindow (25)
+
+ #reason for death
+ Label = Window.GetControl (0x0fffffff)
+ strref = GemRB.GetVar ("QuitGame3")
+ Label.SetText (strref)
+
+ #done
+ Button = Window.GetControl (1)
+ Button.SetText (17237)
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, DonePress)
+ Button.SetFlags (IE_GUI_BUTTON_DEFAULT|IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ GemRB.HideGUI ()
+ GemRB.SetVar ("MessageWindow", -1)
+ GemRB.SetVar ("PortraitWindow", Window.ID)
+ GemRB.UnhideGUI ()
+ #making the playing field gray
+ GUICommon.GameWindow.SetVisible(WINDOW_GRAYED)
+ return
diff --git a/gemrb/GUIScripts/pst/Start.py b/gemrb/GUIScripts/pst/Start.py
new file mode 100644
index 0000000..16afc3f
--- /dev/null
+++ b/gemrb/GUIScripts/pst/Start.py
@@ -0,0 +1,109 @@
+# -*-python-*-
+# 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.
+#
+
+
+# Start.py - intro and main menu screens
+
+###################################################
+
+import GemRB
+from GUIDefines import *
+
+StartWindow = 0
+QuitWindow = 0
+
+def OnLoad():
+ global StartWindow, QuitWindow
+
+ skip_videos = GemRB.GetVar ("SkipIntroVideos")
+
+ if not skip_videos:
+ GemRB.PlayMovie ("BISLOGO", 1)
+ GemRB.PlayMovie ("TSRLOGO", 1)
+ GemRB.PlayMovie ("OPENING", 1)
+
+ GemRB.SetVar ("SkipIntroVideos", 1)
+
+ GemRB.LoadWindowPack("START")
+#quit subwindow
+ QuitWindow = GemRB.LoadWindow(3)
+ QuitTextArea = QuitWindow.GetControl(0)
+ QuitTextArea.SetText(20582)
+ ConfirmButton = QuitWindow.GetControl(1)
+ ConfirmButton.SetText(23787)
+ ConfirmButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitConfirmed)
+ ConfirmButton.SetFlags(IE_GUI_BUTTON_DEFAULT, OP_OR)
+ CancelButton = QuitWindow.GetControl(2)
+ CancelButton.SetText(23789)
+ CancelButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitCancelled)
+ CancelButton.SetFlags(IE_GUI_BUTTON_CANCEL, OP_OR)
+
+#main window
+ StartWindow = GemRB.LoadWindow(0)
+ NewLifeButton = StartWindow.GetControl(0)
+ ResumeLifeButton = StartWindow.GetControl(2)
+ ExitButton = StartWindow.GetControl(3)
+ NewLifeButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, NewLifePress)
+ ResumeLifeButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ResumeLifePress)
+ ExitButton.SetEvent(IE_GUI_BUTTON_ON_PRESS, ExitPress)
+ ExitButton.SetFlags(IE_GUI_BUTTON_CANCEL, OP_OR)
+
+ StartWindow.CreateLabel(0x0fff0000, 0,415,640,30, "EXOFONT", "", 1)
+ Label=StartWindow.GetControl(0x0fff0000)
+ Label.SetText(GEMRB_VERSION)
+
+ QuitWindow.SetVisible(WINDOW_INVISIBLE)
+ StartWindow.SetVisible(WINDOW_VISIBLE)
+
+ GemRB.LoadMusicPL("Main.mus")
+ return
+
+def NewLifePress():
+ if QuitWindow:
+ QuitWindow.Unload()
+ if StartWindow:
+ StartWindow.Unload()
+ #to make difference between ingame change and new life
+ GemRB.SetVar("PlayMode",0)
+ GemRB.SetNextScript("NewLife")
+ return
+
+def ResumeLifePress():
+ if QuitWindow:
+ QuitWindow.Unload()
+ if StartWindow:
+ StartWindow.Unload()
+ #to make difference between ingame load and initial load
+ GemRB.SetVar("PlayMode",0)
+ GemRB.SetNextScript("GUILOAD")
+ return
+
+def ExitPress():
+ StartWindow.SetVisible(WINDOW_GRAYED)
+ QuitWindow.SetVisible(WINDOW_VISIBLE)
+ return
+
+def ExitConfirmed():
+ GemRB.Quit()
+ return
+
+def ExitCancelled():
+ QuitWindow.SetVisible(WINDOW_INVISIBLE)
+ StartWindow.SetVisible(WINDOW_VISIBLE)
+ return
diff --git a/gemrb/GUIScripts/test/Start.py b/gemrb/GUIScripts/test/Start.py
new file mode 100644
index 0000000..99ffba5
--- /dev/null
+++ b/gemrb/GUIScripts/test/Start.py
@@ -0,0 +1,3 @@
+import GemRB
+def OnLoad():
+ GemRB.Quit()
diff --git a/gemrb/GemRB.cfg.noinstall.sample b/gemrb/GemRB.cfg.noinstall.sample
new file mode 100644
index 0000000..fb76d55
--- /dev/null
+++ b/gemrb/GemRB.cfg.noinstall.sample
@@ -0,0 +1,256 @@
+#####################################################
+# #
+# This is the GemRB Configuration file. #
+# Here are defined some default parameters for #
+# basic configuration and paths definition. #
+# #
+# Parameters are defined as a Name=Value pair #
+# The Value can be of three types: #
+# - String #
+# - Integer #
+# - Boolean #
+# #
+# The String value is represented as follows #
+# i.e. H:\GemRB\plugins #
+# Integers are defined as follows #
+# i.e. 12723 #
+# Booleans are represented as 1 or 0 in this file #
+# #
+# Lines starting with # are ignored #
+# #
+#####################################################
+
+#####################################################
+# #
+# Game Type [String] Use one of the following #
+# values: #
+# #
+# auto Attempt to autodetect game type #
+# #
+# bg1 Baldur's Gate #
+# bg2 Baldur's Gate 2 : SoA or ToB #
+# tob Baldur's Gate 2 : ToB (obsolete) #
+# iwd IceWind Dale (no How or ToTL installed)#
+# how IceWind Dale : HoW or ToTL #
+# iwd2 IceWind Dale 2 #
+# pst Planescape Torment #
+# (More will come) #
+# #
+#####################################################
+
+GameType=test
+
+#####################################################
+# Game Name [String] Title for GemRB window, use #
+# anything you wish, e.g. Baldur's Gate 3: RotFL #
+#####################################################
+
+GameName=Baldur's Gate 2
+
+#####################################################
+# Video Parameters #
+#####################################################
+
+#Screen width
+Width=640
+
+#Screen height
+Height=480
+
+#Bits per pixel [Integer:16,32]
+Bpp=32
+
+#Fullscreen [Boolean]
+Fullscreen=0
+
+# Delay before tooltips appear [milliseconds]
+TooltipDelay=500
+
+#####################################################
+# Audio Parameters #
+#####################################################
+# #
+# All volume options are in percents, with 100 #
+# being the normal and default volume #
+# #
+#####################################################
+
+# Choices: openal (default), sdlaudio (faster, but limited featureset), none
+#AudioDriver = openal
+
+# Volume of ambient sounds
+#VolumeAmbients = 100
+
+# Volume during movie playback
+#VolumeMovie = 100
+
+# Volume of background music
+#VolumeMusic = 100
+
+# Volume of sound effects
+#VolumeSFX = 100
+
+# Volume of PC or NPC voices
+#VolumeVoices = 100
+
+#####################################################
+# Case Sensitive Filesystem [Boolean] #
+# #
+# If your installed game files are residing on a #
+# case sensitive filesystem (ext2 on Linux) then #
+# you need to set this value to 1, it has no #
+# effect on Windows #
+#####################################################
+
+#CaseSensitive=1
+
+#GameOnCD=0
+
+#####################################################
+# GUI Parameters #
+#####################################################
+# #
+# GemRB may enhance the GUI of the Infinity Engine #
+# games (so far only in bg2) creating functionally #
+# new buttons or scrollbars where they weren't #
+# present in original games. It may improve its #
+# usability, but is not quite compatible with #
+# mods changing graphics and alignment in the *.chu #
+# files (e.g. the buttons will appear in old #
+# coordinates and may stop being clickable). #
+#####################################################
+
+# Enable all gui enhancements ? [Boolean]
+GUIEnhancements = 1
+
+
+#####################################################
+# Debug #
+#####################################################
+
+# Do not play intro videos [Boolean], useful for development
+#SkipIntroVideos=1
+
+# Draw Frames per Second info [Boolean]
+#DrawFPS=1
+
+# Hide unexplored parts of a map
+#FogOfWar=1
+
+# Enable debug and cheat keystrokes, see docs/en/CheatKeys.txt
+# full listing
+#EnableCheatKeys=1
+
+#####################################################
+# Paths #
+#####################################################
+
+#####################################################
+# Game Paths [String] #
+# #
+# These are the paths where the Game you want to #
+# play is installed. #
+# Enter the full path to the directory. #
+#####################################################
+
+GamePath=../gemrb/tests/minimal
+CD1=/mnt/windows/Programmi/Black Isle/BGII - SoA/
+CD2=/mnt/windows/Programmi/Black Isle/BGII - SoA/CD2/
+CD3=/mnt/windows/Programmi/Black Isle/BGII - SoA/CD3/
+CD4=/mnt/windows/Programmi/Black Isle/BGII - SoA/CD4/
+CD5=/mnt/windows/Programmi/Black Isle/BGII - SoA/CD5/
+
+#####################################################
+# GemRB Cache Path [String] #
+# #
+# This is the path where GemRB will store the #
+# cached files, enter the full path to the GemRB #
+# Cache directory. #
+#####################################################
+
+CachePath=./gemrb/Cache/
+
+#####################################################
+# GemRB Save Path [String] #
+# #
+# This is the path where GemRB looks for saved #
+# games. #
+# Enter the full path to the directory containing #
+# e.g. the 'save' subdirectory. #
+# #
+# You do not have to specify this if you use save #
+# subdir in the GamePath directory. #
+#####################################################
+
+#SavePath=/mnt/windows/Programmi/Black Isle/BGII - SoA/
+
+#####################################################
+# GemRB Path [String] #
+# #
+# This is the path where GemRB is stored, just #
+# enter the full path to the GemRB executable #
+#####################################################
+
+GemRBPath=../gemrb
+
+#####################################################
+# GemRB GUI Scripts Path [String] #
+# #
+# This is the path where GemRB GUI scripts are #
+# stored, usually these are in the GemRB directory #
+# Enter the full path to the directory containing #
+# the 'GUIScript' subdirectory. #
+#####################################################
+
+#GUIScriptsPath=./
+
+#####################################################
+# GemRB Plugins Path [String] #
+# #
+# This is the path containing GemRB plugins #
+# - shared libraries (.so) on Unixes, or #
+# DLLs (.dll) on windows. #
+# Enter the full path to the directory. #
+# #
+# You may need to specify this path if running #
+# GemRB from source directory on Linux. #
+#####################################################
+
+PluginsPath=./gemrb/plugins/
+
+#####################################################
+# Game Data Path [String] #
+# #
+# This is the subdirectory under GamePath where #
+# game data files are stored. #
+# #
+# You probably do NOT want to specify this! #
+#####################################################
+
+#GameDataPath=data
+
+#####################################################
+# Game Data Override Path [String] #
+# #
+# This is the subdirectory under GamePath where #
+# game data override files are stored. #
+# #
+# You probably do NOT want to specify this! #
+#####################################################
+
+#GameOverridePath=override
+
+#####################################################
+# GemRB Data Override Path [String] #
+# #
+# This is the path where GemRB looks for the GemRB #
+# data override directory. #
+# #
+# You probably do NOT want to specify this! #
+#####################################################
+
+#GemRBOverridePath=/usr/share/games/gemrb
+
+#####################################################
+# END #
+#####################################################
diff --git a/gemrb/GemRB.cfg.sample.in b/gemrb/GemRB.cfg.sample.in
new file mode 100644
index 0000000..7ebccd1
--- /dev/null
+++ b/gemrb/GemRB.cfg.sample.in
@@ -0,0 +1,261 @@
+#####################################################
+# #
+# This is the GemRB Configuration file. #
+# Here are defined some default parameters for #
+# basic configuration and paths definition. #
+# #
+# Parameters are defined as a Name=Value pair #
+# The Value can be of three types: #
+# - String #
+# - Integer #
+# - Boolean #
+# #
+# The String value is represented as follows #
+# i.e. H:\GemRB\plugins #
+# Integers are defined as follows #
+# i.e. 12723 #
+# Booleans are represented as 1 or 0 in this file #
+# #
+# Lines starting with # are ignored #
+# #
+#####################################################
+
+#####################################################
+# #
+# Game Type [String] Use one of the following #
+# values: #
+# #
+# auto Attempt to autodetect game type #
+# #
+# bg1 Baldur's Gate #
+# bg2 Baldur's Gate 2 : SoA or ToB #
+# tob Baldur's Gate 2 : ToB (obsolete) #
+# iwd IceWind Dale (no How or ToTL installed)#
+# how IceWind Dale : HoW or ToTL #
+# iwd2 IceWind Dale 2 #
+# pst Planescape Torment #
+# (More will come) #
+# #
+#####################################################
+
+GameType=bg2
+
+#####################################################
+# Game Name [String] Title for GemRB window, use #
+# anything you wish, e.g. Baldur's Gate 3: RotFL #
+#####################################################
+
+GameName=Baldur's Gate 2
+
+#####################################################
+# Video Parameters #
+#####################################################
+
+#Screen width
+Width=640
+
+#Screen height
+Height=480
+
+#Bits per pixel [Integer:16,32]
+Bpp=32
+
+#Fullscreen [Boolean]
+Fullscreen=0
+
+# Delay before tooltips appear [milliseconds]
+TooltipDelay=500
+
+#####################################################
+# Audio Parameters #
+#####################################################
+# #
+# All volume options are in percents, with 100 #
+# being the normal and default volume #
+# #
+#####################################################
+
+# Choices: openal (default), sdlaudio (faster, but limited featureset), none
+#AudioDriver = openal
+
+# Volume of ambient sounds
+#VolumeAmbients = 100
+
+# Volume during movie playback
+#VolumeMovie = 100
+
+# Volume of background music
+#VolumeMusic = 100
+
+# Volume of sound effects
+#VolumeSFX = 100
+
+# Volume of PC or NPC voices
+#VolumeVoices = 100
+
+#####################################################
+# Case Sensitive Filesystem [Boolean] #
+# #
+# If your installed game files are residing on a #
+# case sensitive filesystem (ext2 on Linux) then #
+# you need to set this value to 1, it has no #
+# effect on Windows #
+#####################################################
+
+#CaseSensitive=1
+
+#GameOnCD=0
+
+#####################################################
+# GUI Parameters #
+#####################################################
+# #
+# GemRB may enhance the GUI of the Infinity Engine #
+# games (so far only in bg2) creating functionally #
+# new buttons or scrollbars where they weren't #
+# present in original games. It may improve its #
+# usability, but is not quite compatible with #
+# mods changing graphics and alignment in the *.chu #
+# files (e.g. the buttons will appear in old #
+# coordinates and may stop being clickable). #
+#####################################################
+
+# Enable all gui enhancements ? [Boolean]
+GUIEnhancements = 1
+
+
+#####################################################
+# Debug #
+#####################################################
+
+# Do not play intro videos [Boolean], useful for development
+#SkipIntroVideos=1
+
+# Draw Frames per Second info [Boolean]
+#DrawFPS=1
+
+# Hide unexplored parts of a map
+#FogOfWar=1
+
+# Enable debug and cheat keystrokes, see docs/en/CheatKeys.txt
+# full listing
+#EnableCheatKeys=1
+
+#####################################################
+# Paths #
+#####################################################
+
+#####################################################
+# Game Paths [String] #
+# #
+# These are the paths where the Game you want to #
+# play is installed. #
+# Enter the full path to the directory. #
+#####################################################
+
+GamePath=/mnt/windows/Programmi/Black Isle/BGII - SoA/
+CD1=/mnt/windows/Programmi/Black Isle/BGII - SoA/
+CD2=/mnt/windows/Programmi/Black Isle/BGII - SoA/CD2/
+CD3=/mnt/windows/Programmi/Black Isle/BGII - SoA/CD3/
+CD4=/mnt/windows/Programmi/Black Isle/BGII - SoA/CD4/
+CD5=/mnt/windows/Programmi/Black Isle/BGII - SoA/CD5/
+
+#####################################################
+# GemRB Cache Path [String] #
+# #
+# This is the path where GemRB will store the #
+# cached files, enter the full path to the GemRB #
+# Cache directory. #
+#####################################################
+
+CachePath=./Cache/
+
+#####################################################
+# GemRB Save Path [String] #
+# #
+# This is the path where GemRB looks for saved #
+# games. #
+# Enter the full path to the directory containing #
+# e.g. the 'save' subdirectory. #
+# #
+# You do not have to specify this if you use save #
+# subdir in the GamePath directory. #
+#####################################################
+
+#SavePath=/mnt/windows/Programmi/Black Isle/BGII - SoA/
+
+###### HERE BE DRAGONS #############################
+###### HERE BE DRAGONS #############################
+###### HERE BE DRAGONS #############################
+# You shouldn't need to change anything below this point.
+
+#####################################################
+# GemRB Path [String] #
+# #
+# This is the path where GemRB is stored, just #
+# enter the full path to the GemRB executable #
+#####################################################
+
+#GemRBPath=@DATA_DIR@
+
+#####################################################
+# GemRB GUI Scripts Path [String] #
+# #
+# This is the path where GemRB GUI scripts are #
+# stored, usually these are in the GemRB directory #
+# Enter the full path to the directory containing #
+# the 'GUIScript' subdirectory. #
+#####################################################
+
+#GUIScriptsPath=@DATA_DIR@
+
+#####################################################
+# GemRB Plugins Path [String] #
+# #
+# This is the path containing GemRB plugins #
+# - shared libraries (.so) on Unixes, or #
+# DLLs (.dll) on windows. #
+# Enter the full path to the directory. #
+# #
+# You may need to specify this path if running #
+# GemRB from source directory on Linux. #
+#####################################################
+
+#PluginsPath=@PLUGIN_DIR@
+
+#####################################################
+# Game Data Path [String] #
+# #
+# This is the subdirectory under GamePath where #
+# game data files are stored. #
+# #
+# You probably do NOT want to specify this! #
+#####################################################
+
+#GameDataPath=data
+
+#####################################################
+# Game Data Override Path [String] #
+# #
+# This is the subdirectory under GamePath where #
+# game data override files are stored. #
+# #
+# You probably do NOT want to specify this! #
+#####################################################
+
+#GameOverridePath=override
+
+#####################################################
+# GemRB Data Override Path [String] #
+# #
+# This is the path where GemRB looks for the GemRB #
+# data override directory. #
+# #
+# You probably do NOT want to specify this! #
+#####################################################
+
+#GemRBOverridePath=@DATA_DIR@
+
+#####################################################
+# END #
+#####################################################
diff --git a/gemrb/GemRB.cpp b/gemrb/GemRB.cpp
new file mode 100644
index 0000000..9f8b08b
--- /dev/null
+++ b/gemrb/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 <cstdio>
+
+#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 <SDL.h>
+#endif
+
+#ifdef ANDROID
+#include <SDL/SDL.h>
+#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[])
+{
+ 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;
+ }
+#ifdef ANDROID
+ SDL_ANDROID_SetApplicationPutToBackgroundCallback(&appPutToBackground, &appPutToForeground);
+#endif
+ core->Main();
+ delete( core );
+ textcolor(DEFAULT);
+ return 0;
+}
diff --git a/gemrb/Makefile.am b/gemrb/Makefile.am
new file mode 100644
index 0000000..4feac3d
--- /dev/null
+++ b/gemrb/Makefile.am
@@ -0,0 +1,13 @@
+bin_PROGRAMS = gemrb
+sysconf_DATA = GemRB.cfg.sample.in
+gemrb_SOURCES = GemRB.cpp
+
+gemrb_LDADD = ./core/libgemrb_core.la
+
+SUBDIRS = core plugins includes GUIScripts override docs
+EXTRA_DIST = GemRB.cfg* plugins-prepare.sh CMakeLists.txt
+
+gemrb_LDFLAGS = $(all_libraries) @LIBPTHREAD@
+
+#install-data-local:
+# $(INSTALL_DATA) GemRB.cfg* $(sysconfdir)
diff --git a/gemrb/core/ActorMgr.cpp b/gemrb/core/ActorMgr.cpp
new file mode 100644
index 0000000..ebcee71
--- /dev/null
+++ b/gemrb/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/gemrb/core/ActorMgr.h b/gemrb/core/ActorMgr.h
new file mode 100644
index 0000000..7afcfba
--- /dev/null
+++ b/gemrb/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/gemrb/core/Ambient.cpp b/gemrb/core/Ambient.cpp
new file mode 100644
index 0000000..db543f4
--- /dev/null
+++ b/gemrb/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/gemrb/core/Ambient.h b/gemrb/core/Ambient.h
new file mode 100644
index 0000000..ec4f9be
--- /dev/null
+++ b/gemrb/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 <bitset>
+#include <string>
+#include <vector>
+
+#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.size()) return sounds[i];
+ return NULL;
+ }
+ ieDword getInterval() const { return interval; }
+ ieDword getPerset() const { return perset; }
+ ieDword getAppearance() const { return appearance; }
+ ieDword getFlags() const { return flags; }
+
+ void setActive();
+ void setInactive();
+
+public:
+ char name[32];
+ Point origin;
+ ieWord radius;
+ ieWord height;
+ ieWord gain; // percent
+ std::vector<char *> sounds;
+ ieDword interval; // no pauses if zero
+ ieDword perset;
+ ieDword appearance;
+ ieDword flags;
+
+};
+
+#endif
diff --git a/gemrb/core/AmbientMgr.cpp b/gemrb/core/AmbientMgr.cpp
new file mode 100644
index 0000000..a998eb8
--- /dev/null
+++ b/gemrb/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<Ambient *>::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<Ambient *>::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<Ambient *>::const_iterator it = ambients.begin(); it != ambients.end(); ++it) {
+ if ((*it) -> getName() == name) {
+ return (*it) -> getFlags() & IE_AMBI_ENABLED;
+ }
+ }
+ return false;
+}
diff --git a/gemrb/core/AmbientMgr.h b/gemrb/core/AmbientMgr.h
new file mode 100644
index 0000000..0b6daf1
--- /dev/null
+++ b/gemrb/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 <string>
+#include <vector>
+
+class Ambient;
+
+class GEM_EXPORT AmbientMgr {
+public:
+ AmbientMgr();
+ virtual ~AmbientMgr();
+ virtual void reset() { ambients = std::vector<Ambient *> (); }
+ virtual void setAmbients(const std::vector<Ambient *> &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<Ambient *> ambients;
+ bool active;
+};
+
+#endif
diff --git a/gemrb/core/AnimStructures.h b/gemrb/core/AnimStructures.h
new file mode 100644
index 0000000..9692cf9
--- /dev/null
+++ b/gemrb/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/gemrb/core/Animation.cpp b/gemrb/core/Animation.cpp
new file mode 100644
index 0000000..6ec1caa
--- /dev/null
+++ b/gemrb/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) {
+ pos=index;
+ }
+ starttime = 0;
+ endReached = false;
+}
+
+/* when adding NULL, it means we already added a frame of index */
+void Animation::AddFrame(Sprite2D* frame, 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/gemrb/core/Animation.h b/gemrb/core/Animation.h
new file mode 100644
index 0000000..fcf9a7d
--- /dev/null
+++ b/gemrb/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 <vector>
+
+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/gemrb/core/AnimationFactory.cpp b/gemrb/core/AnimationFactory.cpp
new file mode 100644
index 0000000..e663739
--- /dev/null
+++ b/gemrb/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/gemrb/core/AnimationFactory.h b/gemrb/core/AnimationFactory.h
new file mode 100644
index 0000000..b385362
--- /dev/null
+++ b/gemrb/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/gemrb/core/AnimationMgr.cpp b/gemrb/core/AnimationMgr.cpp
new file mode 100644
index 0000000..7ddece9
--- /dev/null
+++ b/gemrb/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/gemrb/core/AnimationMgr.h b/gemrb/core/AnimationMgr.h
new file mode 100644
index 0000000..d161aff
--- /dev/null
+++ b/gemrb/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/gemrb/core/ArchiveImporter.cpp b/gemrb/core/ArchiveImporter.cpp
new file mode 100644
index 0000000..2c8524f
--- /dev/null
+++ b/gemrb/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/gemrb/core/ArchiveImporter.h b/gemrb/core/ArchiveImporter.h
new file mode 100644
index 0000000..076d679
--- /dev/null
+++ b/gemrb/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/gemrb/core/Audio.cpp b/gemrb/core/Audio.cpp
new file mode 100644
index 0000000..fa114c4
--- /dev/null
+++ b/gemrb/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/gemrb/core/Audio.h b/gemrb/core/Audio.h
new file mode 100644
index 0000000..4606f38
--- /dev/null
+++ b/gemrb/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<SoundHandle> {
+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<SoundHandle> Play(const char* ResRef, int XPos, int YPos, unsigned int flags = 0, unsigned int *length = 0) = 0;
+ virtual Holder<SoundHandle> 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<SoundMgr>) = 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/gemrb/core/Bitmap.cpp b/gemrb/core/Bitmap.cpp
new file mode 100644
index 0000000..2a55fa4
--- /dev/null
+++ b/gemrb/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/gemrb/core/Bitmap.h b/gemrb/core/Bitmap.h
new file mode 100644
index 0000000..2c37bdd
--- /dev/null
+++ b/gemrb/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/gemrb/core/CMakeLists.txt b/gemrb/core/CMakeLists.txt
new file mode 100644
index 0000000..767e033
--- /dev/null
+++ b/gemrb/core/CMakeLists.txt
@@ -0,0 +1,52 @@
+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/Container.cpp
+ Scriptable/Door.cpp
+ Scriptable/InfoPoint.cpp
+ Scriptable/Scriptable.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/gemrb/core/Cache.cpp b/gemrb/core/Cache.cpp
new file mode 100644
index 0000000..647b0cb
--- /dev/null
+++ b/gemrb/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 <ctype.h>
+
+// private inlines
+inline unsigned int Cache::MyHashKey(const char* key) const
+{
+ int nHash = tolower(key[0]);
+ for (int i=1;(i<KEYSIZE) && key[i];i++) {
+ nHash = (nHash << 5) ^ tolower(key[i]);
+ }
+ return nHash % m_nHashTableSize;
+}
+
+Cache::Cache(int nBlockSize, int nHashTableSize)
+{
+ assert( nBlockSize > 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;i<KEYSIZE && key[i];i++) {
+ pAssoc->key[i]=tolower(key[i]);
+ }
+ for (;i<KEYSIZE;i++) {
+ pAssoc->key[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/gemrb/core/Cache.h b/gemrb/core/Cache.h
new file mode 100644
index 0000000..b83e576
--- /dev/null
+++ b/gemrb/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/gemrb/core/Calendar.cpp b/gemrb/core/Calendar.cpp
new file mode 100644
index 0000000..673bdb0
--- /dev/null
+++ b/gemrb/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;i<monthnamecount;i++) {
+ days[i]=atoi(tab->QueryField(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;i<monthnamecount;i++) {
+ if (dayandmonth<days[i]) {
+ char *tmp;
+
+ core->GetTokenDictionary()->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<monthnamecount;i++) {
+ if (dayandmonth<days[i]) {
+ break;
+ }
+ dayandmonth-=days[i];
+ //ignoring single days (they are not months)
+ if (days[i]!=1) month++;
+ }
+ return dayandmonth+1;
+}
+
diff --git a/gemrb/core/Calendar.h b/gemrb/core/Calendar.h
new file mode 100644
index 0000000..28740f0
--- /dev/null
+++ b/gemrb/core/Calendar.h
@@ -0,0 +1,40 @@
+/* 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.
+ *
+ *
+ */
+
+#ifndef CALENDAR_H
+#define CALENDAR_H
+
+#include "exports.h"
+
+class GEM_EXPORT Calendar {
+private:
+ int daysinyear;
+ int monthnamecount;
+ int *days;
+ int *monthnames;
+
+public:
+ Calendar(void);
+ ~Calendar(void);
+ void GetMonthName(int dayandmonth) const;
+ int GetCalendarDay(int date) const;
+};
+
+#endif
diff --git a/gemrb/core/Callback.cpp b/gemrb/core/Callback.cpp
new file mode 100644
index 0000000..8cfb7bf
--- /dev/null
+++ b/gemrb/core/Callback.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 "Callback.h"
+
+Callback::~Callback()
+{
+}
+
+bool Callback::call()
+{
+ return true;
+}
+
+bool Callback::call(int)
+{
+ return true;
+}
diff --git a/gemrb/core/Callback.h b/gemrb/core/Callback.h
new file mode 100644
index 0000000..208e628
--- /dev/null
+++ b/gemrb/core/Callback.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 CALLBACK_H
+#define CALLBACK_H
+
+#include "exports.h"
+
+#include "Holder.h"
+
+class GEM_EXPORT Callback : public Held<Callback> {
+public:
+ virtual ~Callback();
+ virtual bool call ();
+ virtual bool call (int);
+};
+
+typedef Holder<Callback> EventHandler;
+
+#endif
diff --git a/gemrb/core/CharAnimations.cpp b/gemrb/core/CharAnimations.cpp
new file mode 100644
index 0000000..66dc4b1
--- /dev/null
+++ b/gemrb/core/CharAnimations.cpp
@@ -0,0 +1,2408 @@
+/* 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::CheckColorMod()
+{
+ if (!GlobalColorMod.locked) {
+ if (GlobalColorMod.type != RGBModifier::NONE) {
+ GlobalColorMod.type = RGBModifier::NONE;
+ GlobalColorMod.speed = 0;
+ change[0]=change[1]=change[2]=change[3]=true;
+ }
+ }
+ unsigned int location;
+
+ for (location = 0; location < 32; ++location) {
+ if (!ColorMods[location].phase) {
+ if (ColorMods[location].type != RGBModifier::NONE) {
+ ColorMods[location].type = RGBModifier::NONE;
+ ColorMods[location].speed = 0;
+ change[location>>3]=true;
+ }
+ }
+ }
+}
+
+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;
+ }
+ //don't drop the palette, it disables rgb pulse effects
+ //also don't bail out, we need to free the modified palette later
+ //so this entire block is needless
+ /*
+ 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;i<rows;i++) {
+ unsigned long value = 0;
+ unsigned long rmin = 0;
+ unsigned long rmax = 0xffff;
+
+ valid_number(blood->QueryField(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;j<AvatarsCount;j++) {
+ if (rmax<AvatarTable[j].AnimID) break;
+ if (rmin>AvatarTable[j].AnimID) continue;
+ AvatarTable[j].BloodColor = value;
+ }
+ }
+ }
+
+ AutoTable walk("walksnd");
+ if (walk) {
+ int rows = walk->GetRowCount();
+ for(int i=0;i<rows;i++) {
+ ieResRef value;
+ unsigned long rmin = 0;
+ unsigned long rmax = 0xffff;
+ unsigned int range = 0;
+
+ strnuprcpy(value, walk->QueryField(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;j<AvatarsCount;j++) {
+ if (rmax<AvatarTable[j].AnimID) break;
+ if (rmin>AvatarTable[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) {
+ change[i] = true;
+ 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;
+ ColorMods[i].locked = false;
+ }
+ GlobalColorMod.type = RGBModifier::NONE;
+ GlobalColorMod.speed = 0;
+ GlobalColorMod.phase = 0;
+ GlobalColorMod.locked = false;
+ 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; i2<MAX_ORIENT; i2++) {
+ if (Anims[IDb][i2] == tmppoi) {
+ Anims[IDb][i2] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+CharAnimations::~CharAnimations(void)
+{
+ DropAnims();
+ gamedata->FreePalette(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;
+
+ 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;
+ GlobalColorMod.locked = false;
+ }
+ }
+
+ 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 (ColorMods[i].phase > 2*ColorMods[i].speed) {
+ ColorMods[i].type = RGBModifier::NONE;
+ ColorMods[i].phase = 0;
+ ColorMods[i].speed = 0;
+ ColorMods[i].locked = false;
+ }
+ }
+ }
+
+ if (change[0]) {
+ change[0]=0;
+ SetupColors(PAL_MAIN);
+ }
+ if (change[1]) {
+ change[1]=0;
+ SetupColors(PAL_WEAPON);
+ }
+ if (change[2]) {
+ change[2]=0;
+ SetupColors(PAL_OFFHAND);
+ }
+ if (change[3]) {
+ change[3]=0;
+ SetupColors(PAL_HELMET);
+ }
+
+ lastModUpdate += inc*40;
+}
diff --git a/gemrb/core/CharAnimations.h b/gemrb/core/CharAnimations.h
new file mode 100644
index 0000000..ea33310
--- /dev/null
+++ b/gemrb/core/CharAnimations.h
@@ -0,0 +1,233 @@
+/* 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 <vector>
+
+#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
+
+ bool change[4];
+ 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 SetColors(const ieDword *Colors);
+ void CheckColorMod();
+ void SetupColors(PaletteType type);
+ 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/gemrb/core/Compressor.cpp b/gemrb/core/Compressor.cpp
new file mode 100644
index 0000000..e7a675b
--- /dev/null
+++ b/gemrb/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/gemrb/core/Compressor.h b/gemrb/core/Compressor.h
new file mode 100644
index 0000000..3409f80
--- /dev/null
+++ b/gemrb/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 <cstdio>
+
+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/gemrb/core/ControlAnimation.cpp b/gemrb/core/ControlAnimation.cpp
new file mode 100644
index 0000000..6e61884
--- /dev/null
+++ b/gemrb/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/gemrb/core/ControlAnimation.h b/gemrb/core/ControlAnimation.h
new file mode 100644
index 0000000..c85870f
--- /dev/null
+++ b/gemrb/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 <vector>
+
+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/gemrb/core/Core.cpp b/gemrb/core/Core.cpp
new file mode 100644
index 0000000..5fd0b66
--- /dev/null
+++ b/gemrb/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 <cmath>
+#include <ctype.h>
+#ifdef WIN32
+#include "win32def.h"
+#ifdef _DEBUG
+#include <stdlib.h>
+#include <crtdbg.h>
+#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, used to copy variables
+void strnspccpy(char* dest, const char *source, int count)
+{
+ memset(dest,0,count);
+ while(count--) {
+ char c = pl_uppercase[(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/gemrb/core/DataFileMgr.cpp b/gemrb/core/DataFileMgr.cpp
new file mode 100644
index 0000000..2c95d67
--- /dev/null
+++ b/gemrb/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/gemrb/core/DataFileMgr.h b/gemrb/core/DataFileMgr.h
new file mode 100644
index 0000000..8773767
--- /dev/null
+++ b/gemrb/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/gemrb/core/Dialog.cpp b/gemrb/core/Dialog.cpp
new file mode 100644
index 0000000..a8399ee
--- /dev/null
+++ b/gemrb/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/gemrb/core/Dialog.h b/gemrb/core/Dialog.h
new file mode 100644
index 0000000..2b7e921
--- /dev/null
+++ b/gemrb/core/Dialog.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.
+ *
+ *
+ */
+
+#ifndef DIALOG_H
+#define DIALOG_H
+
+#include "exports.h"
+#include "globals.h"
+
+#include <vector>
+
+#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
+
+class Condition;
+class Action;
+
+struct DialogTransition {
+ ieDword Flags;
+ ieStrRef textStrRef;
+ ieStrRef journalStrRef;
+ Condition* condition;
+ std::vector<Action*> 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/gemrb/core/DialogHandler.cpp b/gemrb/core/DialogHandler.cpp
new file mode 100644
index 0000000..687547b
--- /dev/null
+++ b/gemrb/core/DialogHandler.cpp
@@ -0,0 +1,489 @@
+/* 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 "ScriptEngine.h"
+#include "Video.h"
+#include "GameScript/GameScript.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<DialogMgr> 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();
+
+ GameControl *gc = core->GetGameControl();
+
+ if (!gc)
+ return -1;
+
+ //check if we are already in dialog
+ if (gc->GetDialogueFlags()&DF_IN_DIALOG) {
+ return 0;
+ }
+
+ int si = dlg->FindFirstState( tgt );
+ if (si < 0) {
+ return -1;
+ }
+
+ //we need GUI for dialogs
+ //but the guiscript must be in control here
+ //gc->UnhideGUI();
+
+ //no exploring while in dialogue
+ gc->SetScreenFlags(/*SF_GUIENABLED|*/SF_DISABLEMOUSE|SF_LOCKSCROLL, BM_OR);
+ gc->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) ) {
+ gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR);
+ }
+ //opening control size to maximum, enabling dialog window
+ //but the guiscript must be in control here
+ //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;
+ }
+ // FIXME: it's not so nice having this here, but things call EndDialog directly :(
+ core->GetGUIScriptEngine()->RunFunction( "GUIWORLD", "DialogEnded" );
+ //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();
+
+ // do not interrupt during dialog actions (needed for aerie.d polymorph block)
+ char buf[20];
+ strcpy(buf, "SetInterrupt(FALSE)");
+ target->AddAction( GenerateAction( buf ) );
+ for (unsigned int i = 0; i < tr->actions.size(); i++) {
+ target->AddAction(tr->actions[i]);
+ }
+ strcpy(buf, "SetInterrupt(TRUE)");
+ target->AddAction( GenerateAction( buf ) );
+ }
+
+ 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 (Scriptable *)door;
+ Container *container = area->GetContainerByGlobalID(targetID);
+ if (container) return (Scriptable *)container;
+ InfoPoint *ip = area->GetInfoPointByGlobalID(targetID);
+ if (ip) return (Scriptable *)ip;
+
+ return NULL;
+}
+
+Actor *DialogHandler::GetSpeaker()
+{
+ return GetActorByGlobalID(speakerID);
+}
+
diff --git a/gemrb/core/DialogHandler.h b/gemrb/core/DialogHandler.h
new file mode 100644
index 0000000..499fb0d
--- /dev/null
+++ b/gemrb/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/gemrb/core/DialogMgr.cpp b/gemrb/core/DialogMgr.cpp
new file mode 100644
index 0000000..bc68aa5
--- /dev/null
+++ b/gemrb/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/gemrb/core/DialogMgr.h b/gemrb/core/DialogMgr.h
new file mode 100644
index 0000000..125ae52
--- /dev/null
+++ b/gemrb/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/gemrb/core/DisplayMessage.cpp b/gemrb/core/DisplayMessage.cpp
new file mode 100644
index 0000000..ceecd6b
--- /dev/null
+++ b/gemrb/core/DisplayMessage.cpp
@@ -0,0 +1,239 @@
+/* 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;i<STRREF_COUNT;i++) {
+ strref_table[i]=atoi(tab->QueryField(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( 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;
+ char* text = core->GetString( strref_table[stridx], IE_STR_SOUND );
+ DisplayString(text, color, target);
+ core->FreeString(text);
+}
+
+void DisplayMessage::DisplayString(int stridx, unsigned int color, ieDword flags) const
+{
+ if (stridx<0) return;
+ char* text = core->GetString( stridx, flags);
+ DisplayString(text, color, NULL);
+ core->FreeString(text);
+}
+
+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
+// <charname> - 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
+// <charname> - blah blah
+void DisplayMessage::DisplayConstantStringName(int stridx, unsigned int color, const Scriptable *speaker) const
+{
+ if (stridx<0) return;
+ if(!speaker) return;
+
+ char* text = core->GetString( strref_table[stridx], IE_STR_SOUND|IE_STR_SPEECH );
+ DisplayStringName(text, color, speaker);
+ core->FreeString(text);
+}
+
+// String format is
+// <charname> - blah blah <someoneelse>
+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;
+
+ char* text = core->GetString( stridx, flags);
+ DisplayStringName(text, color, speaker);
+ core->FreeString( text );
+}
+
+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);
+
+ //FIXME: what happens if there is no name?
+ if (name) {
+ 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/gemrb/core/DisplayMessage.h b/gemrb/core/DisplayMessage.h
new file mode 100644
index 0000000..9fc781b
--- /dev/null
+++ b/gemrb/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/gemrb/core/Effect.h b/gemrb/core/Effect.h
new file mode 100644
index 0000000..400c6bc
--- /dev/null
+++ b/gemrb/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/gemrb/core/EffectMgr.cpp b/gemrb/core/EffectMgr.cpp
new file mode 100644
index 0000000..7ca50f4
--- /dev/null
+++ b/gemrb/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/gemrb/core/EffectMgr.h b/gemrb/core/EffectMgr.h
new file mode 100644
index 0000000..689651c
--- /dev/null
+++ b/gemrb/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/gemrb/core/EffectQueue.cpp b/gemrb/core/EffectQueue.cpp
new file mode 100644
index 0000000..ca980d6
--- /dev/null
+++ b/gemrb/core/EffectQueue.cpp
@@ -0,0 +1,1942 @@
+/* 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 <cstdio>
+
+static struct {
+ const char* Name;
+ EffectFunction Function;
+ int Strref;
+ int Flags;
+} Opcodes[MAX_EFFECTS];
+
+static int initialized = 0;
+static EffectDesc *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
+ //this is a hack to support dead only projectiles in PST
+ //if it interferes with something feel free to remove
+ if (value==GEN_DEAD) {
+ if (target->GetStat(IE_STATE_ID)&STATE_DEAD) {
+ return true;
+ }
+ }
+ 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 EffectDesc* FindEffect(const char* effectname)
+{
+ if( !effectname || !effectnames) {
+ return NULL;
+ }
+ void *tmp = bsearch(effectname, effectnames, effectnames_count, sizeof(EffectDesc), find_effect);
+ if( !tmp) {
+ printMessage( "EffectQueue", "", YELLOW);
+ printf("Couldn't assign effect: %s\n", effectname );
+ }
+ return (EffectDesc *) tmp;
+}
+
+static EffectRef fx_protection_from_display_string_ref = { "Protection:String", -1 };
+
+inline static void ResolveEffectRef(EffectRef &effect_reference)
+{
+ if( effect_reference.opcode==-1) {
+ EffectDesc* 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;i<MAX_EFFECTS;i++) {
+ Opcodes[i].Strref=-1;
+ }
+
+ initialized = 1;
+
+ AutoTable efftextTable("efftext");
+
+ int eT = core->LoadSymbol( "effects" );
+ if( eT < 0) {
+ printMessage( "EffectQueue","A critical scripting file is missing!\n",LIGHT_RED );
+ return false;
+ }
+ Holder<SymbolMgr> 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 ) );
+ }
+ }
+ }
+
+ EffectDesc* poi = FindEffect( effectname );
+ if( poi != NULL) {
+ Opcodes[i].Function = poi->Function;
+ Opcodes[i].Name = poi->Name;
+ Opcodes[i].Flags = poi->Flags;
+ //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 );
+
+ return true;
+}
+
+void EffectQueue_ReleaseMemory()
+{
+ if( effectnames) {
+ free (effectnames);
+ }
+ effectnames_count = 0;
+ effectnames = NULL;
+}
+
+void EffectQueue_RegisterOpcodes(int count, const EffectDesc* opcodes)
+{
+ if( ! effectnames) {
+ effectnames = (EffectDesc*) malloc( (count+1) * sizeof( EffectDesc ) );
+ } else {
+ effectnames = (EffectDesc*) realloc( effectnames, (effectnames_count + count + 1) * sizeof( EffectDesc ) );
+ }
+
+ memcpy( effectnames + effectnames_count, opcodes, count * sizeof( EffectDesc ));
+ 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(EffectDesc), 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", -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(false);
+ while(i--) {
+ Actor* actor = game->GetPC( i, false );
+ 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;
+}
+
+//resisted effect based on level
+inline bool check_level(Actor *target, Effect *fx)
+{
+ //skip non level based effects
+ //check if an effect has no level based resistance, but instead the dice sizes/count
+ //adjusts Parameter1 (like a damage causing effect)
+ if( Opcodes[fx->Opcode].Flags & EFFECT_DICED ) {
+ //add the caster level to the dice count
+ if (fx->IsVariable) {
+ fx->DiceThrown+=fx->CasterLevel;
+ }
+ 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;
+ }
+ //there is no level based resistance, but Parameter1 cannot be precalculated
+ //these effects use the Dice fields in a special way
+ if( Opcodes[fx->Opcode].Flags & EFFECT_NO_LEVEL_CHECK ) {
+ 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", -1 };
+static EffectRef fx_opcode_immunity_ref = { "Protection:Opcode", -1 }; //bg2
+static EffectRef fx_opcode_immunity2_ref = { "Protection:Opcode2", -1 };//iwd
+static EffectRef fx_spell_immunity_ref = { "Protection:Spell", -1 }; //bg2
+static EffectRef fx_spell_immunity2_ref = { "Protection:Spell2", -1 };//iwd
+static EffectRef fx_store_spell_sequencer_ref = { "Sequencer:Store", -1 }; //bg2, works against sequencers
+static EffectRef fx_school_immunity_ref = { "Protection:School", -1 };
+static EffectRef fx_secondary_type_immunity_ref = { "Protection:SecondaryType", -1 };
+
+//decrementing immunity effects
+static EffectRef fx_level_immunity_dec_ref = { "Protection:SpellLevelDec", -1 };
+static EffectRef fx_spell_immunity_dec_ref = { "Protection:SpellDec", -1 };
+static EffectRef fx_school_immunity_dec_ref = { "Protection:SchoolDec", -1 };
+static EffectRef fx_secondary_type_immunity_dec_ref = { "Protection:SecondaryTypeDec", -1 };
+
+//bounce effects
+static EffectRef fx_level_bounce_ref = { "Bounce:SpellLevel", -1 };
+//static EffectRef fx_opcode_bounce_ref = { "Bounce:Opcode", -1 };
+static EffectRef fx_spell_bounce_ref = { "Bounce:Spell", -1 };
+static EffectRef fx_school_bounce_ref = { "Bounce:School", -1 };
+static EffectRef fx_secondary_type_bounce_ref = { "Bounce:SecondaryType", -1 };
+
+//decrementing bounce effects
+static EffectRef fx_level_bounce_dec_ref = { "Bounce:SpellLevelDec", -1 };
+static EffectRef fx_spell_bounce_dec_ref = { "Bounce:SpellDec", -1 };
+static EffectRef fx_school_bounce_dec_ref = { "Bounce:SchoolDec", -1 };
+static EffectRef fx_secondary_type_bounce_dec_ref = { "Bounce:SecondaryTypeDec", -1 };
+
+//spelltrap (multiple decrementing immunity)
+static EffectRef fx_spelltrap = { "SpellTrap", -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) ) {
+ 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<<i)) {
+ saved = actor->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 (Owner)
+ fx->CasterID = Owner->GetGlobalID();
+ 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->Opcode<MAX_EFFECTS) {
+ fn = Opcodes[fx->Opcode].Function;
+ if (!(target || (Opcodes[fx->Opcode].Flags & EFFECT_NO_ACTOR))) {
+ printf("targetless opcode without EFFECT_NO_ACTOR: %d, skipping\n", fx->Opcode);
+ return FX_NOT_APPLIED;
+ }
+ }
+ 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<GameTime) {
+ GameTime=0xffffffff;
+ } else {
+ GameTime+=futuretime*AI_UPDATE_TIME;
+ }
+
+ std::list< Effect* >::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", -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", -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", -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", -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( TimingMode<MAX_TIMING_MODE) {;
+ TimingMode = fx_to_delayed[TimingMode];
+ } else {
+ TimingMode = FX_DURATION_JUST_EXPIRED;
+ }
+}
+
+int EffectQueue::ResolveEffect(EffectRef &effect_reference)
+{
+ ResolveEffectRef(effect_reference);
+ return effect_reference.opcode;
+}
+
+// this check goes for the whole effect block, not individual effects
+// But it takes the first effect of the block for the common fields
+
+//returns 1 if effect block applicable
+//returns 0 if effect block disabled
+//returns -1 if effect block bounced
+int EffectQueue::CheckImmunity(Actor *target) const
+{
+ //don't resist if target is non living
+ if( !target) {
+ return 1;
+ }
+
+ if( effects.size() ) {
+ Effect* fx = *effects.begin();
+
+ //projectile immunity
+ if( target->ImmuneToProjectile(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/gemrb/core/EffectQueue.h b/gemrb/core/EffectQueue.h
new file mode 100644
index 0000000..1380c39
--- /dev/null
+++ b/gemrb/core/EffectQueue.h
@@ -0,0 +1,313 @@
+/* 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 <list>
+
+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
+// <avenger> 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)
+
+//return the caster object
+#define GetCasterObject() (core->GetGame()->GetActorByGlobalID(fx->CasterID))
+
+// 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*);
+
+
+/** Cached Effect -> opcode mapping */
+struct EffectRef {
+ const char* Name;
+ int opcode;
+};
+
+/** Links Effect name to a function implementing the effect */
+struct EffectDesc {
+ const char* Name;
+ EffectFunction Function;
+ int Flags;
+ int opcode;
+};
+
+enum EffectFlags {
+ EFFECT_NORMAL = 0,
+ EFFECT_DICED = 1,
+ EFFECT_NO_LEVEL_CHECK = 2,
+ EFFECT_NO_ACTOR = 4
+};
+
+/** 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 EffectDesc *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/gemrb/core/Factory.cpp b/gemrb/core/Factory.cpp
new file mode 100644
index 0000000..797c4a0
--- /dev/null
+++ b/gemrb/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 <cstring>
+
+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/gemrb/core/Factory.h b/gemrb/core/Factory.h
new file mode 100644
index 0000000..d6c1c09
--- /dev/null
+++ b/gemrb/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/gemrb/core/FactoryObject.cpp b/gemrb/core/FactoryObject.cpp
new file mode 100644
index 0000000..8c0da9b
--- /dev/null
+++ b/gemrb/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/gemrb/core/FactoryObject.h b/gemrb/core/FactoryObject.h
new file mode 100644
index 0000000..a1189c6
--- /dev/null
+++ b/gemrb/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/gemrb/core/Font.cpp b/gemrb/core/Font.cpp
new file mode 100644
index 0000000..72c7b88
--- /dev/null
+++ b/gemrb/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 <cassert>
+
+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<len; k++) {
+ if (tmp[i] == ']') {
+ tag[k] = 0;
+ break;
+ }
+ tag[k] = tmp[i++];
+ }
+
+ if (strnicmp( tag, "capital=",8)==0) {
+ sscanf( tag, "capital=%d", &capital);
+ if (capital && (row>=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, palette->back );
+ 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 && i<len; k++) {
+ if (tmp[i] == ']') {
+ tag[k] = 0;
+ break;
+ }
+ tag[k] = tmp[i++];
+ }
+
+ if (strnicmp( tag, "capital=",8)==0) {
+ sscanf( tag, "capital=%d", &capital);
+ if (capital) {
+ 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, palette->back );
+ 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/gemrb/core/Font.h b/gemrb/core/Font.h
new file mode 100644
index 0000000..240bdf4
--- /dev/null
+++ b/gemrb/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 <vector>
+
+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/gemrb/core/GUI/Button.cpp b/gemrb/core/GUI/Button.cpp
new file mode 100644
index 0000000..941cdd4
--- /dev/null
+++ b/gemrb/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 (newtime<starttime) {
+ return;
+ }
+ SourceRGB.r = (SourceRGB.r + DestRGB.r) / 2;
+ SourceRGB.g = (SourceRGB.g + DestRGB.g) / 2;
+ SourceRGB.b = (SourceRGB.b + DestRGB.b) / 2;
+ SourceRGB.a = (SourceRGB.a + DestRGB.a) / 2;
+ if (SourceRGB.r == DestRGB.r &&
+ SourceRGB.g == DestRGB.g &&
+ SourceRGB.b == DestRGB.b &&
+ SourceRGB.a == DestRGB.a) {
+ starttime = 0;
+ return;
+ }
+ starttime = newtime + 40;
+}
+
+/** Draws the Control on the Output Display */
+void Button::Draw(unsigned short x, unsigned short y)
+{
+ if (!Changed && !(Owner->Flags&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<Sprite2D*>::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<Sprite2D*>::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/gemrb/core/GUI/Button.h b/gemrb/core/GUI/Button.h
new file mode 100644
index 0000000..1ae3d91
--- /dev/null
+++ b/gemrb/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 <list>
+
+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<Sprite2D*> 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/gemrb/core/GUI/Console.cpp b/gemrb/core/GUI/Console.cpp
new file mode 100644
index 0000000..9cf4b95
--- /dev/null
+++ b/gemrb/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;i<HISTORY_SIZE;i++) {
+ History[i] = ( unsigned char * ) malloc( max );
+ History[i][0] = 0;
+ }
+ CurPos = 0;
+ HistPos = 0;
+ HistMax = 0;
+ palette = NULL;
+}
+
+Console::~Console(void)
+{
+ free( Buffer );
+ for (size_t i=0;i<HISTORY_SIZE;i++) {
+ free( History[i] );
+ }
+ Video *video = core->GetVideoDriver();
+
+ 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;i<HistMax;i++) {
+ if (!strnicmp((const char *) History[i],(const char *) Buffer,max) )
+ return;
+ }
+ if (History[0][0]) {
+ for (i=HISTORY_SIZE-1; i>0; i--) {
+ memcpy(History[i], History[i-1], max);
+ }
+ }
+ memcpy(History[0], Buffer, max);
+ if (HistMax<HISTORY_SIZE) {
+ HistMax++;
+ }
+}
+
+bool Console::SetEvent(int /*eventType*/, EventHandler /*handler*/)
+{
+ return false;
+}
diff --git a/gemrb/core/GUI/Console.h b/gemrb/core/GUI/Console.h
new file mode 100644
index 0000000..c693eec
--- /dev/null
+++ b/gemrb/core/GUI/Console.h
@@ -0,0 +1,93 @@
+/* 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 Console.h
+ * Declares Console widget, input field for direct poking into GemRB innards.
+ * @author The GemRB Project
+ */
+
+#ifndef CONSOLE_H
+#define CONSOLE_H
+
+#include "GUI/Control.h"
+#include "GUI/TextArea.h"
+
+class Palette;
+
+/**
+ * @class Console
+ * Widget displaying debugging console, input field for direct poking
+ * into GemRB innards.
+ * The console accepts and executes python statements and has already
+ * GemRB python module loaded, so almost any command
+ * from GUIScripts can be used.
+ */
+
+/** the number of remembered lines in the cheat console*/
+#define HISTORY_SIZE 5
+
+class Console : public Control {
+public:
+ Console(void);
+ ~Console(void);
+ /** Draws the Console on the Output Display */
+ void Draw(unsigned short x, unsigned short y);
+ /** Set Font */
+ void SetFont(Font* f);
+ /** 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);
+private:
+ /** Text Editing Cursor Sprite */
+ Sprite2D* Cursor;
+ /** Text Font */
+ Font* font;
+ /** Background */
+ Sprite2D* Back;
+ /** Max Edit Text Length */
+ unsigned short max;
+ /** Text Buffer */
+ unsigned char* Buffer;
+ /** History Buffer */
+ unsigned char* History[HISTORY_SIZE];
+ /** Cursor Position */
+ unsigned short CurPos;
+ /** History Position and size */
+ unsigned short HistPos, HistMax;
+ /** 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);
+ bool SetEvent(int eventType, EventHandler handler);
+private:
+ void HistoryBack();
+ void HistoryForward();
+ void HistoryAdd(bool force);
+};
+
+#endif
diff --git a/gemrb/core/GUI/Control.cpp b/gemrb/core/GUI/Control.cpp
new file mode 100644
index 0000000..fc02eb9
--- /dev/null
+++ b/gemrb/core/GUI/Control.cpp
@@ -0,0 +1,270 @@
+/* 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/Control.h"
+
+#include "GUI/Window.h"
+
+#include "win32def.h"
+
+#include "ControlAnimation.h"
+#include "Interface.h"
+#include "ScriptEngine.h"
+#include "Video.h"
+
+#include <cstdio>
+#include <cstring>
+
+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", 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/gemrb/core/GUI/Control.h b/gemrb/core/GUI/Control.h
new file mode 100644
index 0000000..50a5ad2
--- /dev/null
+++ b/gemrb/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/gemrb/core/GUI/EventMgr.cpp b/gemrb/core/GUI/EventMgr.cpp
new file mode 100644
index 0000000..41e22b2
--- /dev/null
+++ b/gemrb/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+10<x) return false;
+ if (dc_x>x+10) return false;
+ if (dc_y+10<y) return false;
+ if (dc_y>y+10) return false;
+ if (dc_time<thisTime) return false;
+ return true;
+}
+
+/** BroadCast Mouse Move Event */
+void EventMgr::MouseDown(unsigned short x, unsigned short y, unsigned short Button,
+ unsigned short Mod)
+{
+ std::vector< int>::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/gemrb/core/GUI/EventMgr.h b/gemrb/core/GUI/EventMgr.h
new file mode 100644
index 0000000..bda4ae8
--- /dev/null
+++ b/gemrb/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 <vector>
+
+#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/gemrb/core/GUI/GameControl.cpp b/gemrb/core/GUI/GameControl.cpp
new file mode 100644
index 0000000..0794c08
--- /dev/null
+++ b/gemrb/core/GUI/GameControl.cpp
@@ -0,0 +1,2849 @@
+/* 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 "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.h"
+
+#include <cmath>
+
+#define DEBUG_SHOW_INFOPOINTS 0x01
+#define DEBUG_SHOW_CONTAINERS 0x02
+#define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS
+#define DEBUG_SHOW_LIGHTMAP 0x08
+
+#ifdef TOUCHSCREEN
+# define SCROLL_BORDER 32
+#else
+# define SCROLL_BORDER 5
+#endif
+
+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
+};
+static const Color gray = {
+ 0x80, 0x80, 0x80, 0xff
+};
+
+//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;
+#ifdef TOUCHSCREEN
+ touched=false;
+#endif
+ 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; i<formationcount; i++) {
+ for(j=0;j<FORMATIONSIZE;j++) {
+ short k=(short) atoi(tab->QueryField(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.x<viewport.x) {
+ p.x=viewport.x;
+ draw|= D_LEFT;
+ }
+ if (p.y<viewport.y) {
+ p.y=viewport.y;
+ draw |= D_UP;
+ }
+ int tmp;
+
+ Sprite2D *spr = core->GetScrollCursorSprite(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|GA_NO_LOS, 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--;
+ }
+ }
+ }
+
+#ifdef TOUCHSCREEN
+ if (moveY < 0 && scrolling)
+ video->DrawLine(screen.x+4, screen.y+SCROLL_BORDER, screen.w+screen.x-4, screen.y+SCROLL_BORDER, red);
+ else
+ video->DrawLine(screen.x+4, screen.y+SCROLL_BORDER, screen.w+screen.x-4, screen.y+SCROLL_BORDER, gray);
+ if (moveY > 0 && scrolling)
+ video->DrawLine(screen.x+4, screen.h-SCROLL_BORDER, screen.w+screen.x-4, screen.h-SCROLL_BORDER, red);
+ else
+ video->DrawLine(screen.x+4, screen.h-SCROLL_BORDER, screen.w+screen.x-4, screen.h-SCROLL_BORDER, gray);
+ if (moveX < 0 && scrolling)
+ video->DrawLine(screen.x+SCROLL_BORDER, screen.y+4, screen.x+SCROLL_BORDER, screen.h+screen.y-4, red);
+ else
+ video->DrawLine(screen.x+SCROLL_BORDER, screen.y+4, screen.x+SCROLL_BORDER, screen.h+screen.y-4, gray);
+ if (moveX > 0 && scrolling)
+ video->DrawLine(screen.w+screen.x-SCROLL_BORDER, screen.y+4, screen.w+screen.x-SCROLL_BORDER, screen.h-4, red);
+ else
+ video->DrawLine(screen.w+screen.x-SCROLL_BORDER, screen.y+4, screen.w+screen.x-SCROLL_BORDER, screen.h-4, gray);
+#endif
+}
+
+/** 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", -1 };
+static EffectRef damage_ref = { "Damage", -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(0);
+ } else {
+ src->CastSpellPointEnd(0);
+ }
+ }
+ }
+ 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 ^= FOG_DRAWFOG;
+ printf("Show Fog-Of-War: %s\n", core->FogOfWar & FOG_DRAWFOG ? "ON" : "OFF");
+ break;
+ case '8': //show searchmap over area
+ core->FogOfWar ^= FOG_DRAWSEARCHMAP;
+ printf("Show searchmap %s\n", core->FogOfWar & FOG_DRAWSEARCHMAP ? "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;
+ }
+
+#ifdef TOUCHSCREEN
+ int mousescrollspd = core->GetMouseScrollSpeed();
+ Region region;
+ Map* map;
+ Point mapsize;
+ Region viewport = core->GetVideoDriver()->GetViewport();
+ moveX = 0;
+ moveY = 0;
+ // Top scroll area
+ region=Region(XPos, YPos, Width, YPos+SCROLL_BORDER);
+ if (region.PointInside(x, y)) {
+ // Check for end of map area
+ if (viewport.y > 0)
+ moveY = -mousescrollspd;
+ }
+ // Bottom scroll area
+ region=Region(XPos, Height-SCROLL_BORDER, Width, Height);
+ if (region.PointInside(x, y)) {
+ // Check for end of map area
+ map = core->GetGame()->GetCurrentArea();
+ if (map != NULL) {
+ mapsize = map->TMap->GetMapSize();
+ if((viewport.y + viewport.h) < mapsize.y)
+ moveY = mousescrollspd;
+ }
+ }
+ // Left scroll area
+ region=Region(XPos, YPos, XPos+SCROLL_BORDER, Height);
+ if (region.PointInside(x, y)) {
+ // Check for end of map area
+ if(viewport.x > 0)
+ moveX = -mousescrollspd;
+ }
+ // Right scroll area
+ region=Region(Width-SCROLL_BORDER, YPos, Width, Height);
+ if (region.PointInside(x, y)) {
+ // Check for end of map area
+ map = core->GetGame()->GetCurrentArea();
+ if (map != NULL) {
+ mapsize = map->TMap->GetMapSize();
+ if((viewport.x + viewport.w) < mapsize.x)
+ moveX = mousescrollspd;
+ }
+ }
+ if ((moveX != 0 || moveY != 0) && touched) {
+ scrolling = true;
+ return;
+ } else {
+ moveX = 0;
+ moveY = 0;
+ scrolling = false;
+ Video* video = core->GetVideoDriver();
+ video->SetDragCursor(NULL);
+ }
+#endif
+
+ 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;
+ }
+}
+
+/** Global Mouse Move Event */
+void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y)
+{
+ if (ScreenFlags & SF_DISABLEMOUSE) {
+ return;
+ }
+
+ if (Owner->Visible!=WINDOW_VISIBLE) {
+ return;
+ }
+
+#ifndef TOUCHSCREEN
+ 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);
+ }
+#else
+(void)x;
+(void)y;
+#endif
+}
+
+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) {
+ strncpy(Tmp, "RemoveTraps([-1])", sizeof(Tmp) );
+ } else {
+ strncpy(Tmp, "PickLock([-1])", sizeof(Tmp) );
+ }
+ source->AddAction( GenerateActionDirect( Tmp, tgt ) );
+}
+
+//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) {
+ strncpy(Tmp, "RemoveTraps([-1])", sizeof(Tmp) );
+ } else {
+ strncpy(Tmp, "PickLock([-1])", sizeof(Tmp) );
+ }
+ source->AddAction( GenerateActionDirect( Tmp, tgt ) );
+}
+
+//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);
+ strncpy(Tmp, "RemoveTraps([-1])", sizeof(Tmp) );
+ source->AddAction( GenerateActionDirect( Tmp, tgt ) );
+}
+
+//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) {
+ if (spellIndex<0) {
+ strncpy(Tmp, "SpellPointNoDec(\"\",[0.0])", sizeof(Tmp) );
+ } else {
+ strncpy(Tmp, "SpellPoint(\"\",[0.0])", sizeof(Tmp) );
+ }
+ } else {
+ //using item on target
+ strncpy(Tmp, "UseItemPoint(\"\",[0,0],0)", sizeof(Tmp) );
+ }
+ Action* action = GenerateAction( Tmp );
+ action->pointParameter=tgt;
+ if (spellOrItem>=0) {
+ if (spellIndex<0) {
+ sprintf(action->string0Parameter,"%.8s",spellName);
+ } else {
+ 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) {
+ if (spellIndex<0) {
+ sprintf(Tmp, "NIDSpecial7()");
+ } else {
+ sprintf(Tmp, "NIDSpecial6()");
+ }
+ } else {
+ //using item on target
+ sprintf(Tmp, "NIDSpecial5()");
+ }
+ Action* action = GenerateActionDirect( Tmp, tgt);
+ if (spellOrItem>=0) {
+ if (spellIndex<0) {
+ sprintf(action->string0Parameter,"%.8s",spellName);
+ } else {
+ 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;
+ }
+
+ core->SetEventFlag(EF_RESETTARGET);
+
+ if (target_mode == TARGET_MODE_ATTACK) {
+ actor->ClearPath();
+ actor->ClearActions();
+ snprintf(Tmp, sizeof(Tmp), "BashDoor(\"%s\")", container->GetScriptName());
+ actor->AddAction(GenerateAction(Tmp));
+ return;
+ }
+
+ if ((target_mode == TARGET_MODE_PICK)) {
+ TryToPick(actor, container);
+ 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;
+ }
+
+ core->SetEventFlag(EF_RESETTARGET);
+
+ if (target_mode == TARGET_MODE_ATTACK) {
+ actor->ClearPath();
+ actor->ClearActions();
+ snprintf(Tmp, sizeof(Tmp), "BashDoor(\"%s\")", door->GetScriptName());
+ actor->AddAction(GenerateAction(Tmp));
+ return;
+ }
+
+ if (target_mode == TARGET_MODE_PICK) {
+ TryToPick(actor, door);
+ 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);
+ return true;
+ }
+
+ switch(trap->Type) {
+ case ST_TRAVEL:
+ actor->UseExit(trap->GetGlobalID());
+ 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;
+#ifdef TOUCHSCREEN
+ touched=true;
+#endif
+ }
+}
+
+/** 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;
+
+#ifdef TOUCHSCREEN
+ touched=false;
+ if (scrolling) {
+ moveX = 0;
+ moveY = 0;
+ scrolling=false;
+ Video* video = core->GetVideoDriver();
+ video->SetDragCursor(NULL);
+ 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;
+ }
+#endif
+ 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 | target_types);
+ 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)<EA_CHARMED)) {
+ if (target_mode==TARGET_MODE_NONE) {
+ PerformActionOn(actor);
+ }
+ }
+ return;
+ }
+
+ Actor *pc = core->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 (overInfoPoint->Type==ST_TRAVEL) {
+ int i = game->selected.size();
+ ieDword exitID = overInfoPoint->GetGlobalID();
+ while(i--) {
+ game->selected[i]->UseExit(exitID);
+ }
+ } else {
+ if (HandleActiveRegion(overInfoPoint, pc, p)) {
+ core->SetEventFlag(EF_RESETTARGET);
+ 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<Actor *> 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;i<game->selected.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;i<game->selected.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() {
+ target_types = GA_NO_DEAD|GA_NO_HIDDEN;
+ SetTargetMode(TARGET_MODE_NONE);
+}
+
+void GameControl::UpdateTargetMode() {
+ SetTargetMode(target_mode);
+}
+
+/** 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 || (pc && 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<ImageMgr> 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)
+{
+ memset(spellName, 0, sizeof(ieResRef));
+ 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(ieResRef spellname, int type, int level, int idx, Actor *u, int targettype, int cnt)
+{
+ memcpy(spellName, spellname, sizeof(ieResRef));
+ 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/gemrb/core/GUI/GameControl.h b/gemrb/core/GUI/GameControl.h
new file mode 100644
index 0000000..0ff21d3
--- /dev/null
+++ b/gemrb/core/GUI/GameControl.h
@@ -0,0 +1,257 @@
+/* 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;
+#ifdef TOUCHSCREEN
+ bool touched; // true, if player touched screen (left button down and hold)
+#endif
+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;
+ //the name of the spell to cast
+ ieResRef spellName;
+ //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 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();
+ void UpdateTargetMode();
+
+ // 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(ieResRef spellname, int type, int level, int slot, Actor *actor, int targettype, int cnt);
+ bool SetEvent(int eventType, EventHandler handler);
+};
+
+#endif
diff --git a/gemrb/core/GUI/Label.cpp b/gemrb/core/GUI/Label.cpp
new file mode 100644
index 0000000..4d67af4
--- /dev/null
+++ b/gemrb/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/gemrb/core/GUI/Label.h b/gemrb/core/GUI/Label.h
new file mode 100644
index 0000000..b0a841d
--- /dev/null
+++ b/gemrb/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/gemrb/core/GUI/MapControl.cpp b/gemrb/core/GUI/MapControl.cpp
new file mode 100644
index 0000000..4abd158
--- /dev/null
+++ b/gemrb/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)<dist) {
+ if (LinkedLabel) {
+ LinkedLabel->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) && (xp<vp.w) && (yp>vp.y) && (yp<vp.h)) {
+ mouseIsDragging = true;
+ } else {
+ mouseIsDragging = false;
+ }
+ lastMouseX = x;
+ lastMouseY = y;
+}
+
+/** Mouse Button Up */
+void MapControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short Button,
+ unsigned short /*Mod*/)
+{
+ if (!mouseIsDown) {
+ return;
+ }
+
+ mouseIsDown = false;
+ mouseIsDragging = false;
+ switch(Value) {
+ case MAP_REVEAL:
+ ViewHandle(x,y);
+ NotePosX = (short) SCREEN_TO_MAPX(x) * MAP_MULT / MAP_DIV;
+ NotePosY = (short) SCREEN_TO_MAPY(y) * MAP_MULT / MAP_DIV;
+ ClickHandle(Button);
+ return;
+ case MAP_NO_NOTES:
+ ViewHandle(x,y);
+ return;
+ case MAP_VIEW_NOTES:
+ //left click allows setting only when in MAP_SET_NOTE mode
+ if ((Button == GEM_MB_ACTION) ) {
+ ViewHandle(x,y);
+ }
+ ClickHandle(Button);
+ return;
+ default:
+ ClickHandle(Button);
+ return;
+ }
+}
+
+/** Special Key Press */
+void MapControl::OnSpecialKeyPress(unsigned char Key)
+{
+ 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;
+ default:
+ break;
+ }
+
+ if (ScrollX > 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/gemrb/core/GUI/MapControl.h b/gemrb/core/GUI/MapControl.h
new file mode 100644
index 0000000..1154bf8
--- /dev/null
+++ b/gemrb/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/gemrb/core/GUI/Progressbar.cpp b/gemrb/core/GUI/Progressbar.cpp
new file mode 100644
index 0000000..d31f589
--- /dev/null
+++ b/gemrb/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 <cstring>
+
+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; i<Count ;i++ ) {
+ Sprite2D *Knob = PBarAnim->GetFrame(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/gemrb/core/GUI/Progressbar.h b/gemrb/core/GUI/Progressbar.h
new file mode 100644
index 0000000..411ae55
--- /dev/null
+++ b/gemrb/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/gemrb/core/GUI/ScrollBar.cpp b/gemrb/core/GUI/ScrollBar.cpp
new file mode 100644
index 0000000..aea1b98
--- /dev/null
+++ b/gemrb/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;i<SB_RES_COUNT;i++) {
+ Frames[i]=NULL;
+ }
+}
+
+ScrollBar::~ScrollBar(void)
+{
+ Video *video=core->GetVideoDriver();
+ for(int i=0;i<SB_RES_COUNT;i++) {
+ if(Frames[i]) {
+ video->FreeSprite(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/gemrb/core/GUI/ScrollBar.h b/gemrb/core/GUI/ScrollBar.h
new file mode 100644
index 0000000..4a2d172
--- /dev/null
+++ b/gemrb/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/gemrb/core/GUI/Slider.cpp b/gemrb/core/GUI/Slider.cpp
new file mode 100644
index 0000000..09812ab
--- /dev/null
+++ b/gemrb/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 <cmath>
+
+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/gemrb/core/GUI/Slider.h b/gemrb/core/GUI/Slider.h
new file mode 100644
index 0000000..66f26ae
--- /dev/null
+++ b/gemrb/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/gemrb/core/GUI/TextArea.cpp b/gemrb/core/GUI/TextArea.cpp
new file mode 100644
index 0000000..b19ad57
--- /dev/null
+++ b/gemrb/core/GUI/TextArea.cpp
@@ -0,0 +1,979 @@
+/* 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 <cstdio>
+#include <cstdlib>
+
+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<ImageMgr> 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 (startrow<rows) {
+ startrow++;
+ }
+ }
+
+ /** Forcing redraw of whole screen before drawing text*/
+ Owner->Invalidate();
+ 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 || (startrow<tmp) )
+ break;
+ startrow -= tmp;
+ free(lines.front() );
+ lines.erase(lines.begin());
+ lrows.erase(lrows.begin());
+ } else {
+ free(lines.back() );
+ lines.pop_back();
+ lrows.pop_back();
+ }
+ count--;
+ }
+ UpdateControls();
+}
+
+void TextArea::UpdateControls()
+{
+ int pos;
+
+ CalcRowCount();
+ Changed = true;
+ if (sb) {
+ ScrollBar* bar = ( ScrollBar* ) sb;
+ if (Flags & IE_GUI_TEXTAREA_AUTOSCROLL)
+ pos = rows - ( ( Height - 5 ) / ftext->maxHeight );
+ 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<Key-'0';i++) {
+ do {
+ seltext++;
+ if ((unsigned int) seltext>=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<lines.size()) {
+ CurLine++;
+ }
+ break;
+ case GEM_END:
+ CurLine=lines.size()-1;
+ CurPos = GetRowLength((unsigned int) CurLine);
+ break;
+ case GEM_LEFT:
+ if (CurPos > 0) {
+ CurPos--;
+ } else {
+ if (CurLine) {
+ CurLine--;
+ CurPos = GetRowLength(CurLine);
+ }
+ }
+ break;
+ case GEM_RIGHT:
+ len = GetRowLength(CurLine);
+ if (CurPos < len) {
+ CurPos++;
+ } else {
+ if(CurLine<lines.size()) {
+ CurPos=0;
+ CurLine++;
+ }
+ }
+ break;
+ case GEM_DELETE:
+ len = GetRowLength(CurLine);
+ //printf("len: %d Before: %s\n",len, lines[CurLine]);
+ if (CurPos>=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;
+}
+
+void TextArea::SelectText(const char *select)
+{
+ int i = lines.size();
+ while(i--) {
+ if (!stricmp(lines[i], select) ) {
+ CurLine = i;
+ if (sb) {
+ ScrollBar* bar = ( ScrollBar* ) sb;
+ bar->SetPos( i );
+ } else {
+ SetRow( i );
+ }
+ RedrawTextArea( VarName, i);
+ CalcRowCount();
+ Owner->Invalidate();
+ core->RedrawAll();
+ break;
+ }
+ }
+}
+
+const char* TextArea::QueryText()
+{
+ if ( Value<lines.size() ) {
+ return ( const char * ) lines[Value];
+ }
+ return ( const char *) "";
+}
+
+bool TextArea::SetEvent(int eventType, EventHandler handler)
+{
+ Changed = true;
+
+ switch (eventType) {
+ case IE_GUI_TEXTAREA_ON_CHANGE:
+ TextAreaOnChange = handler;
+ break;
+ case IE_GUI_TEXTAREA_OUT_OF_TEXT:
+ TextAreaOutOfText = handler;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+void TextArea::PadMinRow()
+{
+ int row = 0;
+ int i=(int) (lines.size()-1);
+ //minrow -1 ->gap
+ //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/gemrb/core/GUI/TextArea.h b/gemrb/core/GUI/TextArea.h
new file mode 100644
index 0000000..2ba24a4
--- /dev/null
+++ b/gemrb/core/GUI/TextArea.h
@@ -0,0 +1,177 @@
+/* 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);
+ /** Set the TextArea value to the line number containing the string parameter */
+ void SelectText(const char *select);
+ /** 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/gemrb/core/GUI/TextEdit.cpp b/gemrb/core/GUI/TextEdit.cpp
new file mode 100644
index 0000000..969ea88
--- /dev/null
+++ b/gemrb/core/GUI/TextEdit.cpp
@@ -0,0 +1,233 @@
+/* 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};
+ //Original engine values
+ //Color white = {0xc8, 0xc8, 0xc8, 0x00}, black = {0x3c, 0x3c, 0x3c, 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/gemrb/core/GUI/TextEdit.h b/gemrb/core/GUI/TextEdit.h
new file mode 100644
index 0000000..f92e913
--- /dev/null
+++ b/gemrb/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/gemrb/core/GUI/Window.cpp b/gemrb/core/GUI/Window.cpp
new file mode 100644
index 0000000..ed1395a
--- /dev/null
+++ b/gemrb/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/gemrb/core/GUI/Window.h b/gemrb/core/GUI/Window.h
new file mode 100644
index 0000000..a4798dd
--- /dev/null
+++ b/gemrb/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 <vector>
+
+// 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/gemrb/core/GUI/WorldMapControl.cpp b/gemrb/core/GUI/WorldMapControl.cpp
new file mode 100644
index 0000000..cca132f
--- /dev/null
+++ b/gemrb/core/GUI/WorldMapControl.cpp
@@ -0,0 +1,417 @@
+/* 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
+ // NOTE: it would be better to initialize these colors from
+ // some 2da file
+ 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;i<ec;i++) {
+ WMPAreaEntry *m = worldmap->GetEntry(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;i<ec;i++) {
+ WMPAreaEntry *m = worldmap->GetEntry(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;i<ec;i++) {
+ WMPAreaEntry *ae = worldmap->GetEntry(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(h<th)
+ h=th;
+ if(w<tw)
+ w=tw;
+ }
+ if (ae->X > 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;
+ }
+ MouseIsDown = false;
+ if (lastCursor==IE_CURSOR_NORMAL) {
+ RunEventHandler( WorldMapControlOnPress );
+ }
+}
+
+/** 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)
+{
+ Palette* pal;
+ // FIXME: clearly it can cause palettes to be re-created several times,
+ // because setting background color creates all palettes anew.
+ switch (which) {
+ case IE_GUI_WMAP_COLOR_BACKGROUND:
+ pal = core->CreatePalette( pal_normal->front, color );
+ gamedata->FreePalette( pal_normal );
+ pal_normal = pal;
+ pal = core->CreatePalette( pal_selected->front, color );
+ gamedata->FreePalette( pal_selected );
+ pal_selected = pal;
+ pal = core->CreatePalette( pal_notvisited->front, color );
+ gamedata->FreePalette( pal_notvisited );
+ pal_notvisited = pal;
+ break;
+ case IE_GUI_WMAP_COLOR_NORMAL:
+ pal = core->CreatePalette( color, pal_normal->back );
+ gamedata->FreePalette( pal_normal );
+ pal_normal = pal;
+ break;
+ case IE_GUI_WMAP_COLOR_SELECTED:
+ pal = core->CreatePalette( color, pal_selected->back );
+ gamedata->FreePalette( pal_selected );
+ pal_selected = pal;
+ break;
+ case IE_GUI_WMAP_COLOR_NOTVISITED:
+ pal = core->CreatePalette( color, pal_notvisited->back );
+ gamedata->FreePalette( pal_notvisited );
+ pal_notvisited = pal;
+ break;
+ default:
+ break;
+ }
+
+ Changed = true;
+}
diff --git a/gemrb/core/GUI/WorldMapControl.h b/gemrb/core/GUI/WorldMapControl.h
new file mode 100644
index 0000000..c02cef7
--- /dev/null
+++ b/gemrb/core/GUI/WorldMapControl.h
@@ -0,0 +1,116 @@
+/* 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_BACKGROUND 0
+#define IE_GUI_WMAP_COLOR_NORMAL 1
+#define IE_GUI_WMAP_COLOR_SELECTED 2
+#define IE_GUI_WMAP_COLOR_NOTVISITED 3
+
+
+/**
+ * @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/gemrb/core/Game.cpp b/gemrb/core/Game.cpp
new file mode 100644
index 0000000..f5c0f10
--- /dev/null
+++ b/gemrb/core/Game.cpp
@@ -0,0 +1,1848 @@
+/* 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;
+ hasInfra = false;
+ familiarBlock = false;
+ //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; slot<PCs.size(); slot++) {
+ if (PCs[slot]->InParty==partyID) {
+ return slot;
+ }
+ }
+ return -1;
+}
+
+Actor* Game::FindPC(unsigned int partyID)
+{
+ for (unsigned int slot=0; slot<PCs.size(); slot++) {
+ if (PCs[slot]->InParty==partyID) return PCs[slot];
+ }
+ return NULL;
+}
+
+Actor* Game::FindPC(const char *scriptingname)
+{
+ for (unsigned int slot=0; slot<PCs.size(); slot++) {
+ if (strnicmp(PCs[slot]->GetScriptName(),scriptingname,32)==0 ) {
+ return PCs[slot];
+ }
+ }
+ return NULL;
+}
+
+Actor* Game::FindNPC(unsigned int partyID)
+{
+ for (unsigned int slot=0; slot<NPCs.size(); slot++) {
+ if (NPCs[slot]->InParty==partyID) return NPCs[slot];
+ }
+ return NULL;
+}
+
+Actor* Game::FindNPC(const char *scriptingname)
+{
+ for (unsigned int slot=0; slot<NPCs.size(); slot++) {
+ if (strnicmp(NPCs[slot]->GetScriptName(),scriptingname,32)==0 )
+ {
+ return NPCs[slot];
+ }
+ }
+ return NULL;
+}
+
+Actor *Game::GetGlobalActorByGlobalID(ieDword globalID)
+{
+ unsigned int slot;
+
+ for (slot=0; slot<PCs.size(); slot++) {
+ if (PCs[slot]->GetGlobalID()==globalID ) {
+ return PCs[slot];
+ }
+ }
+ for (slot=0; slot<NPCs.size(); slot++) {
+ if (NPCs[slot]->GetGlobalID()==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() ) {
+ Actor *ac = PCs[i++];
+
+ if (IsAlive(ac) ) {
+ if (!slot--) {
+ return ac;
+ }
+ }
+ }
+ return NULL;
+ }
+ return PCs[slot];
+}
+
+int Game::InStore(Actor* pc) const
+{
+ for (unsigned int i = 0; i < NPCs.size(); i++) {
+ if (NPCs[i] == pc) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int Game::InParty(Actor* pc) const
+{
+ for (unsigned int i = 0; i < PCs.size(); i++) {
+ if (PCs[i] == pc) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int Game::DelPC(unsigned int slot, bool autoFree)
+{
+ if (slot >= 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");
+
+ if (!start || !strta) {
+ printMessage("Game","Game is missing character start data.\n",RED);
+ abort();
+ }
+ // 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 );
+
+ if (strta.load("startare")) {
+ strnlwrcpy(actor->Area, strta->QueryField( strta->GetRowIndex(area), 0 ), 8 );
+ } else {
+ strnlwrcpy(actor->Area, CurrentArea, 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;i<size; i++) {
+ Actor *a = GetPC(i, false);
+ a->PCStats->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; i<PCs.size(); i++) {
+ if (onlyalive) {
+ if (PCs[i]->GetStat(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();
+
+ //call area customization script for PST
+ //moved here because the current area is set here
+ ScriptEngine *sE = core->GetGUIScriptEngine();
+ if (core->HasFeature(GF_AREA_OVERRIDE) && sE) {
+ //area ResRef is accessible by GemRB.GetGameString (STR_AREANAME)
+ sE->RunFunction("Maze", "CustomizeArea");
+ }
+
+ 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<MapMgr> mM(IE_ARE_CLASS_ID);
+ ScriptEngine *sE = core->GetGUIScriptEngine();
+
+ //this shouldn't happen
+ if (!mM) {
+ return -1;
+ }
+
+ int index = FindMap(ResRef);
+ if (index>=0) {
+ return index;
+ }
+
+ bool hide = false;
+ if (loadscreen && sE) {
+ hide = core->HideGCWindow();
+ sE->RunFunction("LoadScreen", "StartLoadScreen");
+ sE->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;i<MAX_LEVEL;i++) {
+ //row shouldn't be larger than maxrow
+ int row = i<maxrow?i:maxrow;
+ int maxcol = table->GetColumnCount(row)-1;
+ for(int j=0;j<MAX_CRLEVEL;j++) {
+ //col shouldn't be larger than maxcol
+ int col = j<maxcol?j:maxcol;
+ crtable[i][j]=atoi(table->QueryField(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; i<PCs.size(); i++) {
+ if (PCs[i]->GetStat(IE_STATE_ID)&STATE_DEAD) {
+ continue;
+ }
+ PCs[i]->AddExperience(individual);
+ }
+}
+
+bool Game::EveryoneStopped() const
+{
+ for (unsigned int i=0; i<PCs.size(); i++) {
+ if (PCs[i]->GetNextStep() ) 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; i<PCs.size(); i++) {
+ if (flags&ENP_ONLYSELECT) {
+ if(!PCs[i]->Selected) {
+ 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) {
+printf("Actor %s is not near!\n", PCs[i]->LongName);
+ 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; i<PCs.size(); i++) {
+ if (PCs[i]==actor) {
+ continue;
+ }
+ if (PCs[i]->GetStat(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; i<PCs.size(); i++) {
+ if (PCs[i]->GetInternalFlag()&IF_JUSTDIED) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void Game::IncrementChapter()
+{
+ //chapter first set to 0 (prologue)
+ ieDword chapter = (ieDword) -1;
+ locals->Lookup("CHAPTER",chapter);
+ //increment chapter only if it exists
+ locals->SetAt("CHAPTER", chapter+1, core->HasFeature(GF_NO_NEW_VARIABLES) );
+ //clear statistics
+ for (unsigned int i=0; i<PCs.size(); i++) {
+ //all PCs must have this!
+ PCs[i]->PCStats->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 (Reputation<r) {
+ displaymsg->DisplayConstantStringValue(STR_GOTREP,0xc0c000,(r-Reputation)/10);
+ }
+ Reputation = r;
+ for (unsigned int i=0; i<PCs.size(); i++) {
+ PCs[i]->SetBase(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 (old<PartyGold) {
+ displaymsg->DisplayConstantStringValue( 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; i<PCs.size(); i++) {
+ if (PCInCombat (PCs[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+//returns true if the protagonist (or the whole party died)
+bool Game::EveryoneDead() const
+{
+ //if there are no PCs, then we assume everyone dead
+ if (!PCs.size() ) {
+ return true;
+ }
+ if (protagonist==PM_NO) {
+ Actor *nameless = PCs[0];
+ if (nameless->GetStat(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; i<PCs.size(); i++) {
+ if (!(PCs[i]->GetStat(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;idx<Maps.size();idx++) {
+ Maps[idx]->UpdateScripts();
+ 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;i<idx;i++) {
+ DelMap( (unsigned int) i, false );
+ }
+ }
+
+ // perhaps a StartMusic action stopped the area music?
+ // (we should probably find a less silly way to handle this,
+ // because nothing can ever stop area music now..)
+ if (!core->GetMusicMgr()->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;i<PCs.size();i++) {
+ Actor *actor = PCs[i];
+ if (!IsAlive(actor)) continue;
+ if (actor->GetCurrentArea()!=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;idx<Maps.size();idx++) {
+ Map *map = Maps[idx];
+
+ printf("%s\n",map->GetScriptName());
+ }
+ 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;idx<Attackers.size(); idx++) {
+ Actor *actor = GetActorByGlobalID(Attackers[idx]);
+ if (!actor) {
+ printf("Name: ???\n");
+ continue;
+ }
+ Actor *whom = GetActorByGlobalID(actor->LastTarget);
+ printf("Name: %s Attacking : %s\n", actor->ShortName, whom?whom->ShortName:"???");
+ }
+
+ printf("Party size: %d\n", (int) PCs.size());
+ for(idx=0;idx<PCs.size();idx++) {
+ Actor *actor = PCs[idx];
+
+ printf("Name: %s Order %d %s\n",actor->ShortName, 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);
+}
+
+ieByte *Game::AllocateMazeData()
+{
+ if (mazedata) {
+ free(mazedata);
+ }
+ mazedata = (ieByte*)malloc(MAZE_DATA_SIZE);
+ return mazedata;
+}
+
diff --git a/gemrb/core/Game.h b/gemrb/core/Game.h
new file mode 100644
index 0000000..2bb0798
--- /dev/null
+++ b/gemrb/core/Game.h
@@ -0,0 +1,485 @@
+/* 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 <vector>
+
+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;
+};
+
+//pst maze data structures (TODO: create a separate class?)
+struct maze_entry {
+ ieDword override;
+ ieDword accessible;
+ ieDword valid;
+ ieDword trapped;
+ ieDword traptype;
+ ieWord walls;
+ ieDword visited;
+};
+
+struct maze_header {
+ ieDword maze_sizex, maze_sizey;
+ ieDword pos1x, pos1y; //nordom's position
+ ieDword pos2x, pos2y; //main hall position
+ ieDword pos3x, pos3y; //foyer entrance
+ ieDword pos4x, pos4y; //unknown
+ ieDword trapcount; //based on map size
+ ieDword initialized; //set to 1
+ ieDword unknown2c; //unknown
+ ieDword unknown30; //unknown
+};
+
+#define MAZE_ENTRY_SIZE sizeof(maze_entry)
+#define MAZE_HEADER_SIZE sizeof(maze_header)
+#define MAZE_MAX_DIM 8
+#define MAZE_ENTRY_COUNT (MAZE_MAX_DIM*MAZE_MAX_DIM)
+#define MAZE_DATA_SIZE (MAZE_ENTRY_COUNT*MAZE_ENTRY_SIZE+MAZE_HEADER_SIZE)
+#define MAZE_DATA_SIZE_HARDCODED 1720
+
+//maze header indices
+#define MH_POS1X 0
+#define MH_POS1Y 1
+#define MH_POS2X 2
+#define MH_POS2Y 3
+#define MH_POS3X 4
+#define MH_POS3Y 5
+#define MH_POS4X 6
+#define MH_POS4Y 7
+#define MH_TRAPCOUNT 8
+#define MH_INITED 9
+#define MH_UNKNOWN2C 10
+#define MH_UNKNOWN30 11
+
+//maze entry indices
+#define ME_OVERRIDE 0
+#define ME_VALID 1
+#define ME_ACCESSIBLE 2
+#define ME_TRAP 3
+#define ME_WALLS 4
+#define ME_VISITED 5
+
+//ME_WALL bitfields
+#define WALL_SOUTH 1
+#define WALL_NORTH 2
+#define WALL_EAST 4
+#define WALL_WEST 8
+
+#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;
+ bool familiarBlock;
+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);
+ /** updates the infravision info */
+ void Infravision();
+ /** 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);
+ /** Allocates maze data */
+ ieByte *AllocateMazeData();
+private:
+ bool DetermineStartPosType(const TableMgr *strta);
+ ieResRef *GetDream(Map *area);
+ void PlayerDream();
+};
+
+#endif // ! GAME_H
diff --git a/gemrb/core/GameData.cpp b/gemrb/core/GameData.cpp
new file mode 100644
index 0000000..7153cee
--- /dev/null
+++ b/gemrb/core/GameData.cpp
@@ -0,0 +1,511 @@
+/* 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 <cstdio>
+
+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> 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> 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<TableMgr> 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<TableMgr> 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<ImageMgr> 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<ItemMgr> 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<SpellMgr> 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;
+}
+
+// this is a HACK and should be replaced with a proper copying mechanism to save on lookups
+Spell* GameData::GetUncachedSpell(const ieResRef resname, bool silent)
+{
+ DataStream* str = GetResource( resname, IE_SPL_CLASS_ID, silent );
+ PluginHolder<SpellMgr> sm(IE_SPL_CLASS_ID);
+ if (!sm) {
+ delete ( str );
+ return NULL;
+ }
+ if (!sm->Open( str, true )) {
+ return NULL;
+ }
+
+ Spell *spell = new Spell();
+ //this is required for storing the 'source'
+ strnlwrcpy(spell->Name, resname, 8);
+ sm->GetSpell( spell, silent );
+
+ 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<EffectMgr> 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, true ) ) {
+ 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<AnimationMgr> 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<ImageMgr> 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/gemrb/core/GameData.h b/gemrb/core/GameData.h
new file mode 100644
index 0000000..e1ce10f
--- /dev/null
+++ b/gemrb/core/GameData.h
@@ -0,0 +1,122 @@
+/* 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<TableMgr> 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<TableMgr> 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);
+ Spell* GetUncachedSpell(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<Table> tables;
+};
+
+extern GEM_EXPORT GameData * gamedata;
+
+template <class T>
+class ResourceHolder : public Holder<T>
+{
+public:
+ ResourceHolder()
+ {
+ }
+ ResourceHolder(const char* resname)
+ : Holder<T>(static_cast<T*>(gamedata->GetResource(resname,&T::ID)))
+ {
+ }
+ ResourceHolder(const char* resname, const ResourceManager& manager, bool silent = false)
+ : Holder<T>(static_cast<T*>(manager.GetResource(resname,&T::ID,silent)))
+ {
+ }
+};
+
+#endif
diff --git a/gemrb/core/GameScript/Actions.cpp b/gemrb/core/GameScript/Actions.cpp
new file mode 100644
index 0000000..fb34e6c
--- /dev/null
+++ b/gemrb/core/GameScript/Actions.cpp
@@ -0,0 +1,7202 @@
+/* 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"
+#include "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.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*AI_UPDATE_TIME + 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->string0Parameter,
+ 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>=MAX_SCRIPTS) {
+ return;
+ }
+ //clearing the queue, and checking script level was intentionally removed
+ Sender->SetScript( parameters->string0Parameter, parameters->int0Parameter, false );
+}
+
+void GameScript::ForceAIScript(Scriptable* Sender, Action* parameters)
+{
+ if (parameters->int0Parameter>=MAX_SCRIPTS) {
+ return;
+ }
+ Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
+ if (!tar || tar->Type != ST_ACTOR) {
+ return;
+ }
+ Actor* actor = ( Actor* ) tar;
+ //clearing the queue, and checking script level was intentionally removed
+ 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;
+ //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 );
+ 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();
+ }
+}
+
+//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;
+ break;
+ 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 );
+}
+
+//same as PlaySequence, but the value comes from a variable
+//ToDo: create a PlaySequenceCore in GSUtils
+void GameScript::PlaySequenceGlobal(Scriptable* Sender, Action* parameters)
+{
+ Scriptable* tar;
+ ieDword value;
+
+ value = (ieDword) CheckVariable( Sender, parameters->string0Parameter );
+
+ 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 value;
+ anim->sequence=value;
+ 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( value );
+}
+
+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(0);
+ Sender->ReleaseCurrentAction();
+ return;
+ }
+
+ //the target was converted to a point
+ if(!Sender->LastTargetPos.isempty()) {
+ Sender->CastSpellPointEnd(0);
+ 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 != 0xffffffff) {
+ if (PersonalDistance(tar, Sender) > dist || !Sender->GetCurrentArea()->IsVisible(Sender->Pos, tar->Pos)) {
+ 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(0);
+ 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 || !Sender->GetCurrentArea()->IsVisible(Sender->Pos, parameters->pointParameter)) {
+ 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;
+ } else {
+ if (!Sender->SpellResRef[0]) {
+ Sender->SetSpellResRef(spellres);
+ }
+ }
+
+ //if target was set, fire spell
+ if (Sender->LastTarget) {
+ Sender->CastSpellEnd(0);
+ Sender->ReleaseCurrentAction();
+ return;
+ }
+
+ //the target was converted to a point
+ if(!Sender->LastTargetPos.isempty()) {
+ Sender->CastSpellPointEnd(0);
+ 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;
+ } else {
+ if (!Sender->SpellResRef[0]) {
+ Sender->SetSpellResRef(spellres);
+ }
+ }
+
+ //if target was set, fire spell
+ if (!Sender->LastTargetPos.isempty()) {
+ Sender->CastSpellPointEnd(0);
+ 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;
+ } else {
+ if (!Sender->SpellResRef[0]) {
+ Sender->SetSpellResRef(spellres);
+ }
+ }
+
+ //if target was set, fire spell
+ if (Sender->LastTarget) {
+ Sender->CastSpellEnd(0);
+ Sender->ReleaseCurrentAction();
+ return;
+ }
+
+ //the target was converted to a point
+ if(!Sender->LastTargetPos.isempty()) {
+ Sender->CastSpellPointEnd(0);
+ 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;
+ } else {
+ if (!Sender->SpellResRef[0]) {
+ Sender->SetSpellResRef(spellres);
+ }
+ }
+
+ //if target was set, fire spell
+ if (!Sender->LastTargetPos.isempty()) {
+ Sender->CastSpellPointEnd(0);
+ 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 (FIXED)
+void GameScript::ReallyForceSpell(Scriptable* Sender, Action* parameters)
+{
+ ieResRef spellres;
+ int level;
+
+ if (!ResolveSpellName( spellres, parameters) ) {
+ Sender->ReleaseCurrentAction();
+ return;
+ } else {
+ if (!Sender->SpellResRef[0]) {
+ Sender->SetSpellResRef(spellres);
+ }
+ }
+
+ 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 (parameters->string0Parameter[0]) {
+ level = parameters->int0Parameter;
+ } else {
+ level = parameters->int1Parameter;
+ }
+ if (tar->Type==ST_ACTOR) {
+ Sender->CastSpellEnd(level);
+ } else {
+ Sender->CastSpellPointEnd(level);
+ }
+ 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;
+ int level;
+
+ if (!ResolveSpellName( spellres, parameters) ) {
+ Sender->ReleaseCurrentAction();
+ return;
+ } else {
+ if (!Sender->SpellResRef[0]) {
+ Sender->SetSpellResRef(spellres);
+ }
+ }
+
+ //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);
+ if (parameters->string0Parameter[0]) {
+ level = parameters->int0Parameter;
+ } else {
+ level = parameters->int1Parameter;
+ }
+ Sender->CastSpellPointEnd(level);
+ 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;
+ int level;
+
+ if (!ResolveSpellName( spellres, parameters) ) {
+ Sender->ReleaseCurrentAction();
+ return;
+ } else {
+ if (!Sender->SpellResRef[0]) {
+ Sender->SetSpellResRef(spellres);
+ }
+ }
+
+ Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] );
+ if (!tar) {
+ Sender->ReleaseCurrentAction();
+ return;
+ }
+ Sender->LastTargetPos=parameters->pointParameter;
+
+ Sender->CastSpell (spellres, tar, false, true);
+ if (parameters->string0Parameter[0]) {
+ level = parameters->int0Parameter;
+ } else {
+ level = parameters->int1Parameter;
+ }
+ if (tar->Type==ST_ACTOR) {
+ Sender->CastSpellEnd(parameters->int1Parameter);
+ } else {
+ Sender->CastSpellPointEnd(parameters->int1Parameter);
+ }
+ 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;
+ }
+ // make sure we're in the same area, otherwise Dynaheir joins when Minsc does
+ // but she's in another area and needs to be rescued first!
+ Actor* act = ( Actor* ) Sender;
+ Game *game = core->GetGame();
+ if (act->GetCurrentArea() != game->GetCurrentArea()) {
+ return;
+ }
+
+ /* calling this, so it is simpler to change */
+ /* i'm not sure this is required here at all */
+ SetBeenInPartyFlags(Sender, parameters);
+ 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 );
+ }
+ }
+ game->JoinParty( act, JP_JOIN );
+}
+
+void GameScript::LeaveParty(Scriptable* Sender, Action* /*parameters*/)
+{
+ if (Sender->Type != ST_ACTOR) {
+ return;
+ }
+ Actor* act = ( Actor* ) Sender;
+ core->GetGame()->LeaveParty( act );
+}
+
+//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", -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,IE_INV_ITEM_UNDROPPABLE,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,IE_INV_ITEM_UNDROPPABLE, 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)<MAX_OPERATING_DISTANCE) {
+ while (MoveItemCore(ac, Sender, parameters->string0Parameter,IE_INV_ITEM_UNDROPPABLE,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,IE_INV_ITEM_UNDROPPABLE, 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;i<rows;i++) {
+ MoveItemCore(tar, Sender, tab->QueryField(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;i<rows;i++) {
+ int j = game->GetPartySize(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;i<rows;i++) {
+ int j = game->GetPartySize(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, LR_DAMAGELUCK, 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]) {
+ //the spell resref is in the string parameter
+ type = parameters->int0Parameter;
+ } else {
+ //the spell number is in the int0 parameter
+ type = parameters->int1Parameter;
+ }
+ 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;
+ }
+
+ if (core->InCutSceneMode()) {
+ //cannot use container in dialog or cutscene
+ 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::GenerateMaze(Scriptable* /*Sender*/, Action* /*parameters*/)
+{
+ core->SetEventFlag(EF_CREATEMAZE);
+}
+
+void GameScript::FixEngineRoom(Scriptable* Sender, Action* /*parameters*/)
+{
+ int value = CheckVariable( Sender, "EnginInMaze","GLOBAL");
+ if (value) {
+ SetVariable(Sender, "EnginInMaze", "GLOBAL", 0);
+ //this works only because the engine room exit depends only on the EnginInMaze variable
+ ScriptEngine *sE = core->GetGUIScriptEngine();
+ sE->RunFunction("Maze", "CustomizeArea");
+ }
+}
+
+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<<ACT_TURN)) {
+ return;
+ }
+
+ int skill = actor->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;i<MAX_OBJECT_FIELDS;i++) {
+ int val = ob->objectFields[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
+static EffectRef fx_iwd_visual_spell_hit_ref = { "IWDVisualSpellHit", -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;i<count;i++) {
+ //roll a random number between 0 and column #
+ j = core->Roll(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/gemrb/core/GameScript/GSUtils.cpp b/gemrb/core/GameScript/GSUtils.cpp
new file mode 100644
index 0000000..9fe3c6c
--- /dev/null
+++ b/gemrb/core/GameScript/GSUtils.cpp
@@ -0,0 +1,2309 @@
+/* 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 "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.h"
+
+#include <cstdio>
+
+//these tables will get freed by Core
+Holder<SymbolMgr> triggersTable;
+Holder<SymbolMgr> actionsTable;
+Holder<SymbolMgr> overrideActionsTable;
+Holder<SymbolMgr> 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;
+//don't create new variables
+bool NoCreate = false;
+bool HasKaputz = false;
+//released by ReleaseMemory
+ieResRef *ObjectIDSTableNames;
+int ObjectFieldsCount = 7;
+int ExtraParametersCount = 0;
+int InDebug = 0;
+int happiness[3][20];
+int RandomNumValue;
+// reaction modifiers (by reputation and charisma)
+int rmodrep[20];
+int rmodchr[25];
+Gem_Polygon **polygons;
+
+void InitScriptTables()
+{
+ //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 );
+ // make sure to copy the HP, to avoid things like magically-healing trolls
+ tar->BaseStats[IE_HITPOINTS]=src->BaseStats[IE_HITPOINTS];
+ 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("<NULL>\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)<MAX_OPERATING_DISTANCE) {
+ Sender->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)
+{
+ //copy the whole resref, including the terminating zero
+ strnuprcpy(item->ItemResRef, resref, 8);
+ core->ResolveRandomItem(item);
+ if (a==-1) {
+ //use the default charge counts of the item
+ Item *origitem = gamedata->GetItem(item->ItemResRef);
+ if (origitem) {
+ for(int i=0;i<3;i++) {
+ ITMExtHeader *e = origitem->GetExtHeader(i);
+ item->Usages[i]=e?e->Charges:0;
+ }
+ gamedata->FreeItem(origitem, item->ItemResRef, 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;
+ Actor *tar = NULL;
+ ieDword targetID = 0;
+ if (target->Type==ST_ACTOR) {
+ tar = (Actor *) target;
+ targetID = tar->GetGlobalID();
+ }
+ if (actor == tar) {
+ Sender->ReleaseCurrentAction();
+ return;
+ }
+
+ //will return false on any errors (eg, unusable weapon)
+ if (!actor->GetCombatDetails(tohit, leftorright, wi, header, hittingheader, Flags, DamageBonus, speed, CriticalBonus, style, tar)) {
+ actor->SetStance(IE_ANI_READY);
+ Sender->ReleaseCurrentAction();
+ return;
+ }
+
+ if (header) wi.range *= 10;
+ else wi.range = 0;
+
+ if ( target->Type == ST_DOOR || target->Type == ST_CONTAINER) {
+ wi.range += 10;
+ }
+ 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) ||
+ (!Sender->GetCurrentArea()->IsVisible(Sender->Pos, target->Pos))) {
+ 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<SymbolMgr> 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;i<ObjectFieldsCount;i++) {
+ object->objectFields[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 (Nesting<MaxObjectNesting) {
+ memmove(object->objectFilters+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 && 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(target->GetGlobalID());
+ } else {
+ ((Actor *) Sender)->UseExit(0);
+ }
+ // 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;
+ }
+
+ //chasing is unbreakable
+ //TODO: is this true?
+ Sender->CurrentActionInterruptable = false;
+
+ 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 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, NoCreate );
+ return;
+ }
+ if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
+ Sender->locals->SetAt( VarName, value, NoCreate );
+ return;
+ }
+ Game *game = core->GetGame();
+ if (HasKaputz && !strnicmp(newVarName,"KAPUTZ",6) ) {
+ 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, NoCreate);
+ }
+ 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, NoCreate );
+ }
+}
+
+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, NoCreate );
+ return;
+ }
+ if (strnicmp( newVarName, "LOCALS", 6 ) == 0) {
+ Sender->locals->SetAt( poi, value, NoCreate );
+ return;
+ }
+ Game *game = core->GetGame();
+ if (HasKaputz && !strnicmp(newVarName,"KAPUTZ",6) ) {
+ game->kaputz->SetAt( poi, value, NoCreate );
+ return;
+ }
+ if (strnicmp(newVarName,"GLOBAL",6) ) {
+ Map *map=game->GetMap(game->FindMap(newVarName));
+ if (map) {
+ map->locals->SetAt( poi, value, NoCreate);
+ }
+ else if (InDebug&ID_VARIABLES) {
+ printMessage("GameScript"," ",YELLOW);
+ printf("Invalid variable %s in setvariable\n",VarName);
+ }
+ }
+ else {
+ game->locals->SetAt( poi, ( ieDword ) value, NoCreate );
+ }
+}
+
+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 (HasKaputz && !strnicmp(newVarName,"KAPUTZ",6) ) {
+ 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 (HasKaputz && !strnicmp(newVarName,"KAPUTZ",6) ) {
+ 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 (a<b) {
+ return 1;
+ }
+ break;
+ case EQUALS:
+ if (a==b) {
+ return 1;
+ }
+ break;
+ case GREATER_THAN:
+ if (a>b) {
+ 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*5; //FIXME: this empirical constant shouldn't be needed!
+}
+
+/* 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 (count<picks) {
+ for(i=0;i<count;i++) {
+ selects[i]=i;
+ }
+ while(i++<picks) {
+ selects[i]=-1;
+ }
+ } else {
+ for(i=0;i<picks;i++) {
+ selects[i]=rand()%count;
+retry:
+ for(j=0;j<i;j++) {
+ if(selects[i]==selects[j]) {
+ selects[i]++;
+ goto retry;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < picks; i++) {
+ if (selects[i]<0)
+ continue;
+ int spnum = atoi( tm->QueryField( 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/gemrb/core/GameScript/GSUtils.h b/gemrb/core/GameScript/GSUtils.h
new file mode 100644
index 0000000..fe3a9bf
--- /dev/null
+++ b/gemrb/core/GameScript/GSUtils.h
@@ -0,0 +1,140 @@
+/* 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<SymbolMgr> triggersTable;
+extern Holder<SymbolMgr> actionsTable;
+extern Holder<SymbolMgr> overrideActionsTable;
+extern Holder<SymbolMgr> 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 bool NoCreate;
+extern bool HasKaputz;
+extern ieResRef *ObjectIDSTableNames;
+extern int ObjectFieldsCount;
+extern int ExtraParametersCount;
+extern int InDebug;
+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);
+
+#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);
+
+GEM_EXPORT void FreeSrc(SrcVector *poi, const ieResRef key);
+GEM_EXPORT 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/gemrb/core/GameScript/GameScript.cpp b/gemrb/core/GameScript/GameScript.cpp
new file mode 100644
index 0000000..47564c2
--- /dev/null
+++ b/gemrb/core/GameScript/GameScript.cpp
@@ -0,0 +1,2344 @@
+/* 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},
+ {"iscreaturehiddeninshadows", GameScript::IsCreatureHiddenInShadows, 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},
+ {"fixengineroom", GameScript::FixEngineRoom, 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
+ {"generatemodronmaze", GameScript::GenerateMaze, 0},
+ {"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::SpellNoDec, AF_BLOCKING|AF_DIRECT|AF_ALIVE},
+ //{"nidspecial8", GameScript::SpellPoint, AF_BLOCKING|AF_ALIVE}, //not needed
+ {"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},
+ {"playsequenceglobal", GameScript::PlaySequenceGlobal, 0}, //pst
+ {"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()
+{
+ triggersTable.release();
+ actionsTable.release();
+ objectsTable.release();
+ overrideActionsTable.release();
+ if (ObjectIDSTableNames)
+ free(ObjectIDSTableNames);
+ ObjectIDSTableNames = NULL;
+}
+
+void printFunction(Holder<SymbolMgr> 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<int> missing_triggers;
+ std::list<int> missing_actions;
+ std::list<int> missing_objects;
+ std::list<int>::iterator l;
+
+ PluginMgr::Get()->RegisterCleanup(CleanupIEScript);
+
+ NoCreate = core->HasFeature(GF_NO_NEW_VARIABLES);
+ HasKaputz = core->HasFeature(GF_HAS_KAPUTZ);
+
+ 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<SymbolMgr> 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), <actions>, 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<ObjectFieldsCount;i++) {
+ if (objectFields[i]) {
+ return false;
+ }
+ }
+ //commit suicide
+ Release();
+ return true;
+}
+
diff --git a/gemrb/core/GameScript/GameScript.h b/gemrb/core/GameScript/GameScript.h
new file mode 100644
index 0000000..f0925d3
--- /dev/null
+++ b/gemrb/core/GameScript/GameScript.h
@@ -0,0 +1,1535 @@
+/* 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 GAMESCRIPT_H
+#define GAMESCRIPT_H
+
+#include "exports.h"
+
+#include "SymbolMgr.h"
+#include "Variables.h"
+#include "Scriptable/Actor.h"
+#include "System/DataStream.h"
+
+#include <cstdio>
+#include <vector>
+
+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<ieDword> SrcVector;
+
+struct targettype {
+ Scriptable *actor; //hmm, could be door
+ unsigned int distance;
+};
+
+typedef std::list<targettype> 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;i<MAX_OBJECT_FIELDS;i++) {
+ printf("%d ",objectFields[i]);
+ }
+ printf("\n");
+ printf("Filters: ");
+ for(i=0;i<MAX_NESTING;i++) {
+ printf("%d ",objectFilters[i]);
+ }
+ printf("\n");
+ }
+
+ void Release()
+ {
+ GSASSERT( canary == (unsigned long) 0xdeadbeef, canary );
+ canary = 0xdddddddd;
+ delete this;
+ }
+ bool ReadyToDie();
+};
+
+class GEM_EXPORT Trigger {
+public:
+ Trigger()
+ {
+ flags = 0;
+ objectParameter = NULL;
+ string0Parameter[0] = 0;
+ string1Parameter[0] = 0;
+ int0Parameter = 0;
+ int1Parameter = 0;
+ pointParameter.null();
+ canary = (unsigned long) 0xdeadbeef;
+ }
+ ~Trigger()
+ {
+ if (objectParameter) {
+ objectParameter->Release();
+ 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<Trigger*> 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:"<NULL>", string1Parameter?string1Parameter:"<NULL>");
+ 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<Action*> 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<Response*> 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<ResponseBlock*> 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 FixEngineRoom(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 GenerateMaze(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 PlaySequenceGlobal(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/gemrb/core/GameScript/Matching.cpp b/gemrb/core/GameScript/Matching.cpp
new file mode 100644
index 0000000..be43aff
--- /dev/null
+++ b/gemrb/core/GameScript/Matching.cpp
@@ -0,0 +1,674 @@
+/* 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"
+#include "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.h"
+
+/* return a Targets object with a single scriptable inside */
+inline static Targets* ReturnScriptableAsTarget(Scriptable *sc)
+{
+ if (!sc) return NULL;
+ Targets *tgts = new Targets();
+ tgts->AddTarget(sc, 0, 0);
+ return tgts;
+}
+
+/* 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", -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 ReturnScriptableAsTarget(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] );
+ if (aC) {
+ if (!aC->ValidTarget(ga_flags)) {
+ return NULL;
+ }
+ return ReturnScriptableAsTarget(aC);
+ }
+ Door *door = map->GetDoorByGlobalID( (ieDword) oC->objectFields[1]);
+ if (door) {
+ return ReturnScriptableAsTarget(door);
+ }
+
+ Container* cont = map->GetContainerByGlobalID((ieDword) oC->objectFields[1]);
+ if (cont) {
+ return ReturnScriptableAsTarget(cont);
+ }
+
+ InfoPoint* trap = map->GetInfoPointByGlobalID((ieDword) oC->objectFields[1]);
+ if (trap) {
+ return ReturnScriptableAsTarget(trap);
+ }
+
+ return NULL;
+ }
+
+ 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 *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/gemrb/core/GameScript/Matching.h b/gemrb/core/GameScript/Matching.h
new file mode 100644
index 0000000..a01fa22
--- /dev/null
+++ b/gemrb/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"
+
+GEM_EXPORT 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/gemrb/core/GameScript/Objects.cpp b/gemrb/core/GameScript/Objects.cpp
new file mode 100644
index 0000000..2cc9b3b
--- /dev/null
+++ b/gemrb/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 (bestac<ac) {
+ bestac=ac;
+ scr=t->actor;
+ }
+ }
+
+ 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) || (worsthp<hp)) {
+ worsthp=hp;
+ scr=t->actor;
+ }
+ }
+ 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 (besthp<hp) {
+ besthp=hp;
+ scr=t->actor;
+ }
+ }
+ 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 (besthp<hp) {
+ besthp=hp;
+ scr=t->actor;
+ }
+ }
+ 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) || (dist<mindist) ) {
+ ac = newactor;
+ mindist = dist;
+ }
+ }
+ if (ac) {
+ parameters->AddTarget(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/gemrb/core/GameScript/Triggers.cpp b/gemrb/core/GameScript/Triggers.cpp
new file mode 100644
index 0000000..da8f35e
--- /dev/null
+++ b/gemrb/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
+#include "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.h"
+
+//-------------------------------------------------------------
+// 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<nn>.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<<parameters->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_<name>_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 cnt<parameters->int0Parameter;
+}
+
+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 cnt<parameters->int0Parameter;
+}
+
+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 cnt<parameters->int0Parameter;
+}
+
+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 cnt<parameters->int0Parameter;
+}
+
+//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 is the same as inventory flags
+ like: 1 - identified, 2 - unstealable, 4 - stolen, 8 - undroppable, etc. */
+/** 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;
+ }
+ //personaldistance is needed for modron constructs in PST maze
+ int distance = PersonalDistance(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)
+{
+ Scriptable* target = GetActorFromObject( Sender, parameters->objectParameter );
+ if (!target) {
+ return 0;
+ }
+ if (target->Type != ST_ACTOR) {
+ return 0;
+ }
+ Actor* actor = ( Actor* ) target;
+ int sk = actor->GetSkill( parameters->int1Parameter );
+ if (sk<0) return 0;
+ if ( sk == 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)
+{
+ Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter );
+ if (!tar || tar->Type != ST_ACTOR) {
+ return 0;
+ }
+ Actor* actor = ( Actor* ) tar;
+ int sk = actor->GetSkill( parameters->int1Parameter );
+ if (sk<0) return 0;
+ if ( sk > 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)
+{
+ Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter );
+ if (!tar || tar->Type != ST_ACTOR) {
+ return 0;
+ }
+ Actor* actor = ( Actor* ) tar;
+ int sk = actor->GetSkill( parameters->int1Parameter );
+ if (sk<0) return 0;
+ if ( sk < 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:"<NULL>");
+ 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:"<NULL>");
+ 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)<parameters->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)<parameters->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)<parameters->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)<parameters->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 (stat<value)
+ return 1;
+ break;
+ case DM_RAISE:
+ if (stat>value)
+ 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/gemrb/core/GlobalTimer.cpp b/gemrb/core/GlobalTimer.cpp
new file mode 100644
index 0000000..1f71723
--- /dev/null
+++ b/gemrb/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<AnimationRef *>::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 (x<goal.x) {
+ x+=speed;
+ if (x>goal.x) x=goal.x;
+ } else {
+ x-=speed;
+ if (x<goal.x) x=goal.x;
+ }
+ if (y<goal.y) {
+ y+=speed;
+ if (y>goal.y) y=goal.y;
+ } else {
+ y-=speed;
+ if (y<goal.y) y=goal.y;
+ }
+ } else {
+ x=goal.x;
+ y=goal.y;
+ }
+ currentVP.x=x;
+ currentVP.y=y;
+ }
+
+ if (shakeCounter) {
+ shakeCounter-=count;
+ if (shakeCounter<0) {
+ shakeCounter=0;
+ }
+ if (shakeCounter) {
+ x += (rand()%shakeX) - (shakeX>>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 (fadeFromCounter<fadeFromMax) {
+ fadeFromCounter=fadeFromMax;
+ }
+ //don't freeze gametime when already dark
+ } else {
+ fadeFromCounter+=count;
+ if (fadeToCounter>fadeFromMax) {
+ 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<AnimationRef*>::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<AnimationRef*>::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/gemrb/core/GlobalTimer.h b/gemrb/core/GlobalTimer.h
new file mode 100644
index 0000000..ebf955c
--- /dev/null
+++ b/gemrb/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 <vector>
+
+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<AnimationRef*> 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/gemrb/core/Holder.h b/gemrb/core/Holder.h
new file mode 100644
index 0000000..170a4c8
--- /dev/null
+++ b/gemrb/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 <cassert>
+#include <cstddef>
+
+template <class T>
+class Held {
+public:
+ Held() : RefCount(0) {}
+ void acquire() { ++RefCount; }
+ void release() { assert(RefCount && "Broken Held usage.");
+ if (!--RefCount) delete static_cast<T*>(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 T>
+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>,T,ptr)
+ T* get() const { return ptr; }
+ void release() {
+ if (ptr)
+ ptr->release();
+ ptr = NULL;
+ }
+protected:
+ T *ptr;
+};
+
+#endif
diff --git a/gemrb/core/Image.cpp b/gemrb/core/Image.cpp
new file mode 100644
index 0000000..8b03e09
--- /dev/null
+++ b/gemrb/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/gemrb/core/Image.h b/gemrb/core/Image.h
new file mode 100644
index 0000000..9628d65
--- /dev/null
+++ b/gemrb/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/gemrb/core/ImageFactory.cpp b/gemrb/core/ImageFactory.cpp
new file mode 100644
index 0000000..17ad0d0
--- /dev/null
+++ b/gemrb/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/gemrb/core/ImageFactory.h b/gemrb/core/ImageFactory.h
new file mode 100644
index 0000000..c0576d9
--- /dev/null
+++ b/gemrb/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/gemrb/core/ImageMgr.cpp b/gemrb/core/ImageMgr.cpp
new file mode 100644
index 0000000..c38c486
--- /dev/null
+++ b/gemrb/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/gemrb/core/ImageMgr.h b/gemrb/core/ImageMgr.h
new file mode 100644
index 0000000..40d4709
--- /dev/null
+++ b/gemrb/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/gemrb/core/ImageWriter.cpp b/gemrb/core/ImageWriter.cpp
new file mode 100644
index 0000000..4c2eae7
--- /dev/null
+++ b/gemrb/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/gemrb/core/ImageWriter.h b/gemrb/core/ImageWriter.h
new file mode 100644
index 0000000..a02a44b
--- /dev/null
+++ b/gemrb/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/gemrb/core/IniSpawn.cpp b/gemrb/core/IniSpawn.cpp
new file mode 100644
index 0000000..b7c1ee4
--- /dev/null
+++ b/gemrb/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<DataFileMgr> 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<DataFileMgr> 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;i<sizeof(ieResRef) && (*field)[i];i++) {
+ if ((*field)[i]==',') {
+ (*field)[i]='\0';
+ break;
+ }
+ }
+ if (!count) break;
+ while(*s && *s!=',') s++;
+ s++;
+ if (*s==' ') s++; //this is because there is one single screwed up entry in ar1100.ini
+ }
+}
+
+inline void GetElements(const char *s, ieVariable *storage, int count)
+{
+ while(count--) {
+ ieVariable *field = storage+count;
+ strnuprcpy(*field, s, sizeof(ieVariable)-1);
+ for(size_t i=0;i<sizeof(ieVariable) && (*field)[i];i++) {
+ if ((*field)[i]==',') {
+ (*field)[i]='\0';
+ break;
+ }
+ }
+ while(*s && *s!=',') s++;
+ s++;
+ }
+}
+
+// possible values implemented in DiffMode, but not needed here
+// BINARY_LESS_OR_EQUALS 6 //(left has only bits in right)
+// BINARY_MORE_OR_EQUALS 7 //(left has equal or more bits than right)
+// BINARY_INTERSECT 8 //(left and right has at least one common bit)
+// BINARY_NOT_INTERSECT 9 //(no common bits)
+// BINARY_MORE 10 //left has more bits than right
+// BINARY_LESS 11 //left has less bits than right
+
+int IniSpawn::GetDiffMode(const char *keyword)
+{
+ if (!keyword) return NO_OPERATION; //-1
+ if (keyword[0]==0) return NO_OPERATION; //-1
+ if (!stricmp(keyword,"less_or_equal_to") ) return LESS_OR_EQUALS; //0 (gemrb ext)
+ if (!stricmp(keyword,"equal_to") ) return EQUALS; // 1
+ if (!stricmp(keyword,"less_than") ) return LESS_THAN; // 2
+ if (!stricmp(keyword,"greater_than") ) return GREATER_THAN; //3
+ if (!stricmp(keyword,"greater_or_equal_to") ) return GREATER_THAN; //4 (gemrb ext)
+ if (!stricmp(keyword,"not_equal_to") ) return NOT_EQUALS; //5
+ return NO_OPERATION;
+}
+
+//unimplemented tags:
+// check_crowd
+// good_mod, law_mod, lady_mod, murder_mod
+// control_var
+// spec_area
+// death_faction
+// death_team
+// check_by_view_port
+// do_not_spawn
+// time_of_day
+// hold_selected_point_key
+// inc_spawn_point_index
+// find_safest_point
+// exit
+// spawn_time_of_day
+// PST only
+// auto_buddy
+// detail_level
+void IniSpawn::ReadCreature(DataFileMgr *inifile, const char *crittername, CritterEntry &critter)
+{
+ const char *s;
+ int ps;
+
+ memset(&critter,0,sizeof(critter));
+
+ //all specvars are using global, but sometimes it is explicitly given
+ s = inifile->GetKeyAsString(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<DataFileMgr> 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;y<namelessvarcount;y++) {
+ const char* Key = inifile->GetKeyNameByIndex("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;y<localscount;y++) {
+ const char* Key = inifile->GetKeyNameByIndex("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;i<game->GetPartySize(false);i++) {
+ MoveBetweenAreasCore(game->GetPC(i, false),NamelessSpawnArea,NamelessSpawnPoint,-1, true);
+ }
+
+ //certain variables are set when nameless dies
+ for (i=0;i<namelessvarcount;i++) {
+ SetVariable(game, NamelessVar[i].Name,"GLOBAL", NamelessVar[i].Value);
+ }
+}
+
+void IniSpawn::SpawnCreature(CritterEntry &critter)
+{
+ if (!critter.creaturecount) {
+ return;
+ }
+
+ ieDword specvar = CheckVariable(map, critter.SpecVar, critter.SpecContext);
+
+ if (critter.SpecVar[0]) {
+ if (critter.SpecVarOperator>=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;i<event.crittercount;i++) {
+ CritterEntry* critter = event.critters+i;
+ for(int j=0;j<critter->SpawnCount;j++) {
+ SpawnCreature(*critter);
+ }
+ }
+}
+
+//execute the initial spawn
+void IniSpawn::InitialSpawn()
+{
+ SpawnGroup(enterspawn);
+ //these variables are set when entering first
+ for (int i=0;i<localscount;i++) {
+ SetVariable(map, Locals[i].Name,"LOCALS", Locals[i].Value);
+ }
+}
+
+//checks if a respawn event occurred
+void IniSpawn::CheckSpawn()
+{
+ for(int i=0;i<eventcount;i++) {
+ SpawnGroup(eventspawns[i]);
+ }
+}
diff --git a/gemrb/core/IniSpawn.h b/gemrb/core/IniSpawn.h
new file mode 100644
index 0000000..9a371b5
--- /dev/null
+++ b/gemrb/core/IniSpawn.h
@@ -0,0 +1,168 @@
+/* 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.
+ *
+ *
+ */
+
+/**
+ * @file IniSpawn.h
+ * Declares IniSpawn a class for the special creature re/spawn features
+ * PST has. The information is originally stored in <arearesref>.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<crittercount;i++) {
+ delete[] critters[i].CreFile;
+ }
+ delete[] critters;
+ }
+ }
+};
+
+/**
+ * @class Spawn
+ * Class for the special creature re/spawn features that are unique to PST.
+ */
+
+struct VariableSpec {
+ ieVariable Name;
+ ieDword Value;
+};
+
+class GEM_EXPORT IniSpawn {
+public:
+ IniSpawn(Map *owner);
+ ~IniSpawn();
+
+private:
+ Map *map; //owner
+ ieResRef NamelessSpawnArea;
+ int namelessvarcount;
+ VariableSpec *NamelessVar;
+ int localscount;
+ VariableSpec *Locals;
+ Point NamelessSpawnPoint;
+ int NamelessState;
+ SpawnEntry enterspawn;
+ int last_spawndate;
+ int eventcount;
+ SpawnEntry *eventspawns;
+
+ void ReadCreature(DataFileMgr *inifile,
+ const char *crittername, CritterEntry &critter);
+ void ReadSpawnEntry(DataFileMgr *inifile,
+ const char *entryname, SpawnEntry &entry);
+ //spawns a single creature
+ void SpawnCreature(CritterEntry &critter);
+ void SpawnGroup(SpawnEntry &event);
+ //gets the spec var operation code from a keyword
+ int GetDiffMode(const char *keyword);
+public:
+ /* called by action of the same name */
+ void SetNamelessDeath(const ieResRef area, Point &pos, ieDword state);
+ void InitSpawn(const ieResRef DefaultArea);
+ void RespawnNameless();
+ void InitialSpawn();
+ void CheckSpawn();
+};
+
+#endif // ! INISPAWN_H
diff --git a/gemrb/core/Interface.cpp b/gemrb/core/Interface.cpp
new file mode 100644
index 0000000..c4db5c9
--- /dev/null
+++ b/gemrb/core/Interface.cpp
@@ -0,0 +1,5492 @@
+/* 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.
+*
+*
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 "SymbolMgr.h"
+#include "TileMap.h"
+#include "Video.h"
+#include "WorldMapMgr.h"
+#include "GameScript/GameScript.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 "Scriptable/Container.h"
+#include "System/FileStream.h"
+#include "System/VFS.h"
+
+#if defined(__HAIKU__)
+#include <unistd.h>
+#endif
+
+#include <cstdlib>
+#include <time.h>
+#include <vector>
+
+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;
+
+ strncpy( GameType, "auto", sizeof( GameType )-1);
+ 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;
+ IgnoreOriginalINI = 0;
+ 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;
+ SpecialSpellsCount = -1;
+ SpecialSpells = NULL;
+
+ 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;
+ }
+
+ if (SpecialSpells) {
+ free(SpecialSpells);
+ }
+ SurgeSpells.clear();
+
+ 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;i<sizeof(FogSprites)/sizeof(Sprite2D *);i++ ) {
+ video->FreeSprite(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;
+ }
+
+ if (EventFlag&EF_CREATEMAZE) {
+ EventFlag&=~EF_CREATEMAZE;
+ guiscript->RunFunction( "Maze", "CreateMaze", false );
+ return;
+ }
+
+ if ((EventFlag&EF_RESETTARGET) && gc) {
+ EventFlag&=~EF_RESETTARGET;
+ EventFlag|=EF_TARGETMODE;
+ gc->ResetTargetMode();
+ return;
+ }
+
+ if ((EventFlag&EF_TARGETMODE) && gc) {
+ EventFlag&=~EF_TARGETMODE;
+ gc->UpdateTargetMode();
+ 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 * 5 * 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;i<fix;i++) {
+ for (int j=0;j<columns;j++) {
+ mem[rows*j+i]=(ieWordSigned) strtol(tab->QueryField(0,j),NULL,0 );
+ }
+ }
+ }
+ for (int j=0;j<columns;j++) {
+ for( int i=0;i<rows-fix;i++) {
+ mem[rows*j+i+fix] = (ieWordSigned) strtol(tab->QueryField(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, 5, 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::ReadSpecialSpells()
+{
+ int i;
+ bool result = true;
+
+ AutoTable table("splspec");
+ if (table) {
+ SpecialSpellsCount = table->GetRowCount();
+ SpecialSpells = (SpellDescType *) malloc( sizeof(SpellDescType) * SpecialSpellsCount);
+ for (i=0;i<SpecialSpellsCount;i++) {
+ strnlwrcpy(SpecialSpells[i].resref, table->GetRowName(i),8 );
+ //if there are more flags, compose this value into a bitfield
+ SpecialSpells[i].value = atoi(table->QueryField(i,0) );
+ }
+ } else {
+ result = false;
+ }
+
+ table.load("wildmag");
+ if (table) {
+ SurgeSpell ss;
+ for (i = 0; (unsigned)i < table->GetRowCount(); i++) {
+ strncpy(ss.spell, table->QueryField(i, 0), 8);
+ ss.message = strtol(table->QueryField(i, 1), NULL, 0);
+ // comment ignored
+ SurgeSpells.push_back(ss);
+ }
+ } else {
+ result = false;
+ }
+
+ return result;
+}
+
+int Interface::GetSpecialSpell(ieResRef resref)
+{
+ for (int i=0;i<SpecialSpellsCount;i++) {
+ if (!strnicmp(resref, SpecialSpells[i].resref, sizeof(ieResRef))) {
+ return SpecialSpells[i].value;
+ }
+ }
+ return 0;
+}
+
+//disable spells based on some circumstances
+int Interface::CheckSpecialSpell(ieResRef resref, Actor *actor)
+{
+ int sp = GetSpecialSpell(resref);
+
+ //the identify spell is always disabled on the menu
+ if (sp&SP_IDENTIFY) {
+ return 1;
+ }
+
+ //if actor is silenced, and spell cannot be cast in silence, disable it
+ if (actor->GetStat(IE_STATE_ID) & STATE_SILENCED ) {
+ if (!(sp&SP_SILENCE)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+bool Interface::ReadAuxItemTables()
+{
+ int idx;
+ bool flag = true;
+
+ if (ItemExclTable) {
+ ItemExclTable->RemoveAll(NULL);
+ } else {
+ ItemExclTable = new Variables();
+ ItemExclTable->SetType(GEM_VARIABLES_INT);
+ }
+
+ 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, DamageInfoStruct> ((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; j<cols; j++) {
+ reputationmod[i][j] = atoi(tm->QueryField(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 (CursorCount<IE_CURSOR_WAY) {
+ printStatus( "ERROR", LIGHT_RED );
+ return GEM_ERROR;
+ }
+ video->SetCursor( 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;i<sizeof(FogSprites)/sizeof(Sprite2D *);i++ ) {
+ if (FogSprites[i]) {
+ Sprite2D* alphasprite = video->CreateAlpha( 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<AnimationMgr> 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
+ char *description = strdup("CD1/data");
+ for (i = 0; i < MAX_CD; i++) {
+ for (size_t j=0;j<CD[i].size();j++) {
+ description[2]='1'+i;
+ PathJoin( path, CD[i][j].c_str(), GameDataPath, NULL);
+ gamedata->AddSource(path, description, PLUGIN_RESOURCE_DIRECTORY);
+ }
+ }
+ free(description);
+
+ 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", "Initializing GUI Script Engine...", WHITE );
+ guiscript = PluginHolder<ScriptEngine>(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" );
+
+ {
+ // re-set the gemrb override path, since we now have the correct GameType if 'auto' was used
+ char path[_MAX_PATH];
+ PathJoin( path, GemRBOverridePath, "override", GameType, NULL);
+ gamedata->AddSource(path, "GemRB Override", PLUGIN_RESOURCE_DIRECTORY, RM_REPLACE_SAME_SOURCE);
+ }
+
+ printMessage( "Core", "Reading Game Options...\n", WHITE );
+ if (!LoadGemRBINI()) {
+ printf( "Cannot Load INI\nTermination in Progress...\n" );
+ return GEM_ERROR;
+ }
+
+ //loading baldur.ini
+ if (!IgnoreOriginalINI) {
+ 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<StringMgr>(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<ImageMgr> pal16im(Palette16);
+ if (pal16im)
+ pal16 = pal16im->GetImage();
+ ResourceHolder<ImageMgr> pal32im(Palette32);
+ if (pal32im)
+ pal32 = pal32im->GetImage();
+ ResourceHolder<ImageMgr> 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<WindowMgr>(IE_CHU_CLASS_ID);
+ if (windowmgr == NULL) {
+ printStatus( "ERROR", LIGHT_RED );
+ return GEM_ERROR;
+ }
+ printStatus( "OK", LIGHT_GREEN );
+
+ 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<MusicMgr>(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<DataFileMgr>(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<DataFileMgr>(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<DataFileMgr>(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<DataFileMgr>(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 = ReadSpecialSpells();
+ printMessage( "Core", "Reading special spells table...", WHITE);
+ if (ret) {
+ printStatus( "OK", LIGHT_GREEN );
+ } else {
+ printStatus( "NOT FOUND", YELLOW );
+ }
+
+ 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) );
+ }
+}
+
+ieDword Interface::HasFeature(int position) const
+{
+ return GameFeatures[position>>5] & (1<<(position&31));
+}
+
+/** 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 <filename>, 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 <filename>, 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 = );
+ CONFIG_INT("IgnoreOriginalINI", IgnoreOriginalINI = );
+#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;i<MAX_CD;i++) {
+ char keyname[] = { 'C', 'D', '1'+i, '\0' };
+ if (stricmp(name, keyname) == 0) {
+ for(char *path = strtok(value, SPathListSeparator);
+ path;
+ path = strtok(NULL,SPathListSeparator)) {
+ CD[i].push_back(path);
+ }
+ }
+ }
+ }
+ }
+ fclose( config );
+
+ // WARNING: Don't move ResolveFilePath into the loop
+ // Otherwise, it won't obey CaseSensitive set at the end
+ // of the config file.
+
+ if (stricmp( GameType, "tob" ) == 0) {
+ strncpy( GameType, "bg2", sizeof(GameType) );
+ }
+
+#ifdef DATADIR
+ if (!GemRBPath[0]) {
+ strcpy( GemRBPath, DATADIR );
+ }
+#endif
+ ResolveFilePath(GemRBPath);
+
+ if (!GemRBOverridePath[0]) {
+ strcpy( GemRBOverridePath, GemRBPath );
+ } else {
+ ResolveFilePath(GemRBOverridePath);
+ }
+
+ if (!PluginsPath[0]) {
+#ifdef PLUGINDIR
+ strcpy( PluginsPath, PLUGINDIR );
+#else
+ PathJoin( PluginsPath, GemRBPath, "plugins", NULL );
+#endif
+ } else {
+ ResolveFilePath( PluginsPath );
+ }
+
+ if (!GUIScriptsPath[0]) {
+ strcpy( GUIScriptsPath, GemRBPath );
+ } else {
+ ResolveFilePath( GUIScriptsPath );
+ }
+
+ if (!GameName[0]) {
+ strcpy( GameName, GEMRB_STRING );
+ }
+
+ ResolveFilePath( GamePath );
+
+ if (!SavePath[0]) {
+ // FIXME: maybe should use UserDir instead of GamePath
+ strcpy( SavePath, GamePath );
+ } else {
+ ResolveFilePath( SavePath );
+ }
+
+ if (! CachePath[0]) {
+ PathJoin( CachePath, UserDir, "Cache", NULL );
+ } else {
+ ResolveFilePath(CachePath);
+ }
+
+ for (i = 0; i < MAX_CD; ++i) {
+ if (!CD[i].size()) {
+ char cd[] = { 'C', 'D', '1'+i, '\0' };
+ char name[_MAX_PATH];
+
+ PathJoin(name, GamePath, cd, NULL);
+ CD[i].push_back(name);
+ } else {
+ size_t cnt = CD[i].size();
+ while(cnt--) {
+ ResolveFilePath( CD[i][cnt] );
+ }
+ }
+ }
+
+ for (i = 0; i < ModPath.size(); ++i) {
+ ResolveFilePath(ModPath[i]);
+ }
+
+ FixPath( GUIScriptsPath, true );
+ FixPath( PluginsPath, true );
+ FixPath( GemRBPath, true );
+ FixPath( GemRBOverridePath, true );
+
+ if (GamePath[0]) {
+ FixPath( GamePath, true );
+ }
+
+ //FixPath( SavePath, false );
+ //mkdir( SavePath, S_IREAD|S_IWRITE|S_IEXEC );
+ //chmod( SavePath, S_IREAD|S_IWRITE|S_IEXEC );
+ FixPath( SavePath, true );
+
+ FixPath( CachePath, false );
+ mkdir( CachePath, S_IREAD|S_IWRITE|S_IEXEC );
+ chmod( CachePath, S_IREAD|S_IWRITE|S_IEXEC );
+
+ printStatus( "OK", LIGHT_GREEN );
+
+ // Missing GameType is a common users' error
+ if (!GameType[0]) {
+ printMessage("Config","GameType was not set in your config file.\n", LIGHT_RED);
+ return false;
+ }
+
+ if ( StupidityDetector( CachePath )) {
+ printMessage("Core"," ",LIGHT_RED);
+ printf( "Cache path %s doesn't exist, not a folder or contains alien files!\n", CachePath );
+ return false;
+ }
+ if (!KeepCache) DelTree((const char *) CachePath, false);
+ FixPath( CachePath, true );
+
+ return true;
+}
+
+static void upperlower(int upper, int lower)
+{
+ pl_uppercase[lower]=(ieByte) upper;
+ pl_lowercase[upper]=(ieByte) lower;
+}
+
+static const char *game_flags[GF_COUNT+1]={
+ "HasKaputz", //0 GF_HAS_KAPUTZ
+ "AllStringsTagged", //1 GF_ALL_STRINGS_TAGGED
+ "HasSongList", //2 GF_HAS_SONGLIST
+ "TeamMovement", //3 GF_TEAM_MOVEMENT
+ "UpperButtonText", //4 GF_UPPER_BUTTON_TEXT
+ "LowerLabelText", //5 GF_LOWER_LABEL_TEXT
+ "HasPartyIni", //6 GF_HAS_PARTY_INI
+ "SoundFolders", //7 GF_SOUNDFOLDERS
+ "IgnoreButtonFrames", //8 GF_IGNORE_BUTTON_FRAMES
+ "OneByteAnimationID", //9 GF_ONE_BYTE_ANIMID
+ "HasDPLAYER", //10GF_HAS_DPLAYER
+ "HasEXPTABLE", //11GF_HAS_EXPTABLE
+ "HasBeastsIni", //12GF_HAS_BEASTS_INI
+ "HasDescIcon", //13GF_HAS_DESC_ICON
+ "HasPickSound", //14GF_HAS_PICK_SOUND
+ "IWDMapDimensions", //15GF_IWD_MAP_DIMENSIONS
+ "AutomapIni", //16GF_AUTOMAP_INI
+ "SmallFog", //17GF_SMALL_FOG
+ "ReverseDoor", //18GF_REVERSE_DOOR
+ "ProtagonistTalks", //19GF_PROTAGONIST_TALKS
+ "HasSpellList", //20GF_HAS_SPELLLIST
+ "IWD2ScriptName", //21GF_IWD2_SCRIPTNAME
+ "DialogueScrolls", //22GF_DIALOGUE_SCROLLS
+ "KnowWorld", //23GF_KNOW_WORLD
+ "ReverseToHit", //24GF_REVERSE_TOHIT
+ "SaveForHalfDamage", //25GF_SAVE_FOR_HALF
+ "CharNameIsGabber", //26GF_CHARNAMEISGABBER
+ "MagicBit", //27GF_MAGICBIT
+ "CheckAbilities", //28GF_CHECK_ABILITIES
+ "ChallengeRating", //29GF_CHALLENGERATING
+ "SpellBookIconHack", //30GF_SPELLBOOKICONHACK
+ "EnhancedEffects", //31GF_ENHANCED_EFFECTS
+ "DeathOnZeroStat", //32GF_DEATH_ON_ZERO_STAT
+ "SpawnIni", //33GF_SPAWN_INI
+ "IWD2DeathVarFormat", //34GF_IWD2_DEATHVARFORMAT
+ "HasResDataIni", //35GF_RESDATA_INI
+ "OverrideCursorPos", //36GF_OVERRIDE_CURSORPOS
+ "BreakableWeapons", //37GF_BREAKABLE_WEAPONS
+ "3EdRules", //38GF_3ED_RULES
+ "LevelslotPerClass", //39GF_LEVELSLOT_PER_CLASS
+ "SelectiveMagicRes", //40GF_SELECTIVE_MAGIC_RES
+ "HasHideInShadows", //41GF_HAS_HIDE_IN_SHADOWS
+ "AreaVisitedVar", //42GF_AREA_VISITED_VAR
+ "ProperBackstab", //43GF_PROPER_BACKSTAB
+ "OnScreenText", //44GF_ONSCREEN_TEXT
+ "HasSpecificDamageBonus", //45GF_SPECIFIC_DMG_BONUS
+ "StrrefSaveGame", //46GF_STRREF_SAVEGAME
+ "HasWisdomBonusTable",//47GF_WISDOM_BONUS
+ "BiographyIsRes", //48GF_BIOGRAPHY_RES
+ "NoBiography", //49GF_NO_BIOGRAPHY
+ "StealIsAttack", //50GF_STEAL_IS_ATTACK
+ "CutsceneAreascripts",//51GF_CUTSCENE_AREASCRIPTS
+ "FlexibleWorldmap", //52GF_FLEXIBLE_WMAP
+ "AutoSearchHidden", //53GF_AUTOSEARCH_HIDDEN
+ "PSTStateFlags", //54GF_PST_STATE_FLAGS
+ "NoDropCanMove", //55GF_NO_DROP_CAN_MOVE
+ "JournalHasSections", //56GF_JOURNAL_HAS_SECTIONS
+ "CastingSounds", //57GF_CASTING_SOUNDS
+ "EnhancedCastingSounds", //58GF_CASTING_SOUNDS2
+ "ForceAreaScript", //59GF_FORCE_AREA_SCRIPT
+ "AreaOverride", //60GF_AREA_OVERRIDE
+ "NoNewVariables", //61GF_NO_NEW_VARIABLES
+ NULL //for our own safety, this marks the end of the pole
+};
+
+/** Loads gemrb.ini */
+bool Interface::LoadGemRBINI()
+{
+ DataStream* inifile = gamedata->GetResource( "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<DataFileMgr> 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;i<GF_COUNT;i++) {
+ if (!game_flags[i]) {
+ printf("Fix the game flags!\n");
+ abort();
+ }
+ SetFeature( ini->GetKeyAsInt( "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->front = color;
+ pal->back = back;
+ 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", -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]<EA_GOODCUTOFF) {
+ ab->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<ImageMgr> 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<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);
+}
+/** 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) {
+ // TODO: this seems to never be called? (EndDialog is called from elsewhere instead)
+ gc->dialoghandler->EndDialog();
+ } else if ( (int)var !=-3) {
+ if ( (int) var == -1) {
+ guiscript->RunFunction( "GUIWORLD", "DialogStarted" );
+ }
+ 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;i<tw;i++) {
+ Window *tmp = windows[topwin[i]];
+ if (tmp->Visible==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; WindowIndex<windows.size();WindowIndex++) {
+ Window* win = windows[WindowIndex];
+ delete win;
+ }
+ windows.clear();
+ topwin.clear();
+ evntmgr->Clear();
+ 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<SymbolMgr> 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<SymbolMgr> Interface::GetSymbol(unsigned int index) const
+{
+ if (index >= symbols.size()) {
+ return Holder<SymbolMgr>();
+ }
+ if (!symbols[index].sm) {
+ return Holder<SymbolMgr>();
+ }
+ 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<MoviePlayer> 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;i<cnt;i++) {
+ frames[i] = atoi (sttable->QueryField(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) {
+ // don't mess with controls/etc if we're already in a cutscene
+ if (active == (gc->GetScreenFlags()&SF_CUTSCENE))
+ return;
+
+ gc->SetCutSceneMode( active );
+ }
+ if (game) {
+ if (active) {
+ game->ControlStatus |= CS_HIDEGUI;
+ } else {
+ game->ControlStatus &= ~CS_HIDEGUI;
+ }
+ SetEventFlag(EF_CONTROL);
+ }
+ video->SetMouseEnabled(!active);
+}
+
+/** returns true if in dialogue or cutscene */
+bool Interface::InCutSceneMode() const
+{
+ GameControl *gc = GetGameControl();
+ if (!gc || (gc->GetDialogueFlags()&DF_IN_DIALOG) || (gc->GetScreenFlags()&SF_DISABLEMOUSE) ) {
+ return true;
+ }
+ return false;
+}
+
+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<SaveGame> 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<SaveGameMgr> gam_mgr(IE_GAM_CLASS_ID);
+ PluginHolder<WorldMapMgr> 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<ArchiveImporter> 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<WorldMapMgr> 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;i<ItemTypes;i++) {
+ unsigned int value = 0;
+ unsigned int k = 1;
+ for (int j=0;j<InvSlotTypes;j++) {
+ if (strtol(it->QueryField(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;i++) {
+ if (idx==slottypes[i].slot) {
+ break;
+ }
+ }
+ return i;
+}
+
+ieDword Interface::QuerySlot(unsigned int idx) const
+{
+ if (idx>=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;j<i;j++) {
+ if (Prefix) {
+ snprintf(ItemName,sizeof(ItemName),"%s%02d",Prefix, j+1);
+ } else {
+ strnlwrcpy(ItemName,tab->GetRowName(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;k<l;k++) {
+ strnlwrcpy(itemlist->ResRefs[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();
+ if (ReadItem(str, itm)) return itm;
+ delete itm;
+ return NULL;
+}
+
+CREItem *Interface::ReadItem(DataStream *str, CREItem *itm)
+{
+ 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;
+ }
+ 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;loop<MAX_LOOP;loop++) {
+ int i,j,k;
+ char *endptr;
+ ieResRef NewItem;
+
+ void* lookup;
+ if ( !RtRows->Lookup( 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<StoreMgr> 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<StoreMgr> 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<DialogMgr> 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;size<s->ItemsCount;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)
+{
+ Actor *ret = NULL;
+ int slot = 0;
+ 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()) {
+ if (actor->InParty<slot || !ret) {
+ ret = actor;
+ slot = actor->InParty;
+ }
+ }
+ }
+
+ if (forced && !ret) {
+ return game->FindPC((unsigned int) 0);
+ }
+ return ret;
+}
+
+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;
+ }
+
+ int header = spell->GetHeaderIndexFromLevel(level);
+ EffectQueue *fxqueue = spell->GetEffectBlock(caster, actor->Pos, header, 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;
+ }
+ int header = spell->GetHeaderIndexFromLevel(level);
+ Projectile *pro = spell->GetProjectile(caster, header, pos);
+ pro->SetCaster(caster->GetGlobalID(), level);
+ 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<MapMgr> 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<ActorMgr> 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<SaveGameMgr> 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<WorldMapMgr> 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<ArchiveImporter> 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];
+}
+
+//The maze columns are used only in the maze spell, no need to restrict them further
+int Interface::GetIntelligenceBonus(int column, int value) const
+{
+ //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<<flag)) {
+ displaymsg->DisplayConstantString(STR_AP_UNUSABLE+flag, 0xff0000);
+ gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR);
+ return 1;
+ }
+ return 0;
+}
+
+void Interface::RegisterOpcodes(int count, const EffectDesc *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<SymbolMgr> 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;i<CD[disc_number-1].size();i++) {
+ char name[_MAX_PATH];
+
+ PathJoin(name, CD[disc_number-1][i].c_str(),path,NULL);
+ if (file_exists (name)) {
+ GetGUIScriptEngine()->RunFunction( "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/gemrb/core/Interface.h b/gemrb/core/Interface.h
new file mode 100644
index 0000000..af2c42a
--- /dev/null
+++ b/gemrb/core/Interface.h
@@ -0,0 +1,811 @@
+/* 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 <map>
+#include <string>
+
+#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 EffectDesc;
+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<SymbolMgr> 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;
+};
+
+struct SpellDescType {
+ ieResRef resref;
+ ieStrRef value;
+};
+#define SP_IDENTIFY 1 //any spell that cannot be cast from the menu
+#define SP_SILENCE 2 //any spell that can be cast in silence
+
+struct SurgeSpell {
+ ieResRef spell;
+ ieStrRef message;
+};
+
+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
+#define EF_CREATEMAZE 1024 //call the maze generator
+#define EF_RESETTARGET 2048 //reset the mouse cursor
+#define EF_TARGETMODE 4096 //update the mouse cursor
+
+//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<Video> video;
+ Holder<Audio> AudioDriver;
+ std::string VideoDriverName;
+ std::string AudioDriverName;
+ ProjectileServer * projserv;
+ Image * pal256;
+ Image * pal32;
+ Image * pal16;
+ std::vector<Font*> fonts;
+ EventMgr * evntmgr;
+ Holder<WindowMgr> windowmgr;
+ Window* ModalWindow;
+ char WindowPack[10];
+ Holder<ScriptEngine> guiscript;
+ SaveGameIterator *sgiterator;
+ /** Windows Array */
+ std::vector<Window*> windows;
+ std::vector<int> topwin;
+ Variables * vars;
+ Variables * tokens;
+ Variables * lists;
+ Holder<MusicMgr> music;
+ std::vector<Symbol> symbols;
+ Holder<DataFileMgr> INIparty;
+ Holder<DataFileMgr> INIbeasts;
+ Holder<DataFileMgr> INIquests;
+ Holder<DataFileMgr> INIresdata;
+ Game * game;
+ Calendar * calendar;
+ WorldMapArray* worldmap;
+ ieDword GameFeatures[(GF_COUNT+31)/32];
+ //ieDword GameFeatures; //the first 32 bits
+ //ieDword GameFeatures2;//the second 32 bits
+ ieResRef ButtonFont;
+ ieResRef CursorBam;
+ ieResRef ScrollCursorBam;
+ ieResRef GroundCircleBam[MAX_CIRCLE_SIZE];
+ int GroundCircleScale[MAX_CIRCLE_SIZE];
+ ieDword FullScreen;
+ ieResRef TooltipFont;
+ ieResRef TooltipBackResRef;
+ ieResRef MovieFont;
+ ieResRef *DefSound; //default sounds
+ int DSCount;
+ Color TooltipColor;
+ int TooltipMargin;
+ ieResRef Palette16;
+ ieResRef Palette32;
+ ieResRef Palette256;
+ ieDword* slotmatrix; //itemtype vs slottype
+ SlotType* slottypes;
+ int ItemTypes;
+ int tooltip_x;
+ int tooltip_y;
+ int tooltip_currtextw;
+ // the control owning the tooltip
+ Control* tooltip_ctrl;
+ // Currently dragged item or NULL
+ CREItem* DraggedItem;
+ int DraggedPortrait;
+ // Current Store
+ Store* CurrentStore;
+ // Index of current container
+ Container* CurrentContainer;
+ bool UseContainer;
+ // Scrolling speed
+ int mousescrollspd;
+ bool update_scripts;
+ /** Next Script Name */
+ char NextScript[64];
+ /** Function to call every main loop iteration */
+ EventHandler TickHook;
+ int SpecialSpellsCount;
+ SpellDescType *SpecialSpells;
+public:
+ Holder<StringMgr> strings;
+ GlobalTimer * timer;
+ Palette *InfoTextPalette;
+ int SaveAsOriginal; //if true, saves files in compatible mode
+ int QuitFlag;
+ int EventFlag;
+ Holder<SaveGame> LoadGameIndex;
+ int VersionOverride;
+ unsigned int SlotTypes; //this is the same as the inventory size
+ ieResRef GlobalScript;
+ ieResRef WorldMapName[2];
+ Variables * AreaAliasTable;
+ Variables * ItemExclTable;
+ Variables * ItemDialTable, *ItemDial2Table;
+ Variables * ItemTooltipTable;
+ Sprite2D **Cursors;
+ int CursorCount;
+ //Sprite2D *ArrowSprites[MAX_ORIENT/2];
+ Sprite2D *FogSprites[32];
+ Sprite2D **TooltipBack;
+ Sprite2D *WindowFrames[4];
+ Sprite2D *GroundCircles[MAX_CIRCLE_SIZE][6];
+ std::vector<char *> musiclist;
+ std::multimap<ieDword, DamageInfoStruct> DamageInfoMap;
+ std::vector<ModalStatesStruct> ModalStates;
+ TimeStruct Time;
+ std::vector<SurgeSpell> SurgeSpells;
+public:
+ Interface(int iargc, char *iargv[]);
+ ~Interface(void);
+ int Init(void);
+ //TODO: Core Methods in Interface Class
+ void SetFeature(int value, int position);
+ /* don't rely on the exact return value of this function */
+ ieDword HasFeature(int position) const;
+ bool IsAvailable(SClass_ID filetype) const;
+ const char * TypeExt(SClass_ID type) const;
+ ProjectileServer* GetProjectileServer() const;
+ Video * GetVideoDriver() const;
+ /* create or change a custom string */
+ ieStrRef UpdateString(ieStrRef strref, const char *text) const;
+ /* returns a newly created string */
+ char * GetString(ieStrRef strref, ieDword options = 0) const;
+ /* makes sure the string is freed in TLKImp */
+ void FreeString(char *&str) const;
+ /* sets the floattext color */
+ void SetInfoTextColor(const Color &color);
+ /** returns a gradient set */
+ Color * GetPalette(unsigned index, int colors, Color *buffer) const;
+ /** Returns a preloaded Font */
+ Font * GetFont(const char *) const;
+ Font * GetFont(unsigned int index) const;
+ /** Returns the button font */
+ Font * GetButtonFont() const;
+ /** Returns the Event Manager */
+ EventMgr * GetEventMgr() const;
+ /** Returns the Window Manager */
+ WindowMgr * GetWindowMgr() const;
+ /** Get GUI Script Manager */
+ ScriptEngine * GetGUIScriptEngine() const;
+ /** core for summoning creatures, returns the last created Actor
+ may apply a single fx on the summoned creature normally an unsummon effect */
+ Actor *SummonCreature(const ieResRef resource, const ieResRef vvcres, Scriptable *Owner, Actor *target, const Point &position, int eamod, int level, Effect *fx, bool sexmod=1);
+ /** Loads a WindowPack (CHUI file) in the Window Manager */
+ bool LoadWindowPack(const char *name);
+ /** Loads a Window in the Window Manager */
+ int LoadWindow(unsigned short WindowID);
+ /** Creates a Window in the Window Manager */
+#ifdef WIN32
+#ifdef CreateWindow
+#undef CreateWindow
+#endif
+#endif
+ int CreateWindow(unsigned short WindowID, int XPos, int YPos, unsigned int Width, unsigned int Height, char* Background);
+ /** Sets a Window on the Top */
+ void SetOnTop(int Index);
+ /** Add a window to the Window List */
+ void AddWindow(Window * win);
+ /** Get a Control on a Window */
+ int GetControl(unsigned short WindowIndex, unsigned long ControlID) const;
+ /** Adjust the scrolling of the control (if applicable) */
+ int AdjustScrolling(unsigned short WindowIndex, unsigned short ControlIndex, short x, short y);
+ /** Set the Text of a Control */
+ int SetText(unsigned short WindowIndex, unsigned short ControlIndex, const char * string);
+ /** Set the Tooltip text of a Control */
+ int SetTooltip(unsigned short WindowIndex, unsigned short ControlIndex, const char * string);
+ /** sets tooltip to be displayed */
+ void DisplayTooltip(int x, int y, Control* ctrl);
+ /** Actually draws tooltip on the screen. Called from SDLVideoDriver */
+ void DrawTooltip();
+ /** returns the label which should receive game messages (overrides messagetextarea) */
+ Label *GetMessageLabel() const;
+ /** returns the textarea of the main game screen */
+ TextArea *GetMessageTextArea() const;
+ /** returns the Window Visible Flag */
+ int GetVisible(unsigned short WindowIndex) const;
+ /** Set a Window Visible Flag */
+ int SetVisible(unsigned short WindowIndex, int visible);
+ /** Show a Window in Modal Mode */
+ int ShowModal(unsigned short WindowIndex, int Shadow);
+ /** Set the Status of a Control in a Window */
+ int SetControlStatus(unsigned short WindowIndex, unsigned short ControlIndex, unsigned long Status);
+ /** Get a Window from the Loaded Window List */
+ Window * GetWindow(unsigned short WindowIndex) const;
+ /** Returns true if wnd is a valid window with WindowIndex */
+ bool IsValidWindow(unsigned short WindowID, Window *wnd) const;
+ /** Removes a Loaded Window */
+ int DelWindow(unsigned short WindowIndex);
+ /** Removes all Loaded Windows */
+ void DelAllWindows();
+ /** Redraws all window */
+ void RedrawAll();
+ /** Refreshes any control associated with the variable name with value*/
+ void RedrawControls(const char *varname, unsigned int value);
+ /** Popup the Console */
+ void PopupConsole();
+ /** Draws the Console */
+ void DrawConsole();
+ /** Get the SaveGameIterator */
+ SaveGameIterator * GetSaveGameIterator() const;
+ /** Get the Variables Dictionary */
+ Variables * GetDictionary() const;
+ /** Get the Token Dictionary */
+ Variables * GetTokenDictionary() const;
+ /** Get the Music Manager */
+ MusicMgr * GetMusicMgr() const;
+ /** Loads an IDS Table, returns -1 on error or the Symbol Table Index on success */
+ int LoadSymbol(const char * ResRef);
+ /** Gets the index of a loaded Symbol Table, returns -1 on error */
+ int GetSymbolIndex(const char * ResRef) const;
+ /** Gets a Loaded Symbol Table by its index, returns NULL on error */
+ Holder<SymbolMgr> GetSymbol(unsigned int index) const;
+ /** Frees a Loaded Symbol Table, returns false on error, true on success */
+ bool DelSymbol(unsigned int index);
+ /** Plays a Movie */
+ int PlayMovie(const char * ResRef);
+ /** Generates traditional random number xdy+z */
+ int Roll(int dice, int size, int add) const;
+ /** Loads a Game Compiled Script */
+ int LoadScript(const char * ResRef);
+ /** Enables/Disables the CutScene Mode */
+ void SetCutSceneMode(bool active);
+ /** returns true if in cutscene mode */
+ bool InCutSceneMode() const;
+ /** Updates the Game Script Engine State */
+ void GSUpdate(bool update_scripts)
+ {
+ if(update_scripts) {
+ timer->Update();
+ }
+ else {
+ timer->Freeze();
+ }
+ }
+ /** Get the Party INI Interpreter */
+ DataFileMgr * GetPartyINI() const
+ {
+ return INIparty.get();
+ }
+ DataFileMgr * GetBeastsINI() const
+ {
+ return INIbeasts.get();
+ }
+ DataFileMgr * GetQuestsINI() const
+ {
+ return INIquests.get();
+ }
+ DataFileMgr * GetResDataINI() const
+ {
+ return INIresdata.get();
+ }
+ /** Gets the Game class */
+ Game * GetGame() const
+ {
+ return game;
+ }
+ /** Gets the Calendar class */
+ Calendar * GetCalendar() const
+ {
+ return calendar;
+ }
+
+ /** Gets the WorldMap class, returns the current worldmap or the first worldmap containing the area*/
+ WorldMap * GetWorldMap(const char *area = NULL);
+ void SetWindowFrame(int i, Sprite2D *Picture);
+ /** hides the game control window (if it exists), and reports success */
+ bool HideGCWindow();
+ /** unhides the game control window, if it exists */
+ void UnhideGCWindow();
+ GameControl *GetGameControl() const;
+ /** if backtomain is not null then goes back to main screen */
+ void QuitGame(int backtomain);
+ /** sets up load game */
+ void SetupLoadGame(Holder<SaveGame> save, int ver_override);
+ /** load saved game by index (-1 is default), ver_override is an optional parameter
+ to override the saved game's version */
+ void LoadGame(SaveGame *save, int ver_override);
+ /** fix changes in global script/worldmap*/
+ void UpdateMasterScript();
+ /*reads the filenames of the portraits folder into a list */
+ int GetPortraits(TextArea* ta, bool smallorlarge);
+ /*reads the filenames of the sounds folder into a list */
+ int GetCharSounds(TextArea *ta);
+ /*reads the filenames of the characters folder into a list */
+ int GetCharacters(TextArea *ta);
+ unsigned int GetInventorySize() const { return SlotTypes-1; }
+ ieDword FindSlot(unsigned int idx) const;
+ ieDword QuerySlot(unsigned int idx) const;
+ ieDword QuerySlotType(unsigned int idx) const;
+ ieDword QuerySlottip(unsigned int idx) const;
+ ieDword QuerySlotID(unsigned int idx) const;
+ ieDword QuerySlotFlags(unsigned int idx) const;
+ ieDword QuerySlotEffects(unsigned int idx) const;
+ const char * QuerySlotResRef(unsigned int idx) const;
+ /*returns true if an itemtype is acceptable for a slottype, also checks the usability flags */
+ int CanUseItemType(int slottype, Item *item, Actor *actor=NULL, bool feedback=false, bool equipped=false) const;
+ /*removes single file from cache*/
+ void RemoveFromCache(const ieResRef resref, SClass_ID SClassID);
+ /*removes all files from directory*/
+ void DelTree(const char *path, bool onlysaved);
+ /*returns 0,1,2 based on how the file should be saved */
+ int SavedExtension(const char *filename);
+ /*returns true if the file should never be deleted accidentally */
+ bool ProtectedExtension(const char *filename);
+ /*returns true if the directory path isn't good as a Cache */
+ bool StupidityDetector(const char* Pt);
+ /*handles the load screen*/
+ void LoadProgress(int percent);
+
+ Palette* CreatePalette(const Color &color, const Color &back);
+
+ void DragItem(CREItem* item, const ieResRef Picture);
+ CREItem* GetDraggedItem() const { return DraggedItem; }
+ /* use this only when the dragged item is dropped */
+ void ReleaseDraggedItem();
+ int GetDraggedPortrait() const { return DraggedPortrait; }
+ void SetDraggedPortrait(int dp, int cursor=-1);
+ CREItem *ReadItem(DataStream *str);
+ CREItem *ReadItem(DataStream *str, CREItem *itm);
+ bool ResolveRandomItem(CREItem *itm);
+ ieStrRef GetRumour(const ieResRef resname);
+ Container *GetCurrentContainer();
+ int CloseCurrentContainer();
+ void SetCurrentContainer(Actor *actor, Container *arg, bool flag=false);
+ Store *GetCurrentStore();
+ int CloseCurrentStore();
+ Store *SetCurrentStore(const ieResRef resname, ieDword owner);
+ void SetMouseScrollSpeed(int speed);
+ int GetMouseScrollSpeed();
+ // FIXME: due to Win32 we have to allocate/release all common
+ // memory from Interface. Yes, it is ugly.
+ ITMExtHeader *GetITMExt(int count);
+ SPLExtHeader *GetSPLExt(int count);
+ //creates a standalone effect with opcode
+ Effect *GetEffect(ieDword opcode);
+ Effect *GetFeatures(int count);
+ void FreeITMExt(ITMExtHeader *p, Effect *e);
+ void FreeSPLExt(SPLExtHeader *p, Effect *e);
+ WorldMapArray *NewWorldMapArray(int count);
+ void DoTheStoreHack(Store *s);
+ /** plays stock gui sound referenced by index */
+ void PlaySound(int idx);
+ /** returns the first selected PC, if forced is set, then it returns
+ first PC if none was selected */
+ Actor *GetFirstSelectedPC(bool forced);
+ Actor *GetFirstSelectedActor();
+ /** returns a cursor sprite (not cached) */
+ Sprite2D *GetCursorSprite();
+ /** returns a scroll cursor sprite */
+ Sprite2D *GetScrollCursorSprite(int frameNum, int spriteNum);
+ /** returns 0 for unmovable, -1 for movable items, otherwise it
+ returns gold value! */
+ int CanMoveItem(const CREItem *item) const;
+ int GetMaximumAbility() const;
+ int GetStrengthBonus(int column, int value, int ex) const;
+ int GetIntelligenceBonus(int column, int value) const;
+ int GetDexterityBonus(int column, int value) const;
+ int GetConstitutionBonus(int column, int value) const;
+ int GetCharismaBonus(int column, int value) const;
+ int GetLoreBonus(int column, int value) const;
+ int GetWisdomBonus(int column, int value) const;
+ int GetReputationMod(int column) const;
+
+ /** applies the spell on the target */
+ void ApplySpell(const ieResRef resname, Actor *target, Scriptable *caster, int level);
+ /** applies the spell on the area or on a scriptable object */
+ void ApplySpellPoint(const ieResRef resname, Map *area, const Point &pos, Scriptable *caster, int level);
+ /** applies a single effect on the target */
+ int ApplyEffect(Effect *fx, Actor *target, Scriptable *caster);
+ /** applies an effect queue on the target */
+ int ApplyEffectQueue(EffectQueue *fxqueue, Actor *actor, Scriptable *caster);
+ int ApplyEffectQueue(EffectQueue *fxqueue, Actor *actor, Scriptable *caster, Point p);
+ Effect *GetEffect(const ieResRef resname, int level, const Point &p);
+ /** dumps an area object to the cache */
+ int SwapoutArea(Map *map);
+ /** saves (exports a character to the characters folder */
+ int WriteCharacter(const char *name, Actor *actor);
+ /** saves the game object to the destination folder */
+ int WriteGame(const char *folder);
+ /** saves the worldmap object to the destination folder */
+ int WriteWorldMap(const char *folder);
+ /** saves the .are and .sto files to the destination folder */
+ int CompressSave(const char *folder);
+ /** receives an autopause reason, returns 1 if pause was triggered by this call, -1 if it was already triggered */
+ int Autopause(ieDword reason);
+ /** registers engine opcodes */
+ void RegisterOpcodes(int count, const EffectDesc *opcodes);
+ /** reads a list of resrefs into an array, returns array size */
+ int ReadResRefTable(const ieResRef tablename, ieResRef *&data);
+ /** frees the data */
+ void FreeResRefTable(ieResRef *&table, int &count);
+ /** Returns the item tooltip value for the xth extension header */
+ int GetItemTooltip(const ieResRef itemname, int idx, int identified);
+ /** Returns the item exclusion value */
+ int GetItemExcl(const ieResRef itemname) const;
+ /** Returns the strref for the item dialog */
+ int GetItemDialStr(const ieResRef itemname) const;
+ /** Returns the strref for the item dialog */
+ int GetItemDialRes(const ieResRef itemname, ieResRef dialog) const;
+ /** Returns the virtual worldmap entry of a sub-area */
+ int GetAreaAlias(const ieResRef areaname) const;
+ /** Returns up to 3 resources from resref, choosing rows randomly
+ unwanted return variables could be omitted */
+ void GetResRefFrom2DA(const ieResRef resref, ieResRef resource1, ieResRef resource2 = NULL, ieResRef resource3 = NULL);
+ /** returns a numeric list read from a 2da. The 0th element is the number of elements in the list */
+ ieDword *GetListFrom2DA(const ieResRef resref);
+ /** translates a stat symbol to numeric value */
+ ieDword TranslateStat(const char *stat_name);
+ /** Opens CD prompt window and waits for the specified disc */
+ void WaitForDisc(int disc_number, const char* path);
+ /** Returns the music playlist corresponding to the provided type */
+ /** it allows scrapping the entry, hence it isn't const */
+ char *GetMusicPlaylist(int SongType) const;
+ /** Removes the extraneus EOL newline and carriage return */
+ void StripLine(char * string, size_t size);
+ /** Returns the DeathVarFormat of the day */
+ static const char *GetDeathVarFormat();
+ int CheckSpecialSpell(ieResRef resref, Actor *actor);
+ int GetSpecialSpell(ieResRef resref);
+ int GetSpecialSpellsCount() { return SpecialSpellsCount; }
+ SpellDescType *GetSpecialSpells() { return SpecialSpells; }
+private:
+ int LoadSprites();
+ bool LoadConfig(void);
+ bool LoadConfig(const char *filename);
+ bool LoadGemRBINI();
+ bool LoadINI(const char * filename);
+ bool InitItemTypes();
+ bool ReadRandomItems();
+ bool ReadItemTable(const ieResRef item, const char *Prefix);
+ bool ReadAbilityTables();
+ bool ReadAbilityTable(const ieResRef name, ieWordSigned *mem, int cols, int rows);
+ bool ReadMusicTable(const ieResRef name, int col);
+ bool ReadDamageTypeTable();
+ bool ReadReputationModTable();
+ bool ReadGameTimeTable();
+ bool ReadSpecialSpells();
+ bool ReadModalStates();
+ /** Reads table of area name mappings for WorldMap (PST only) */
+ bool ReadAreaAliasTable(const ieResRef name);
+ /** Reads itemexcl, itemdial, tooltip */
+ bool ReadAuxItemTables();
+ /** handles the QuitFlag bits (main loop events) */
+ void HandleFlags();
+ /** handles the EventFlag bits (conditional events) */
+ void HandleEvents();
+ /** handles hardcoded gui behaviour */
+ void HandleGUIBehaviour();
+ /** Creates a game control, closes all other windows */
+ GameControl* StartGameControl();
+ /** Executes everything (non graphical) in the main game loop */
+ void GameLoop(void);
+ /** the internal (without cache) part of GetListFrom2DA */
+ ieDword *GetListFrom2DAInternal(const ieResRef resref);
+public:
+ char GameDataPath[_MAX_PATH];
+ char GameOverridePath[_MAX_PATH];
+ char GameSoundsPath[_MAX_PATH];
+ char GameScriptsPath[_MAX_PATH];
+ char GamePortraitsPath[_MAX_PATH];
+ char GameCharactersPath[_MAX_PATH];
+ char GemRBOverridePath[_MAX_PATH];
+ ieResRef GameNameResRef;
+ ieResRef GoldResRef; //MISC07.itm
+ Variables *RtRows;
+ char UserDir[_MAX_PATH];
+ int argc;
+ char **argv;
+ char GameName[_MAX_PATH];
+ char GameType[_MAX_PATH];
+ char GemRBPath[_MAX_PATH];
+ char PluginsPath[_MAX_PATH];
+ char CachePath[_MAX_PATH];
+ char GUIScriptsPath[_MAX_PATH];
+ char SavePath[_MAX_PATH];
+ char INIConfig[_MAX_PATH];
+ char GamePath[_MAX_PATH];
+ std::vector<std::string> CD[MAX_CD];
+ std::vector<std::string> ModPath;
+ int Width, Height, Bpp, ForceStereo;
+ unsigned int TooltipDelay;
+ int IgnoreOriginalINI;
+ unsigned int FogOfWar;
+ bool CaseSensitive, GameOnCD, SkipIntroVideos, DrawFPS;
+ bool GUIEnhancements;
+ bool KeepCache;
+ Variables *plugin_flags;
+ /** The Main program loop */
+ void Main(void);
+ /** returns true if the game is paused */
+ bool IsFreezed();
+ /** Draws the Visible windows in the Windows Array */
+ void DrawWindows(void);
+ /** Sends a termination signal to the Video Driver */
+ bool Quit(void);
+ /** CheatKey support */
+ inline void EnableCheatKeys(int Flag)
+ {
+ CheatFlag=(Flag > 0);
+ }
+
+ inline bool CheatEnabled()
+ {
+ return CheatFlag;
+ }
+
+ inline void SetEventFlag(int Flag)
+ {
+ EventFlag|=Flag;
+ }
+ inline void ResetEventFlag(int Flag)
+ {
+ EventFlag&=~Flag;
+ }
+
+ static void SanityCheck(const char *ver);
+
+ /** Set Next Script */
+ void SetNextScript(const char *script);
+ /** Console is on Screen */
+ bool ConsolePopped;
+ /** Cheats enabled? */
+ bool CheatFlag;
+ /** The Console Object */
+ Console * console;
+
+ Audio* GetAudioDrv(void) const;
+
+ void SetTickHook(EventHandler);
+
+#ifdef _DEBUG
+ int FileStreamPtrCount;
+ int CachedFileStreamPtrCount;
+#endif
+};
+
+extern GEM_EXPORT Interface * core;
+
+#endif
diff --git a/gemrb/core/Inventory.cpp b/gemrb/core/Inventory.cpp
new file mode 100644
index 0000000..0879437
--- /dev/null
+++ b/gemrb/core/Inventory.cpp
@@ -0,0 +1,1856 @@
+/* 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 the inventory of stores (.sto), area containers (.are)
+//or actors (.cre).
+
+#include "Inventory.h"
+
+#include "win32def.h"
+#include "strrefs.h"
+
+#include "DisplayMessage.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "Item.h"
+#include "ScriptEngine.h"
+#include "Scriptable/Actor.h"
+
+#include <cstdio>
+
+static int SLOT_HEAD = -1;
+static int SLOT_MAGIC = -1;
+static int SLOT_FIST = -1;
+static int SLOT_MELEE = -1;
+static int LAST_MELEE = -1;
+static int SLOT_RANGED = -1;
+static int LAST_RANGED = -1;
+static int SLOT_QUICK = -1;
+static int LAST_QUICK = -1;
+static int SLOT_INV = -1;
+static int LAST_INV = -1;
+static int SLOT_LEFT = -1;
+
+//IWD2 style slots
+static bool IWD2 = false;
+static int MagicBit = 0;
+
+static void InvalidSlot(int slot)
+{
+ printMessage("Inventory"," ",LIGHT_RED);
+ printf("Invalid slot: %d!\n",slot);
+ abort();
+}
+
+//This inline function returns both an item pointer and the slot data.
+//slot is a dynamic slot number (SLOT_*)
+inline Item *Inventory::GetItemPointer(ieDword slot, CREItem *&item) const
+{
+ item = GetSlotItem(slot);
+ if (!item) return NULL;
+ if (!item->ItemResRef[0]) return NULL;
+ return gamedata->GetItem(item->ItemResRef);
+}
+
+void Inventory::Init(int mb)
+{
+ SLOT_MAGIC=-1;
+ SLOT_FIST=-1;
+ SLOT_MELEE=-1;
+ LAST_MELEE=-1;
+ SLOT_RANGED=-1;
+ LAST_RANGED=-1;
+ SLOT_QUICK=-1;
+ LAST_QUICK=-1;
+ SLOT_LEFT=-1;
+ //TODO: set this correctly
+ IWD2 = false;
+ MagicBit = mb;
+}
+
+Inventory::Inventory()
+{
+ Owner = NULL;
+ InventoryType = INVENTORY_HEAP;
+ Changed = false;
+ Weight = 0;
+ Equipped = IW_NO_EQUIPPED;
+ EquippedHeader = 0;
+ ItemExcl = 0;
+ memset(ItemTypes, 0, sizeof(ItemTypes));
+}
+
+Inventory::~Inventory()
+{
+ for (size_t i = 0; i < Slots.size(); i++) {
+ if (Slots[i]) {
+ delete( Slots[i] );
+ Slots[i] = NULL;
+ }
+ }
+}
+
+// duplicates the source inventory into the current one
+// also changes the items to not drop, so simulacrum and similar don't become factories
+void Inventory::CopyFrom(const Actor *source)
+{
+ if (!source) {
+ return;
+ }
+
+ SetSlotCount(source->inventory.GetSlotCount());
+
+ // allocate the items and mark them undroppable
+ CREItem *tmp, *item;
+ for (size_t i = 0; i < source->inventory.Slots.size(); i++) {
+ item = source->inventory.Slots[i];
+ if (item) {
+ tmp = new CREItem();
+ memcpy(tmp, item, sizeof(CREItem));
+ tmp->Flags |= IE_INV_ITEM_UNDROPPABLE;
+ int ret = AddSlotItem(tmp, i);
+ if (ret != ASI_SUCCESS) {
+ delete tmp;
+ }
+ }
+ }
+
+ // preserve the equipped status
+ Equipped = source->inventory.GetEquipped();
+ EquippedHeader = source->inventory.GetEquippedHeader();
+
+ Changed = true;
+ CalculateWeight();
+}
+
+CREItem *Inventory::GetItem(unsigned int slot)
+{
+ if (slot >= Slots.size() ) {
+ InvalidSlot(slot);
+ return NULL;
+ }
+ CREItem *item = Slots[slot];
+ Slots.erase(Slots.begin()+slot);
+ return item;
+}
+
+//This hack sets the charge counters for non-rechargeable items,
+//if their charge is zero
+inline void HackCharges(CREItem *item)
+{
+ Item *itm = gamedata->GetItem( item->ItemResRef );
+ if (itm) {
+ for (int i=0;i<3;i++) {
+ if (item->Usages[i]) {
+ continue;
+ }
+ ITMExtHeader *h = itm->GetExtHeader(i);
+ if (h && !(h->RechargeFlags&IE_ITEM_RECHARGE)) {
+ //HACK: the original (bg2) allows for 0 charged gems
+ if (h->Charges) {
+ item->Usages[i] = h->Charges;
+ } else {
+ item->Usages[i] = 1;
+ }
+ }
+ }
+ gamedata->FreeItem( itm, item->ItemResRef, false );
+ }
+}
+
+void Inventory::AddItem(CREItem *item)
+{
+ if (!item) return; //invalid items get no slot
+ Slots.push_back(item);
+ HackCharges(item);
+ //this will update the flags (needed for unmovable items in containers)
+ //but those *can* be picked up (like the bg2 portal key), so we skip it
+ //Changed=true;
+}
+
+void Inventory::CalculateWeight()
+{
+ if (!Changed) {
+ return;
+ }
+ Weight = 0;
+ for (size_t i = 0; i < Slots.size(); i++) {
+ CREItem *slot = Slots[i];
+ if (!slot) {
+ continue;
+ }
+ if (slot->Weight == -1) {
+ Item *itm = gamedata->GetItem( slot->ItemResRef );
+ if (itm) {
+ //simply adding the item flags to the slot
+ slot->Flags |= (itm->Flags<<8);
+ //some slot flags might be affected by the item flags
+ if (!(slot->Flags & IE_INV_ITEM_CRITICAL)) {
+ slot->Flags |= IE_INV_ITEM_DESTRUCTIBLE;
+ }
+ //this is for converting IWD items magic flag
+ if (MagicBit) {
+ if (slot->Flags&IE_INV_ITEM_UNDROPPABLE) {
+ slot->Flags|=IE_INV_ITEM_MAGICAL;
+ slot->Flags&=~IE_INV_ITEM_UNDROPPABLE;
+ }
+ }
+
+ if (!(slot->Flags & IE_INV_ITEM_MOVABLE)) {
+ slot->Flags |= IE_INV_ITEM_UNDROPPABLE;
+ }
+
+ if (slot->Flags & IE_INV_ITEM_STOLEN2) {
+ slot->Flags |= IE_INV_ITEM_STOLEN;
+ }
+
+ //auto identify basic items
+ if (!itm->LoreToID) {
+ slot->Flags |= IE_INV_ITEM_IDENTIFIED;
+ }
+
+ //if item is stacked mark it as so
+ if (itm->StackAmount) {
+ slot->Flags |= IE_INV_ITEM_STACKED;
+ }
+
+ slot->Weight = itm->Weight;
+ slot->StackAmount = itm->StackAmount;
+ gamedata->FreeItem( itm, slot->ItemResRef, false );
+ }
+ else {
+ printMessage( "Inventory", " ", LIGHT_RED);
+ printf("Invalid item: %s!\n", slot->ItemResRef);
+ slot->Weight = 0;
+ }
+ } else {
+ slot->Flags &= ~IE_INV_ITEM_ACQUIRED;
+ }
+ if (slot->Weight > 0) {
+ Weight += slot->Weight * ((slot->Usages[0] && slot->StackAmount > 1) ? slot->Usages[0] : 1);
+ }
+ }
+ Changed = false;
+}
+
+void Inventory::AddSlotEffects(ieDword index)
+{
+ CREItem* slot;
+
+ const Item *itm = GetItemPointer(index, slot);
+ if (!itm) {
+ printMessage("Inventory","Invalid item equipped...\n",LIGHT_RED);
+ return;
+ }
+ ItemExcl|=itm->ItemExcl;
+ ieDword pos = itm->ItemType/32;
+ ieDword bit = itm->ItemType%32;
+ if (pos<4) {
+ ItemTypes[pos]|=1<<bit;
+ }
+
+ ieWord gradient = itm->GetWieldedGradient();
+ if (gradient!=0xffff) {
+ Owner->SetBase(IE_COLORS, gradient);
+ }
+
+ //get the equipping effects
+ EffectQueue *eqfx = itm->GetEffectBlock(Owner, Owner->Pos, -1, index, 0);
+ gamedata->FreeItem( itm, slot->ItemResRef, false );
+
+ Owner->RefreshEffects(eqfx);
+ //call gui for possible paperdoll animation changes
+ if (Owner->InParty) {
+ core->SetEventFlag(EF_UPDATEANIM);
+ }
+}
+
+//no need to know the item effects 'personally', the equipping slot
+//is stored in them
+void Inventory::RemoveSlotEffects(ieDword index)
+{
+ Owner->fxqueue.RemoveEquippingEffects(index);
+ Owner->RefreshEffects(NULL);
+ //call gui for possible paperdoll animation changes
+ if (Owner->InParty) {
+ core->SetEventFlag(EF_UPDATEANIM);
+ }
+}
+
+void Inventory::SetInventoryType(int arg)
+{
+ InventoryType = arg;
+}
+
+void Inventory::SetSlotCount(unsigned int size)
+{
+ if (Slots.size()) {
+ printf("Inventory size changed???\n");
+ //we don't allow reassignment,
+ //if you want this, delete the previous Slots here
+ abort();
+ }
+ Slots.assign((size_t) size, NULL);
+}
+
+/** if you supply a "" string, then it checks if the slot is empty */
+bool Inventory::HasItemInSlot(const char *resref, unsigned int slot) const
+{
+ if (slot >= Slots.size()) {
+ return false;
+ }
+ const CREItem *item = Slots[slot];
+ if (!item) {
+ return false;
+ }
+ if (!resref[0]) {
+ return true;
+ }
+ if (strnicmp( item->ItemResRef, resref, 8 )==0) {
+ return true;
+ }
+ return false;
+}
+
+bool Inventory::HasItemType(ieDword type) const
+{
+ if (type>255) return false;
+ int idx = type/32;
+ int bit = type%32;
+ return (ItemTypes[idx] & (1<<bit) )!=0;
+}
+
+/** counts the items in the inventory, if stacks == 1 then stacks are
+ accounted for their heap size */
+int Inventory::CountItems(const char *resref, bool stacks) const
+{
+ int count = 0;
+ size_t slot = Slots.size();
+ while(slot--) {
+ const CREItem *item = Slots[slot];
+ if (!item) {
+ continue;
+ }
+ if (resref && resref[0]) {
+ if (!strnicmp(resref, item->ItemResRef, 8) )
+ continue;
+ }
+ if (stacks && (item->Flags&IE_INV_ITEM_STACKED) ) {
+ count+=item->Usages[0];
+ }
+ else {
+ count++;
+ }
+ }
+ return count;
+}
+
+/** this function can look for stolen, equipped, identified, destructible
+ etc, items. You just have to specify the flags in the bitmask
+ specifying 1 in a bit signifies a requirement */
+bool Inventory::HasItem(const char *resref, ieDword flags) const
+{
+ size_t slot = Slots.size();
+ while(slot--) {
+ const CREItem *item = Slots[slot];
+ if (!item) {
+ continue;
+ }
+ if ( (flags&item->Flags)!=flags) {
+ continue;
+ }
+ if (resref[0] && strnicmp(item->ItemResRef, resref,8) ) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+}
+
+void Inventory::KillSlot(unsigned int index)
+{
+ if (InventoryType==INVENTORY_HEAP) {
+ Slots.erase(Slots.begin()+index);
+ return;
+ }
+ CREItem *item = Slots[index];
+ if (!item) {
+ return;
+ }
+
+ //the used up item vanishes from the quickslot bar
+ if (Owner->IsSelected()) {
+ core->SetEventFlag( EF_ACTION );
+ }
+
+ Slots[index] = NULL;
+ int effect = core->QuerySlotEffects( index );
+ if (!effect) {
+ return;
+ }
+ RemoveSlotEffects( index );
+ Item *itm = gamedata->GetItem(item->ItemResRef);
+ //this cannot happen, but stuff happens!
+ if (!itm) {
+ return;
+ }
+ ItemExcl &= ~itm->ItemExcl;
+
+ switch (effect) {
+ case SLOT_EFFECT_LEFT:
+ UpdateShieldAnimation(0);
+ break;
+ case SLOT_EFFECT_MISSILE:
+ //getting a new projectile of the same type
+ if (Equipped + SLOT_MELEE == (int) index) {
+ if (Equipped < 0) {
+ //always get the projectile weapon header (this quiver was equipped)
+ ITMExtHeader *header = itm->GetWeaponHeader(true);
+ Equipped = FindRangedProjectile(header->ProjectileQualifier);
+ if (Equipped!=IW_NO_EQUIPPED) {
+ EquipItem(Equipped+SLOT_MELEE);
+ } else {
+ EquipItem(SLOT_FIST);
+ }
+ }
+ }
+ UpdateWeaponAnimation();
+ break;
+ case SLOT_EFFECT_MELEE:
+ // reset Equipped if it was the removed item
+ if (Equipped+SLOT_MELEE == (int)index)
+ Equipped = IW_NO_EQUIPPED;
+ else if (Equipped < 0) {
+ //always get the projectile weapon header (this is a bow, because Equipped is negative)
+ ITMExtHeader *header = itm->GetWeaponHeader(true);
+ if (header) {
+ //find the equipped type
+ int type = header->ProjectileQualifier;
+ int weaponslot = FindTypedRangedWeapon(type);
+ CREItem *item2 = Slots[weaponslot];
+ if (item2) {
+ Item *itm2 = gamedata->GetItem(item2->ItemResRef);
+ if (itm2) {
+ if (type == header->ProjectileQualifier) {
+ Equipped = FindRangedProjectile(header->ProjectileQualifier);
+ if (Equipped!=IW_NO_EQUIPPED) {
+ EquipItem(Equipped+SLOT_MELEE);
+ } else {
+ EquipItem(SLOT_FIST);
+ }
+ }
+ gamedata->FreeItem(itm2, item2->ItemResRef, false);
+ }
+ }
+ }
+ }
+ // reset Equipped if it is a ranged weapon slot
+ // but not magic weapon slot!
+
+ UpdateWeaponAnimation();
+ break;
+ case SLOT_EFFECT_HEAD:
+ Owner->SetUsedHelmet("");
+ break;
+ case SLOT_EFFECT_ITEM:
+ //remove the armor type only if this item is responsible for it
+ if ((ieDword) (itm->AnimationType[0]-'1') == Owner->GetBase(IE_ARMOR_TYPE)) {
+ Owner->SetBase(IE_ARMOR_TYPE, 0);
+ }
+ break;
+ }
+ gamedata->FreeItem(itm, item->ItemResRef, false);
+}
+/** if resref is "", then destroy ALL items
+this function can look for stolen, equipped, identified, destructible
+etc, items. You just have to specify the flags in the bitmask
+specifying 1 in a bit signifies a requirement */
+unsigned int Inventory::DestroyItem(const char *resref, ieDword flags, ieDword count)
+{
+ unsigned int destructed = 0;
+ size_t slot = Slots.size();
+
+ while(slot--) {
+ //ignore the fist slot
+ if (slot == (unsigned int)SLOT_FIST) {
+ continue;
+ }
+
+ CREItem *item = Slots[slot];
+ if (!item) {
+ continue;
+ }
+ // here you can simply destroy all items of a specific type
+ if ( (flags&item->Flags)!=flags) {
+ continue;
+ }
+ if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
+ continue;
+ }
+ //we need to acknowledge that the item was destroyed
+ //use unequip stuff, decrease encumbrance etc,
+ //until that, we simply erase it
+ ieDword removed;
+
+ if (item->Flags&IE_INV_ITEM_STACKED) {
+ removed=item->Usages[0];
+ if (count && (removed + destructed > count) ) {
+ removed = count - destructed;
+ item = RemoveItem( (unsigned int) slot, removed );
+ }
+ else {
+ KillSlot( (unsigned int) slot);
+ }
+ } else {
+ removed=1;
+ KillSlot( (unsigned int) slot);
+ }
+ delete item;
+ Changed = true;
+ destructed+=removed;
+ if (count && (destructed>=count) )
+ break;
+ }
+ if (Changed && Owner && Owner->InParty) displaymsg->DisplayConstantString(STR_LOSTITEM, 0xbcefbc);
+
+ return destructed;
+}
+
+CREItem *Inventory::RemoveItem(unsigned int slot, unsigned int count)
+{
+ CREItem *item;
+
+ if (slot >= Slots.size() ) {
+ InvalidSlot(slot);
+ return NULL;
+ }
+ Changed = true;
+ item = Slots[slot];
+
+ if (!item) {
+ return NULL;
+ }
+
+ if (!count || !(item->Flags&IE_INV_ITEM_STACKED) ) {
+ KillSlot(slot);
+ return item;
+ }
+ if (count >= item->Usages[0]) {
+ KillSlot(slot);
+ return item;
+ }
+
+ CREItem *returned = new CREItem(*item);
+ item->Usages[0]-=count;
+ returned->Usages[0]=(ieWord) count;
+ return returned;
+}
+
+//flags set disable item transfer
+//except for undroppable and equipped, which are opposite (and shouldn't be set)
+int Inventory::RemoveItem(const char *resref, unsigned int flags, CREItem **res_item)
+{
+ size_t slot = Slots.size();
+ unsigned int mask = (flags^(IE_INV_ITEM_UNDROPPABLE|IE_INV_ITEM_EQUIPPED));
+ if (core->HasFeature(GF_NO_DROP_CAN_MOVE) ) {
+ mask &= ~IE_INV_ITEM_UNDROPPABLE;
+ }
+ while(slot--) {
+ CREItem *item = Slots[slot];
+ if (!item) {
+ continue;
+ }
+
+ if (flags && (mask&item->Flags)==flags) {
+ continue;
+ }
+ if (!flags && (mask&item->Flags)!=0) {
+ continue;
+ }
+ if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
+ continue;
+ }
+ *res_item=RemoveItem( (unsigned int) slot, 0);
+ return (int) slot;
+ }
+ *res_item = NULL;
+ return -1;
+}
+
+void Inventory::SetSlotItem(CREItem* item, unsigned int slot)
+{
+ if (slot >= Slots.size() ) {
+ InvalidSlot(slot);
+ return;
+ }
+ Changed = true;
+ if (Slots[slot]) {
+ delete Slots[slot];
+ }
+
+ HackCharges(item);
+
+ Slots[slot] = item;
+
+ //update the action bar next time
+ if (Owner->IsSelected()) {
+ core->SetEventFlag( EF_ACTION );
+ }
+}
+
+int Inventory::AddSlotItem(CREItem* item, int slot, int slottype)
+{
+ int twohanded = item->Flags&IE_INV_ITEM_TWOHANDED;
+ if (slot >= 0) {
+ if ((unsigned)slot >= Slots.size()) {
+ InvalidSlot(slot);
+ return ASI_FAILED;
+ }
+
+ //check for equipping weapons
+ if (WhyCantEquip(slot,twohanded)) {
+ return ASI_FAILED;
+ }
+
+ if (!Slots[slot]) {
+ item->Flags |= IE_INV_ITEM_ACQUIRED;
+ SetSlotItem(item, slot);
+ EquipItem(slot);
+ return ASI_SUCCESS;
+ }
+
+ CREItem *myslot = Slots[slot];
+ if (ItemsAreCompatible( myslot, item )) {
+ //calculate with the max movable stock
+ int chunk = item->Usages[0];
+ int newamount = myslot->Usages[0]+chunk;
+ if (newamount>myslot->StackAmount) {
+ newamount=myslot->StackAmount;
+ chunk = item->Usages[0]-newamount;
+ }
+ if (!chunk) {
+ return -1;
+ }
+ myslot->Flags |= IE_INV_ITEM_ACQUIRED;
+ myslot->Usages[0] = (ieWord) (myslot->Usages[0] + chunk);
+ item->Usages[0] = (ieWord) (item->Usages[0] - chunk);
+ Changed = true;
+ EquipItem(slot);
+ if (item->Usages[0] == 0) {
+ delete item;
+ return ASI_SUCCESS;
+ }
+ return ASI_PARTIAL;
+ }
+ return ASI_FAILED;
+ }
+
+ bool which;
+ if (slot==SLOT_AUTOEQUIP) {
+ which=true;
+ } else {
+ which=false;
+ }
+ int res = ASI_FAILED;
+ int max = (int) Slots.size();
+ for (int i = 0;i<max;i++) {
+ //never autoequip in the magic slot!
+ if (i==SLOT_MAGIC)
+ continue;
+ if ((i<SLOT_INV || i>LAST_INV)!=which)
+ continue;
+ if (!(core->QuerySlotType(i)&slottype))
+ continue;
+ //the slot has been disabled for this actor
+ if (i>=SLOT_MELEE && i<=LAST_MELEE) {
+ if (Owner->GetQuickSlot(i-SLOT_MELEE)==0xffff) {
+ continue;
+ }
+ }
+ int part_res = AddSlotItem (item, i);
+ if (part_res == ASI_SUCCESS) return ASI_SUCCESS;
+ else if (part_res == ASI_PARTIAL) res = ASI_PARTIAL;
+ }
+
+ return res;
+}
+
+//Used by FillSlot
+void Inventory::TryEquipAll(int slot)
+{
+ for(int i=SLOT_INV;i<=LAST_INV;i++) {
+ CREItem *item = Slots[i];
+ if (!item) {
+ continue;
+ }
+
+ Slots[i]=NULL;
+ if (AddSlotItem(item, slot) == ASI_SUCCESS) {
+ return;
+ }
+ //try to stuff it back, it should work
+ if (AddSlotItem(item, i) != ASI_SUCCESS) {
+ delete item;
+ }
+ }
+}
+
+int Inventory::AddStoreItem(STOItem* item, int action)
+{
+ CREItem *temp;
+ int ret = -1;
+
+ // item->PurchasedAmount is the number of items bought
+ // (you can still add grouped objects in a single step,
+ // just set up STOItem)
+ while (item->PurchasedAmount) {
+ //the first part of a STOItem is essentially a CREItem
+ temp = new CREItem();
+ memcpy( temp, item, sizeof( CREItem ) );
+ //except the Expired flag
+ temp->Expired=0;
+ if (action==STA_STEAL) {
+ temp->Flags |= IE_INV_ITEM_STOLEN;
+ }
+ temp->Flags &= ~IE_INV_ITEM_SELECTED;
+
+ ret = AddSlotItem( temp, SLOT_ONLYINVENTORY );
+ if (ret != ASI_SUCCESS) {
+ delete temp;
+ break;
+ }
+ if (item->InfiniteSupply!=-1) {
+ if (!item->AmountInStock) {
+ break;
+ }
+ item->AmountInStock--;
+ }
+ item->PurchasedAmount--;
+ }
+ return ret;
+}
+
+/* could the source item be dropped on the target item to merge them */
+bool Inventory::ItemsAreCompatible(CREItem* target, CREItem* source) const
+{
+ if (!target) {
+ //this isn't always ok, please check!
+ printMessage("Inventory","Null item encountered by ItemsAreCompatible()",YELLOW);
+ return true;
+ }
+
+ if (!(source->Flags&IE_INV_ITEM_STACKED) ) {
+ return false;
+ }
+
+ if (!strnicmp( target->ItemResRef, source->ItemResRef,8 )) {
+ return true;
+ }
+ return false;
+}
+
+//depletes a magical item
+//if flags==0 then magical weapons are not harmed
+int Inventory::DepleteItem(ieDword flags)
+{
+ for (size_t i = 0; i < Slots.size(); i++) {
+ CREItem *item = Slots[i];
+ if (!item) {
+ continue;
+ }
+
+ //don't harm critical items
+ //don't harm nonmagical items
+ //don't harm indestructible items
+ if ( (item->Flags&(IE_INV_ITEM_CRITICAL|IE_INV_DEPLETABLE)) != IE_INV_DEPLETABLE) {
+ continue;
+ }
+
+ //if flags = 0 then weapons are not depleted
+ if (!flags) {
+ Item *itm = gamedata->GetItem( item->ItemResRef );
+ if (!itm)
+ continue;
+ //if the item is usable in weapon slot, then it is weapon
+ int weapon = core->CanUseItemType( SLOT_WEAPON, itm );
+ gamedata->FreeItem( itm, item->ItemResRef, false );
+ if (weapon)
+ continue;
+ }
+ //deplete item
+ item->Usages[0]=0;
+ item->Usages[1]=0;
+ item->Usages[2]=0;
+ }
+ return -1;
+}
+
+// if flags is 0, skips undroppable items
+// if flags is IE_INV_ITEM_UNDROPPABLE, doesn't skip undroppable items
+// TODO: once all callers have been checked, this can be reversed to make more sense
+int Inventory::FindItem(const char *resref, unsigned int flags) const
+{
+ unsigned int mask = (flags^IE_INV_ITEM_UNDROPPABLE);
+ if (core->HasFeature(GF_NO_DROP_CAN_MOVE) ) {
+ mask &= ~IE_INV_ITEM_UNDROPPABLE;
+ }
+ for (size_t i = 0; i < Slots.size(); i++) {
+ const CREItem *item = Slots[i];
+ if (!item) {
+ continue;
+ }
+ if ( mask & item->Flags ) {
+ continue;
+ }
+ if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
+ continue;
+ }
+ return (int) i;
+ }
+ return -1;
+}
+
+bool Inventory::DropItemAtLocation(unsigned int slot, unsigned int flags, Map *map, const Point &loc)
+{
+ if (slot >= Slots.size()) {
+ return false;
+ }
+ //these slots will never 'drop' the item
+ if ((slot==(unsigned int) SLOT_FIST) || (slot==(unsigned int) SLOT_MAGIC)) {
+ return false;
+ }
+
+ CREItem *item = Slots[slot];
+ if (!item) {
+ return false;
+ }
+ //if you want to drop undoppable items, simply set IE_INV_UNDROPPABLE
+ //by default, it won't drop them
+ if ( ((flags^IE_INV_ITEM_UNDROPPABLE)&item->Flags)!=flags) {
+ return false;
+ }
+ if (!map) {
+ return false;
+ }
+ map->AddItemToLocation(loc, item);
+ Changed = true;
+ KillSlot(slot);
+ return true;
+}
+
+bool Inventory::DropItemAtLocation(const char *resref, unsigned int flags, Map *map, const Point &loc)
+{
+ bool dropped = false;
+
+ if (!map) {
+ return false;
+ }
+
+ //this loop is going from start
+ for (size_t i = 0; i < Slots.size(); i++) {
+ //these slots will never 'drop' the item
+ if ((i==(unsigned int) SLOT_FIST) || (i==(unsigned int) SLOT_MAGIC)) {
+ continue;
+ }
+ CREItem *item = Slots[i];
+ if (!item) {
+ continue;
+ }
+ //if you want to drop undoppable items, simply set IE_INV_UNDROPPABLE
+ //by default, it won't drop them
+ if ( ((flags^IE_INV_ITEM_UNDROPPABLE)&item->Flags)!=flags) {
+ continue;
+ }
+ if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
+ continue;
+ }
+ // mark it as unequipped, so it doesn't cause problems in stores
+ item->Flags &= ~ IE_INV_ITEM_EQUIPPED;
+ map->AddItemToLocation(loc, item);
+ Changed = true;
+ dropped = true;
+ KillSlot((unsigned int) i);
+ //if it isn't all items then we stop here
+ if (resref[0])
+ break;
+ }
+
+ //dropping gold too
+ if (!resref[0]) {
+ if (Owner->Type==ST_ACTOR) {
+ Actor *act = (Actor *) Owner;
+ if (! act->BaseStats[IE_GOLD]) {
+ return dropped;
+ }
+ CREItem *gold = new CREItem();
+
+ gold->Expired=0;
+ gold->Flags=0;
+ gold->Usages[1]=0;
+ gold->Usages[2]=0;
+ memcpy(gold->ItemResRef, core->GoldResRef, sizeof(ieResRef) );
+ gold->Usages[0] = act->BaseStats[IE_GOLD];
+ act->BaseStats[IE_GOLD] = 0;
+ map->AddItemToLocation(loc, gold);
+ }
+ }
+ return dropped;
+}
+
+CREItem *Inventory::GetSlotItem(unsigned int slot) const
+{
+ if (slot >= Slots.size() ) {
+ InvalidSlot(slot);
+ return NULL;
+ }
+ return Slots[slot];
+}
+
+ieDword Inventory::GetItemFlag(unsigned int slot) const
+{
+ const CREItem *item = GetSlotItem(slot);
+ if (!item) {
+ return 0;
+ }
+ return item->Flags;
+}
+
+bool Inventory::ChangeItemFlag(unsigned int slot, ieDword arg, int op)
+{
+ CREItem *item = GetSlotItem(slot);
+ if (!item) {
+ return false;
+ }
+ switch (op) {
+ case BM_SET: item->Flags = arg; break;
+ case BM_OR: item->Flags |= arg; break;
+ case BM_NAND: item->Flags &= ~arg; break;
+ case BM_XOR: item->Flags ^= arg; break;
+ case BM_AND: item->Flags &= arg; break;
+ }
+ return true;
+}
+
+//this is the low level equipping
+//all checks have been made previously
+bool Inventory::EquipItem(unsigned int slot)
+{
+ ITMExtHeader *header;
+
+ if (!Owner) {
+ //maybe assertion too?
+ return false;
+ }
+ CREItem *item = GetSlotItem(slot);
+ if (!item) {
+ return false;
+ }
+
+ int weaponslot;
+
+ // add effects of an item just being equipped to actor's effect queue
+ int effect = core->QuerySlotEffects( slot );
+ Item *itm = gamedata->GetItem(item->ItemResRef);
+ if (!itm) {
+ printf("Invalid item Equipped: %s Slot: %d\n", item->ItemResRef, slot);
+ return false;
+ }
+ switch (effect) {
+ case SLOT_EFFECT_LEFT:
+ //no idea if the offhand weapon has style, or simply the right
+ //hand style is dominant
+ UpdateShieldAnimation(itm);
+ break;
+ case SLOT_EFFECT_MELEE:
+ //if weapon is ranged, then find quarrel for it and equip that
+ slot -= SLOT_MELEE;
+ weaponslot = slot;
+ EquippedHeader = 0;
+ header = itm->GetExtHeader(EquippedHeader);
+ if (header && header->AttackType == ITEM_AT_BOW) {
+ //find the ranged projectile associated with it.
+ slot = FindRangedProjectile(header->ProjectileQualifier);
+ EquippedHeader = itm->GetWeaponHeaderNumber(true);
+ } else if (header && header->AttackType == ITEM_AT_PROJECTILE) {
+ EquippedHeader = itm->GetWeaponHeaderNumber(true);
+ } else {
+ EquippedHeader = itm->GetWeaponHeaderNumber(false);
+ }
+ header = itm->GetExtHeader(EquippedHeader);
+ if (header) {
+ SetEquippedSlot(slot, EquippedHeader);
+ if (slot != IW_NO_EQUIPPED) {
+ Owner->SetupQuickSlot(ACT_WEAPON1+weaponslot, slot+SLOT_MELEE, EquippedHeader);
+ }
+ effect = 0; // SetEquippedSlot will already call AddSlotEffects
+ UpdateWeaponAnimation();
+ }
+ break;
+ case SLOT_EFFECT_MISSILE:
+ //Get the ranged header of the projectile (so we theoretically allow shooting of daggers)
+ EquippedHeader = itm->GetWeaponHeaderNumber(true);
+ header = itm->GetExtHeader(EquippedHeader);
+ if (header) {
+ weaponslot = FindTypedRangedWeapon(header->ProjectileQualifier);
+ if (weaponslot != SLOT_FIST) {
+ weaponslot -= SLOT_MELEE;
+ SetEquippedSlot((ieWordSigned) (slot-SLOT_MELEE), EquippedHeader);
+ //It is unsure if we can have multiple equipping headers for bows/arrow
+ //It is unclear which item's header index should go there
+ Owner->SetupQuickSlot(ACT_WEAPON1+weaponslot, slot, 0);
+ }
+ UpdateWeaponAnimation();
+ }
+ break;
+ case SLOT_EFFECT_HEAD:
+ Owner->SetUsedHelmet(itm->AnimationType);
+ break;
+ case SLOT_EFFECT_ITEM:
+ //adjusting armour level if needed
+ {
+ int l = itm->AnimationType[0]-'1';
+ if (l>=0 && l<=3) {
+ Owner->SetBase(IE_ARMOR_TYPE, l);
+ } else {
+ UpdateShieldAnimation(itm);
+ }
+ }
+ break;
+ }
+ gamedata->FreeItem(itm, item->ItemResRef, false);
+ if (effect) {
+ if (item->Flags & IE_INV_ITEM_CURSED) {
+ item->Flags|=IE_INV_ITEM_UNDROPPABLE;
+ }
+ if (effect == SLOT_EFFECT_MISSILE) {
+ slot = FindRangedWeapon();
+ }
+ AddSlotEffects( slot );
+ }
+ return true;
+}
+
+//the removecurse flag will check if it is possible to move the item to the inventory
+//after a remove curse spell
+bool Inventory::UnEquipItem(unsigned int slot, bool removecurse)
+{
+ CREItem *item = GetSlotItem(slot);
+ if (!item) {
+ return false;
+ }
+ if (removecurse) {
+ if (item->Flags & IE_INV_ITEM_MOVABLE) {
+ item->Flags&=~IE_INV_ITEM_UNDROPPABLE;
+ }
+ if (FindCandidateSlot(SLOT_INVENTORY,0,item->ItemResRef)<0) {
+ return false;
+ }
+ }
+ if (!core->HasFeature(GF_NO_DROP_CAN_MOVE) || (item->Flags&IE_INV_ITEM_CURSED) ) {
+ if (item->Flags & IE_INV_ITEM_UNDROPPABLE ) {
+ return false;
+ }
+ }
+ item->Flags &= ~IE_INV_ITEM_EQUIPPED; //no idea if this is needed, won't hurt
+ return true;
+}
+
+// find the projectile
+// type = 1 - bow
+// 2 - xbow
+// 4 - sling
+//returns equipped code
+int Inventory::FindRangedProjectile(unsigned int type) const
+{
+ for(int i=SLOT_RANGED;i<=LAST_RANGED;i++) {
+ CREItem *Slot;
+
+ const Item *itm = GetItemPointer(i, Slot);
+ if (!itm) continue;
+ ITMExtHeader *ext_header = itm->GetExtHeader(0);
+ unsigned int weapontype = 0;
+ if (ext_header) {
+ weapontype = ext_header->ProjectileQualifier;
+ }
+ gamedata->FreeItem(itm, Slot->ItemResRef, false);
+ if (weapontype & type) {
+ return i-SLOT_MELEE;
+ }
+ }
+ return IW_NO_EQUIPPED;
+}
+
+// find which bow is attached to the projectile marked by 'Equipped'
+// returns slotcode
+int Inventory::FindRangedWeapon() const
+{
+ if (Equipped>=0) return SLOT_FIST;
+ return FindSlotRangedWeapon(Equipped+SLOT_MELEE);
+}
+
+int Inventory::FindSlotRangedWeapon(unsigned int slot) const
+{
+ if ((int)slot >= SLOT_MELEE) return SLOT_FIST;
+ CREItem *Slot;
+ Item *itm = GetItemPointer(slot, Slot);
+ if (!itm) return SLOT_FIST;
+
+ //always look for a ranged header when looking for a projectile/projector
+ ITMExtHeader *ext_header = itm->GetWeaponHeader(true);
+ unsigned int type = 0;
+ if (ext_header) {
+ type = ext_header->ProjectileQualifier;
+ }
+ gamedata->FreeItem(itm, Slot->ItemResRef, false);
+ return FindTypedRangedWeapon(type);
+}
+
+
+// find bow for a specific projectile type
+int Inventory::FindTypedRangedWeapon(unsigned int type) const
+{
+ if (!type) {
+ return SLOT_FIST;
+ }
+ for(int i=SLOT_MELEE;i<=LAST_MELEE;i++) {
+ CREItem *Slot;
+
+ const Item *itm = GetItemPointer(i, Slot);
+ if (!itm) continue;
+ //always look for a ranged header when looking for a projectile/projector
+ ITMExtHeader *ext_header = itm->GetWeaponHeader(true);
+ int weapontype = 0;
+ if (ext_header) {
+ weapontype = ext_header->ProjectileQualifier;
+ }
+ gamedata->FreeItem(itm, Slot->ItemResRef, false);
+ if (weapontype & type) {
+ return i;
+ }
+ }
+ return SLOT_FIST;
+}
+
+void Inventory::SetHeadSlot(int arg) { SLOT_HEAD=arg; }
+void Inventory::SetFistSlot(int arg) { SLOT_FIST=arg; }
+void Inventory::SetMagicSlot(int arg) { SLOT_MAGIC=arg; }
+void Inventory::SetWeaponSlot(int arg)
+{
+ if (SLOT_MELEE==-1) {
+ SLOT_MELEE=arg;
+ }
+ LAST_MELEE=arg;
+}
+
+//ranged slots should be before MELEE slots
+void Inventory::SetRangedSlot(int arg)
+{
+ assert(SLOT_MELEE!=-1);
+ if (SLOT_RANGED==-1) {
+ SLOT_RANGED=arg;
+ }
+ LAST_RANGED=arg;
+}
+
+void Inventory::SetQuickSlot(int arg)
+{
+ if (SLOT_QUICK==-1) {
+ SLOT_QUICK=arg;
+ }
+ LAST_QUICK=arg;
+}
+
+void Inventory::SetInventorySlot(int arg)
+{
+ if (SLOT_INV==-1) {
+ SLOT_INV=arg;
+ }
+ LAST_INV=arg;
+}
+
+//multiple shield slots are allowed
+//but in this case they should be interspersed with melee slots
+void Inventory::SetShieldSlot(int arg)
+{
+ if (SLOT_LEFT!=-1) {
+ assert(SLOT_MELEE+1==SLOT_LEFT);
+ IWD2=true;
+ return;
+ }
+ SLOT_LEFT=arg;
+}
+
+int Inventory::GetHeadSlot()
+{
+ return SLOT_HEAD;
+}
+
+int Inventory::GetFistSlot()
+{
+ return SLOT_FIST;
+}
+
+int Inventory::GetMagicSlot()
+{
+ return SLOT_MAGIC;
+}
+
+int Inventory::GetWeaponSlot()
+{
+ return SLOT_MELEE;
+}
+
+int Inventory::GetQuickSlot()
+{
+ return SLOT_QUICK;
+}
+
+int Inventory::GetInventorySlot()
+{
+ return SLOT_INV;
+}
+
+//if shield slot is empty, call again for fist slot!
+int Inventory::GetShieldSlot() const
+{
+ if (IWD2) {
+ if (Equipped>=0 && Equipped<=3) {
+ return Equipped*2+SLOT_MELEE+1;
+ }
+ return -1;
+ }
+ return SLOT_LEFT;
+}
+
+int Inventory::GetEquippedSlot() const
+{
+ if (Equipped == IW_NO_EQUIPPED) {
+ return SLOT_FIST;
+ }
+ if (IWD2 && Equipped>=0) {
+ //i've absolutely NO idea what is this 4 (Avenger)
+ //Equipped should be 0-3 in iWD2, no???
+ if (Equipped >= 4) {
+ return SLOT_MELEE;
+ }
+ return Equipped*2+SLOT_MELEE;
+ }
+ return Equipped+SLOT_MELEE;
+}
+
+bool Inventory::SetEquippedSlot(ieWordSigned slotcode, ieWord header)
+{
+ EquippedHeader = header;
+
+ //doesn't work if magic slot is used, refresh the magic slot just in case
+ if (HasItemInSlot("",SLOT_MAGIC) && (slotcode!=SLOT_MAGIC-SLOT_MELEE)) {
+ Equipped = SLOT_MAGIC-SLOT_MELEE;
+ UpdateWeaponAnimation();
+ return false;
+ }
+
+ //if it is an illegal code, make it fist
+ if ((size_t) (slotcode+SLOT_MELEE)>Slots.size()) {
+ slotcode=IW_NO_EQUIPPED;
+ }
+
+ //unequipping (fist slot will be used now)
+ if (slotcode == IW_NO_EQUIPPED || !HasItemInSlot("",slotcode+SLOT_MELEE)) {
+ if (Equipped != IW_NO_EQUIPPED) {
+ RemoveSlotEffects( SLOT_MELEE+Equipped);
+ }
+ Equipped = IW_NO_EQUIPPED;
+ //fist slot equipping effects
+ AddSlotEffects(SLOT_FIST);
+ UpdateWeaponAnimation();
+ return true;
+ }
+
+ //equipping a weapon, but remove its effects first
+ if (Equipped != IW_NO_EQUIPPED) {
+ RemoveSlotEffects( SLOT_MELEE+Equipped);
+ }
+
+ Equipped = slotcode;
+ int effects = core->QuerySlotEffects( SLOT_MELEE+Equipped );
+ if (effects) {
+ CREItem* item = GetSlotItem(SLOT_MELEE+Equipped);
+ item->Flags|=IE_INV_ITEM_EQUIPPED;
+ if (item->Flags & IE_INV_ITEM_CURSED) {
+ item->Flags|=IE_INV_ITEM_UNDROPPABLE;
+ }
+ AddSlotEffects( SLOT_MELEE+Equipped);
+ }
+ UpdateWeaponAnimation();
+ return true;
+}
+
+int Inventory::GetEquipped() const
+{
+ return Equipped;
+}
+
+int Inventory::GetEquippedHeader() const
+{
+ return EquippedHeader;
+}
+
+//returns the fist weapon if there is nothing else
+//This will return the actual weapon, I mean the bow in the case of bow+arrow combination
+CREItem *Inventory::GetUsedWeapon(bool leftorright, int &slot) const
+{
+ CREItem *ret;
+
+ if (SLOT_MAGIC!=-1) {
+ slot = SLOT_MAGIC;
+ ret = GetSlotItem(slot);
+ if (ret && ret->ItemResRef[0]) {
+ return ret;
+ }
+ }
+ if (leftorright) {
+ //no shield slot
+ slot = GetShieldSlot();
+ if (slot>=0) {
+ ret = GetSlotItem(slot);
+ if (ret) {
+ return ret;
+ } else {
+ //we don't want to return fist for shield slot
+ return NULL;
+ }
+ }
+ }
+ slot = GetEquippedSlot();
+ if((core->QuerySlotEffects(slot) & SLOT_EFFECT_MISSILE) == SLOT_EFFECT_MISSILE) {
+ slot = FindRangedWeapon();
+ }
+ ret = GetSlotItem(slot);
+ if (!ret) {
+ //return fist weapon
+ slot = SLOT_FIST;
+ ret = GetSlotItem(slot);
+ }
+ return ret;
+}
+
+// Returns index of first empty slot or slot with the same
+// item and not full stack. On fail returns -1
+// Can be used to check for full inventory
+int Inventory::FindCandidateSlot(int slottype, size_t first_slot, const char *resref)
+{
+ if (first_slot >= Slots.size())
+ return -1;
+
+ for (size_t i = first_slot; i < Slots.size(); i++) {
+ if (!(core->QuerySlotType( (unsigned int) i) & slottype) ) {
+ continue;
+ }
+
+ CREItem *item = Slots[i];
+
+ if (!item) {
+ return (int) i; //this is a good empty slot
+ }
+ if (!resref) {
+ continue;
+ }
+ if (!(item->Flags&IE_INV_ITEM_STACKED) ) {
+ continue;
+ }
+ if (strnicmp( item->ItemResRef, resref, 8 )!=0) {
+ continue;
+ }
+ // check if the item fits in this slot, we use the cached
+ // stackamount value
+ if (item->Usages[0]<item->StackAmount) {
+ return (int) i;
+ }
+ }
+
+ return -1;
+}
+
+void Inventory::AddSlotItemRes(const ieResRef ItemResRef, int SlotID, int Charge0, int Charge1, int Charge2)
+{
+ CREItem *TmpItem = new CREItem();
+ strnlwrcpy(TmpItem->ItemResRef, ItemResRef, 8);
+ TmpItem->Expired=0;
+ TmpItem->Usages[0]=(ieWord) Charge0;
+ TmpItem->Usages[1]=(ieWord) Charge1;
+ TmpItem->Usages[2]=(ieWord) Charge2;
+ TmpItem->Flags=0;
+ if (core->ResolveRandomItem(TmpItem) && gamedata->Exists(TmpItem->ItemResRef, IE_ITM_CLASS_ID)) {
+ AddSlotItem( TmpItem, SlotID );
+ } else {
+ delete TmpItem;
+ }
+ CalculateWeight();
+}
+
+void Inventory::SetSlotItemRes(const ieResRef ItemResRef, int SlotID, int Charge0, int Charge1, int Charge2)
+{
+ if(ItemResRef[0]) {
+ CREItem *TmpItem = new CREItem();
+ strnlwrcpy(TmpItem->ItemResRef, ItemResRef, 8);
+ TmpItem->Expired=0;
+ TmpItem->Usages[0]=(ieWord) Charge0;
+ TmpItem->Usages[1]=(ieWord) Charge1;
+ TmpItem->Usages[2]=(ieWord) Charge2;
+ TmpItem->Flags=0;
+ if (core->ResolveRandomItem(TmpItem) && gamedata->Exists(TmpItem->ItemResRef, IE_ITM_CLASS_ID)) {
+ SetSlotItem( TmpItem, SlotID );
+ } else {
+ delete TmpItem;
+ }
+ } else {
+ //if the item isn't creatable, we still destroy the old item
+ KillSlot( SlotID );
+ }
+ CalculateWeight();
+}
+
+void Inventory::BreakItemSlot(ieDword slot)
+{
+ ieResRef newItem;
+ CREItem *Slot;
+
+ const Item *itm = GetItemPointer(slot, Slot);
+ if (!itm) return;
+ //if it is the magic weapon slot, don't break it, just remove it, because it couldn't be removed
+ if(slot ==(unsigned int) SLOT_MAGIC) {
+ newItem[0]=0;
+ } else {
+ memcpy(newItem, itm->ReplacementItem,sizeof(newItem) );
+ }
+ gamedata->FreeItem( itm, Slot->ItemResRef, true );
+ //this depends on setslotitemres using setslotitem
+ SetSlotItemRes(newItem, slot, 0,0,0);
+}
+
+void Inventory::dump()
+{
+ printf( "INVENTORY:\n" );
+ for (unsigned int i = 0; i < Slots.size(); i++) {
+ CREItem* itm = Slots[i];
+
+ if (!itm) {
+ continue;
+ }
+
+ printf ( "%2u: %8.8s - (%d %d %d) Fl:0x%x Wt: %d x %dLb\n", i, itm->ItemResRef, itm->Usages[0], itm->Usages[1], itm->Usages[2], itm->Flags, itm->StackAmount, itm->Weight );
+ }
+
+ printf( "Equipped: %d\n", Equipped );
+ Changed = true;
+ CalculateWeight();
+ printf( "Total weight: %d\n", Weight );
+}
+
+void Inventory::EquipBestWeapon(int flags)
+{
+ int i;
+ int damage = -1;
+ ieDword best_slot = SLOT_FIST;
+ ITMExtHeader *header;
+ CREItem *Slot;
+ char AnimationType[2]={0,0};
+ ieWord MeleeAnimation[3]={100,0,0};
+
+ //cannot change equipment when holding magic weapons
+ if (Equipped == SLOT_MAGIC-SLOT_MELEE) {
+ return;
+ }
+
+ if (flags&EQUIP_RANGED) {
+ for(i=SLOT_RANGED;i<LAST_RANGED;i++) {
+ const Item *itm = GetItemPointer(i, Slot);
+ if (!itm) continue;
+ //best ranged
+ int tmp = itm->GetDamagePotential(true, header);
+ if (tmp>damage) {
+ best_slot = i;
+ damage = tmp;
+ memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
+ memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
+ }
+ gamedata->FreeItem( itm, Slot->ItemResRef, false );
+ }
+
+ //ranged melee weapons like throwing daggers (not bows!)
+ for(i=SLOT_MELEE;i<=LAST_MELEE;i++) {
+ const Item *itm = GetItemPointer(i, Slot);
+ if (!itm) continue;
+ //best ranged
+ int tmp = itm->GetDamagePotential(true, header);
+ if (tmp>damage) {
+ best_slot = i;
+ damage = tmp;
+ memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
+ memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
+ }
+ gamedata->FreeItem( itm, Slot->ItemResRef, false );
+ }
+ }
+
+ if (flags&EQUIP_MELEE) {
+ for(i=SLOT_MELEE;i<=LAST_MELEE;i++) {
+ const Item *itm = GetItemPointer(i, Slot);
+ if (!itm) continue;
+ //the Slot flag is enough for this
+ //though we need animation type/damagepotential anyway
+ if (Slot->Flags&IE_INV_ITEM_BOW) continue;
+ //best melee
+ int tmp = itm->GetDamagePotential(false, header);
+ if (tmp>damage) {
+ best_slot = i;
+ damage = tmp;
+ memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
+ memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
+ }
+ gamedata->FreeItem( itm, Slot->ItemResRef, false );
+ }
+ }
+
+ EquipItem(best_slot);
+ UpdateWeaponAnimation();
+}
+
+#define ID_NONEED 0 //id is not important
+#define ID_NEED 1 //id is important
+#define ID_NO 2 //shouldn't id
+
+/* returns true if there are more item usages not fitting in given array */
+bool Inventory::GetEquipmentInfo(ItemExtHeader *array, int startindex, int count)
+{
+ int pos = 0;
+ int actual = 0;
+ memset(array, 0, count * sizeof(ItemExtHeader) );
+ for(unsigned int idx=0;idx<Slots.size();idx++) {
+ if (!core->QuerySlotEffects(idx)) {
+ continue;
+ }
+ CREItem *slot;
+
+ const Item *itm = GetItemPointer(idx, slot);
+ if (!itm) {
+ continue;
+ }
+ for(int ehc=0;ehc<itm->ExtHeaderCount;ehc++) {
+ ITMExtHeader *ext_header = itm->ext_headers+ehc;
+ if (ext_header->Location!=ITEM_LOC_EQUIPMENT) {
+ continue;
+ }
+ //skipping if we cannot use the item
+ int idreq1 = (slot->Flags&IE_INV_ITEM_IDENTIFIED);
+ int idreq2 = ext_header->IDReq;
+ switch (idreq2) {
+ case ID_NO:
+ if (idreq1) continue;
+ break;
+ case ID_NEED:
+ if (!idreq1) continue;
+ default:;
+ }
+
+ actual++;
+ if (actual>startindex) {
+
+ //store the item, return if we can't store more
+ if (!count) {
+ gamedata->FreeItem(itm, slot->ItemResRef, false);
+ return true;
+ }
+ count--;
+ memcpy(array[pos].itemname, slot->ItemResRef, sizeof(ieResRef) );
+ array[pos].slot = idx;
+ array[pos].headerindex = ehc;
+ int slen = ((char *) &(array[pos].itemname)) -((char *) &(array[pos].AttackType));
+ memcpy(&(array[pos].AttackType), &(ext_header->AttackType), slen);
+ if (ext_header->Charges) {
+ //don't modify ehc, it is a counter
+ if (ehc>=CHARGE_COUNTERS) {
+ array[pos].Charges=slot->Usages[0];
+ } else {
+ array[pos].Charges=slot->Usages[ehc];
+ }
+ } else {
+ array[pos].Charges=0xffff;
+ }
+ pos++;
+ }
+ }
+ gamedata->FreeItem(itm, slot->ItemResRef, false);
+ }
+
+ return false;
+}
+
+//The slot index value is optional, if you supply it,
+// then ItemExcl will be returned as if the item was already unequipped
+ieDword Inventory::GetEquipExclusion(int index) const
+{
+ if (index<0) {
+ return ItemExcl;
+ }
+ CREItem *slot;
+ const Item *itm = GetItemPointer(index, slot);
+ if (!itm) {
+ return ItemExcl;
+ }
+ ieDword ret = ItemExcl&~itm->ItemExcl;
+ gamedata->FreeItem(itm, slot->ItemResRef, false);
+ return ret;
+}
+
+void Inventory::UpdateShieldAnimation(Item *it)
+{
+ char AnimationType[2]={0,0};
+ int WeaponType = -1;
+
+ if (it) {
+ memcpy(AnimationType, it->AnimationType, 2);
+ if (core->CanUseItemType(SLOT_WEAPON, it))
+ WeaponType = IE_ANI_WEAPON_2W;
+ else
+ WeaponType = IE_ANI_WEAPON_1H;
+ } else {
+ WeaponType = IE_ANI_WEAPON_1H;
+ }
+ Owner->SetUsedShield(AnimationType, WeaponType);
+}
+
+void Inventory::UpdateWeaponAnimation()
+{
+ int slot = GetEquippedSlot();
+ int effect = core->QuerySlotEffects( slot );
+ if (effect == SLOT_EFFECT_MISSILE) {
+ // ranged weapon
+ slot = FindRangedWeapon();
+ }
+ int WeaponType = -1;
+
+ char AnimationType[2]={0,0};
+ ieWord MeleeAnimation[3]={100,0,0};
+ CREItem *Slot;
+
+ // TODO: fix bows?
+
+ ITMExtHeader *header = 0;
+ const Item *itm = GetItemPointer(slot, Slot);
+ if (itm) {
+ itm->GetDamagePotential(false, header);
+ memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
+ //for twohanded flag, you don't need itm
+ if (Slot->Flags & IE_INV_ITEM_TWOHANDED)
+ WeaponType = IE_ANI_WEAPON_2H;
+ else {
+
+ // Examine shield slot to check if we're using two weapons
+ // TODO: for consistency, use same Item* access method as above
+ bool twoweapon = false;
+ int slot = GetShieldSlot();
+ CREItem* si = NULL;
+ if (slot>0) {
+ si = GetSlotItem( (ieDword) slot );
+ }
+ if (si) {
+ Item* it = gamedata->GetItem(si->ItemResRef);
+ if (core->CanUseItemType(SLOT_WEAPON, it))
+ twoweapon = true;
+ gamedata->FreeItem(it, si->ItemResRef, false);
+ }
+
+ if (twoweapon)
+ WeaponType = IE_ANI_WEAPON_2W;
+ else
+ WeaponType = IE_ANI_WEAPON_1H;
+ }
+ }
+
+ if (header)
+ memcpy(MeleeAnimation,header->MeleeAnimation, sizeof(MeleeAnimation) );
+ if (itm)
+ gamedata->FreeItem( itm, Slot->ItemResRef, false );
+ Owner->SetUsedWeapon(AnimationType, MeleeAnimation, WeaponType);
+}
+
+//this function will also check disabled slots (if that feature will be imped)
+bool Inventory::IsSlotBlocked(int slot) const
+{
+ if (slot<SLOT_MELEE) return false;
+ if (slot>LAST_MELEE) return false;
+ int otherslot;
+ if (IWD2) {
+ otherslot = slot+1;
+ } else {
+ otherslot = SLOT_LEFT;
+ }
+ return HasItemInSlot("",otherslot);
+}
+
+inline bool Inventory::TwoHandedInSlot(int slot) const
+{
+ CREItem *item;
+
+ item = GetSlotItem(slot);
+ if (!item) return false;
+ if (item->Flags&IE_INV_ITEM_TWOHANDED) {
+ return true;
+ }
+ return false;
+}
+
+int Inventory::WhyCantEquip(int slot, int twohanded) const
+{
+ // check only for hand slots
+ if ((slot<SLOT_MELEE || slot>LAST_MELEE) && (slot != SLOT_LEFT) ) {
+ return 0;
+ }
+
+ //magic items have the highest priority
+ if ( HasItemInSlot("", SLOT_MAGIC)) {
+ //magic weapon is in use
+ return STR_MAGICWEAPON;
+ }
+
+ //can't equip in shield slot if a weapon slot is twohanded
+ for (int i=SLOT_MELEE; i<=LAST_MELEE;i++) {
+ //see GetShieldSlot
+ int otherslot;
+ if (IWD2) {
+ otherslot = ++i;
+ } else {
+ otherslot = SLOT_LEFT;
+ }
+ if (slot==otherslot) {
+ if (TwoHandedInSlot(i)) {
+ return STR_TWOHANDED_USED;
+ }
+ }
+ }
+
+ if (twohanded) {
+ if (IWD2) {
+ if (slot>=SLOT_MELEE&&slot<=LAST_MELEE && (slot&1) ) {
+ return STR_NOT_IN_OFFHAND;
+ }
+ } else {
+ if (slot==SLOT_LEFT) {
+ return STR_NOT_IN_OFFHAND;
+ }
+ }
+ if (IsSlotBlocked(slot)) {
+ //cannot equip two handed while shield slot is in use?
+ return STR_OFFHAND_USED;
+ }
+ }
+ return 0;
+}
+
+//recharge items on rest, if rest was partial, recharge only 'hours'
+//if this latter functionality is unwanted, then simply don't recharge if
+//hours != 0
+void Inventory::ChargeAllItems(int hours)
+{
+ //this loop is going from start
+ for (size_t i = 0; i < Slots.size(); i++) {
+ CREItem *item = Slots[i];
+ if (!item) {
+ continue;
+ }
+
+ Item *itm = gamedata->GetItem( item->ItemResRef );
+ if (!itm)
+ continue;
+ for(int h=0;h<CHARGE_COUNTERS;h++) {
+ ITMExtHeader *header = itm->GetExtHeader(h);
+ if (header && (header->RechargeFlags&IE_ITEM_RECHARGE)) {
+ unsigned short add = header->Charges;
+ if (hours && add>hours) add=hours;
+ add+=item->Usages[h];
+ if(add>header->Charges) add=header->Charges;
+ item->Usages[h]=add;
+ }
+ }
+ gamedata->FreeItem( itm, item->ItemResRef, false );
+ }
+}
+
+#define ITM_STEALING (IE_INV_ITEM_UNSTEALABLE | IE_INV_ITEM_MOVABLE | IE_INV_ITEM_EQUIPPED)
+unsigned int Inventory::FindStealableItem()
+{
+ unsigned int slot;
+ int inc;
+
+ slot = core->Roll(1, Slots.size(),-1);
+ inc = slot&1?1:-1;
+
+ printf("Start Slot: %d, increment: %d\n", slot, inc);
+ //as the unsigned value underflows, it will be greater than Slots.size()
+ for(;slot<Slots.size(); slot+=inc) {
+ CREItem *item = Slots[slot];
+ //can't steal empty slot
+ if (!item) continue;
+ //bit 1 is stealable slot
+ if (!(core->QuerySlotFlags(slot)&1) ) continue;
+ //can't steal equipped weapon
+ if ((unsigned int) (Equipped+SLOT_MELEE) == core->QuerySlot(slot)) continue;
+ //can't steal flagged items
+ if ((item->Flags & ITM_STEALING) != IE_INV_ITEM_MOVABLE) continue;
+ return slot;
+ }
+ return 0;
+}
+
+// extension to allow more or less than head gear to avert critical hits:
+// If an item with bit 25 set is equipped in a non-helmet slot, aversion is enabled
+// If an item with bit 25 set is equipped in a helmet slot, aversion is disabled
+bool Inventory::ProvidesCriticalAversion()
+{
+ for (size_t i = 0; i < Slots.size(); i++) {
+ CREItem *item = Slots[i];
+ if (!item || ! (item->Flags & IE_INV_ITEM_EQUIPPED)) {
+ continue;
+ }
+
+ Item *itm = gamedata->GetItem(item->ItemResRef);
+ if (!itm) {
+ continue;
+ }
+
+ for (int h = 0; h < itm->ExtHeaderCount; h++) {
+ ITMExtHeader *header = itm->GetExtHeader(h);
+ if ((int)i == SLOT_HEAD) {
+ if (header && (header->RechargeFlags & IE_ITEM_TOGGLE_CRITS)) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ if (header && (header->RechargeFlags & IE_ITEM_TOGGLE_CRITS)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
diff --git a/gemrb/core/Inventory.h b/gemrb/core/Inventory.h
new file mode 100644
index 0000000..dae7d4c
--- /dev/null
+++ b/gemrb/core/Inventory.h
@@ -0,0 +1,351 @@
+/* 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 Inventory.h
+ * Declares Inventory, class implementing creatures' and containers'
+ * inventory and item management
+ * @author The GemRB Project
+ */
+
+#ifndef INVENTORY_H
+#define INVENTORY_H
+
+#include "exports.h"
+#include "ie_types.h"
+#include "win32def.h"
+
+#include "Item.h" //needs item for itmextheader
+#include "Store.h"
+
+#include <vector>
+
+class Map;
+
+//AddSlotItem return values
+#define ASI_FAILED 0
+#define ASI_PARTIAL 1
+#define ASI_SUCCESS 2
+#define ASI_SWAPPED 3 //not returned normally, but Gui uses this value
+
+//AddSlotItem extra slot ID's
+#define SLOT_AUTOEQUIP -1
+#define SLOT_ONLYINVENTORY -3
+
+//slottypes (bitfield)
+#define SLOT_HELM 1
+#define SLOT_ARMOUR 2
+#define SLOT_SHIELD 4
+#define SLOT_GLOVE 8
+#define SLOT_RING 16
+#define SLOT_AMULET 32
+#define SLOT_BELT 64
+#define SLOT_BOOT 128
+#define SLOT_WEAPON 256
+#define SLOT_QUIVER 512
+#define SLOT_CLOAK 1024
+#define SLOT_ITEM 2048 //quick item
+#define SLOT_SCROLL 4096
+#define SLOT_BAG 8192
+#define SLOT_POTION 16384
+#define SLOT_ANY 32767
+#define SLOT_INVENTORY 32768
+#define SLOT_ALL 65535
+
+//weapon slot types (1000==not equipped)
+#define IW_NO_EQUIPPED 1000
+
+/** Inventory types */
+typedef enum ieInventoryType {
+ INVENTORY_HEAP = 0,
+ INVENTORY_CREATURE = 1
+} ieInventoryType;
+
+// !!! Keep these synchronized with GUIDefines.py !!!
+typedef enum ieCREItemFlagBits {
+ IE_INV_ITEM_IDENTIFIED = 1,
+ IE_INV_ITEM_UNSTEALABLE = 2,
+ IE_INV_ITEM_STOLEN = 4,
+ //in iwd/iwd2 this flag means 'magical', some hack is needed
+ IE_INV_ITEM_UNDROPPABLE =8,
+ //just recently acquired
+ IE_INV_ITEM_ACQUIRED = 0x10, //this is a gemrb extension
+ //is this item destructible normally?
+ IE_INV_ITEM_DESTRUCTIBLE = 0x20,//this is a gemrb extension
+ //is this item already equipped?
+ IE_INV_ITEM_EQUIPPED = 0x40, //this is a gemrb extension
+ //selected for sale, using the same bit, hope it is ok
+ IE_INV_ITEM_SELECTED = 0x40, //this is a gemrb extension
+ //is this item stackable?
+ IE_INV_ITEM_STACKED = 0x80, //this is a gemrb extension
+ //these flags are coming from the original item, but these are immutable
+ IE_INV_ITEM_CRITICAL = 0x100, //coming from original item
+ IE_INV_ITEM_TWOHANDED = 0x200,
+ IE_INV_ITEM_MOVABLE = 0x400, //same as undroppable
+ IE_INV_ITEM_RESELLABLE = 0x800, //item will appear in shop when sold
+ IE_INV_ITEM_CURSED = 0x1000, //item is cursed
+ IE_INV_ITEM_UNKNOWN2000 = 0x2000, //totally unknown
+ IE_INV_ITEM_MAGICAL = 0x4000, //magical
+ IE_INV_ITEM_BOW = 0x8000, //
+ IE_INV_ITEM_SILVER = 0x10000,
+ IE_INV_ITEM_COLDIRON = 0x20000,
+ IE_INV_ITEM_STOLEN2 = 0x40000, //same as 4
+ IE_INV_ITEM_CONVERSIBLE = 0x80000,
+ IE_INV_ITEM_PULSATING = 0x100000
+} ieCREItemFlagBits;
+
+#define IE_INV_DEPLETABLE (IE_INV_ITEM_MAGICAL|IE_INV_ITEM_DESTRUCTIBLE)
+
+//equip flags
+#define EQUIP_ANY 0
+#define EQUIP_MELEE 1
+#define EQUIP_RANGED 2
+
+//FIXME:
+//actually this header shouldn't be THIS large, i was just
+//lazy to pick the interesting elements
+//it could be possible that some elements need to be added from the
+//item header itself
+struct ItemExtHeader {
+ ieDword slot;
+ ieDword headerindex;
+ //from itmextheader
+ ieByte AttackType;
+ ieByte IDReq;
+ ieByte Location;
+ ieByte unknown1;
+ ieResRef UseIcon;
+ ieByte Target;
+ ieByte TargetNumber;
+ ieWord Range;
+ ieWord ProjectileType;
+ ieWord Speed;
+ ieWord THAC0Bonus;
+ ieWord DiceSides;
+ ieWord DiceThrown;
+ ieWord DamageBonus;
+ ieWord DamageType;
+ ieWord FeatureCount;
+ ieWord FeatureOffset;
+ ieWord Charges;
+ ieWord ChargeDepletion;
+ ieDword RechargeFlags; //this is a bitfield with many bits
+ ieWord ProjectileAnimation;
+ ieWord MeleeAnimation[3];
+ int ProjectileQualifier; //this is a derived value determined on load time
+ //other data
+ ieResRef itemname;
+};
+
+/**
+ * @class CREItem
+ * Class holding Item instance specific values and providing link between
+ * an Inventory and a stack of Items.
+ * It's keeping info on whether Item was identified, for example.
+ */
+
+class GEM_EXPORT CREItem {
+public:
+ ieResRef ItemResRef;
+ //recent research showed that this field is used by the create item
+ //for days effect. This field shows the expiration in gametime hours
+ ieWord Expired;
+ ieWord Usages[CHARGE_COUNTERS];
+ ieDword Flags;
+ // 2 cached values from associated item. LEAVE IT SIGNED!
+ /** Weight of items in the stack */
+ int Weight;
+ /** Amount of items in this stack */
+ int StackAmount;
+
+ CREItem()
+ {
+ Weight=-1; //invalid weight
+ StackAmount=0;
+ }
+};
+
+/**
+ * @class Inventory
+ * Class implementing creatures' and containers' inventory and item management
+ */
+
+class GEM_EXPORT Inventory {
+private:
+ std::vector<CREItem*> Slots;
+ Actor* Owner;
+ int InventoryType;
+ int Changed;
+ /** Total weight of all items in Inventory */
+ int Weight;
+
+ ieWordSigned Equipped;
+ ieWord EquippedHeader;
+ /** this isn't saved */
+ ieDword ItemExcl;
+ ieDword ItemTypes[4]; //256 bits
+public:
+ Inventory();
+ virtual ~Inventory();
+
+ /** duplicates the source inventory into the current one, marking items as undroppable */
+ void CopyFrom(const Actor *source);
+ /** Removes an item from the inventory, destroys slot.
+ * Use it for containers only */
+ CREItem *GetItem(unsigned int idx);
+ /** adds an item to the inventory */
+ void AddItem(CREItem *item);
+ /** Returns number of items in the inventory */
+ int CountItems(const char *resref, bool charges) const;
+ /** looks for a particular item in a slot */
+ bool HasItemInSlot(const char *resref, unsigned int slot) const;
+ /** returns true if contains one itemtype equipped */
+ bool HasItemType(ieDword type) const;
+ /** Looks for a particular item in the inventory. */
+ /* flags: see ieCREItemFlagBits */
+ bool HasItem(const char *resref, ieDword flags) const;
+
+ void CalculateWeight(void);
+ void SetInventoryType(int arg);
+ void SetOwner(Actor* act) { Owner = act; }
+
+ /** returns number of all slots in the inventory */
+ int GetSlotCount() const { return (int)Slots.size(); }
+
+ /** sets inventory size, for the first time */
+ void SetSlotCount(unsigned int size);
+
+
+ /** Returns CREItem in specified slot.
+ * If count !=0 it splits the item and returns only requested amount */
+ CREItem* RemoveItem(unsigned int slot, unsigned int count = 0);
+ /** returns slot of removed item, you can delete the removed item */
+ int RemoveItem(const char* resref, unsigned int flags, CREItem **res_item);
+
+ /** adds CREItem to the inventory. If slot == -1, finds
+ ** first eligible slot, eventually splitting the item to
+ ** more slots. If slot == -3 then finds the first empty inventory slot
+ ** Returns 2 if completely successful, 1 if partially, 0 else.
+ ** slottype is an optional filter for searching eligible slots */
+ int AddSlotItem(CREItem* item, int slot, int slottype=-1);
+ /** tries to equip all inventory items in a given slot */
+ void TryEquipAll(int slot);
+ /** Adds STOItem to the inventory, it is never wielded, action might be STA_STEAL or STA_BUY */
+ /** The amount of items is stored in PurchasedAmount */
+ int AddStoreItem(STOItem* item, int action);
+
+ /** flags: see ieCREItemFlagBits */
+ /** count == ~0 means to destroy all */
+ /** returns the number of destroyed items */
+ unsigned int DestroyItem(const char *resref, ieDword flags, ieDword count);
+ /** flags: see ieCREItemFlagBits */
+ void SetItemFlags(CREItem* item, ieDword flags);
+ void SetSlotItem(CREItem* item, unsigned int slot);
+ int GetWeight() const {return Weight;}
+
+ bool ItemsAreCompatible(CREItem* target, CREItem* source) const;
+ //depletes charged items
+ int DepleteItem(ieDword flags);
+ //charges recharging items
+ void ChargeAllItems(int hours);
+ /** Finds the first slot of named item, if resref is empty, finds the first filled! slot */
+ int FindItem(const char *resref, unsigned int flags) const;
+ bool DropItemAtLocation(unsigned int slot, unsigned int flags, Map *map, const Point &loc);
+ bool DropItemAtLocation(const char *resref, unsigned int flags, Map *map, const Point &loc);
+ bool SetEquippedSlot(ieWordSigned slotcode, ieWord header);
+ int GetEquipped() const;
+ int GetEquippedHeader() const;
+ //right hand
+ int GetEquippedSlot() const;
+ //left hand
+ int GetShieldSlot() const;
+ void AddSlotEffects( ieDword slot);
+ //void AddAllEffects();
+ /** Returns item in specified slot. Does NOT change inventory */
+ CREItem* GetSlotItem(ieDword slot) const;
+ /** Returns the item's inventory flags */
+ ieDword GetItemFlag(unsigned int slot) const;
+ /** Changes the inventory flags */
+ bool ChangeItemFlag(ieDword slot, ieDword value, int mode);
+ /** Equips the item, don't use it directly for weapons */
+ bool EquipItem(ieDword slot);
+ bool UnEquipItem(ieDword slot, bool removecurse);
+ /** Returns equipped weapon, also its slot */
+ CREItem *GetUsedWeapon(bool leftorright, int &slot) const;
+ /** returns slot of launcher weapon currently equipped */
+ int FindRangedWeapon() const;
+ /** returns slot of launcher weapon for specified projectile type */
+ int FindTypedRangedWeapon(unsigned int type) const;
+ /** returns slot of launcher weapon for projectile in specified slot */
+ int FindSlotRangedWeapon(ieDword slot) const;
+ /** Returns a slot which might be empty, or capable of holding item (or part of it) */
+ int FindCandidateSlot(int slottype, size_t first_slot, const char *resref = NULL);
+ /** Creates an item in the slot*/
+ void SetSlotItemRes(const ieResRef ItemResRef, int Slot, int Charge0=1, int Charge1=0, int Charge2=0);
+ /** Adds item to slot*/
+ void AddSlotItemRes(const ieResRef ItemResRef, int Slot, int Charge0=1, int Charge1=0, int Charge2=0);
+ /** breaks the item (weapon) in slot */
+ void BreakItemSlot(ieDword slot);
+ /** Lists all items in the Inventory on terminal for debugging */
+ void dump();
+ /** Equips best weapon */
+ void EquipBestWeapon(int flags);
+ /** returns the struct of the usable items, returns true if there are more */
+ bool GetEquipmentInfo(ItemExtHeader *array, int startindex, int count);
+ /** returns the exclusion bits */
+ ieDword GetEquipExclusion(int index) const;
+ /** returns if a slot is temporarily blocked */
+ bool IsSlotBlocked(int slot) const;
+ /** returns true if a two handed weapon is in slot */
+ inline bool TwoHandedInSlot(int slot) const;
+ /** returns the strref for the reason why the item cannot be equipped */
+ int WhyCantEquip(int slot, int twohanded) const;
+ /** returns a slot that has a stealable item */
+ unsigned int FindStealableItem();
+ /** checks if any equipped item provides critical hit aversion */
+ bool ProvidesCriticalAversion();
+ //setting important constants
+ static void Init(int mb);
+ static void SetHeadSlot(int arg);
+ static void SetFistSlot(int arg);
+ static void SetMagicSlot(int arg);
+ static void SetWeaponSlot(int arg);
+ static void SetRangedSlot(int arg);
+ static void SetQuickSlot(int arg);
+ static void SetInventorySlot(int arg);
+ static void SetShieldSlot(int arg);
+ static int GetHeadSlot();
+ static int GetFistSlot();
+ static int GetMagicSlot();
+ static int GetWeaponSlot();
+ static int GetRangedSlot();
+ static int GetQuickSlot();
+ static int GetInventorySlot();
+private:
+ int FindRangedProjectile(unsigned int type) const;
+ // called by KillSlot
+ void RemoveSlotEffects( /*CREItem* slot*/ ieDword slot );
+ void KillSlot(ieDword index);
+ inline Item *GetItemPointer(ieDword slot, CREItem *&Slot) const;
+ void UpdateWeaponAnimation();
+ void UpdateShieldAnimation(Item *it);
+};
+
+#endif
diff --git a/gemrb/core/Item.cpp b/gemrb/core/Item.cpp
new file mode 100644
index 0000000..0c9eed4
--- /dev/null
+++ b/gemrb/core/Item.cpp
@@ -0,0 +1,248 @@
+/* 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 the .itm (item) files of the engine
+// Items are all the weapons, armor, carriable quest objects, etc.
+
+#include "Item.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+#include "Projectile.h"
+#include "ProjectileServer.h"
+
+ITMExtHeader::ITMExtHeader(void)
+{
+ features = NULL;
+}
+
+ITMExtHeader::~ITMExtHeader(void)
+{
+ delete [] features;
+}
+
+Item::Item(void)
+{
+ ext_headers = NULL;
+ equipping_features = NULL;
+}
+
+Item::~Item(void)
+{
+ //core->FreeITMExt( ext_headers, equipping_features );
+ delete [] ext_headers;
+ delete [] equipping_features;
+}
+
+//-1 will return equipping feature block
+//otherwise returns the n'th feature block
+EffectQueue *Item::GetEffectBlock(Scriptable *self, const Point &pos, int usage, ieDwordSigned invslot, ieDword pro) const
+{
+ Effect *features;
+ int count;
+
+ if (usage>=ExtHeaderCount) {
+ return NULL;
+ }
+ if (usage>=0) {
+ features = ext_headers[usage].features;
+ count = ext_headers[usage].FeatureCount;
+ } else {
+ features = equipping_features;
+ count = EquippingFeatureCount;
+ }
+ EffectQueue *fxqueue = new EffectQueue();
+
+ for (int i=0;i<count;i++) {
+ Effect *fx = features+i;
+ fx->InventorySlot = invslot;
+ fx->CasterLevel = ITEM_CASTERLEVEL; //items all have casterlevel 10
+ if (usage >= 0) {
+ //this is not coming from the item header, but from the recharge flags
+ fx->SourceFlags = ext_headers[usage].RechargeFlags;
+ } else {
+ fx->SourceFlags = 0;
+ }
+ if (fx->Target != FX_TARGET_SELF) {
+ fx->Projectile = pro;
+ fxqueue->AddEffect( fx );
+ } else {
+ Actor *target = (self->Type==ST_ACTOR)?(Actor *) self:NULL;
+ fx->Projectile = 0;
+ fx->PosX=pos.x;
+ fx->PosY=pos.y;
+ if (target) {
+ core->ApplyEffect(fx, target, self);
+ }
+ }
+ }
+
+ //adding a pulse effect for weapons (PST)
+ //if it is an equipping effect block
+ if ((usage==-1) && (WieldColor!=0xffff)) {
+ if (Flags&IE_ITEM_PULSATING) {
+ Effect *tmp = BuildGlowEffect(WieldColor);
+ if (tmp) {
+ tmp->InventorySlot = invslot;
+ tmp->Projectile=pro;
+ fxqueue->AddEffect( tmp );
+ delete tmp;
+ }
+ }
+ }
+ return fxqueue;
+}
+
+/** returns the average damage this weapon would cause */
+int Item::GetDamagePotential(bool ranged, ITMExtHeader *&header) const
+{
+ header = GetWeaponHeader(ranged);
+ if (header) {
+ return header->DiceThrown*(header->DiceSides+1)/2+header->DamageBonus;
+ }
+ return -1;
+}
+
+int Item::GetWeaponHeaderNumber(bool ranged) const
+{
+ for(int ehc=0; ehc<ExtHeaderCount; ehc++) {
+ ITMExtHeader *ext_header = GetExtHeader(ehc);
+ if (ext_header->Location!=ITEM_LOC_WEAPON) {
+ continue;
+ }
+ unsigned char AType = ext_header->AttackType;
+ if (ranged) {
+ if ((AType!=ITEM_AT_PROJECTILE) && (AType!=ITEM_AT_BOW) ) {
+ continue;
+ }
+ } else {
+ if (AType!=ITEM_AT_MELEE) {
+ continue;
+ }
+ }
+ return ehc;
+ }
+ return 0xffff; //invalid extheader number
+}
+
+int Item::GetEquipmentHeaderNumber(int cnt) const
+{
+ for(int ehc=0; ehc<ExtHeaderCount; ehc++) {
+ ITMExtHeader *ext_header = GetExtHeader(ehc);
+ if (ext_header->Location!=ITEM_LOC_EQUIPMENT) {
+ continue;
+ }
+ if (ext_header->AttackType!=ITEM_AT_MAGIC) {
+ continue;
+ }
+
+ if (cnt) {
+ cnt--;
+ continue;
+ }
+ return ehc;
+ }
+ return 0xffff; //invalid extheader number
+}
+
+ITMExtHeader *Item::GetWeaponHeader(bool ranged) const
+{
+ //start from the beginning
+ return GetExtHeader(GetWeaponHeaderNumber(ranged)) ;
+}
+
+int Item::UseCharge(ieWord *Charges, int header, bool expend) const
+{
+ ITMExtHeader *ieh = GetExtHeader(header);
+ if (!ieh) return 0;
+ int type = ieh->ChargeDepletion;
+
+ int ccount = 0;
+ if ((header>=CHARGE_COUNTERS) || (header<0/*weapon header*/)) {
+ header = 0;
+ }
+ ccount=Charges[header];
+
+ //if the item started from 0 charges, then it isn't depleting
+ if (ieh->Charges==0) {
+ return CHG_NONE;
+ }
+ if (expend) {
+ Charges[header] = --ccount;
+ }
+
+ if (ccount>0) {
+ return CHG_NONE;
+ }
+ if (type == CHG_NONE) {
+ Charges[header]=0;
+ }
+ return type;
+}
+
+//returns a projectile loaded with the effect queue
+Projectile *Item::GetProjectile(Scriptable *self, int header, const Point &target, ieDwordSigned invslot, int miss) const
+{
+ ITMExtHeader *eh = GetExtHeader(header);
+ if (!eh) {
+ return NULL;
+ }
+ ieDword idx = eh->ProjectileAnimation;
+ Projectile *pro = core->GetProjectileServer()->GetProjectileByIndex(idx);
+ int usage ;
+ if (header>= 0)
+ usage = header;
+ else
+ usage = GetWeaponHeaderNumber(header==-2);
+ if (!miss) {
+ EffectQueue *fx = GetEffectBlock(self, target, usage, invslot, idx);
+ pro->SetEffects(fx);
+ }
+ return pro;
+}
+
+//this is the implementation of the weapon glow effect in PST
+static EffectRef glow_ref = { "Color:PulseRGB", -1 };
+//this type of colour uses PAL32, a PST specific palette
+#define PALSIZE 32
+static Color ActorColor[PALSIZE];
+
+Effect *Item::BuildGlowEffect(int gradient) const
+{
+ //palette entry to to RGB conversion
+ core->GetPalette( gradient, PALSIZE, ActorColor );
+ ieDword rgb = (ActorColor[16].r<<16) | (ActorColor[16].g<<8) | ActorColor[16].b;
+ ieDword location = 0;
+ ieDword speed = 128;
+ Effect *fx = EffectQueue::CreateEffect(glow_ref, rgb, location|(speed<<16), FX_DURATION_INSTANT_WHILE_EQUIPPED);
+ return fx;
+}
+
+unsigned int Item::GetCastingDistance(int idx) const
+{
+ ITMExtHeader *seh = GetExtHeader(idx);
+ if (!seh) {
+ printMessage("Item", "Cannot retrieve item header!!! ",RED);
+ printf("required header: %d, maximum: %d\n", idx, (int) ExtHeaderCount);
+ return 0;
+ }
+ return (unsigned int) seh->Range;
+}
diff --git a/gemrb/core/Item.h b/gemrb/core/Item.h
new file mode 100644
index 0000000..d395268
--- /dev/null
+++ b/gemrb/core/Item.h
@@ -0,0 +1,268 @@
+/* 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 Item.h
+ * Declares Item, class for all things your PCs can pick, carry and wear
+ * and many they can't.
+ * @author The GemRB Project
+ */
+
+#ifndef ITEM_H
+#define ITEM_H
+
+#include "exports.h"
+#include "ie_types.h"
+
+#include "EffectQueue.h"
+
+class Projectile;
+
+// Item Flags bits
+// !!! Keep these synchronized with GUIDefines.h !!!
+#define IE_ITEM_CRITICAL 0x00000001
+#define IE_ITEM_TWO_HANDED 0x00000002
+#define IE_ITEM_MOVABLE 0x00000004
+#define IE_ITEM_DISPLAYABLE 0x00000008
+#define IE_ITEM_CURSED 0x00000010
+#define IE_ITEM_NOT_COPYABLE 0x00000020
+#define IE_ITEM_MAGICAL 0x00000040
+#define IE_ITEM_BOW 0x00000080
+#define IE_ITEM_SILVER 0x00000100
+#define IE_ITEM_COLD_IRON 0x00000200
+#define IE_ITEM_STOLEN 0x00000400
+#define IE_ITEM_CONVERSABLE 0x00000800
+#define IE_ITEM_PULSATING 0x00001000
+#define IE_ITEM_UNSELLABLE ( IE_ITEM_CRITICAL | IE_ITEM_STOLEN )
+
+//modder extensions
+#define IE_ITEM_NO_DISPEL 0x01000000 //disables destruction by dispelling
+#define IE_ITEM_TOGGLE_CRITS 0x02000000 //toggles critical hit avertion
+
+//Extended header recharge flags
+#define IE_ITEM_USESTRENGTH 1 //weapon
+#define IE_ITEM_BREAKABLE 2 //weapon
+#define IE_ITEM_HOSTILE 0x400 //equipment
+#define IE_ITEM_RECHARGE 0x800 //equipment
+#define IE_ITEM_IGNORESHIELD 0x10000 //weapon
+#define IE_ITEM_KEEN 0x20000 //weapon
+
+//modder extensions
+#define IE_ITEM_BACKSTAB 0x01000000 //can used for backstab (ranged weapon)
+
+//item use locations (weapons are not listed in equipment list)
+#define ITEM_LOC_WEAPON 1 //this is a weapon slot (uses thac0 etc)
+#define ITEM_LOC_EQUIPMENT 3 //this slot appears on equipment list
+//other item locations appear useless
+
+//attack types
+#define ITEM_AT_MELEE 1
+#define ITEM_AT_PROJECTILE 2
+#define ITEM_AT_MAGIC 3
+#define ITEM_AT_BOW 4
+
+//target types
+#define TARGET_INVALID 0 //all the rest (default)
+#define TARGET_CREA 1 //single living creature
+#define TARGET_INV 2 //inventory item (not used?)
+#define TARGET_DEAD 3 //creature, item or point
+#define TARGET_AREA 4 //point target
+#define TARGET_SELF 5 //self
+#define TARGET_UNKNOWN 6 //unknown (use default)
+#define TARGET_NONE 7 //self
+
+//projectile qualifiers
+#define PROJ_ARROW 1
+#define PROJ_BOLT 2
+#define PROJ_BULLET 4
+
+//charge depletion flags
+#define CHG_NONE 0
+#define CHG_BREAK 1
+#define CHG_NOSOUND 2
+#define CHG_DAY 3
+
+//items caster level is hardcoded to 10
+#define ITEM_CASTERLEVEL 10
+/**
+ * @class ITMExtHeader
+ * Header for special effects and uses of an Item.
+ */
+
+class GEM_EXPORT ITMExtHeader {
+public:
+ ITMExtHeader();
+ ~ITMExtHeader();
+ ieByte AttackType;
+ ieByte IDReq;
+ ieByte Location;
+ ieByte unknown1;
+ ieResRef UseIcon;
+ ieByte Target;
+ ieByte TargetNumber;
+ ieWord Range;
+ //this is partially redundant, but the original engine uses this value to
+ //determine projectile type (ProjectileQualifier is almost always set too)
+ //We use this field only when really needed, and resolve the redundancy
+ //in the exporter. The reason: using bitfields is more flexible.
+ //ieWord ProjectileType;
+ ieWord Speed;
+ ieWord THAC0Bonus;
+ ieWord DiceSides;
+ ieWord DiceThrown;
+ ieWordSigned DamageBonus; //this must be signed!!!
+ ieWord DamageType;
+ ieWord FeatureCount;
+ ieWord FeatureOffset;
+ ieWord Charges;
+ ieWord ChargeDepletion;
+ ieDword RechargeFlags; //this is a bitfield with many bits
+ ieWord ProjectileAnimation;
+ ieWord MeleeAnimation[3];
+ //this value is set in projectiles and launchers too
+ int ProjectileQualifier; //this is a derived value determined on load time
+ Effect *features;
+};
+
+/**
+ * @class Item
+ * Class for all things your PCs can pick, carry and wear and many they can't.
+ */
+
+class GEM_EXPORT Item {
+public:
+ Item();
+ ~Item();
+
+ ITMExtHeader *ext_headers;
+ Effect *equipping_features;
+ ieResRef Name; //the resref of the item itself!
+
+ ieStrRef ItemName;
+ ieStrRef ItemNameIdentified;
+ ieResRef ReplacementItem;
+ ieDword Flags;
+ ieWord ItemType;
+ ieDword UsabilityBitmask;
+ char AnimationType[2];
+ ieByte MinLevel;
+ ieByte unknown1;
+ ieByte MinStrength;
+ ieByte unknown2;
+ ieByte MinStrengthBonus;
+ //kit1
+ ieByte MinIntelligence;
+ //kit2
+ ieByte MinDexterity;
+ //kit3
+ ieByte MinWisdom;
+ //kit4
+ ieByte MinConstitution;
+ ieByte WeaProf;
+ ieByte MinCharisma;
+ ieByte unknown3;
+ ieDword KitUsability;
+ ieDword Price;
+ ieWord StackAmount;
+ ieResRef ItemIcon;
+ ieWord LoreToID;
+ ieResRef GroundIcon;
+ ieDword Weight;
+ ieStrRef ItemDesc;
+ ieStrRef ItemDescIdentified;
+ ieResRef DescriptionIcon;
+ ieDword Enchantment;
+ ieDword ExtHeaderOffset;
+ ieWord ExtHeaderCount;
+ ieDword FeatureBlockOffset;
+ ieWord EquippingFeatureOffset;
+ ieWord EquippingFeatureCount;
+
+ // PST only
+ ieResRef Dialog;
+ ieStrRef DialogName;
+ ieWord WieldColor;
+
+ // PST and IWD2 only
+ char unknown[26];
+ // flag items to mutually exclusive to equip
+ ieDword ItemExcl;
+public:
+ ieStrRef GetItemName(bool identified) const
+ {
+ if(identified) {
+ if((int) ItemNameIdentified>=0) return ItemNameIdentified;
+ return ItemName;
+ }
+ if((int) ItemName>=0) {
+ return ItemName;
+ }
+ return ItemNameIdentified;
+ }
+ ieStrRef GetItemDesc(bool identified) const
+ {
+ if(identified) {
+ if((int) ItemDescIdentified>=0) return ItemDescIdentified;
+ return ItemDesc;
+ }
+ if((int) ItemDesc>=0) {
+ return ItemDesc;
+ }
+ return ItemDescIdentified;
+ }
+
+ //returns if the item is usable, expend will also expend a charge
+ int UseCharge(ieWord *Charges, int header, bool expend) const;
+
+ //returns the requested extended header
+ //-1 will return melee weapon header, -2 the ranged one
+ ITMExtHeader *GetExtHeader(int which) const
+ {
+ if(which < 0)
+ return GetWeaponHeader(which == -2) ;
+ if(ExtHeaderCount<=which) {
+ return NULL;
+ }
+ return ext_headers+which;
+ }
+ ieDword GetWieldedGradient() const
+ {
+ return WieldColor;
+ }
+
+ //-1 will return the equipping feature block
+ EffectQueue *GetEffectBlock(Scriptable *self, const Point &pos, int header, ieDwordSigned invslot, ieDword pro) const;
+ //returns a projectile created from an extended header
+ //if miss is non-zero, then no effects will be loaded
+ Projectile *GetProjectile(Scriptable *self, int header, const Point &target, ieDwordSigned invslot, int miss) const;
+ //Builds an equipping glow effect from gradient colour
+ //this stuff is not item specific, could be moved elsewhere
+ Effect *BuildGlowEffect(int gradient) const;
+ //returns the average damage of the weapon (doesn't check for special effects)
+ int GetDamagePotential(bool ranged, ITMExtHeader *&header) const;
+ //returns the weapon header
+ ITMExtHeader *GetWeaponHeader(bool ranged) const;
+ int GetWeaponHeaderNumber(bool ranged) const;
+ int GetEquipmentHeaderNumber(int cnt) const;
+ unsigned int GetCastingDistance(int header) const;
+private:
+};
+
+#endif // ! ITEM_H
diff --git a/gemrb/core/ItemMgr.cpp b/gemrb/core/ItemMgr.cpp
new file mode 100644
index 0000000..5701189
--- /dev/null
+++ b/gemrb/core/ItemMgr.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 "ItemMgr.h"
+
+ItemMgr::ItemMgr(void)
+{
+}
+
+ItemMgr::~ItemMgr(void)
+{
+}
diff --git a/gemrb/core/ItemMgr.h b/gemrb/core/ItemMgr.h
new file mode 100644
index 0000000..1013689
--- /dev/null
+++ b/gemrb/core/ItemMgr.h
@@ -0,0 +1,48 @@
+/* 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 ItemMgr.h
+ * Declares ItemMgr class, loader for Item objects
+ * @author The GemRB Project
+ */
+
+
+#ifndef ITEMMGR_H
+#define ITEMMGR_H
+
+#include "Item.h"
+#include "Plugin.h"
+#include "System/DataStream.h"
+
+/**
+ * @class ItemMgr
+ * Abstract loader for Item objects
+ */
+
+class GEM_EXPORT ItemMgr : public Plugin {
+public:
+ ItemMgr(void);
+ virtual ~ItemMgr(void);
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual Item* GetItem(Item *s) = 0;
+};
+
+#endif
diff --git a/gemrb/core/LRUCache.cpp b/gemrb/core/LRUCache.cpp
new file mode 100644
index 0000000..afb72ac
--- /dev/null
+++ b/gemrb/core/LRUCache.cpp
@@ -0,0 +1,219 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2007
+ *
+ * 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 "LRUCache.h"
+
+#include <cassert>
+#include <cstdio>
+
+struct VarEntry {
+ VarEntry* prev;
+ VarEntry* next;
+ void* data;
+ char* key;
+};
+
+LRUCache::LRUCache() : v(), head(0), tail(0) {
+ v.SetType(GEM_VARIABLES_POINTER);
+ v.ParseKey(1);
+}
+
+LRUCache::~LRUCache()
+{
+
+}
+
+int LRUCache::GetCount() const
+{
+ return v.GetCount();
+}
+
+void LRUCache::SetAt(const char* key, void* value)
+{
+ void* p;
+ if (v.Lookup(key, p)) {
+ VarEntry* e = (VarEntry*) p;
+ e->data = value;
+ Touch(key);
+ return;
+ }
+
+ VarEntry* e = new VarEntry();
+ e->prev = 0;
+ e->next = head;
+ e->data = value;
+ e->key = new char[strlen(key)+1];
+ strcpy(e->key, key);
+
+ if (head)
+ head->prev = e;
+ head = e;
+ if (tail == 0) tail = head;
+
+ v.SetAt(key, (void*)e);
+}
+
+bool LRUCache::Lookup(const char* key, void*& value) const
+{
+ void* p;
+ if (v.Lookup(key, p)) {
+ VarEntry* e = (VarEntry*) p;
+ value = e->data;
+ return true;
+ }
+ return false;
+}
+
+bool LRUCache::Touch(const char* key)
+{
+ void* p;
+ if (!v.Lookup(key, p)) return false;
+ VarEntry* e = (VarEntry*) p;
+
+ // already head?
+ if (!e->prev) return true;
+
+ removeFromList(e);
+
+ // re-add e as head:
+ e->prev = 0;
+ e->next = head;
+ head->prev = e;
+ head = e;
+ if (tail == 0) tail = head;
+ return true;
+}
+
+bool LRUCache::Remove(const char* key)
+{
+ void* p;
+ if (!v.Lookup(key, p)) return false;
+ VarEntry* e = (VarEntry*) p;
+ v.Remove(key);
+ removeFromList(e);
+ delete[] e->key;
+ delete e;
+ return true;
+}
+
+bool LRUCache::getLRU(unsigned int n, const char*& key, void*& value) const
+{
+ VarEntry* e = tail;
+ for (unsigned int i = 0; i < n; ++i) {
+ if (!e) return false;
+ e = e->prev;
+ }
+ if (!e) return false;
+
+ key = e->key;
+ value = e->data;
+ return true;
+}
+
+void LRUCache::removeFromList(VarEntry* e)
+{
+ if (e->prev) {
+ assert(e != head);
+ e->prev->next = e->next;
+ } else {
+ assert(e == head);
+ head = e->next;
+ }
+
+ if (e->next) {
+ assert(e != tail);
+ e->next->prev = e->prev;
+ } else {
+ assert(e == tail);
+ tail = e->prev;
+ }
+
+ e->prev = e->next = 0;
+}
+
+
+void testLRUCache()
+{
+ int i;
+ LRUCache c;
+
+ int t[100];
+ for (i = 0; i < 100; ++i) t[i] = 1000+i;
+ char* k[100];
+ for (i = 0; i < 100; ++i) {
+ k[i] = new char[5];
+ sprintf(k[i], "k%03d", i);
+ }
+
+ bool r;
+ void* p;
+ const char* k2 = 0;
+
+ r = c.Lookup("k050", p);
+ assert(!r);
+
+ c.SetAt("k050", &t[50]);
+ r = c.Lookup("k050", p);
+ assert(r);
+ assert(p == &t[50]);
+
+ for (i = 0; i < 100; ++i)
+ c.SetAt(k[i], &t[i]);
+
+ r = c.getLRU(0, k2, p);
+ assert(r);
+ assert(strcmp(k2, "k000") == 0);
+ assert(p == &t[0]);
+
+ c.Touch("k000");
+ r = c.getLRU(0, k2, p);
+ assert(r);
+ assert(strcmp(k2, "k001") == 0);
+ assert(p == &t[1]);
+
+ r = c.getLRU(1, k2, p);
+ assert(r);
+ assert(strcmp(k2, "k002") == 0);
+ assert(p == &t[2]);
+
+ c.Remove("k001");
+
+ r = c.getLRU(0, k2, p);
+ assert(r);
+ assert(strcmp(k2, "k002") == 0);
+ assert(p == &t[2]);
+
+ for (i = 0; i < 98; ++i) {
+ r = c.getLRU(0, k2, p);
+ assert(r);
+ assert(strcmp(k2, k[2+i]) == 0);
+ assert(p == &t[2+i]);
+ c.Remove(k2);
+ }
+
+ assert(c.GetCount() == 1);
+
+ r = c.getLRU(0, k2, p);
+ assert(r);
+ assert(strcmp(k2, "k000") == 0);
+ assert(p == &t[0]);
+
+ assert(!c.getLRU(1, k2, p));
+}
diff --git a/gemrb/core/LRUCache.h b/gemrb/core/LRUCache.h
new file mode 100644
index 0000000..31add80
--- /dev/null
+++ b/gemrb/core/LRUCache.h
@@ -0,0 +1,59 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2007
+ *
+ * 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 LRUCACHE_H
+#define LRUCACHE_H
+
+#include "exports.h"
+
+#include "Variables.h"
+
+struct VarEntry;
+
+class GEM_EXPORT LRUCache {
+public:
+ LRUCache();
+ ~LRUCache();
+
+ // set value, overwriting any previous entry
+ void SetAt(const char* key, void* value);
+ bool Lookup(const char* key, void*& value) const;
+ bool Touch(const char* key);
+ bool Remove(const char* key);
+
+ int GetCount() const;
+
+ // return n-th LRU entry. key remains owned by LRUCache.
+ // (n = 0 is least recently used, n = 1 the next least recently used,
+ // etc...)
+ bool getLRU(unsigned int n, const char*& key, void*& value) const;
+
+private:
+ // internal storage
+ Variables v;
+ VarEntry* head;
+ VarEntry* tail;
+
+ void removeFromList(VarEntry* e);
+};
+
+
+
+#endif
diff --git a/gemrb/core/Makefile.am b/gemrb/core/Makefile.am
new file mode 100644
index 0000000..26bea79
--- /dev/null
+++ b/gemrb/core/Makefile.am
@@ -0,0 +1,119 @@
+lib_LTLIBRARIES = libgemrb_core.la
+libgemrb_core_la_LDFLAGS = -version-info 0:0:0 @LIBDL@
+AM_CPPFLAGS = -DGEM_BUILD_DLL
+libgemrb_core_la_SOURCES = \
+ ActorMgr.cpp \
+ Ambient.cpp \
+ AmbientMgr.cpp \
+ Animation.cpp \
+ AnimationFactory.cpp \
+ AnimationMgr.cpp \
+ ArchiveImporter.cpp \
+ Audio.cpp \
+ Bitmap.cpp \
+ Cache.cpp \
+ Calendar.cpp \
+ Callback.cpp \
+ CharAnimations.cpp \
+ Compressor.cpp \
+ ControlAnimation.cpp \
+ Core.cpp \
+ DataFileMgr.cpp \
+ Dialog.cpp \
+ DialogHandler.cpp \
+ DialogMgr.cpp \
+ DisplayMessage.cpp \
+ EffectMgr.cpp \
+ EffectQueue.cpp \
+ Factory.cpp \
+ FactoryObject.cpp \
+ Font.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 \
+ Game.cpp \
+ GameData.cpp \
+ GameScript/Actions.cpp \
+ GameScript/GSUtils.cpp \
+ GameScript/GameScript.cpp \
+ GameScript/Matching.cpp \
+ GameScript/Objects.cpp \
+ GameScript/Triggers.cpp \
+ GlobalTimer.cpp \
+ Image.cpp \
+ ImageFactory.cpp \
+ ImageMgr.cpp \
+ ImageWriter.cpp \
+ IniSpawn.cpp \
+ Interface.cpp \
+ Inventory.cpp \
+ Item.cpp \
+ ItemMgr.cpp \
+ LRUCache.cpp \
+ Map.cpp \
+ MapMgr.cpp \
+ MoviePlayer.cpp \
+ MusicMgr.cpp \
+ Palette.cpp \
+ PalettedImageMgr.cpp \
+ Particles.cpp \
+ Plugin.cpp \
+ PluginMgr.cpp \
+ Polygon.cpp \
+ Projectile.cpp \
+ ProjectileMgr.cpp \
+ ProjectileServer.cpp \
+ Region.cpp \
+ Resource.cpp \
+ ResourceDesc.cpp \
+ ResourceManager.cpp \
+ ResourceSource.cpp \
+ SaveGameIterator.cpp \
+ SaveGameMgr.cpp \
+ ScriptEngine.cpp \
+ Scriptable/Actor.cpp \
+ Scriptable/Container.cpp \
+ Scriptable/Door.cpp \
+ Scriptable/InfoPoint.cpp \
+ Scriptable/Scriptable.cpp \
+ Scriptable/PCStatStruct.cpp \
+ ScriptedAnimation.cpp \
+ SoundMgr.cpp \
+ Spell.cpp \
+ SpellMgr.cpp \
+ Spellbook.cpp \
+ Sprite2D.cpp \
+ SpriteCover.cpp \
+ Store.cpp \
+ StoreMgr.cpp \
+ StringMgr.cpp \
+ SymbolMgr.cpp \
+ System/CachedFileStream.cpp \
+ System/DataStream.cpp \
+ System/FileStream.cpp \
+ System/MemoryStream.cpp \
+ System/VFS.cpp \
+ TableMgr.cpp \
+ Tile.cpp \
+ TileMap.cpp \
+ TileMapMgr.cpp \
+ TileOverlay.cpp \
+ TileSetMgr.cpp \
+ Variables.cpp \
+ Video.cpp \
+ WindowMgr.cpp \
+ WorldMap.cpp \
+ WorldMapMgr.cpp
+
+noinst_HEADERS = *.h
diff --git a/gemrb/core/Map.cpp b/gemrb/core/Map.cpp
new file mode 100644
index 0000000..f89036c
--- /dev/null
+++ b/gemrb/core/Map.cpp
@@ -0,0 +1,3704 @@
+/* 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.
+ *
+ *
+ */
+
+// This class represents the .ARE (game area) files in the engine
+
+#include "Map.h"
+
+#include "win32def.h"
+
+#include "Ambient.h"
+#include "AmbientMgr.h"
+#include "Audio.h"
+#include "DisplayMessage.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "MapMgr.h"
+#include "MusicMgr.h"
+#include "ImageMgr.h"
+#include "Palette.h"
+#include "Particles.h"
+#include "PathFinder.h"
+#include "Projectile.h"
+#include "ScriptedAnimation.h"
+#include "TileMap.h"
+#include "Video.h"
+#include "WorldMap.h"
+#include "strrefs.h"
+#include "GameScript/GSUtils.h"
+#include "GUI/GameControl.h"
+#include "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.h"
+
+#include <cmath>
+#include <cassert>
+
+#define YESNO(x) ( (x)?"Yes":"No")
+
+// TODO: fix this hardcoded resource reference
+static ieResRef PortalResRef={"EF03TPR3"};
+static unsigned int PortalTime = 15;
+static unsigned int MAX_CIRCLESIZE = 8;
+static int MaxVisibility = 30;
+static int VisibilityPerimeter; //calculated from MaxVisibility
+static int NormalCost = 10;
+static int AdditionalCost = 4;
+static unsigned char Passable[16] = {
+ 4, 1, 1, 1, 1, 1, 1, 1, 0, 1, 8, 0, 0, 0, 3, 1
+};
+static Point **VisibilityMasks=NULL;
+
+static bool PathFinderInited = false;
+static Variables Spawns;
+static int LargeFog;
+static TerrainSounds *terrainsounds=NULL;
+static int tsndcount = -1;
+
+void ReleaseSpawnGroup(void *poi)
+{
+ delete (SpawnGroup *) poi;
+}
+
+void Map::ReleaseMemory()
+{
+ if (VisibilityMasks) {
+ for (int i=0;i<MaxVisibility;i++) {
+ free(VisibilityMasks[i]);
+ }
+ free(VisibilityMasks);
+ VisibilityMasks = NULL;
+ }
+ Spawns.RemoveAll(ReleaseSpawnGroup);
+ PathFinderInited = false;
+ if (terrainsounds) {
+ delete [] terrainsounds;
+ terrainsounds = NULL;
+ }
+}
+
+inline static AnimationObjectType SelectObject(Actor *actor, int q, AreaAnimation *a, ScriptedAnimation *sca, Particles *spark, Projectile *pro)
+{
+ int actorh;
+ if (actor) {
+ actorh = actor->Pos.y;
+ if (q) actorh = 0;
+ } else {
+ actorh = 0x7fffffff;
+ }
+
+ int aah;
+ if (a) {
+ //aah = a->Pos.y;//+a->height;
+ aah = a->GetHeight();
+ } else {
+ aah = 0x7fffffff;
+ }
+
+ int scah;
+ if (sca) {
+ scah = sca->YPos;//+sca->ZPos;
+ } else {
+ scah = 0x7fffffff;
+ }
+
+ int spah;
+ if (spark) {
+ //no idea if this should be plus or minus (or here at all)
+ spah = spark->GetHeight();//+spark->pos.h;
+ } else {
+ spah = 0x7fffffff;
+ }
+
+ int proh;
+ if (pro) {
+ proh = pro->GetHeight();
+ } else {
+ proh = 0x7fffffff;
+ }
+
+ if (proh<actorh && proh<scah && proh<aah && proh<spah) return AOT_PROJECTILE;
+
+ if (spah<actorh && spah<scah && spah<aah) return AOT_SPARK;
+
+ if (aah<actorh && aah<scah) return AOT_AREA;
+
+ if (scah<actorh) return AOT_SCRIPTED;
+
+ return AOT_ACTOR;
+}
+
+//returns true if creature must be embedded in the area
+//npcs in saved game shouldn't be embedded either
+inline static bool MustSave(Actor *actor)
+{
+ if (actor->Persistent()) {
+ return false;
+ }
+
+ //check for familiars, summons?
+ return true;
+}
+
+//Preload spawn group entries (creature resrefs that reference groups of creatures)
+void InitSpawnGroups()
+{
+ ieResRef GroupName;
+ int i;
+
+ AutoTable tab("spawngrp");
+
+ Spawns.RemoveAll(NULL);
+ Spawns.SetType( GEM_VARIABLES_POINTER );
+
+ if (!tab)
+ return;
+
+ i=tab->GetColNamesCount();
+ while (i--) {
+ int j=tab->GetRowCount();
+ while (j--) {
+ const char *crename = tab->QueryField( j,i );
+ if (crename[0] != '*') break;
+ }
+ if (j>0) {
+ SpawnGroup *creatures = new SpawnGroup(j);
+ //difficulty
+ creatures->Level = (ieDword) atoi( tab->QueryField(0,i) );
+ for (;j;j--) {
+ strnlwrcpy( creatures->ResRefs[j-1], tab->QueryField(j,i), 8 );
+ }
+ strnlwrcpy( GroupName, tab->GetColumnName( i ), 8 );
+ Spawns.SetAt( GroupName, (void*) creatures );
+ }
+ }
+}
+
+//Preload the searchmap configuration
+void InitPathFinder()
+{
+ PathFinderInited = true;
+ tsndcount = 0;
+ AutoTable tm("pathfind");
+
+ if (!tm) {
+ return;
+ }
+
+ const char* poi;
+
+ for (int i = 0; i < 16; i++) {
+ poi = tm->QueryField( 0, i );
+ if (*poi != '*')
+ Passable[i] = atoi( poi );
+ }
+ poi = tm->QueryField( 1, 0 );
+ if (*poi != '*')
+ NormalCost = atoi( poi );
+ poi = tm->QueryField( 1, 1 );
+ if (*poi != '*')
+ AdditionalCost = atoi( poi );
+ int rc = tm->GetRowCount()-2;
+ if (rc>0) {
+ terrainsounds = new TerrainSounds[rc];
+ tsndcount = rc;
+ while(rc--) {
+ strnuprcpy(terrainsounds[rc].Group,tm->GetRowName(rc+2), sizeof(ieResRef)-1 );
+ for(int i = 0; i<16;i++) {
+ strnuprcpy(terrainsounds[rc].Sounds[i], tm->QueryField(rc+2, i), sizeof(ieResRef)-1 );
+ }
+ }
+ }
+}
+
+void AddLOS(int destx, int desty, int slot)
+{
+ for (int i=0;i<MaxVisibility;i++) {
+ int x = ((destx*i + MaxVisibility/2) / MaxVisibility) * 16;
+ int y = ((desty*i + MaxVisibility/2) / MaxVisibility) * 12;
+ if (LargeFog) {
+ x += 16;
+ y += 12;
+ }
+ VisibilityMasks[i][slot].x=(short) x;
+ VisibilityMasks[i][slot].y=(short) y;
+ }
+}
+
+void InitExplore()
+{
+ LargeFog = !core->HasFeature(GF_SMALL_FOG);
+
+ //circle perimeter size for MaxVisibility
+ int x = MaxVisibility;
+ int y = 0;
+ int xc = 1 - ( 2 * MaxVisibility );
+ int yc = 1;
+ int re = 0;
+ VisibilityPerimeter = 0;
+ while (x>=y) {
+ VisibilityPerimeter+=8;
+ y++;
+ re += yc;
+ yc += 2;
+ if (( ( 2 * re ) + xc ) > 0) {
+ x--;
+ re += xc;
+ xc += 2;
+ }
+ }
+
+ int i;
+ VisibilityMasks = (Point **) malloc(MaxVisibility * sizeof(Point *) );
+ for (i=0;i<MaxVisibility;i++) {
+ VisibilityMasks[i] = (Point *) malloc(VisibilityPerimeter*sizeof(Point) );
+ }
+
+ x = MaxVisibility;
+ y = 0;
+ xc = 1 - ( 2 * MaxVisibility );
+ yc = 1;
+ re = 0;
+ VisibilityPerimeter = 0;
+ while (x>=y) {
+ AddLOS (x, y, VisibilityPerimeter++);
+ AddLOS (-x, y, VisibilityPerimeter++);
+ AddLOS (-x, -y, VisibilityPerimeter++);
+ AddLOS (x, -y, VisibilityPerimeter++);
+ AddLOS (y, x, VisibilityPerimeter++);
+ AddLOS (-y, x, VisibilityPerimeter++);
+ AddLOS (-y, -x, VisibilityPerimeter++);
+ AddLOS (y, -x, VisibilityPerimeter++);
+ y++;
+ re += yc;
+ yc += 2;
+ if (( ( 2 * re ) + xc ) > 0) {
+ x--;
+ re += xc;
+ xc += 2;
+ }
+ }
+}
+
+Map::Map(void)
+ : Scriptable( ST_AREA )
+{
+ area=this;
+ TMap = NULL;
+ LightMap = NULL;
+ HeightMap = NULL;
+ SmallMap = NULL;
+ MapSet = NULL;
+ SrchMap = NULL;
+ Walls = NULL;
+ WallCount = 0;
+ queue[PR_SCRIPT] = NULL;
+ queue[PR_DISPLAY] = NULL;
+ INISpawn = NULL;
+ //no one needs this queue
+ //queue[PR_IGNORE] = NULL;
+ Qcount[PR_SCRIPT] = 0;
+ Qcount[PR_DISPLAY] = 0;
+ //no one needs this queue
+ //Qcount[PR_IGNORE] = 0;
+ lastActorCount[PR_SCRIPT] = 0;
+ lastActorCount[PR_DISPLAY] = 0;
+ //no one needs this
+ //lastActorCount[PR_IGNORE] = 0;
+ if (!PathFinderInited) {
+ InitPathFinder();
+ InitSpawnGroups();
+ InitExplore();
+ }
+ ExploredBitmap = NULL;
+ VisibleBitmap = NULL;
+ version = 0;
+ MasterArea = core->GetGame()->MasterArea(scriptName);
+ Background = NULL;
+ BgDuration = 0;
+}
+
+Map::~Map(void)
+{
+ unsigned int i;
+
+ free( MapSet );
+ free( SrchMap );
+ delete TMap;
+ delete INISpawn;
+ aniIterator aniidx;
+ for (aniidx = animations.begin(); aniidx != animations.end(); aniidx++) {
+ delete (*aniidx);
+ }
+
+ for (i = 0; i < actors.size(); i++) {
+ Actor* a = actors[i];
+ //don't delete NPC/PC
+ if (a && !a->Persistent() ) {
+ delete a;
+ }
+ }
+
+ for (i = 0; i < entrances.size(); i++) {
+ delete entrances[i];
+ }
+ for (i = 0; i < spawns.size(); i++) {
+ delete spawns[i];
+ }
+ delete LightMap;
+ delete HeightMap;
+ core->GetVideoDriver()->FreeSprite( SmallMap );
+ for (i = 0; i < QUEUE_COUNT; i++) {
+ free(queue[i]);
+ queue[i] = NULL;
+ }
+
+ proIterator pri;
+
+ for (pri = projectiles.begin(); pri != projectiles.end(); pri++) {
+ delete (*pri);
+ }
+
+ scaIterator sci;
+
+ for (sci = vvcCells.begin(); sci != vvcCells.end(); sci++) {
+ delete (*sci);
+ }
+
+ spaIterator spi;
+
+ for (spi = particles.begin(); spi != particles.end(); spi++) {
+ delete (*spi);
+ }
+
+ for (i = 0; i < ambients.size(); i++) {
+ delete ambients[i];
+ }
+ for (i = 0; i < mapnotes.size(); i++) {
+ delete mapnotes[i];
+ }
+
+ //malloc-d in AREImp
+ free( ExploredBitmap );
+ free( VisibleBitmap );
+ if (Walls) {
+ for(i=0;i<WallCount;i++) {
+ delete Walls[i];
+ }
+ free( Walls );
+ }
+ WallCount=0;
+}
+
+void Map::ChangeTileMap(Image* lm, Sprite2D* sm)
+{
+ delete LightMap;
+ core->GetVideoDriver()->FreeSprite(SmallMap);
+
+ LightMap = lm;
+ SmallMap = sm;
+
+ TMap->UpdateDoors();
+}
+
+void Map::AddTileMap(TileMap* tm, Image* lm, Bitmap* sr, Sprite2D* sm, Bitmap* hm)
+{
+ // CHECKME: leaks? Should the old TMap, LightMap, etc... be freed?
+ TMap = tm;
+ LightMap = lm;
+ HeightMap = hm;
+ SmallMap = sm;
+ Width = (unsigned int) (TMap->XCellCount * 4);
+ Height = (unsigned int) (( TMap->YCellCount * 64 + 63) / 12);
+ //Filling Matrices
+ MapSet = (unsigned short *) malloc(sizeof(unsigned short) * Width * Height);
+ //Internal Searchmap
+ int y = sr->GetHeight();
+ SrchMap = (unsigned short *) calloc(Width * Height, sizeof(unsigned short));
+ while(y--) {
+ int x=sr->GetWidth();
+ while(x--) {
+ SrchMap[y*Width+x] = Passable[sr->GetAt(x,y)&PATH_MAP_AREAMASK];
+ }
+ }
+
+ //delete the original searchmap
+ delete sr;
+}
+
+void Map::MoveToNewArea(const char *area, const char *entrance, unsigned int direction, int EveryOne, Actor *actor)
+{
+ char command[256];
+
+ //change loader MOS image here
+ //check worldmap entry, if that doesn't contain anything,
+ //make a random pick
+
+ Game* game = core->GetGame();
+ if (EveryOne==CT_WHOLE) {
+ //copy the area name if it exists on the worldmap
+ unsigned int index;
+
+ WMPAreaEntry* entry = core->GetWorldMap()->FindNearestEntry(area, index);
+ if (entry) {
+ memcpy (game->PreviousArea, entry->AreaName, 8);
+ }
+
+ core->GetGameControl()->AutoSave();
+ }
+ Map* map = game->GetMap(area, false);
+ if (!map) {
+ printMessage("Map", " ", LIGHT_RED);
+ printf("Invalid map: %s\n",area);
+ command[0]=0;
+ return;
+ }
+ Entrance* ent = map->GetEntrance( entrance );
+ int X,Y, face;
+ if (!ent) {
+ // no entrance found, try using direction flags
+
+ face = -1; // should this be handled per-case?
+
+ // ok, so the original engine tries these in a different order
+ // (north first, then south) but it doesn't seem to matter
+ if (direction & 0x1) { // north
+ X = map->TMap->XCellCount * 32;
+ Y = 0;
+ } else if (direction & 0x2) { // east
+ X = map->TMap->XCellCount * 64;
+ Y = map->TMap->YCellCount * 32;
+ } else if (direction & 0x4) { // south
+ X = map->TMap->XCellCount * 32;
+ Y = map->TMap->YCellCount * 64;
+ } else if (direction & 0x8) { // west
+ X = 0;
+ Y = map->TMap->YCellCount * 32;
+ } else {
+ // crashes in original engine
+ printMessage("Map", " ", YELLOW);
+ printf( "WARNING!!! EntryPoint '%s' does not exist and direction %d is invalid\n", entrance, direction );
+ X = map->TMap->XCellCount * 64;
+ Y = map->TMap->YCellCount * 64;
+ }
+ } else {
+ X = ent->Pos.x;
+ Y = ent->Pos.y;
+ face = ent->Face;
+ }
+ //LeaveArea is the same in ALL engine versions
+ sprintf(command, "LeaveArea(\"%s\",[%d.%d],%d)", area, X, Y, face);
+
+ if (EveryOne&CT_GO_CLOSER) {
+ int i=game->GetPartySize(false);
+ while (i--) {
+ Actor *pc = game->GetPC(i,false);
+ if (pc->GetCurrentArea()==this) {
+ pc->UseExit(0);
+ pc->ClearPath();
+ pc->ClearActions();
+ pc->AddAction( GenerateAction( command ) );
+ pc->ProcessActions(true);
+ }
+ }
+ return;
+ }
+ if (EveryOne&CT_SELECTED) {
+ int i=game->GetPartySize(false);
+ while (i--) {
+ Actor *pc = game->GetPC(i,false);
+
+ if (!pc->IsSelected()) {
+ continue;
+ }
+ if (pc->GetCurrentArea()==this) {
+ pc->UseExit(0);
+ pc->ClearPath();
+ pc->ClearActions();
+ pc->AddAction( GenerateAction( command ) );
+ pc->ProcessActions(true);
+ }
+ }
+ return;
+ }
+
+ actor->ClearPath();
+ actor->ClearActions();
+ actor->AddAction( GenerateAction( command ) );
+ actor->ProcessActions(true);
+}
+
+void Map::UseExit(Actor *actor, InfoPoint *ip)
+{
+ Game *game=core->GetGame();
+
+ int EveryOne = ip->CheckTravel(actor);
+ switch(EveryOne) {
+ case CT_GO_CLOSER:
+ displaymsg->DisplayConstantString(STR_WHOLEPARTY,0xffffff); //white
+ if (game->EveryoneStopped()) {
+ ip->Flags&=~TRAP_RESET; //exit triggered
+ }
+ return;
+ //no ingame message for these events
+ case CT_CANTMOVE: case CT_SELECTED:
+ return;
+ case CT_ACTIVE: case CT_WHOLE: case CT_MOVE_SELECTED:
+ break;
+ }
+
+ if (ip->Destination[0] != 0) {
+ // the 0 here is default orientation, can infopoints specify that or
+ // is an entrance always provided?
+ MoveToNewArea(ip->Destination, ip->EntranceName, 0, EveryOne, actor);
+ return;
+ }
+ if (ip->Scripts[0]) {
+ ip->LastTriggerObject = ip->LastTrigger = ip->LastEntered = actor->GetGlobalID();
+ ip->ExecuteScript( 1 );
+ ip->ProcessActions(true);
+ }
+}
+
+//Draw two overlapped animations to achieve the original effect
+//PlayOnce makes sure that if we stop drawing them, they will go away
+void Map::DrawPortal(InfoPoint *ip, int enable)
+{
+ ieDword gotportal = HasVVCCell(PortalResRef, ip->Pos);
+
+ if (enable) {
+ if (gotportal>PortalTime) return;
+ ScriptedAnimation *sca = gamedata->GetScriptedAnimation(PortalResRef, false);
+ if (sca) {
+ sca->SetBlend();
+ sca->PlayOnce();
+ sca->XPos=ip->Pos.x;
+ sca->YPos=ip->Pos.y;
+ sca->ZPos=gotportal;
+ AddVVCell(sca);
+ }
+ return;
+ }
+}
+
+void Map::UpdateScripts()
+{
+ bool has_pcs = false;
+ size_t i=actors.size();
+ while (i--) {
+ if (actors[i]->InParty) {
+ has_pcs = true;
+ break;
+ }
+ }
+
+ // if masterarea, then we allow 'any' actors
+ // if not masterarea, we allow only players
+ // if (!GetActorCount(MasterArea) ) {
+ // fuzzie changed this because the previous code was wrong
+ // (GetActorCount(false) returns only non-PCs) - it is not
+ // well-tested so feel free to change if there are problems
+ // (for example, the CanFree seems like it would be needed to
+ // check for any running scripts, such as following, but it seems
+ // to work ok anyway in my testing - if you change it you probably
+ // also want to change the actor updating code below so it doesn't
+ // add new actions while we are trying to get rid of the area!)
+ if (!has_pcs && !(MasterArea && actors.size()) /*&& !CanFree()*/) {
+ return;
+ }
+
+ // fuzzie added this check because some area scripts (eg, AR1600 when
+ // escaping Brynnlaw) were executing after they were meant to be done,
+ // and this seems the nicest way of handling that for now - it's quite
+ // possibly wrong (so if you have problems, revert this and find
+ // another way)
+ if (has_pcs) {
+ //Run all the Map Scripts (as in the original)
+ //The default area script is in the last slot anyway
+ ExecuteScript( MAX_SCRIPTS );
+ }
+
+ //Execute Pending Actions
+ //if it is only here, then the drawing will fail
+ ProcessActions(false);
+
+ // If scripts frozen, return.
+ // This fixes starting a new IWD game. The above ProcessActions pauses the
+ // game for a textscreen, but one of the actor->ProcessActions calls
+ // below starts a cutscene, hiding the mouse. - wjp, 20060805
+ if (core->GetGameControl()->GetDialogueFlags() & DF_FREEZE_SCRIPTS) return;
+
+ //Run actor scripts (only for 0 priority)
+ int q=Qcount[PR_SCRIPT];
+
+ Game *game = core->GetGame();
+ Actor *timestop_owner = game->timestop_owner;
+ bool timestop = game->timestop_end>game->GameTime;
+
+ while (q--) {
+ Actor* actor = queue[PR_SCRIPT][q];
+ //actor just moved away, don't run its script from this side
+ if (actor->GetCurrentArea()!=this) {
+ actor->no_more_steps = true;
+ continue;
+ }
+ if (timestop && actor!=timestop_owner && actor->Modified[IE_DISABLETIMESTOP] ) {
+ actor->no_more_steps = true;
+ continue;
+ }
+
+ //Avenger moved this here from ApplyAllEffects (this one modifies the effect queue)
+ //.. but then fuzzie moved this here from UpdateActorState, because otherwise
+ //immobile actors (see check below) never become mobile again!
+ actor->fxqueue.Cleanup();
+
+ //if the actor is immobile, don't run the scripts
+ //FIXME: this is not universaly true, only some states have this effect
+ if (!game->StateOverrideFlag && !game->StateOverrideTime) {
+ if (/*actor->Immobile() ||*/ actor->GetStat(IE_STATE_ID) & STATE_SLEEP) {
+ actor->no_more_steps = true;
+ continue;
+ }
+ }
+ actor->no_more_steps = false;
+
+ /*
+ * we run scripts all at once because one of the actions in ProcessActions
+ * might remove us from a cutscene and then bad things can happen when
+ * scripts are queued unexpectedly (such as an ogre in a cutscene -> dialog
+ * -> cutscene transition in the first bg1 cutscene exploiting the race
+ * condition to murder player1) - it is entirely possible that we should be
+ * doing this differently (for example by storing the cutscene state at the
+ * start of this function, or by changing the cutscene state at a later
+ * point, etc), but i did it this way for now because it seems least painful
+ * and we should probably be staggering the script executions anyway
+ */
+ actor->ExecuteScript( MAX_SCRIPTS );
+
+ }
+
+ //clean up effects on dead actors too
+ q=Qcount[PR_DISPLAY];
+ while(q--) {
+ Actor* actor = queue[PR_DISPLAY][q];
+ actor->fxqueue.Cleanup();
+ }
+
+ q=Qcount[PR_SCRIPT];
+ while (q--) {
+ Actor* actor = queue[PR_SCRIPT][q];
+ if (actor->no_more_steps) continue;
+
+ actor->ProcessActions(false);
+
+ actor->UpdateActorState(game->GameTime);
+
+ actor->inventory.CalculateWeight();
+ actor->SetBase( IE_ENCUMBRANCE, actor->inventory.GetWeight() );
+
+ //TODO:calculate actor speed!
+ int speed = (int) actor->GetStat(IE_MOVEMENTRATE);
+ if (speed) {
+ speed = 1500/speed;
+ }
+ if (core->GetResDataINI()) {
+ ieDword animid = actor->BaseStats[IE_ANIMATION_ID];
+ if (core->HasFeature(GF_ONE_BYTE_ANIMID)) {
+ animid = animid & 0xff;
+ }
+ if (animid < (ieDword)CharAnimations::GetAvatarsCount()) {
+ AvatarStruct *avatar = CharAnimations::GetAvatarStruct(animid);
+ if (avatar->RunScale && (actor->GetInternalFlag() & IF_RUNNING)) {
+ speed = avatar->RunScale;
+ } else if (avatar->WalkScale) {
+ speed = avatar->WalkScale;
+ } else {
+ //printf("no walkscale for anim %d!\n", actor->BaseStats[IE_ANIMATION_ID]);
+ }
+ }
+ }
+ actor->speed = speed;
+ }
+
+ // We need to step through the list of actors until all of them are done
+ // taking steps.
+ bool more_steps = true;
+ ieDword time = game->Ticks; // make sure everything moves at the same time
+ while (more_steps) {
+ more_steps = false;
+
+ q=Qcount[PR_SCRIPT];
+ while (q--) {
+ Actor* actor = queue[PR_SCRIPT][q];
+ if (actor->no_more_steps) continue;
+
+ // try to exclude actors which only just died
+ // (shouldn't we not be stepping actors which don't have a path anyway?)
+ // following fails on Immobile creatures, don't think it's a problem, but replace with next line if it is
+ if (!actor->ValidTarget(GA_NO_DEAD)) continue;
+ //if (actor->GetStat(IE_STATE_ID)&STATE_DEAD || actor->GetInternalFlag() & IF_JUSTDIED) continue;
+
+ actor->no_more_steps = DoStepForActor(actor, actor->speed, time);
+ if (!actor->no_more_steps) more_steps = true;
+ }
+ }
+
+ //Check if we need to start some door scripts
+ int doorCount = 0;
+ while (true) {
+ Door* door = TMap->GetDoor( doorCount++ );
+ if (!door)
+ break;
+ if (door->Scripts[0])
+ door->ExecuteScript( 1 );
+ //Execute Pending Actions
+ door->ProcessActions(false);
+ }
+
+ //Check if we need to start some container scripts
+ int containerCount = 0;
+ while (true) {
+ Container* container = TMap->GetContainer( containerCount++ );
+ if (!container)
+ break;
+ if (container->Scripts[0])
+ container->ExecuteScript( 1 );
+ //Execute Pending Actions
+ container->ProcessActions(false);
+ }
+
+ //Check if we need to start some trap scripts
+ int ipCount = 0;
+ while (true) {
+ //For each InfoPoint in the map
+ InfoPoint* ip = TMap->GetInfoPoint( ipCount++ );
+ if (!ip)
+ break;
+ //If this InfoPoint has no script and it is not a Travel Trigger, skip it
+ // InfoPoints of all types don't run scripts if TRAP_DEACTIVATED is set
+ // (eg, TriggerActivation changes this, see lightning room from SoA)
+ int wasActive = (!(ip->Flags&TRAP_DEACTIVATED) ) || (ip->Type==ST_TRAVEL);
+
+ //If this InfoPoint is a Switch Trigger
+ if (ip->Type == ST_TRIGGER) {
+ //Check if this InfoPoint was activated
+ if (ip->LastTrigger) {
+ if (wasActive && ip->Scripts[0]) {
+ //Run the InfoPoint script
+ ip->ExecuteScript( 1 );
+ }
+ }
+ //Execute Pending Actions
+ ip->ProcessActions(false);
+ continue;
+ }
+
+ if (ip->IsPortal()) {
+ DrawPortal(ip, ip->Trapped&PORTAL_TRAVEL);
+ }
+
+ if (wasActive) {
+ q=Qcount[PR_SCRIPT];
+ ieDword exitID = ip->GetGlobalID();
+ while (q--) {
+ Actor* actor = queue[PR_SCRIPT][q];
+ if (ip->Type == ST_PROXIMITY) {
+ if(ip->Entered(actor)) {
+ //if trap triggered, then mark actor
+ actor->SetInTrap(ipCount);
+ wasActive|=TRAP_USEPOINT;
+ }
+ } else {
+ //ST_TRAVEL
+ //don't move if doing something else
+ // added CurrentAction as part of blocking action fixes
+ if (actor->CannotPassEntrance(exitID) ) {
+ continue;
+ }
+ //this is needed, otherwise the travel
+ //trigger would be activated anytime
+ //Well, i don't know why is it here, but lets try this
+ if (ip->Entered(actor)) {
+ UseExit(actor, ip);
+ }
+ }
+ }
+ }
+
+ if (wasActive) {
+ ip->ExecuteScript( 1 );
+ //Play the PST specific enter sound
+ if (wasActive&TRAP_USEPOINT) {
+ core->GetAudioDrv()->Play(ip->EnterWav, ip->TrapLaunch.x, ip->TrapLaunch.y);
+ }
+ }
+ //Execute Pending Actions
+ ip->ProcessActions(false);
+ }
+}
+
+void Map::ResolveTerrainSound(ieResRef &sound, Point &Pos) {
+ for(int i=0;i<tsndcount;i++) {
+ if (!memcmp(sound, terrainsounds[i].Group, sizeof(ieResRef) ) ) {
+ int type = GetInternalSearchMap( Pos.x/16, Pos.y/12 )&PATH_MAP_AREAMASK;
+ memcpy(sound, terrainsounds[i].Sounds[type], sizeof(ieResRef) );
+ return;
+ }
+ }
+}
+
+bool Map::DoStepForActor(Actor *actor, int speed, ieDword time) {
+ bool no_more_steps = true;
+
+ if (actor->BlocksSearchMap()) {
+ ClearSearchMapFor(actor);
+
+ PathNode * step = actor->GetNextStep();
+ if (step && step->Next) {
+ //we should actually wait for a short time and check then
+ if (GetBlocked(step->Next->x*16+8,step->Next->y*12+6,actor->size)) {
+ actor->NewPath();
+ }
+ }
+ }
+ if (!(actor->GetBase(IE_STATE_ID)&STATE_CANTMOVE) ) {
+ if (!actor->Immobile()) {
+ no_more_steps = actor->DoStep( speed, time );
+ if (actor->BlocksSearchMap()) {
+ BlockSearchMap( actor->Pos, actor->size, actor->InParty?PATH_MAP_PC:PATH_MAP_NPC);
+ }
+ }
+ }
+
+ return no_more_steps;
+}
+
+void Map::ClearSearchMapFor( Movable *actor ) {
+ Actor** nearActors = GetAllActorsInRadius(actor->Pos, GA_NO_DEAD|GA_NO_LOS, MAX_CIRCLE_SIZE*2*16);
+ BlockSearchMap( actor->Pos, actor->size, PATH_MAP_FREE);
+
+ // Restore the searchmap areas of any nearby actors that could
+ // have been cleared by this BlockSearchMap(..., 0).
+ // (Necessary since blocked areas of actors may overlap.)
+ int i=0;
+ while(nearActors[i]!=NULL) {
+ if(nearActors[i]!=actor && nearActors[i]->BlocksSearchMap())
+ BlockSearchMap( nearActors[i]->Pos, nearActors[i]->size, nearActors[i]->InParty?PATH_MAP_PC:PATH_MAP_NPC);
+ ++i;
+ }
+ free(nearActors);
+}
+
+void Map::DrawHighlightables( Region screen )
+{
+ Region vp = core->GetVideoDriver()->GetViewport();
+ unsigned int i = 0;
+ Container *c;
+
+ while ( (c = TMap->GetContainer(i++))!=NULL ) {
+ Color tint = LightMap->GetPixel( c->Pos.x / 16, c->Pos.y / 12);
+ tint.a = 255;
+
+ if (c->Highlight) {
+ if (c->Type==IE_CONTAINER_PILE) {
+ Color tint = LightMap->GetPixel( c->Pos.x / 16, c->Pos.y / 12);
+ tint.a = 255;
+ c->DrawPile(true, screen, tint);
+ } else {
+ c->DrawOutline();
+ }
+ } else if (c->Type==IE_CONTAINER_PILE) {
+ if (c->outline->BBox.InsideRegion( vp )) {
+ c->DrawPile(false, screen, tint);
+ }
+ }
+ }
+
+ Door *d;
+ i = 0;
+ while ( (d = TMap->GetDoor(i++))!=NULL ) {
+ if (d->Highlight) d->DrawOutline();
+ }
+
+ InfoPoint *p;
+ i = 0;
+ while ( (p = TMap->GetInfoPoint(i++))!=NULL ) {
+ if (p->Highlight) p->DrawOutline();
+ }
+}
+
+Actor *Map::GetNextActor(int &q, int &index)
+{
+retry:
+ switch(q) {
+ case PR_SCRIPT:
+ if (index--)
+ return queue[q][index];
+ q--;
+ return NULL;
+ case PR_DISPLAY:
+ if (index--)
+ return queue[q][index];
+ q--;
+ index = Qcount[q];
+ goto retry;
+ default:
+ return NULL;
+ }
+}
+
+AreaAnimation *Map::GetNextAreaAnimation(aniIterator &iter, ieDword gametime)
+{
+retry:
+ if (iter==animations.end()) {
+ return NULL;
+ }
+ AreaAnimation *a = *(iter++);
+ if (!a->Schedule(gametime) ) {
+ goto retry;
+ }
+ if (!IsVisible( a->Pos, !(a->Flags & A_ANI_NOT_IN_FOG)) ) {
+ goto retry;
+ }
+ return a;
+}
+
+Particles *Map::GetNextSpark(spaIterator &iter)
+{
+ if (iter==particles.end()) {
+ return NULL;
+ }
+ return *iter;
+}
+
+//doesn't increase iterator, because we might need to erase it from the list
+Projectile *Map::GetNextProjectile(proIterator &iter)
+{
+ if (iter==projectiles.end()) {
+ return NULL;
+ }
+ return *iter;
+}
+
+Projectile *Map::GetNextTrap(proIterator &iter)
+{
+ Projectile *pro;
+
+ do {
+ pro=GetNextProjectile(iter);
+ iter++;
+ //logic to determine dormant traps
+ //if (pro && pro->IsTrap()) break;
+ } while(pro);
+ return pro;
+}
+
+size_t Map::GetProjectileCount(proIterator &iter)
+{
+ iter = projectiles.begin();
+ return projectiles.size();
+}
+
+ieDword Map::GetTrapCount(proIterator &iter)
+{
+ ieDword cnt=0;
+ iter=projectiles.begin();
+ while(GetNextTrap(iter)) {
+ cnt++;
+ }
+ //
+ iter = projectiles.begin();
+ return cnt;
+}
+
+
+//doesn't increase iterator, because we might need to erase it from the list
+ScriptedAnimation *Map::GetNextScriptedAnimation(scaIterator &iter)
+{
+ if (iter==vvcCells.end()) {
+ return NULL;
+ }
+ return *iter;
+}
+
+static ieDword oldgametime = 0;
+
+//Draw the game area (including overlays, actors, animations, weather)
+void Map::DrawMap(Region screen)
+{
+ if (!TMap) {
+ return;
+ }
+ Game *game = core->GetGame();
+ ieDword gametime = game->GameTime;
+
+ //area specific spawn.ini files (a PST feature)
+ if (INISpawn) {
+ INISpawn->CheckSpawn();
+ }
+
+ //Blit the Background Map Animations (before actors)
+ Video* video = core->GetVideoDriver();
+ int bgoverride = false;
+
+ if (Background) {
+ if (BgDuration<gametime) {
+ video->FreeSprite(Background);
+ } else {
+ video->BlitSprite(Background,0,0,true);
+ bgoverride = true;
+ }
+ }
+
+ if (!bgoverride) {
+ int rain;
+ if (HasWeather()) {
+ //zero when the weather particles are all gone
+ rain = game->weather->GetPhase()-P_EMPTY;
+ } else {
+ rain = 0;
+ }
+
+ TMap->DrawOverlays( screen, rain );
+
+ //Draw Outlines
+ DrawHighlightables( screen );
+ }
+
+ Region vp = video->GetViewport();
+ //if it is only here, then the scripting will fail?
+ GenerateQueues();
+ SortQueues();
+ //drawing queues 1 and 0
+ //starting with lower priority
+ //so displayed, but inactive actors (dead) will be drawn over
+ int q = PR_DISPLAY;
+ int index = Qcount[q];
+ Actor* actor = GetNextActor(q, index);
+ aniIterator aniidx = animations.begin();
+ scaIterator scaidx = vvcCells.begin();
+ proIterator proidx = projectiles.begin();
+ spaIterator spaidx = particles.begin();
+
+ AreaAnimation *a = GetNextAreaAnimation(aniidx, gametime);
+ ScriptedAnimation *sca = GetNextScriptedAnimation(scaidx);
+ Projectile *pro = GetNextProjectile(proidx);
+ Particles *spark = GetNextSpark(spaidx);
+
+ while (actor || a || sca || spark || pro) {
+ switch(SelectObject(actor,q,a,sca,spark,pro)) {
+ case AOT_ACTOR:
+ actor->Draw( screen );
+ actor->UpdateAnimations();
+ actor = GetNextActor(q, index);
+ break;
+ case AOT_AREA:
+ //draw animation
+ a->Draw( screen, this );
+ a = GetNextAreaAnimation(aniidx,gametime);
+ break;
+ case AOT_SCRIPTED:
+ {
+ Point Pos(0,0);
+
+ Color tint = LightMap->GetPixel( sca->XPos / 16, sca->YPos / 12);
+ tint.a = 255;
+ bool endReached = sca->Draw(screen, Pos, tint, this, 0, -1);
+ if (endReached) {
+ delete( sca );
+ scaidx=vvcCells.erase(scaidx);
+ } else {
+ scaidx++;
+ }
+ }
+ sca = GetNextScriptedAnimation(scaidx);
+ break;
+ case AOT_PROJECTILE:
+ {
+ int drawn;
+ if (gametime>oldgametime) {
+ drawn = pro->Update();
+ } else {
+ drawn = 1;
+ }
+ if (drawn) {
+ pro->Draw( screen );
+ proidx++;
+ } else {
+ delete( pro );
+ proidx = projectiles.erase(proidx);
+ }
+ }
+ pro = GetNextProjectile(proidx);
+ break;
+ case AOT_SPARK:
+ {
+ int drawn;
+ if (gametime>oldgametime) {
+ drawn = spark->Update();
+ } else {
+ drawn = 1;
+ }
+ if (drawn) {
+ spark->Draw( screen );
+ spaidx++;
+ } else {
+ delete( spark );
+ spaidx=particles.erase(spaidx);
+ }
+ }
+ spark = GetNextSpark(spaidx);
+ break;
+ default:
+ abort();
+ }
+ }
+
+ if ((core->FogOfWar&FOG_DRAWSEARCHMAP) && SrchMap) {
+ DrawSearchMap(screen);
+ } else {
+ if ((core->FogOfWar&FOG_DRAWFOG) && TMap) {
+ TMap->DrawFogOfWar( ExploredBitmap, VisibleBitmap, screen );
+ }
+ }
+
+ int ipCount = 0;
+ while (true) {
+ //For each InfoPoint in the map
+ InfoPoint* ip = TMap->GetInfoPoint( ipCount++ );
+ if (!ip)
+ break;
+ ip->DrawOverheadText(screen);
+ }
+
+ int cnCount = 0;
+ while (true) {
+ //For each Container in the map
+ Container* cn = TMap->GetContainer( cnCount++ );
+ if (!cn)
+ break;
+ cn->DrawOverheadText(screen);
+ }
+
+ int drCount = 0;
+ while (true) {
+ //For each Door in the map
+ Door* dr = TMap->GetDoor( drCount++ );
+ if (!dr)
+ break;
+ dr->DrawOverheadText(screen);
+ }
+
+ size_t i = actors.size();
+ while (i--) {
+ //For each Actor present
+ //This must go AFTER the fog!
+ //(maybe we should be using the queue?)
+ Actor* actor = actors[i];
+ actor->DrawOverheadText(screen);
+ }
+
+ oldgametime=gametime;
+}
+
+void Map::DrawSearchMap(const Region &screen)
+{
+ Color inaccessible = { 128, 128, 128, 128 };
+ Color impassible = { 128, 64, 64, 128 }; // red-ish
+ Color sidewall = { 64, 64, 128, 128 }; // blue-ish
+ Video *vid=core->GetVideoDriver();
+ Region rgn=vid->GetViewport();
+ Region block;
+
+ block.w=16;
+ block.h=12;
+ int w = screen.w/16+2;
+ int h = screen.h/12+2;
+
+ for(int x=0;x<w;x++) {
+ for(int y=0;y<h;y++) {
+ unsigned char blockvalue = GetBlocked(x+rgn.x/16, y+rgn.y/12);
+ if (!(blockvalue & PATH_MAP_PASSABLE)) {
+ block.x=screen.x+x*16-(rgn.x % 16);
+ block.y=screen.y+y*12-(rgn.y % 12);
+ if (blockvalue == PATH_MAP_IMPASSABLE) { // 0
+ vid->DrawRect(block,impassible);
+ } else if (blockvalue & PATH_MAP_SIDEWALL) {
+ vid->DrawRect(block,sidewall);
+ } else {
+ vid->DrawRect(block,inaccessible);
+ }
+ }
+ }
+ }
+}
+
+//adding animation in order, based on its height parameter
+void Map::AddAnimation(AreaAnimation* panim)
+{
+ //copy external memory to core memory for msvc's sake
+ AreaAnimation *anim = new AreaAnimation();
+ memcpy(anim, panim, sizeof(AreaAnimation) );
+
+ anim->InitAnimation();
+
+ aniIterator iter;
+
+ int Height = anim->GetHeight();
+printf("Adding %s at height %d, Pos: %d.%d\n", anim->Name, Height, anim->Pos.x, anim->Pos.y);
+ for(iter=animations.begin(); (iter!=animations.end()) && ((*iter)->GetHeight()<Height); iter++) ;
+ animations.insert(iter, anim);
+}
+
+//reapplying all of the effects on the actors of this map
+//this might be unnecessary later
+void Map::UpdateEffects()
+{
+ size_t i = actors.size();
+ while (i--) {
+ actors[i]->RefreshEffects(NULL);
+ }
+}
+
+void Map::Shout(Actor* actor, int shoutID, unsigned int radius)
+{
+ size_t i=actors.size();
+ while (i--) {
+ Actor *listener = actors[i];
+
+ if (radius) {
+ if (Distance(actor->Pos, listener->Pos)>radius) {
+ continue;
+ }
+ }
+ if (shoutID) {
+ listener->LastHeard = actor->GetGlobalID();
+ listener->LastShout = shoutID;
+ } else {
+ listener->LastHelp = actor->GetGlobalID();
+ }
+ }
+}
+
+bool Map::AnyEnemyNearPoint(const Point &p)
+{
+ ieDword gametime = core->GetGame()->GameTime;
+ size_t i = actors.size();
+ while (i--) {
+ Actor *actor = actors[i];
+
+ if (actor->Schedule(gametime, true) ) {
+ continue;
+ }
+ if (Distance(actor->Pos, p) > SPAWN_RANGE) {
+ continue;
+ }
+ if (actor->GetStat(IE_EA)<EA_EVILCUTOFF) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+}
+
+void Map::ActorSpottedByPlayer(Actor *actor)
+{
+ unsigned int animid;
+
+ if(core->HasFeature(GF_HAS_BEASTS_INI)) {
+ animid=actor->BaseStats[IE_ANIMATION_ID];
+ if(core->HasFeature(GF_ONE_BYTE_ANIMID)) {
+ animid&=0xff;
+ }
+ if (animid < (ieDword)CharAnimations::GetAvatarsCount()) {
+ AvatarStruct *avatar = CharAnimations::GetAvatarStruct(animid);
+ core->GetGame()->SetBeastKnown(avatar->Bestiary);
+ }
+ }
+
+ if (!(actor->GetInternalFlag()&IF_STOPATTACK)) {
+ if (actor->Modified[IE_EA]>=EA_EVILCUTOFF) {
+ core->Autopause(AP_ENEMY);
+ }
+ }
+}
+
+void Map::AddActor(Actor* actor)
+{
+ //setting the current area for the actor as this one
+ strnlwrcpy(actor->Area, scriptName, 8);
+ actor->SetMap(this);
+ actors.push_back( actor );
+ //if a visible aggressive actor was put on the map, it is an autopause reason
+ //guess game is always loaded? if not, then we'll crash
+ ieDword gametime = core->GetGame()->GameTime;
+
+ if (IsVisible(actor->Pos, false) && actor->Schedule(gametime, true) ) {
+ ActorSpottedByPlayer(actor);
+ }
+ if (actor->InParty && core->HasFeature(GF_AREA_VISITED_VAR)) {
+ char key[32];
+ snprintf(key, sizeof(key),"%s_visited", scriptName);
+ core->GetGame()->locals->SetAt(key, 1);
+ }
+
+}
+
+bool Map::AnyPCSeesEnemy()
+{
+ ieDword gametime = core->GetGame()->GameTime;
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+ if (actor->Modified[IE_EA]>=EA_EVILCUTOFF) {
+ if (IsVisible(actor->Pos, false) && actor->Schedule(gametime, true) ) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//Make an actor gone for (almost) good
+//If the actor was in the party, it will be moved to the npc storage
+//If the actor is in the NPC storage, its area and some other fields
+//that are needed for proper reentry will be zeroed out
+//If the actor isn't in the NPC storage, it is destructed
+void Map::DeleteActor(int i)
+{
+ Actor *actor = actors[i];
+ if (actor) {
+ Game *game = core->GetGame();
+ //this makes sure that a PC will be demoted to NPC
+ game->LeaveParty( actor );
+ //this frees up the spot under the feet circle
+ ClearSearchMapFor( actor );
+ //remove the area reference from the actor
+ actor->SetMap(NULL);
+ //don't destroy the object in case it is a persistent object
+ //otherwise there is a dead reference causing a crash on save
+ if (!game->InStore(actor) ) {
+ delete actor;
+ }
+ }
+ //remove the actor from the area's actor list
+ actors.erase( actors.begin()+i );
+}
+
+Door *Map::GetDoorByGlobalID(ieDword objectID)
+{
+ if (!objectID) return NULL;
+
+ int doorCount = 0;
+ while (true) {
+ Door* door = TMap->GetDoor( doorCount++ );
+ if (!door)
+ return NULL;
+ if (door->GetGlobalID() == objectID)
+ return door;
+ }
+}
+
+Container *Map::GetContainerByGlobalID(ieDword objectID)
+{
+ if (!objectID) return NULL;
+
+ int containerCount = 0;
+ while (true) {
+ Container* container = TMap->GetContainer( containerCount++ );
+ if (!container)
+ return NULL;
+ if (container->GetGlobalID() == objectID)
+ return container;
+ }
+}
+
+InfoPoint *Map::GetInfoPointByGlobalID(ieDword objectID)
+{
+ if (!objectID) return NULL;
+
+ int ipCount = 0;
+ while (true) {
+ InfoPoint* ip = TMap->GetInfoPoint( ipCount++ );
+ if (!ip)
+ return NULL;
+ if (ip->GetGlobalID() == objectID)
+ return ip;
+ }
+}
+
+Actor* Map::GetActorByGlobalID(ieDword objectID)
+{
+ if (!objectID) {
+ return NULL;
+ }
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+
+ if (actor->GetGlobalID()==objectID) {
+ return actor;
+ }
+ }
+ return NULL;
+}
+
+/** flags:
+ GA_SELECT 16 - unselectable actors don't play
+ GA_NO_DEAD 32 - dead actors don't play
+ GA_POINT 64 - not actor specific
+ GA_NO_HIDDEN 128 - hidden actors don't play
+*/
+Actor* Map::GetActor(const Point &p, int flags)
+{
+ ieDword gametime = core->GetGame()->GameTime;
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+
+ if (!actor->IsOver( p ))
+ continue;
+ if (!actor->ValidTarget(flags) ) {
+ continue;
+ }
+ if (!actor->Schedule(gametime, true) ) {
+ continue;
+ }
+ return actor;
+ }
+ return NULL;
+}
+
+Actor* Map::GetActorInRadius(const Point &p, int flags, unsigned int radius)
+{
+ ieDword gametime = core->GetGame()->GameTime;
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+
+ if (PersonalDistance( p, actor ) > radius)
+ continue;
+ if (!actor->ValidTarget(flags) ) {
+ continue;
+ }
+ if (!actor->Schedule(gametime, true) ) {
+ continue;
+ }
+ return actor;
+ }
+ return NULL;
+}
+
+//maybe consider using a simple list
+Actor **Map::GetAllActorsInRadius(const Point &p, int flags, unsigned int radius)
+{
+ ieDword count = 1;
+ size_t i;
+
+ ieDword gametime = core->GetGame()->GameTime;
+ i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+
+ if (PersonalDistance( p, actor ) > radius)
+ continue;
+ if (!actor->ValidTarget(flags) ) {
+ continue;
+ }
+ if (!actor->Schedule(gametime, true) ) {
+ continue;
+ }
+ if (!(flags&GA_NO_LOS)) {
+ if (!IsVisible(actor->Pos, p)) {
+ continue;
+ }
+ }
+ count++;
+ }
+
+ Actor **ret = (Actor **) malloc( sizeof(Actor*) * count);
+ i = actors.size();
+ int j = 0;
+ while (i--) {
+ Actor* actor = actors[i];
+
+ if (PersonalDistance( p, actor ) > radius)
+ continue;
+ if (!actor->ValidTarget(flags) ) {
+ continue;
+ }
+ if (!actor->Schedule(gametime, true) ) {
+ continue;
+ }
+ if (!(flags&GA_NO_LOS)) {
+ if (!IsVisible(actor->Pos, p)) {
+ continue;
+ }
+ }
+
+ ret[j++]=actor;
+ }
+
+ ret[j]=NULL;
+ return ret;
+}
+
+
+Actor* Map::GetActor(const char* Name, int flags)
+{
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+ if (strnicmp( actor->GetScriptName(), Name, 32 ) == 0) {
+ if (!actor->ValidTarget(flags) ) {
+ return NULL;
+ }
+ return actor;
+ }
+ }
+ return NULL;
+}
+
+int Map::GetActorCount(bool any) const
+{
+ if (any) {
+ return (int) actors.size();
+ }
+ int ret = 0;
+ size_t i=actors.size();
+ while (i--) {
+ if (MustSave(actors[i])) {
+ ret++;
+ }
+ }
+ return ret;
+}
+
+void Map::JumpActors(bool jump)
+{
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+ if (actor->Modified[IE_DONOTJUMP]&DNJ_JUMP) {
+ if (jump) {
+ actor->FixPosition();
+ }
+ actor->SetBase(IE_DONOTJUMP,0);
+ }
+ }
+}
+
+void Map::SelectActors()
+{
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+ if (actor->Modified[IE_EA]<EA_CONTROLLABLE) {
+ core->GetGame()->SelectActor(actor, true, SELECT_QUIET);
+ }
+ }
+}
+
+//before writing the area out, perform some cleanups
+void Map::PurgeArea(bool items)
+{
+ InternalFlags |= IF_JUSTDIED; //area marked for swapping out
+
+ //1. remove dead actors without 'keep corpse' flag
+ int i=(int) actors.size();
+ while (i--) {
+ Actor *ac = actors[i];
+
+ if (ac->Modified[IE_STATE_ID]&STATE_NOSAVE) {
+ if (ac->Modified[IE_MC_FLAGS] & MC_KEEP_CORPSE) {
+ continue;
+ }
+ //don't delete persistent actors
+ if (ac->Persistent()) {
+ continue;
+ }
+ //even if you delete it, be very careful!
+ DeleteActor (i);
+ }
+ }
+ //2. remove any non critical items
+ if (items) {
+ i=(int) TMap->GetContainerCount();
+ while (i--) {
+ Container *c = TMap->GetContainer(i);
+ unsigned int j=c->inventory.GetSlotCount();
+ while (j--) {
+ CREItem *itemslot = c->inventory.GetSlotItem(j);
+ if (itemslot->Flags&IE_INV_ITEM_CRITICAL) {
+ continue;
+ }
+ }
+ TMap->CleanupContainer(c);
+ }
+ }
+}
+
+Actor* Map::GetActor(int index, bool any)
+{
+ if (any) {
+ return actors[index];
+ }
+ unsigned int i=0;
+ while (i<actors.size() ) {
+ Actor *ac = actors[i++];
+ if (MustSave(ac) ) {
+ if (!index--) {
+ return ac;
+ }
+ }
+ }
+ return NULL;
+}
+
+Actor* Map::GetActorByDialog(const char *resref)
+{
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+ //if a busy or hostile actor shouldn't be found
+ //set this to GD_CHECK
+ if (strnicmp( actor->GetDialog(GD_NORMAL), resref, 8 ) == 0) {
+ return actor;
+ }
+ }
+ return NULL;
+}
+
+//this function finds an actor by its original resref (not correct yet)
+Actor* Map::GetActorByResource(const char *resref)
+{
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+ if (strnicmp( actor->GetScriptName(), resref, 8 ) == 0) { //temporarily!
+ return actor;
+ }
+ }
+ return NULL;
+}
+
+Actor* Map::GetActorByScriptName(const char *name)
+{
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+ if (strnicmp( actor->GetScriptName(), name, 8 ) == 0) {
+ return actor;
+ }
+ }
+ return NULL;
+}
+
+int Map::GetActorInRect(Actor**& actorlist, Region& rgn, bool onlyparty)
+{
+ actorlist = ( Actor * * ) malloc( actors.size() * sizeof( Actor * ) );
+ int count = 0;
+ size_t i = actors.size();
+ while (i--) {
+ Actor* actor = actors[i];
+//use this function only for party?
+ if (onlyparty && actor->GetStat(IE_EA)>EA_CHARMED) {
+ continue;
+ }
+ // this is called by non-selection code..
+ if (onlyparty && !actor->ValidTarget(GA_SELECT))
+ continue;
+ if (!actor->ValidTarget(GA_NO_DEAD) )
+ continue;
+ if ((actor->Pos.x<rgn.x) || (actor->Pos.y<rgn.y))
+ continue;
+ if ((actor->Pos.x>rgn.x+rgn.w) || (actor->Pos.y>rgn.y+rgn.h) )
+ continue;
+ actorlist[count++] = actor;
+ }
+ actorlist = ( Actor * * ) realloc( actorlist, count * sizeof( Actor * ) );
+ return count;
+}
+
+void Map::PlayAreaSong(int SongType, bool restart, bool hard)
+{
+ //Ok, we use a non constant pointer here, so it is easy to disable
+ //a faulty music list on the fly. I don't want to add a method just for that
+ //crap when we already have that pointer at hand!
+ char* poi = core->GetMusicPlaylist( SongHeader.SongList[SongType] );
+ if (!poi) return;
+
+ //check if restart needed (either forced or the current song is different)
+ if (!restart && core->GetMusicMgr()->CurrentPlayList(poi)) return;
+ int ret = core->GetMusicMgr()->SwitchPlayList( poi, hard );
+ //Here we disable the faulty musiclist entry
+ if (ret) {
+ //Apparently, the playlist manager prefers a *
+ *poi='*';
+ return;
+ }
+ if (SongType == SONG_BATTLE) {
+ core->GetGame()->CombatCounter = 150;
+ }
+}
+
+unsigned int Map::GetBlocked(unsigned int x, unsigned int y)
+{
+ if (y>=Height || x>=Width) {
+ return 0;
+ }
+ unsigned int ret = SrchMap[y*Width+x];
+ if (ret&(PATH_MAP_DOOR_IMPASSABLE|PATH_MAP_ACTOR)) {
+ ret&=~PATH_MAP_PASSABLE;
+ }
+ if (ret&PATH_MAP_DOOR_OPAQUE) {
+ ret=PATH_MAP_SIDEWALL;
+ }
+ return ret;
+}
+
+bool Map::GetBlocked(unsigned int px, unsigned int py, unsigned int size)
+{
+ // We check a circle of radius size-2 around (px,py)
+ // Note that this does not exactly match BG2. BG2's approximations of
+ // these circles are slightly different for sizes 7 and up.
+
+ if (size > MAX_CIRCLESIZE) size = MAX_CIRCLESIZE;
+ if (size < 2) size = 2;
+
+ unsigned int ppx = px/16;
+ unsigned int ppy = py/12;
+ unsigned int r=(size-2)*(size-2)+1;
+ if (size == 2) r = 0;
+ for (unsigned int i=0; i<size-1; i++) {
+ for (unsigned int j=0; j<size-1; j++) {
+ if (i*i+j*j <= r) {
+ if (!(GetBlocked(ppx+i,ppy+j)&PATH_MAP_PASSABLE)) return true;
+ if (!(GetBlocked(ppx+i,ppy-j)&PATH_MAP_PASSABLE)) return true;
+ if (!(GetBlocked(ppx-i,ppy+j)&PATH_MAP_PASSABLE)) return true;
+ if (!(GetBlocked(ppx-i,ppy-j)&PATH_MAP_PASSABLE)) return true;
+ }
+ }
+ }
+ return false;
+}
+
+unsigned int Map::GetBlocked(const Point &c)
+{
+ return GetBlocked(c.x/16, c.y/12);
+}
+
+//flags:0 - never dither (full cover)
+// 1 - dither if polygon wants it
+// 2 - always dither
+
+SpriteCover* Map::BuildSpriteCover(int x, int y, int xpos, int ypos,
+ unsigned int width, unsigned int height, int flags)
+{
+ SpriteCover* sc = new SpriteCover;
+ sc->worldx = x;
+ sc->worldy = y;
+ sc->XPos = xpos;
+ sc->YPos = ypos;
+ sc->Width = width;
+ sc->Height = height;
+
+ Video* video = core->GetVideoDriver();
+ video->InitSpriteCover(sc, flags);
+
+ unsigned int wpcount = GetWallCount();
+ unsigned int i;
+
+ for (i = 0; i < wpcount; ++i)
+ {
+ Wall_Polygon* wp = GetWallGroup(i);
+ if (!wp) continue;
+ if (!wp->PointCovered(x, y)) continue;
+
+ video->AddPolygonToSpriteCover(sc, wp);
+ }
+
+ return sc;
+}
+
+void Map::ActivateWallgroups(unsigned int baseindex, unsigned int count, int flg)
+{
+ unsigned int i;
+
+ if (!Walls) {
+ return;
+ }
+ for(i=baseindex; i < baseindex+count; ++i) {
+ Wall_Polygon* wp = GetWallGroup(i);
+ if (!wp)
+ continue;
+ ieDword value=wp->GetPolygonFlag();
+ if (flg)
+ value&=~WF_DISABLED;
+ else
+ value|=WF_DISABLED;
+ wp->SetPolygonFlag(value);
+ }
+ //all actors will have to generate a new spritecover
+ i=(int) actors.size();
+ while(i--) {
+ actors[i]->SetSpriteCover(NULL);
+ }
+}
+
+
+//this function determines actor drawing order
+//it should be extended to wallgroups, animations, effects!
+void Map::GenerateQueues()
+{
+ int priority;
+
+ unsigned int i=(unsigned int) actors.size();
+ for (priority=0;priority<QUEUE_COUNT;priority++) {
+ if (lastActorCount[priority] != i) {
+ if (queue[priority]) {
+ free(queue[priority]);
+ queue[priority] = NULL;
+ }
+ queue[priority] = (Actor **) calloc( i, sizeof(Actor *) );
+ lastActorCount[priority] = i;
+ }
+ Qcount[priority] = 0;
+ }
+
+ ieDword gametime = core->GetGame()->GameTime;
+ while (i--) {
+ Actor* actor = actors[i];
+
+ if (actor->CheckOnDeath()) {
+ DeleteActor( i );
+ continue;
+ }
+
+ ieDword stance = actor->GetStance();
+ ieDword internalFlag = actor->GetInternalFlag();
+
+ if (internalFlag&IF_ACTIVE) {
+ if ((stance == IE_ANI_TWITCH) && (internalFlag&IF_IDLE) ) {
+ priority = PR_DISPLAY; //display
+ } else {
+ priority = PR_SCRIPT; //run scripts and display
+ }
+ } else {
+ //dead actors are always visible on the map, but run no scripts
+ if (stance == IE_ANI_TWITCH || stance == IE_ANI_DIE) {
+ priority = PR_DISPLAY;
+ } else {
+ //isvisible flag is false (visibilitymap) here,
+ //coz we want to reactivate creatures that
+ //just became visible
+ if (IsVisible(actor->Pos, false) && actor->Schedule(gametime, false) ) {
+ priority = PR_SCRIPT; //run scripts and display, activated now
+ //more like activate!
+ actor->Activate();
+ ActorSpottedByPlayer(actor);
+ } else {
+ priority = PR_IGNORE;
+ }
+ }
+ }
+
+ //we ignore priority 2
+ if (priority>=PR_IGNORE) continue;
+
+ queue[priority][Qcount[priority]] = actor;
+ Qcount[priority]++;
+ }
+}
+
+//the original qsort implementation was flawed
+void Map::SortQueues()
+{
+ for (int q=0;q<QUEUE_COUNT;q++) {
+ Actor **baseline=queue[q];
+ int n = Qcount[q];
+ int i = n/2;
+ int parent, child;
+ Actor * tmp;
+
+ for (;;) {
+ if (i>0) {
+ i--;
+ tmp = baseline[i];
+ } else {
+ n--;
+ if (n<=0) break; //breaking loop
+ tmp = baseline[n];
+ baseline[n] = baseline[0];
+ }
+ parent = i;
+ child = i*2+1;
+ while(child<n) {
+ int chp = child+1;
+ if (chp<n && baseline[chp]->Pos.y < baseline[child]->Pos.y) {
+ child=chp;
+ }
+ if (baseline[child]->Pos.y<tmp->Pos.y) {
+ baseline[parent] = baseline[child];
+ parent = child;
+ child = parent*2+1;
+ } else
+ break;
+ }
+ baseline[parent]=tmp;
+ }
+ }
+}
+
+void Map::AddProjectile(Projectile* pro, const Point &source, ieWord actorID, bool fake)
+{
+ proIterator iter;
+
+ pro->MoveTo(this,source);
+ pro->SetTarget(actorID, fake);
+ int height = pro->GetHeight();
+ for(iter=projectiles.begin();iter!=projectiles.end() && (*iter)->GetHeight()<height; iter++) ;
+ projectiles.insert(iter, pro);
+}
+
+//adding projectile in order, based on its height parameter
+void Map::AddProjectile(Projectile* pro, const Point &source, const Point &dest)
+{
+ proIterator iter;
+
+ pro->MoveTo(this,source);
+ pro->SetTarget(dest);
+ int height = pro->GetHeight();
+ for(iter=projectiles.begin();iter!=projectiles.end() && (*iter)->GetHeight()<height; iter++) ;
+ projectiles.insert(iter, pro);
+}
+
+//returns the longest duration of the VVC cell named 'resource' (if it exists)
+//if P is empty, the position won't be checked
+ieDword Map::HasVVCCell(const ieResRef resource, const Point &p)
+{
+ scaIterator iter;
+ ieDword ret = 0;
+
+ for(iter=vvcCells.begin();iter!=vvcCells.end(); iter++) {
+ if (!p.isempty()) {
+ if ((*iter)->XPos!=p.x) continue;
+ if ((*iter)->YPos!=p.y) continue;
+ }
+ if (strnicmp(resource, (*iter)->ResName, sizeof(ieResRef) )) continue;
+ ieDword tmp = (*iter)->GetSequenceDuration(AI_UPDATE_TIME)-(*iter)->GetCurrentFrame();
+ if (tmp>ret) {
+ ret = tmp;
+ }
+ }
+ return ret;
+}
+
+//adding videocell in order, based on its height parameter
+void Map::AddVVCell(ScriptedAnimation* vvc)
+{
+ scaIterator iter;
+
+ for(iter=vvcCells.begin();iter!=vvcCells.end() && (*iter)->ZPos<vvc->ZPos; iter++) ;
+ vvcCells.insert(iter, vvc);
+}
+
+AreaAnimation* Map::GetAnimation(const char* Name)
+{
+ aniIterator iter;
+
+ for(iter=animations.begin();iter!=animations.end();iter++) {
+ AreaAnimation *anim = *iter;
+
+ if (anim->Name && (strnicmp( anim->Name, Name, 32 ) == 0)) {
+ return anim;
+ }
+ }
+ return NULL;
+}
+
+Spawn *Map::AddSpawn(char* Name, int XPos, int YPos, ieResRef *creatures, unsigned int count)
+{
+ Spawn* sp = new Spawn();
+ strnspccpy(sp->Name, Name, 32);
+ if (count>MAX_RESCOUNT) {
+ count=MAX_RESCOUNT;
+ }
+ sp->Pos.x = (ieWord) XPos;
+ sp->Pos.y = (ieWord) YPos;
+ sp->Count = count;
+ sp->Creatures = (ieResRef *) calloc( count, sizeof(ieResRef) );
+ for( unsigned int i=0;i<count;i++) {
+ strnlwrcpy(sp->Creatures[i],creatures[i],8);
+ }
+ spawns.push_back( sp );
+ return sp;
+}
+
+void Map::AddEntrance(char* Name, int XPos, int YPos, short Face)
+{
+ Entrance* ent = new Entrance();
+ strncpy( ent->Name, Name, 32 );
+ ent->Pos.x = (ieWord) XPos;
+ ent->Pos.y = (ieWord) YPos;
+ ent->Face = (ieWord) Face;
+ entrances.push_back( ent );
+}
+
+Entrance* Map::GetEntrance(const char* Name)
+{
+ size_t i=entrances.size();
+ while (i--) {
+ Entrance *e = entrances[i];
+
+ if (strnicmp( e->Name, Name, 32 ) == 0) {
+ return e;
+ }
+ }
+ return NULL;
+}
+
+bool Map::HasActor(Actor *actor)
+{
+ size_t i=actors.size();
+ while (i--) {
+ if (actors[i] == actor) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Map::RemoveActor(Actor* actor)
+{
+ size_t i=actors.size();
+ while (i--) {
+ if (actors[i] == actor) {
+ ClearSearchMapFor(actor);
+ actors.erase( actors.begin()+i );
+ return;
+ }
+ }
+ printMessage("Map","RemoveActor: actor not found?",YELLOW);
+}
+
+//returns true if none of the partymembers are on the map
+bool Map::CanFree()
+{
+ size_t i=actors.size();
+ while (i--) {
+ if (actors[i]->InParty) {
+ return false;
+ }
+
+ if (actors[i]->GetInternalFlag()&(IF_ACTIVE|IF_USEEXIT) ) {
+ return false;
+ }
+ }
+ //we expect the area to be swapped out, so we simply remove the corpses now
+ PurgeArea(false);
+ return true;
+}
+
+void Map::DebugDump(bool show_actors) const
+{
+ size_t i;
+
+ printf( "DebugDump of Area %s:\n", scriptName );
+ printf ("Scripts:");
+
+ for (i = 0; i < MAX_SCRIPTS; i++) {
+ const char* poi = "<none>";
+ if (Scripts[i]) {
+ poi = Scripts[i]->GetName();
+ }
+ printf( " %.8s", poi );
+ }
+ printf( "Area Global ID: %d\n", GetGlobalID());
+ printf( "OutDoor: %s\n", YESNO(AreaType & AT_OUTDOOR ) );
+ printf( "Day/Night: %s\n", YESNO(AreaType & AT_DAYNIGHT ) );
+ printf( "Extended night: %s\n", YESNO(AreaType & AT_EXTENDED_NIGHT ) );
+ printf( "Weather: %s\n", YESNO(AreaType & AT_WEATHER ) );
+ printf( "Area Type: %d\n", AreaType & (AT_CITY|AT_FOREST|AT_DUNGEON) );
+ printf( "Can rest: %s\n", YESNO(AreaType & AT_CAN_REST) );
+
+ if (show_actors) {
+ printf("\n");
+ i = actors.size();
+ while (i--) {
+ if (!(actors[i]->GetInternalFlag()&(IF_JUSTDIED|IF_REALLYDIED))) {
+ printf("Actor: %s at %d.%d\n", actors[i]->GetName(1), actors[i]->Pos.x, actors[i]->Pos.y);
+ }
+ }
+ }
+}
+
+/******************************************************************************/
+
+void Map::Leveldown(unsigned int px, unsigned int py,
+ unsigned int& level, Point &n, unsigned int& diff)
+{
+ int pos;
+ unsigned int nlevel;
+
+ if (( px >= Width ) || ( py >= Height )) {
+ return;
+ } //walked off the map
+ pos = py * Width + px;
+ nlevel = MapSet[pos];
+ if (!nlevel) {
+ return;
+ } //not even considered
+ if (level <= nlevel) {
+ return;
+ }
+ unsigned int ndiff = level - nlevel;
+ if (ndiff > diff) {
+ level = nlevel;
+ diff = ndiff;
+ n.x = (ieWord) px;
+ n.y = (ieWord) py;
+ }
+}
+
+void Map::SetupNode(unsigned int x, unsigned int y, unsigned int size, unsigned int Cost)
+{
+ unsigned int pos;
+
+ if (( x >= Width ) || ( y >= Height )) {
+ return;
+ }
+ pos = y * Width + x;
+ if (MapSet[pos]) {
+ return;
+ }
+ if (GetBlocked(x*16+8,y*12+6,size)) {
+ MapSet[pos] = 65535;
+ return;
+ }
+ MapSet[pos] = (ieWord) Cost;
+ InternalStack.push( ( x << 16 ) | y );
+}
+
+bool Map::AdjustPositionX(Point &goal, unsigned int radius)
+{
+ unsigned int minx = 0;
+ if ((unsigned int) goal.x > radius)
+ minx = goal.x - radius;
+ unsigned int maxx = goal.x + radius + 1;
+ if (maxx > Width)
+ maxx = Width;
+
+ for (unsigned int scanx = minx; scanx < maxx; scanx++) {
+ if ((unsigned int) goal.y >= radius) {
+ if (GetBlocked( scanx, goal.y - radius ) & PATH_MAP_PASSABLE) {
+ goal.x = (ieWord) scanx;
+ goal.y = (ieWord) (goal.y - radius);
+ return true;
+ }
+ }
+ if (goal.y + radius < Height) {
+ if (GetBlocked( scanx, goal.y + radius ) & PATH_MAP_PASSABLE) {
+ goal.x = (ieWord) scanx;
+ goal.y = (ieWord) (goal.y + radius);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool Map::AdjustPositionY(Point &goal, unsigned int radius)
+{
+ unsigned int miny = 0;
+ if ((unsigned int) goal.y > radius)
+ miny = goal.y - radius;
+ unsigned int maxy = goal.y + radius + 1;
+ if (maxy > Height)
+ maxy = Height;
+ for (unsigned int scany = miny; scany < maxy; scany++) {
+ if ((unsigned int) goal.x >= radius) {
+ if (GetBlocked( goal.x - radius, scany ) & PATH_MAP_PASSABLE) {
+ goal.x = (ieWord) (goal.x - radius);
+ goal.y = (ieWord) scany;
+ return true;
+ }
+ }
+ if (goal.x + radius < Width) {
+ if (GetBlocked( goal.x + radius, scany ) & PATH_MAP_PASSABLE) {
+ goal.x = (ieWord) (goal.x + radius);
+ goal.y = (ieWord) scany;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void Map::AdjustPosition(Point &goal, unsigned int radius)
+{
+ unsigned int maxr = Width;
+ if (maxr < Height) {
+ maxr = Height;
+ }
+ if ((unsigned int) goal.x > Width) {
+ goal.x = (ieWord) Width;
+ }
+ if ((unsigned int) goal.y > Height) {
+ goal.y = (ieWord) Height;
+ }
+
+ for (; radius < maxr; radius++) {
+ //lets make it slightly random where the actor will appear
+ if (rand()&1) {
+ if (AdjustPositionX(goal, radius)) {
+ return;
+ }
+ if (AdjustPositionY(goal, radius)) {
+ return;
+ }
+ } else {
+ if (AdjustPositionY(goal, radius)) {
+ return;
+ }
+ if (AdjustPositionX(goal, radius)) {
+ return;
+ }
+ }
+ }
+}
+
+//run away from dX, dY (ie.: find the best path of limited length that brings us the farthest from dX, dY)
+PathNode* Map::RunAway(const Point &s, const Point &d, unsigned int size, unsigned int PathLen, int flags)
+{
+ Point start(s.x/16, s.y/12);
+ Point goal (d.x/16, d.y/12);
+ unsigned int dist;
+
+ //MapSet entries are made of 16 bits
+ if (PathLen>65535) {
+ PathLen = 65535;
+ }
+
+ memset( MapSet, 0, Width * Height * sizeof( unsigned short ) );
+ while (InternalStack.size())
+ InternalStack.pop();
+
+ if (!( GetBlocked( start.x, start.y) & PATH_MAP_PASSABLE )) {
+ AdjustPosition( start );
+ }
+ unsigned int pos = ( start.x << 16 ) | start.y;
+ InternalStack.push( pos );
+ MapSet[start.y * Width + start.x] = 1;
+ dist = 0;
+ Point best = start;
+ while (InternalStack.size()) {
+ pos = InternalStack.front();
+ InternalStack.pop();
+ unsigned int x = pos >> 16;
+ unsigned int y = pos & 0xffff;
+ long tx = ( x - goal.x );
+ long ty = ( y - goal.y );
+ unsigned int distance = (unsigned int) sqrt( ( double ) ( tx* tx + ty* ty ) );
+ if (dist<distance) {
+ best.x=(ieWord) x;
+ best.y=(ieWord) y;
+ dist=distance;
+ }
+
+ unsigned int Cost = MapSet[y * Width + x] + NormalCost;
+ if (Cost > PathLen) {
+ //printf("Path not found!\n");
+ break;
+ }
+ SetupNode( x - 1, y - 1, size, Cost );
+ SetupNode( x + 1, y - 1, size, Cost );
+ SetupNode( x + 1, y + 1, size, Cost );
+ SetupNode( x - 1, y + 1, size, Cost );
+
+ Cost += AdditionalCost;
+ SetupNode( x, y - 1, size, Cost );
+ SetupNode( x + 1, y, size, Cost );
+ SetupNode( x, y + 1, size, Cost );
+ SetupNode( x - 1, y, size, Cost );
+ }
+
+ //find path backwards from best to start
+ PathNode* StartNode = new PathNode;
+ PathNode* Return = StartNode;
+ StartNode->Next = NULL;
+ StartNode->x = best.x;
+ StartNode->y = best.y;
+ if (flags) {
+ StartNode->orient = GetOrient( start, best );
+ } else {
+ StartNode->orient = GetOrient( best, start );
+ }
+ Point p = best;
+ unsigned int pos2 = start.y * Width + start.x;
+ while (( pos = p.y * Width + p.x ) != pos2) {
+ Return = new PathNode;
+ StartNode->Parent = Return;
+ Return->Next = StartNode;
+ StartNode = Return;
+ unsigned int level = MapSet[pos];
+ unsigned int diff = 0;
+ Point n;
+ Leveldown( p.x, p.y + 1, level, n, diff );
+ Leveldown( p.x + 1, p.y, level, n, diff );
+ Leveldown( p.x - 1, p.y, level, n, diff );
+ Leveldown( p.x, p.y - 1, level, n, diff );
+ Leveldown( p.x - 1, p.y + 1, level, n, diff );
+ Leveldown( p.x + 1, p.y + 1, level, n, diff );
+ Leveldown( p.x + 1, p.y - 1, level, n, diff );
+ Leveldown( p.x - 1, p.y - 1, level, n, diff );
+ Return->x = n.x;
+ Return->y = n.y;
+
+ if (flags) {
+ Return->orient = GetOrient( p, n );
+ } else {
+ Return->orient = GetOrient( n, p );
+ }
+ p = n;
+ if (!diff) {
+ break;
+ }
+ }
+ Return->Parent = NULL;
+ return Return;
+}
+
+bool Map::TargetUnreachable(const Point &s, const Point &d, unsigned int size)
+{
+ Point start( s.x/16, s.y/12 );
+ Point goal ( d.x/16, d.y/12 );
+ memset( MapSet, 0, Width * Height * sizeof( unsigned short ) );
+ while (InternalStack.size())
+ InternalStack.pop();
+
+ if (GetBlocked( d.x, d.y, size )) {
+ return true;
+ }
+ if (GetBlocked( s.x, s.y, size )) {
+ return true;
+ }
+
+ unsigned int pos = ( goal.x << 16 ) | goal.y;
+ unsigned int pos2 = ( start.x << 16 ) | start.y;
+ InternalStack.push( pos );
+ MapSet[goal.y * Width + goal.x] = 1;
+
+ while (InternalStack.size() && pos!=pos2) {
+ pos = InternalStack.front();
+ InternalStack.pop();
+ unsigned int x = pos >> 16;
+ unsigned int y = pos & 0xffff;
+
+ SetupNode( x - 1, y - 1, size, 1 );
+ SetupNode( x + 1, y - 1, size, 1 );
+ SetupNode( x + 1, y + 1, size, 1 );
+ SetupNode( x - 1, y + 1, size, 1 );
+ SetupNode( x, y - 1, size, 1 );
+ SetupNode( x + 1, y, size, 1 );
+ SetupNode( x, y + 1, size, 1 );
+ SetupNode( x - 1, y, size, 1 );
+ }
+ return pos!=pos2;
+}
+
+/* Use this function when you target something by a straight line projectile (like a lightning bolt, arrow, etc)
+*/
+
+PathNode* Map::GetLine(const Point &start, const Point &dest, int flags)
+{
+ int Orientation = GetOrient(start, dest);
+ return GetLine(start, dest, 1, Orientation, flags);
+}
+
+PathNode* Map::GetLine(const Point &start, int Steps, int Orientation, int flags)
+{
+ Point dest=start;
+
+ unsigned int st = Steps>=MaxVisibility?MaxVisibility-1:Steps;
+ int p = VisibilityPerimeter*Orientation/MAX_ORIENT;
+ dest.x += VisibilityMasks[st][p].x;
+ dest.y += VisibilityMasks[st][p].y;
+ //FIXME: calculate dest based on distance and orientation
+ return GetLine(start, dest, Steps, Orientation, flags);
+}
+
+PathNode* Map::GetLine(const Point &start, const Point &dest, int Speed, int Orientation, int flags)
+{
+ PathNode* StartNode = new PathNode;
+ PathNode *Return = StartNode;
+ StartNode->Next = NULL;
+ StartNode->Parent = NULL;
+ StartNode->x = start.x;
+ StartNode->y = start.y;
+ StartNode->orient = Orientation;
+
+ int Count = 0;
+ int Max = Distance(start,dest);
+ for (int Steps = 0; Steps<Max; Steps++) {
+ if (!Count) {
+ StartNode->Next = new PathNode;
+ StartNode->Next->Parent = StartNode;
+ StartNode = StartNode->Next;
+ StartNode->Next = NULL;
+ Count=Speed;
+ } else {
+ Count--;
+ }
+
+ Point p;
+ p.x = (ieWord) start.x + ((dest.x - start.x) * Steps / Max);
+ p.y = (ieWord) start.y + ((dest.y - start.y) * Steps / Max);
+
+ //the path ends here as it would go off the screen, causing problems
+ //maybe there is a better way, but i needed a quick hack to fix
+ //the crash in projectiles
+ if ((signed) p.x<0 || (signed) p.y<0) {
+ return Return;
+ }
+ if ((ieWord) p.x>Width*16 || (ieWord) p.y>Height*12) {
+ return Return;
+ }
+
+ StartNode->x = p.x;
+ StartNode->y = p.y;
+ StartNode->orient = Orientation;
+ bool wall = !( GetBlocked( p ) & PATH_MAP_PASSABLE );
+ if (wall) switch (flags) {
+ case GL_REBOUND:
+ Orientation = (Orientation + 8) &15;
+ //recalculate dest (mirror it)
+ break;
+ case GL_PASS:
+ break;
+ default: //premature end
+ return Return;
+ }
+ }
+
+ return Return;
+}
+
+/*
+ * find a path from start to goal, ending at the specified distance from the
+ * target (the goal must be in sight of the end, if 'sight' is specified)
+ *
+ * if you don't need to find an optimal path near the goal then use FindPath
+ * instead, but don't change this one without testing with combat and dialog,
+ * you can't predict the goal point for those, you *must* path!
+ */
+PathNode* Map::FindPathNear(const Point &s, const Point &d, unsigned int size, unsigned int MinDistance, bool sight)
+{
+ // adjust the start/goal points to be searchmap locations
+ Point start( s.x/16, s.y/12 );
+ Point goal ( d.x/16, d.y/12 );
+ Point orig_goal = goal;
+
+ // re-initialise the path finding structures
+ memset( MapSet, 0, Width * Height * sizeof( unsigned short ) );
+ while (InternalStack.size())
+ InternalStack.pop();
+
+ // set the start point in the path finding structures
+ unsigned int pos2 = ( goal.x << 16 ) | goal.y;
+ unsigned int pos = ( start.x << 16 ) | start.y;
+ InternalStack.push( pos );
+ MapSet[start.y * Width + start.x] = 1;
+
+ unsigned int squaredmindistance = MinDistance * MinDistance;
+ bool found_path = false;
+ while (InternalStack.size()) {
+ pos = InternalStack.front();
+ InternalStack.pop();
+ unsigned int x = pos >> 16;
+ unsigned int y = pos & 0xffff;
+
+ if (pos == pos2) {
+ // we got all the way to the target!
+ found_path = true;
+ break;
+ } else if (MinDistance) {
+ /* check minimum distance:
+ * as an obvious optimisation we only check squared distance: this is a
+ * possible overestimate since the sqrt Distance() rounds down
+ * (some other optimisations could be made here, but you'd be better off
+ * fixing the pathfinder to do A* properly)
+ * caller should have already done PersonalDistance adjustments, this is
+ * simply between the specified points
+ */
+
+ int distx = (x*16 + 8) - d.x;
+ int disty = (y*12 + 6) - d.y;
+ if ((unsigned int)(distx*distx + disty*disty) <= squaredmindistance) {
+ // we are within the minimum distance of the goal
+ Point ourpos(x*16 + 8, y*12 + 6);
+ // sight check is *slow* :(
+ if (!sight || IsVisible(ourpos, d)) {
+ // we got all the way to a suitable goal!
+ goal = Point(x, y);
+ found_path = true;
+ break;
+ }
+ }
+ }
+
+ unsigned int Cost = MapSet[y * Width + x] + NormalCost;
+ if (Cost > 65500) {
+ // cost is far too high, no path found
+ break;
+ }
+
+ // diagonal movements
+ SetupNode( x - 1, y - 1, size, Cost );
+ SetupNode( x + 1, y - 1, size, Cost );
+ SetupNode( x + 1, y + 1, size, Cost );
+ SetupNode( x - 1, y + 1, size, Cost );
+
+ // direct movements
+ Cost += AdditionalCost;
+ SetupNode( x, y - 1, size, Cost );
+ SetupNode( x + 1, y, size, Cost );
+ SetupNode( x, y + 1, size, Cost );
+ SetupNode( x - 1, y, size, Cost );
+ }
+
+ // find path from goal to start
+ PathNode* StartNode = new PathNode;
+ PathNode* Return = StartNode;
+ StartNode->Next = NULL;
+ StartNode->Parent = NULL;
+ if (!found_path) {
+ // this is not really great, we should be finding the path that
+ // went nearest to where we wanted
+ StartNode->x = start.x;
+ StartNode->y = start.y;
+ StartNode->orient = GetOrient( goal, start );
+ return Return;
+ }
+ StartNode->x = goal.x;
+ StartNode->y = goal.y;
+ bool fixup_orient = false;
+ if (orig_goal != goal) {
+ StartNode->orient = GetOrient( orig_goal, goal );
+ } else {
+ // we pathed all the way to original goal!
+ // we don't know correct orientation until we find previous step
+ fixup_orient = true;
+ StartNode->orient = GetOrient( goal, start );
+ }
+ Point p = goal;
+ pos2 = start.y * Width + start.x;
+ while (( pos = p.y * Width + p.x ) != pos2) {
+ unsigned int level = MapSet[pos];
+ unsigned int diff = 0;
+ Point n;
+ Leveldown( p.x, p.y + 1, level, n, diff );
+ Leveldown( p.x + 1, p.y, level, n, diff );
+ Leveldown( p.x - 1, p.y, level, n, diff );
+ Leveldown( p.x, p.y - 1, level, n, diff );
+ Leveldown( p.x - 1, p.y + 1, level, n, diff );
+ Leveldown( p.x + 1, p.y + 1, level, n, diff );
+ Leveldown( p.x + 1, p.y - 1, level, n, diff );
+ Leveldown( p.x - 1, p.y - 1, level, n, diff );
+ if (!diff)
+ return Return;
+
+ if (fixup_orient) {
+ // don't change orientation at end of path? this seems best
+ StartNode->orient = GetOrient( p, n );
+ }
+
+ Return = new PathNode;
+ Return->Next = StartNode;
+ Return->Next->Parent = Return;
+ StartNode = Return;
+
+ StartNode->x = n.x;
+ StartNode->y = n.y;
+ StartNode->orient = GetOrient( p, n );
+ p = n;
+ }
+
+ return Return;
+}
+
+PathNode* Map::FindPath(const Point &s, const Point &d, unsigned int size, int MinDistance)
+{
+ Point start( s.x/16, s.y/12 );
+ Point goal ( d.x/16, d.y/12 );
+ memset( MapSet, 0, Width * Height * sizeof( unsigned short ) );
+ while (InternalStack.size())
+ InternalStack.pop();
+
+ if (GetBlocked( d.x, d.y, size )) {
+ AdjustPosition( goal );
+ }
+ unsigned int pos = ( goal.x << 16 ) | goal.y;
+ unsigned int pos2 = ( start.x << 16 ) | start.y;
+ InternalStack.push( pos );
+ MapSet[goal.y * Width + goal.x] = 1;
+
+ while (InternalStack.size()) {
+ pos = InternalStack.front();
+ InternalStack.pop();
+ unsigned int x = pos >> 16;
+ unsigned int y = pos & 0xffff;
+
+ if (pos == pos2) {
+ //We've found _a_ path
+ //printf("GOAL!!!\n");
+ break;
+ }
+ unsigned int Cost = MapSet[y * Width + x] + NormalCost;
+ if (Cost > 65500) {
+ //printf("Path not found!\n");
+ break;
+ }
+ SetupNode( x - 1, y - 1, size, Cost );
+ SetupNode( x + 1, y - 1, size, Cost );
+ SetupNode( x + 1, y + 1, size, Cost );
+ SetupNode( x - 1, y + 1, size, Cost );
+
+ Cost += AdditionalCost;
+ SetupNode( x, y - 1, size, Cost );
+ SetupNode( x + 1, y, size, Cost );
+ SetupNode( x, y + 1, size, Cost );
+ SetupNode( x - 1, y, size, Cost );
+ }
+
+ //find path from start to goal
+ PathNode* StartNode = new PathNode;
+ PathNode* Return = StartNode;
+ StartNode->Next = NULL;
+ StartNode->Parent = NULL;
+ StartNode->x = start.x;
+ StartNode->y = start.y;
+ StartNode->orient = GetOrient( goal, start );
+ if (pos != pos2) {
+ return Return;
+ }
+ Point p = start;
+ pos2 = goal.y * Width + goal.x;
+ while (( pos = p.y * Width + p.x ) != pos2) {
+ StartNode->Next = new PathNode;
+ StartNode->Next->Parent = StartNode;
+ StartNode = StartNode->Next;
+ StartNode->Next = NULL;
+ unsigned int level = MapSet[pos];
+ unsigned int diff = 0;
+ Point n;
+ Leveldown( p.x, p.y + 1, level, n, diff );
+ Leveldown( p.x + 1, p.y, level, n, diff );
+ Leveldown( p.x - 1, p.y, level, n, diff );
+ Leveldown( p.x, p.y - 1, level, n, diff );
+ Leveldown( p.x - 1, p.y + 1, level, n, diff );
+ Leveldown( p.x + 1, p.y + 1, level, n, diff );
+ Leveldown( p.x + 1, p.y - 1, level, n, diff );
+ Leveldown( p.x - 1, p.y - 1, level, n, diff );
+ if (!diff)
+ return Return;
+ StartNode->x = n.x;
+ StartNode->y = n.y;
+ StartNode->orient = GetOrient( n, p );
+ p = n;
+ }
+ //stepping back on the calculated path
+ if (MinDistance) {
+ while (StartNode->Parent) {
+ Point tar;
+
+ tar.x=StartNode->Parent->x*16;
+ tar.y=StartNode->Parent->y*12;
+ int dist = Distance(tar,d);
+ if (dist+14>=MinDistance) {
+ break;
+ }
+ StartNode = StartNode->Parent;
+ delete StartNode->Next;
+ StartNode->Next = NULL;
+ }
+ }
+ return Return;
+}
+
+//single point visible or not (visible/exploredbitmap)
+//if explored = true then explored otherwise currently visible
+bool Map::IsVisible(const Point &pos, int explored)
+{
+ if (!VisibleBitmap)
+ return false;
+ int sX=pos.x/32;
+ int sY=pos.y/32;
+ if (sX<0) return false;
+ if (sY<0) return false;
+ int w = TMap->XCellCount * 2 + LargeFog;
+ int h = TMap->YCellCount * 2 + LargeFog;
+ if (sX>=w) return false;
+ if (sY>=h) return false;
+ int b0 = (sY * w) + sX;
+ int by = b0/8;
+ int bi = 1<<(b0%8);
+
+ if (explored) return (ExploredBitmap[by] & bi)!=0;
+ return (VisibleBitmap[by] & bi)!=0;
+}
+
+//point a is visible from point b (searchmap)
+bool Map::IsVisible(const Point &s, const Point &d)
+{
+ int sX=s.x/16;
+ int sY=s.y/12;
+ int dX=d.x/16;
+ int dY=d.y/12;
+ int diffx = sX - dX;
+ int diffy = sY - dY;
+
+ // we basically draw a 'line' from (sX, sY) to (dX, dY)
+ // we want to move along the larger axis, to make sure we don't miss anything
+ if (abs( diffx ) >= abs( diffy )) {
+ // (sX - startX)/elevationy = (sX - startX)/fabs(diffx) * diffy
+ double elevationy = fabs((double)diffx ) / diffy;
+ if (sX > dX) {
+ // right to left
+ for (int startx = sX; startx >= dX; startx--) {
+ // sX - startx >= 0, so subtract (due to sign of diffy)
+ //if (GetBlocked( startx, sY - ( int ) ( ( sX - startx ) / elevationy ) ) & PATH_MAP_NO_SEE)
+ if (GetBlocked( startx, sY - ( int ) ( ( sX - startx ) / elevationy ) ) & PATH_MAP_SIDEWALL)
+ return false;
+ }
+ } else {
+ // left to right
+ for (int startx = sX; startx <= dX; startx++) {
+ // sX - startx <= 0, so add (due to sign of diffy)
+ //if (GetBlocked( startx, sY + ( int ) ( ( sX - startx ) / elevationy ) ) & PATH_MAP_NO_SEE)
+ if (GetBlocked( startx, sY + ( int ) ( ( sX - startx ) / elevationy ) ) & PATH_MAP_SIDEWALL)
+ return false;
+ }
+ }
+ } else {
+ // (sY - startY)/elevationx = (sY - startY)/fabs(diffy) * diffx
+ double elevationx = fabs((double)diffy ) / diffx;
+ if (sY > dY) {
+ // bottom to top
+ for (int starty = sY; starty >= dY; starty--) {
+ // sY - starty >= 0, so subtract (due to sign of diffx)
+ //if (GetBlocked( sX - ( int ) ( ( sY - starty ) / elevationx ), starty ) & PATH_MAP_NO_SEE)
+ if (GetBlocked( sX - ( int ) ( ( sY - starty ) / elevationx ), starty ) & PATH_MAP_SIDEWALL)
+ return false;
+ }
+ } else {
+ // top to bottom
+ for (int starty = sY; starty <= dY; starty++) {
+ // sY - starty <= 0, so add (due to sign of diffx)
+ //if (GetBlocked( sX + ( int ) ( ( sY - starty ) / elevationx ), starty ) & PATH_MAP_NO_SEE)
+ if (GetBlocked( sX + ( int ) ( ( sY - starty ) / elevationx ), starty ) & PATH_MAP_SIDEWALL)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+//returns direction of area boundary, returns -1 if it isn't a boundary
+int Map::WhichEdge(const Point &s)
+{
+ unsigned int sX=s.x/16;
+ unsigned int sY=s.y/12;
+ if (!(GetBlocked( sX, sY )&PATH_MAP_TRAVEL)) {
+ printMessage("Map"," ",YELLOW);
+ printf("This isn't a travel region [%d.%d]?\n",sX, sY);
+ return -1;
+ }
+ sX*=Height;
+ sY*=Width;
+ if (sX>sY) { //north or east
+ if (Width*Height>sX+sY) { //
+ return WMP_NORTH;
+ }
+ return WMP_EAST;
+ }
+ //south or west
+ if (Width*Height<sX+sY) { //
+ return WMP_SOUTH;
+ }
+ return WMP_WEST;
+}
+
+//--------ambients----------------
+void Map::SetupAmbients()
+{
+ AmbientMgr *ambim = core->GetAudioDrv()->GetAmbientMgr();
+ if (!ambim) return;
+ ambim->reset();
+ ambim->setAmbients( ambients );
+}
+//--------mapnotes----------------
+//text must be a pointer we can claim ownership of
+void Map::AddMapNote(const Point &point, int color, char *text, ieStrRef strref)
+{
+ MapNote *mn = new MapNote;
+
+ mn->strref = strref;
+ mn->Pos = point;
+ mn->color = (ieWord) color;
+ mn->text = text;
+ RemoveMapNote(point); //delete previous mapnote
+ mapnotes.push_back(mn);
+}
+
+void Map::RemoveMapNote(const Point &point)
+{
+ size_t i = mapnotes.size();
+ while (i--) {
+ if ((point.x==mapnotes[i]->Pos.x) &&
+ (point.y==mapnotes[i]->Pos.y)) {
+ delete mapnotes[i];
+ mapnotes.erase(mapnotes.begin()+i);
+ }
+ }
+}
+
+MapNote *Map::GetMapNote(const Point &point)
+{
+ size_t i = mapnotes.size();
+ while (i--) {
+ if (Distance(point, mapnotes[i]->Pos) < 10 ) {
+ return mapnotes[i];
+ }
+ }
+ return NULL;
+}
+//--------spawning------------------
+void Map::LoadIniSpawn()
+{
+ INISpawn = new IniSpawn(this);
+ INISpawn->InitSpawn(WEDResRef);
+}
+
+void Map::SpawnCreature(const Point &pos, const char *CreName, int radius)
+{
+ SpawnGroup *sg=NULL;
+ Actor *creature;
+ void* lookup;
+ if ( !Spawns.Lookup( CreName, lookup) ) {
+ creature = gamedata->GetCreature(CreName);
+ if ( creature ) {
+ AddActor(creature);
+ creature->SetPosition( pos, true, radius );
+ creature->RefreshEffects(NULL);
+ }
+ return;
+ }
+ sg = (SpawnGroup*)lookup;
+ unsigned int count = 0;
+ int amount = core->GetGame()->GetPartyLevel(true);
+ // if the difficulty is too high, distribute it equally over all the
+ // critters and summon as many as the summed difficulty allows
+ if (amount - (signed)sg->Level < 0) {
+ unsigned int share = sg->Level/sg->Count;
+ amount -= share;
+ if (amount < 0) {
+ // a single critter is also too powerful
+ return;
+ }
+ while (amount >= 0) {
+ count++;
+ amount -= share;
+ }
+ } else {
+ count = sg->Count;
+ }
+
+ while ( count-- ) {
+ creature = gamedata->GetCreature(sg->ResRefs[count]);
+ if ( creature ) {
+ AddActor(creature);
+ creature->SetPosition( pos, true, radius );
+ creature->RefreshEffects(NULL);
+ }
+ }
+}
+
+void Map::TriggerSpawn(Spawn *spawn)
+{
+ //is it still active
+ if (!spawn->Enabled) {
+ return;
+ }
+ //check schedule
+ ieDword bit = 1<<((core->GetGame()->GameTime/AI_UPDATE_TIME)%7200/300);
+ if (!(spawn->appearance & bit)) {
+ return;
+ }
+
+ //check day or night chance
+ if (rand()%100>spawn->DayChance) {
+ return;
+ }
+ // the difficulty check is done in SpawnCreature
+ //create spawns
+ for(unsigned int i = 0;i<spawn->Count;i++) {
+ SpawnCreature(spawn->Pos, spawn->Creatures[i], 0);
+ }
+ //disable spawnpoint
+ spawn->Enabled = 0;
+}
+
+//--------restheader----------------
+/*
+Every spawn has a difficulty associated with it. For CREs this is the xp stat
+and for groups it's the value in the difficulty row.
+For every spawn, the difficulty sum of all spawns up to now (including the
+current) is compared against (party level * rest header difficulty). If it's
+greater, the spawning is aborted. If all the other conditions are true, at
+least one creature is summoned, regardless the difficulty cap.
+*/
+bool Map::Rest(const Point &pos, int hours, int day)
+{
+ if (!RestHeader.CreatureNum || !RestHeader.Enabled || !RestHeader.Maximum) {
+ return false;
+ }
+
+ //based on ingame timer
+ int chance=day?RestHeader.DayChance:RestHeader.NightChance;
+ int spawncount = 1;
+ int spawnamount = core->GetGame()->GetPartyLevel(true) * RestHeader.Difficulty;
+ if (spawnamount < 1) spawnamount = 1;
+ for (int i=0;i<hours;i++) {
+ if ( rand()%100<chance ) {
+ int idx = rand()%RestHeader.CreatureNum;
+ Actor *creature = gamedata->GetCreature(RestHeader.CreResRef[idx]);
+ if (!creature) continue;
+ // ensure a minimum power level, since many creatures have this as 0
+ int cpl = creature->Modified[IE_XP] ? creature->Modified[IE_XP] : 1;
+
+ displaymsg->DisplayString( RestHeader.Strref[idx], 0x00404000, IE_STR_SOUND );
+ while (spawnamount > 0 && spawncount <= RestHeader.Maximum) {
+ SpawnCreature(pos, RestHeader.CreResRef[idx], 20);
+ spawnamount -= cpl;
+ spawncount++;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+//--------explored bitmap-----------
+int Map::GetExploredMapSize() const
+{
+ int x = TMap->XCellCount*2;
+ int y = TMap->YCellCount*2;
+ if (LargeFog) {
+ x++;
+ y++;
+ }
+ return (x*y+7)/8;
+}
+
+void Map::Explore(int setreset)
+{
+ memset (ExploredBitmap, setreset, GetExploredMapSize() );
+}
+
+void Map::SetMapVisibility(int setreset)
+{
+ memset( VisibleBitmap, setreset, GetExploredMapSize() );
+}
+
+// x, y are not in tile coordinates
+void Map::ExploreTile(const Point &pos)
+{
+ int h = TMap->YCellCount * 2 + LargeFog;
+ int y = pos.y/32;
+ if (y < 0 || y >= h)
+ return;
+
+ int w = TMap->XCellCount * 2 + LargeFog;
+ int x = pos.x/32;
+ if (x < 0 || x >= w)
+ return;
+
+ int b0 = (y * w) + x;
+ int by = b0/8;
+ int bi = 1<<(b0%8);
+
+ ExploredBitmap[by] |= bi;
+ VisibleBitmap[by] |= bi;
+}
+
+void Map::ExploreMapChunk(const Point &Pos, int range, int los)
+{
+ Point Tile;
+
+ if (range>MaxVisibility) {
+ range=MaxVisibility;
+ }
+ int p=VisibilityPerimeter;
+ while (p--) {
+ int Pass = 2;
+ bool block = false;
+ bool sidewall = false ;
+ for (int i=0;i<range;i++) {
+ Tile.x = Pos.x+VisibilityMasks[i][p].x;
+ Tile.y = Pos.y+VisibilityMasks[i][p].y;
+
+ if (los) {
+ if (!block) {
+ int type = GetBlocked(Tile);
+ if (type & PATH_MAP_NO_SEE) {
+ block=true;
+ } else if (type & PATH_MAP_SIDEWALL) {
+ sidewall = true;
+ } else if (sidewall)
+ {
+ block=true ;
+ }
+ }
+ if (block) {
+ Pass--;
+ if (!Pass) break;
+ }
+ }
+ ExploreTile(Tile);
+ }
+ }
+}
+
+void Map::UpdateFog()
+{
+ if (!(core->FogOfWar&FOG_DRAWFOG) ) {
+ SetMapVisibility( -1 );
+ Explore(-1);
+ } else {
+ SetMapVisibility( 0 );
+ }
+
+ for (unsigned int e = 0; e<actors.size(); e++) {
+ Actor *actor = actors[e];
+ if (!actor->Modified[ IE_EXPLORE ] ) continue;
+ if (core->FogOfWar&FOG_DRAWFOG) {
+ int state = actor->Modified[IE_STATE_ID];
+ if (state & STATE_CANTSEE) continue;
+ int vis2 = actor->Modified[IE_VISUALRANGE];
+ if ((state&STATE_BLIND) || (vis2<2)) vis2=2; //can see only themselves
+ ExploreMapChunk (actor->Pos, vis2+actor->GetAnims()->GetCircleSize(), 1);
+ }
+ Spawn *sp = GetSpawnRadius(actor->Pos, SPAWN_RANGE); //30 * 12
+ if (sp) {
+ TriggerSpawn(sp);
+ }
+ }
+}
+
+//Valid values are - PATH_MAP_FREE, PATH_MAP_PC, PATH_MAP_NPC
+void Map::BlockSearchMap(const Point &Pos, unsigned int size, unsigned int value)
+{
+ // We block a circle of radius size-1 around (px,py)
+ // Note that this does not exactly match BG2. BG2's approximations of
+ // these circles are slightly different for sizes 6 and up.
+
+ // Note: this is a larger circle than the one tested in GetBlocked.
+ // This means that an actor can get closer to a wall than to another
+ // actor. This matches the behaviour of the original BG2.
+
+ if (size > MAX_CIRCLESIZE) size = MAX_CIRCLESIZE;
+ if (size < 2) size = 2;
+ unsigned int ppx = Pos.x/16;
+ unsigned int ppy = Pos.y/12;
+ unsigned int r=(size-1)*(size-1)+1;
+ if (size == 1) r = 0;
+ for (unsigned int i=0; i<size; i++) {
+ for (unsigned int j=0; j<size; j++) {
+ if (i*i+j*j <= r) {
+ unsigned int ppxpi = ppx+i;
+ unsigned int ppypj = ppy+j;
+ unsigned int ppxmi = ppx-i;
+ unsigned int ppymj = ppy-j;
+ if ((ppxpi<Width) && (ppypj<Height)) {
+ unsigned int pos = ppypj*Width+ppxpi;
+ SrchMap[pos] = (SrchMap[pos]&PATH_MAP_NOTACTOR) | value;
+ }
+
+ if ((ppxpi<Width) && (ppymj<Height)) {
+ unsigned int pos = (ppymj)*Width+ppxpi;
+ SrchMap[pos] = (SrchMap[pos]&PATH_MAP_NOTACTOR) | value;
+ }
+
+ if ((ppxmi<Width) && (ppypj<Height)) {
+ unsigned int pos = (ppypj)*Width+ppxmi;
+ SrchMap[pos] = (SrchMap[pos]&PATH_MAP_NOTACTOR) | value;
+ }
+
+ if ((ppxmi<Width) && (ppymj<Height)) {
+ unsigned int pos = (ppymj)*Width+ppxmi;
+ SrchMap[pos] = (SrchMap[pos]&PATH_MAP_NOTACTOR) | value;
+ }
+ }
+ }
+ }
+}
+
+Spawn* Map::GetSpawn(const char* Name)
+{
+ for (size_t i = 0; i < spawns.size(); i++) {
+ Spawn* sp = spawns[i];
+
+ if (stricmp( sp->Name, Name ) == 0)
+ return sp;
+ }
+ return NULL;
+}
+
+Spawn *Map::GetSpawnRadius(const Point &point, unsigned int radius)
+{
+ for (size_t i = 0; i < spawns.size(); i++) {
+ Spawn* sp = spawns[i];
+
+ if (Distance(point, sp->Pos)<radius) {
+ return sp;
+ }
+ }
+ return NULL;
+}
+
+int Map::ConsolidateContainers()
+{
+ int itemcount = 0;
+ int containercount = (int) TMap->GetContainerCount();
+ while (containercount--) {
+ Container * c = TMap->GetContainer( containercount);
+
+ if (TMap->CleanupContainer(c) ) {
+ continue;
+ }
+ itemcount += c->inventory.GetSlotCount();
+ }
+ return itemcount;
+}
+
+//Pos could be [-1,-1] in which case it copies the ground piles to their
+//original position in the second area
+void Map::CopyGroundPiles(Map *othermap, const Point &Pos)
+{
+ int containercount = (int) TMap->GetContainerCount();
+ while (containercount--) {
+ Container * c = TMap->GetContainer( containercount);
+ if (c->Type==IE_CONTAINER_PILE) {
+ //creating (or grabbing) the container in the other map at the given position
+ Container *othercontainer;
+ if (Pos.isempty()) {
+ othercontainer = othermap->GetPile(c->Pos);
+ } else {
+ othercontainer = othermap->GetPile(Pos);
+ }
+ //transfer the pile to the other container
+ unsigned int i=c->inventory.GetSlotCount();
+ while (i--) {
+ CREItem *item = c->RemoveItem(i, 0);
+ othercontainer->AddItem(item);
+ }
+ }
+ }
+}
+
+void Map::MoveVisibleGroundPiles(const Point &Pos)
+{
+ //creating the container at the given position
+ Container *othercontainer;
+ othercontainer = GetPile(Pos);
+
+ int containercount = (int) TMap->GetContainerCount();
+ while (containercount--) {
+ Container * c = TMap->GetContainer( containercount);
+ if (c->Type==IE_CONTAINER_PILE && IsVisible(c->Pos, true)) {
+ //transfer the pile to the other container
+ unsigned int i=c->inventory.GetSlotCount();
+ while (i--) {
+ CREItem *item = c->RemoveItem(i, 0);
+ othercontainer->AddItem(item);
+ }
+ }
+ }
+}
+
+Container *Map::GetPile(Point position)
+{
+ Point tmp[4];
+ char heapname[32];
+
+ //converting to search square
+ position.x=position.x/16;
+ position.y=position.y/12;
+ sprintf(heapname,"heap_%hd.%hd",position.x,position.y);
+ //pixel position is centered on search square
+ position.x=position.x*16+8;
+ position.y=position.y*12+6;
+ Container *container = TMap->GetContainer(position,IE_CONTAINER_PILE);
+ if (!container) {
+ //bounding box covers the search square
+ tmp[0].x=position.x-8;
+ tmp[0].y=position.y-6;
+ tmp[1].x=position.x+8;
+ tmp[1].y=position.y-6;
+ tmp[2].x=position.x+8;
+ tmp[2].y=position.y+6;
+ tmp[3].x=position.x-8;
+ tmp[3].y=position.y+6;
+ Gem_Polygon* outline = new Gem_Polygon( tmp, 4 );
+ container = AddContainer(heapname, IE_CONTAINER_PILE, outline);
+ container->Pos=position;
+ }
+ return container;
+}
+
+void Map::AddItemToLocation(const Point &position, CREItem *item)
+{
+ Container *container = GetPile(position);
+ container->AddItem(item);
+}
+
+Container* Map::AddContainer(const char* Name, unsigned short Type,
+ Gem_Polygon* outline)
+{
+ Container* c = new Container();
+ c->SetScriptName( Name );
+ c->Type = Type;
+ c->outline = outline;
+ c->SetMap(this);
+ TMap->AddContainer( c );
+ return c;
+}
+
+int Map::GetCursor( const Point &p)
+{
+ if (!IsVisible( p, true ) ) {
+ return IE_CURSOR_INVALID;
+ }
+ switch (GetBlocked( p ) & (PATH_MAP_PASSABLE|PATH_MAP_TRAVEL)) {
+ case 0:
+ return IE_CURSOR_BLOCKED;
+ case PATH_MAP_PASSABLE:
+ return IE_CURSOR_WALK;
+ default:
+ return IE_CURSOR_TRAVEL;
+ }
+}
+
+bool Map::HasWeather()
+{
+ if ((AreaType & (AT_WEATHER|AT_OUTDOOR) ) != (AT_WEATHER|AT_OUTDOOR) ) {
+ return false;
+ }
+ return true;
+}
+
+int Map::GetWeather()
+{
+ if (Rain>=core->Roll(1,100,0) ) {
+ if (Lightning>=core->Roll(1,100,0) ) {
+ return WB_LIGHTNING|WB_RAIN;
+ }
+ return WB_RAIN;
+ }
+ if (Snow>=core->Roll(1,100,0) ) {
+ return WB_SNOW;
+ }
+ if (Fog>=core->Roll(1,100,0) ) {
+ return WB_FOG;
+ }
+ return WB_NORMAL;
+}
+
+void Map::FadeSparkle(const Point &pos, bool forced)
+{
+ spaIterator iter;
+
+ for(iter=particles.begin(); iter!=particles.end();iter++) {
+ if ((*iter)->MatchPos(pos) ) {
+ if (forced) {
+ //particles.erase(iter);
+ (*iter)->SetPhase(P_EMPTY);
+ } else {
+ (*iter)->SetPhase(P_FADE);
+ }
+ return;
+ }
+ }
+}
+
+void Map::Sparkle(ieDword duration, ieDword color, ieDword type, const Point &pos, unsigned int FragAnimID, int Zpos)
+{
+ int style, path, grow, size, width, ttl;
+
+ if (!Zpos) {
+ Zpos = 30;
+ }
+
+ //the high word is ignored in the original engine (compatibility hack)
+ switch(type&0xffff) {
+ case SPARKLE_SHOWER: //simple falling sparks
+ path = SP_PATH_FALL;
+ grow = SP_SPAWN_FULL;
+ size = 100;
+ width = 40;
+ ttl = duration;
+ break;
+ case SPARKLE_PUFF:
+ path = SP_PATH_FOUNT; //sparks go up and down
+ grow = SP_SPAWN_SOME;
+ size = 40;
+ width = 40;
+ ttl = core->GetGame()->GameTime+Zpos;
+ break;
+ case SPARKLE_EXPLOSION: //this isn't in the original engine, but it is a nice effect to have
+ path = SP_PATH_EXPL;
+ grow = SP_SPAWN_SOME;
+ size = 10;
+ width = 40;
+ ttl = core->GetGame()->GameTime+Zpos;
+ break;
+ default:
+ path = SP_PATH_FLIT;
+ grow = SP_SPAWN_SOME;
+ size = 100;
+ width = 40;
+ ttl = duration;
+ break;
+ }
+ Particles *sparkles = new Particles(size);
+ sparkles->SetOwner(this);
+ sparkles->SetRegion(pos.x-width/2, pos.y-Zpos, width, Zpos);
+ sparkles->SetTimeToLive(ttl);
+
+ if (FragAnimID) {
+ style = SP_TYPE_BITMAP;
+ sparkles->SetBitmap(FragAnimID);
+ }
+ else {
+ style = SP_TYPE_POINT;
+ }
+ sparkles->SetType(style, path, grow);
+ sparkles->SetColor(color);
+ sparkles->SetPhase(P_GROW);
+
+ spaIterator iter;
+ for(iter=particles.begin(); (iter!=particles.end()) && ((*iter)->GetHeight()<pos.y); iter++) ;
+ particles.insert(iter, sparkles);
+}
+
+//remove flags from actor if it has left the trigger area it had last entered
+void Map::ClearTrap(Actor *actor, ieDword InTrap)
+{
+ InfoPoint *trap = TMap->GetInfoPoint(InTrap);
+ if (!trap) {
+ actor->SetInTrap(0);
+ } else {
+ if(!trap->outline->PointIn(actor->Pos)) {
+ actor->SetInTrap(0);
+ }
+ }
+}
+
+void Map::SetTrackString(ieStrRef strref, int flg, int difficulty)
+{
+ trackString = strref;
+ trackFlag = flg;
+ trackDiff = (ieWord) difficulty;
+}
+
+bool Map::DisplayTrackString(Actor *target)
+{
+ // this stat isn't saved
+ // according to the HoW manual the chance of success is:
+ // +5% for every three levels and +5% per point of wisdom
+ int skill = target->GetStat(IE_TRACKING);
+ skill += (target->GetStat(IE_LEVEL)/3)*5 + target->GetStat(IE_WIS)*5;
+ if (core->Roll(1, 100, trackDiff) > skill) {
+ displaymsg->DisplayConstantStringName(STR_TRACKINGFAILED, 0xd7d7be, target);
+ return true;
+ }
+ if (trackFlag) {
+ char * str = core->GetString( trackString);
+ core->GetTokenDictionary()->SetAt( "CREATURE", str);
+ displaymsg->DisplayConstantStringName(STR_TRACKING, 0xd7d7be, target);
+ return false;
+ }
+ displaymsg->DisplayStringName(trackString, 0xd7d7be, target, 0);
+ return false;
+}
+
+// returns a lightness level in the range of [0-100]
+// since the lightmap is much smaller than the area, we need to interpolate
+unsigned int Map::GetLightLevel(const Point &Pos)
+{
+ Color c = LightMap->GetPixel(Pos.x/16, Pos.y/12);
+ // at night/dusk/dawn the lightmap color is adjusted by the color overlay. (Only get's darker.)
+ const Color *tint = core->GetGame()->GetGlobalTint();
+ if (tint) {
+ return ((c.r-tint->r)*114 + (c.g-tint->g)*587 + (c.b-tint->b)*299)/2550;
+ }
+ return (c.r*114+c.g*587+c.b*299)/2550;
+}
+
+////////////////////AreaAnimation//////////////////
+//Area animation
+
+AreaAnimation::AreaAnimation()
+{
+ animation=NULL;
+ animcount=0;
+ palette=NULL;
+ covers=NULL;
+}
+
+AreaAnimation::~AreaAnimation()
+{
+ for(int i=0;i<animcount;i++) {
+ if (animation[i]) {
+ delete (animation[i]);
+ }
+ }
+ free(animation);
+ gamedata->FreePalette(palette, PaletteRef);
+ if (covers) {
+ for(int i=0;i<animcount;i++) {
+ delete covers[i];
+ }
+ free (covers);
+ }
+}
+
+Animation *AreaAnimation::GetAnimationPiece(AnimationFactory *af, int animCycle)
+{
+ Animation *anim = af->GetCycle( ( unsigned char ) animCycle );
+ if (!anim)
+ anim = af->GetCycle( 0 );
+ if (!anim) {
+ printf("Cannot load animation: %s\n", BAM);
+ return NULL;
+ }
+ //this will make the animation stop when the game is stopped
+ //a possible gemrb feature to have this flag settable in .are
+ anim->gameAnimation = true;
+ anim->pos = frame;
+ anim->Flags = Flags;
+ anim->x = Pos.x;
+ anim->y = Pos.y;
+ if (anim->Flags&A_ANI_MIRROR) {
+ anim->MirrorAnimation();
+ }
+
+ return anim;
+}
+
+void AreaAnimation::InitAnimation()
+{
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( BAM, IE_BAM_CLASS_ID );
+ if (!af) {
+ printf("Cannot load animation: %s\n", BAM);
+ return;
+ }
+
+ //freeing up the previous animation
+ for(int i=0;i<animcount;i++) {
+ if (animation[i]) {
+ delete (animation[i]);
+ }
+ }
+ free(animation);
+
+ if (Flags & A_ANI_ALLCYCLES) {
+ animcount = (int) af->GetCycleCount();
+
+ animation = (Animation **) malloc(animcount * sizeof(Animation *) );
+ for(int j=0;j<animcount;j++) {
+ animation[j]=GetAnimationPiece(af, j);
+ }
+ } else {
+ animcount = 1;
+ animation = (Animation **) malloc( sizeof(Animation *) );
+ animation[0]=GetAnimationPiece(af, sequence);
+ }
+ if (Flags & A_ANI_PALETTE) {
+ SetPalette(PaletteRef);
+ }
+ if (Flags&A_ANI_BLEND) {
+ BlendAnimation();
+ }
+}
+
+void AreaAnimation::SetPalette(ieResRef Pal)
+{
+ Flags |= A_ANI_PALETTE;
+ gamedata->FreePalette(palette, PaletteRef);
+ strnlwrcpy(PaletteRef, Pal, 8);
+ palette = gamedata->GetPalette(PaletteRef);
+ if (Flags&A_ANI_BLEND) {
+ //re-blending after palette change
+ BlendAnimation();
+ }
+}
+
+void AreaAnimation::BlendAnimation()
+{
+ //Warning! This function will modify a shared palette
+ if (!palette) {
+ // CHECKME: what should we do here? Currently copying palette
+ // from first frame of first animation
+
+ if (animcount == 0 || !animation[0]) return;
+ Sprite2D* spr = animation[0]->GetFrame(0);
+ if (!spr) return;
+ palette = spr->GetPalette()->Copy();
+ PaletteRef[0] = 0;
+ }
+ palette->CreateShadedAlphaChannel();
+}
+
+bool AreaAnimation::Schedule(ieDword gametime) const
+{
+ if (!(Flags&A_ANI_ACTIVE) ) {
+ return false;
+ }
+
+ //check for schedule
+ ieDword bit = 1<<((gametime/AI_UPDATE_TIME)%7200/300);
+ if (appearance & bit) {
+ return true;
+ }
+ return false;
+}
+
+int AreaAnimation::GetHeight() const
+{
+ if (Flags&A_ANI_BACKGROUND) return -9999;
+ return Pos.y+height;
+ //FIXME: this is obviously a hack that is destined to crash, also useless, so
+ //See ar9101 in HoW and ar0602 in bg2 before committing anything
+ //return Pos.y+height-animation[0][0].GetFrame(0)->Height;
+}
+
+void AreaAnimation::Draw(const Region &screen, Map *area)
+{
+ Video* video = core->GetVideoDriver();
+
+ //always draw the animation tinted because tint is also used for
+ //transparency
+ Color tint = {255,255,255,255-(ieByte) transparency};
+ if ((Flags&A_ANI_NO_SHADOW)) {
+ tint = area->LightMap->GetPixel( Pos.x / 16, Pos.y / 12);
+ tint.a = 255-(ieByte) transparency;
+ }
+ if (!(Flags&A_ANI_NO_WALL)) {
+ if (!covers) {
+ covers=(SpriteCover **) calloc( animcount, sizeof(SpriteCover *) );
+ }
+ }
+
+ int ac = animcount;
+ while (ac--) {
+ Animation *anim = animation[ac];
+ Sprite2D *frame = anim->NextFrame();
+ if(covers) {
+ if(!covers[ac] || !covers[ac]->Covers(Pos.x, Pos.y, frame->XPos, frame->YPos, frame->Width, frame->Height)) {
+ delete covers[ac];
+ covers[ac] = area->BuildSpriteCover(Pos.x, Pos.y, -anim->animArea.x,
+ -anim->animArea.y, anim->animArea.w, anim->animArea.h, 0);
+ }
+ }
+ video->BlitGameSprite( frame, Pos.x + screen.x, Pos.y + screen.y,
+ BLIT_TINTED, tint, covers?covers[ac]:0, palette, &screen );
+ }
+}
+
+//change the tileset if needed and possible, return true if changed
+//day_or_night = 1 means the normal day lightmap
+bool Map::ChangeMap(bool day_or_night)
+{
+ //no need of change if the area is not extended night
+ //if (((AreaType&(AT_DAYNIGHT|AT_EXTENDED_NIGHT))!=(AT_DAYNIGHT|AT_EXTENDED_NIGHT))) return false;
+ if (!(AreaType&AT_EXTENDED_NIGHT)) return false;
+ //no need of change if the area already has the right tilemap
+ if ((DayNight == day_or_night) && GetTileMap()) return false;
+
+ PluginHolder<MapMgr> mM(IE_ARE_CLASS_ID);
+ //no need to open and read the .are file again
+ //using the ARE class for this because ChangeMap is similar to LoadMap
+ //it loads the lightmap and the minimap too, besides swapping the tileset
+ if (!mM->ChangeMap(this, day_or_night) && !day_or_night) {
+ printMessage("Map", "Invalid night lightmap, falling back to day lightmap.\n", YELLOW);
+ mM->ChangeMap(this, 1);
+ DayNight = day_or_night;
+ }
+ return true;
+}
+
+void Map::SeeSpellCast(Scriptable *caster, ieDword spell)
+{
+ if (caster->Type!=ST_ACTOR) {
+ return;
+ }
+
+ LastCasterSeen = caster->GetGlobalID();
+ LastSpellSeen = spell;
+
+ size_t i = actors.size();
+ while (i--) {
+ Actor* witness = actors[i];
+ if (CanSee(witness, caster, true, 0)) {
+ witness->LastSpellSeen=LastSpellSeen;
+ witness->LastCasterSeen=LastCasterSeen;
+ }
+ }
+}
+
+short unsigned int Map::GetInternalSearchMap(int x, int y) {
+ if ((unsigned)x >= Width || (unsigned)y >= Height) {
+ return 0;
+ }
+ return SrchMap[x+y*Width];
+}
+
+void Map::SetInternalSearchMap(int x, int y, int value) {
+ if ((unsigned)x >= Width || (unsigned)y >= Height) {
+ return;
+ }
+ SrchMap[x+y*Width] = value;
+}
+
+void Map::SetBackground(const ieResRef &bgResRef, ieDword duration) {
+ Video* video = core->GetVideoDriver();
+
+ ResourceHolder<ImageMgr> bmp(bgResRef);
+
+ if (Background) {
+ video->FreeSprite(Background);
+ }
+ Background = bmp->GetSprite2D();
+ BgDuration = duration;
+}
diff --git a/gemrb/core/Map.h b/gemrb/core/Map.h
new file mode 100644
index 0000000..7e830c6
--- /dev/null
+++ b/gemrb/core/Map.h
@@ -0,0 +1,499 @@
+/* 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.
+ *
+ *
+ */
+
+class Map;
+
+#ifndef MAP_H
+#define MAP_H
+
+#include "exports.h"
+#include "globals.h"
+
+#include "Bitmap.h"
+#include "Image.h"
+#include "IniSpawn.h"
+#include "SpriteCover.h"
+#include "Scriptable/Scriptable.h"
+
+#include <queue>
+
+class Actor;
+class Ambient;
+class Animation;
+class AnimationFactory;
+class GameControl;
+class Particles;
+struct PathNode;
+class Projectile;
+class ScriptedAnimation;
+class TileMap;
+class Wall_Polygon;
+
+//distance of actors from spawn point
+#define SPAWN_RANGE 400
+
+//area flags
+#define AF_SAVE 1
+#define AF_TUTORIAL 2
+#define AF_DEADMAGIC 4
+#define AF_DREAM 8
+
+//area types
+#define AT_OUTDOOR 1
+#define AT_DAYNIGHT 2
+#define AT_WEATHER 4
+#define AT_CITY 8
+#define AT_FOREST 0x10
+#define AT_DUNGEON 0x20
+#define AT_EXTENDED_NIGHT 0x40
+#define AT_CAN_REST 0x80
+
+//area animation flags
+#define A_ANI_ACTIVE 1 //if not set, animation is invisible
+#define A_ANI_BLEND 2 //blend
+#define A_ANI_NO_SHADOW 4 //lightmap doesn't affect it
+#define A_ANI_PLAYONCE 8 //stop after endframe
+#define A_ANI_SYNC 16 //synchronised draw (skip frames if needed)
+#define A_ANI_32 32
+#define A_ANI_NO_WALL 64 //draw after walls (walls don't cover it)
+#define A_ANI_NOT_IN_FOG 0x80 //not visible in fog of war
+#define A_ANI_BACKGROUND 0x100 //draw before actors (actors cover it)
+#define A_ANI_ALLCYCLES 0x200 //draw all cycles, not just the cycle specified
+#define A_ANI_PALETTE 0x400 //has own palette set
+#define A_ANI_MIRROR 0x800 //mirrored
+#define A_ANI_COMBAT 0x1000 //draw in combat too
+
+//creature area flags
+#define AF_CRE_NOT_LOADED 1
+#define AF_NAME_OVERRIDE 8
+
+//getline flags
+#define GL_NORMAL 0
+#define GL_PASS 1
+#define GL_REBOUND 2
+
+//sparkle types
+#define SPARKLE_PUFF 1
+#define SPARKLE_EXPLOSION 2 //not in the original engine
+#define SPARKLE_SHOWER 3
+
+//in areas 10 is a magic number for resref counts
+#define MAX_RESCOUNT 10
+
+struct SongHeaderType {
+ ieDword SongList[MAX_RESCOUNT];
+};
+
+struct RestHeaderType {
+ ieDword Strref[MAX_RESCOUNT];
+ ieResRef CreResRef[MAX_RESCOUNT];
+ ieWord Difficulty;
+ ieWord CreatureNum;
+ ieWord Maximum;
+ ieWord Enabled;
+ ieWord DayChance;
+ ieWord NightChance;
+ ieDword sduration;
+ ieWord rwdist, owdist;
+};
+
+struct Entrance {
+ ieVariable Name;
+ Point Pos;
+ ieWord Face;
+};
+
+class MapNote {
+public:
+ ieStrRef strref;
+ Point Pos;
+ ieWord color;
+ char *text;
+ MapNote() { text=NULL; }
+ ~MapNote() { if(text) free(text); }
+};
+
+class Spawn {
+public:
+ ieVariable Name;
+ Point Pos;
+ ieResRef *Creatures;
+ unsigned int Count;
+ ieWord Difficulty;
+ ieWord Frequency;
+ ieWord Method;
+ ieDword sduration; //spawn duration
+ ieWord rwdist, owdist; //maximum walk distances
+ ieWord Maximum;
+ ieWord Enabled;
+ ieDword appearance;
+ ieWord DayChance;
+ ieWord NightChance;
+ Spawn() { Creatures=NULL; }
+ ~Spawn() { if(Creatures) free(Creatures); }
+ unsigned int GetCreatureCount() { return Count; }
+};
+
+class TerrainSounds {
+public:
+ ieResRef Group;
+ ieResRef Sounds[16];
+};
+
+class SpawnGroup {
+public:
+ ieResRef *ResRefs;
+ unsigned int Count;
+ unsigned int Level;
+
+ SpawnGroup(unsigned int size) {
+ ResRefs = (ieResRef *) calloc(size, sizeof(ieResRef) );
+ Count = size;
+ }
+ ~SpawnGroup() {
+ if (ResRefs) {
+ free(ResRefs);
+ }
+ }
+};
+
+class GEM_EXPORT AreaAnimation {
+public:
+ Animation **animation;
+ int animcount;
+ //dwords, or stuff combining to a dword
+ Point Pos;
+ ieDword appearance;
+ ieDword Flags;
+ //these are on one dword
+ ieWord sequence;
+ ieWord frame;
+ //these are on one dword
+ ieWord transparency;
+ ieWordSigned height;
+ //these are on one dword
+ ieWord unknown3c;
+ ieByte skipcycle;
+ ieByte startchance;
+ ieDword unknown48;
+ //string values, not in any particular order
+ ieVariable Name;
+ ieResRef BAM; //not only for saving back (StaticSequence depends on this)
+ ieResRef PaletteRef;
+ Palette* palette;
+ SpriteCover** covers;
+ AreaAnimation();
+ ~AreaAnimation();
+ void InitAnimation();
+ void SetPalette(ieResRef PaletteRef);
+ void BlendAnimation();
+ bool Schedule(ieDword gametime) const;
+ void Draw(const Region &screen, Map *area);
+ int GetHeight() const;
+private:
+ Animation *GetAnimationPiece(AnimationFactory *af, int animCycle);
+};
+
+enum AnimationObjectType {AOT_AREA, AOT_SCRIPTED, AOT_ACTOR, AOT_SPARK, AOT_PROJECTILE};
+
+//i believe we need only the active actors/visible inactive actors queues
+#define QUEUE_COUNT 2
+
+//priorities when handling actors, we really ignore the third one
+#define PR_SCRIPT 0
+#define PR_DISPLAY 1
+#define PR_IGNORE 2
+
+typedef std::list<AreaAnimation*>::iterator aniIterator;
+typedef std::list<ScriptedAnimation*>::iterator scaIterator;
+typedef std::list<Projectile*>::iterator proIterator;
+typedef std::list<Particles*>::iterator spaIterator;
+
+class GEM_EXPORT Map : public Scriptable {
+public:
+ TileMap* TMap;
+ Image* LightMap;
+ Bitmap* HeightMap;
+ Sprite2D* SmallMap;
+ IniSpawn *INISpawn;
+ ieDword AreaFlags;
+ ieWord AreaType;
+ ieWord Rain, Snow, Fog, Lightning;
+ ieByte* ExploredBitmap;
+ ieByte* VisibleBitmap;
+ int version;
+ ieResRef WEDResRef;
+ bool MasterArea;
+ //this is set by the importer (not stored in the file)
+ bool DayNight;
+ //movies for day/night (only in ToB)
+ ieResRef Dream[2];
+ Sprite2D *Background;
+ ieDword BgDuration;
+
+private:
+ ieStrRef trackString;
+ int trackFlag;
+ ieWord trackDiff;
+ unsigned short* MapSet;
+ unsigned short* SrchMap; //internal searchmap
+ std::queue< unsigned int> InternalStack;
+ unsigned int Width, Height;
+ std::list< AreaAnimation*> animations;
+ std::vector< Actor*> actors;
+ Wall_Polygon **Walls;
+ unsigned int WallCount;
+ std::list< ScriptedAnimation*> vvcCells;
+ std::list< Projectile*> projectiles;
+ std::list< Particles*> particles;
+ std::vector< Entrance*> entrances;
+ std::vector< Ambient*> ambients;
+ std::vector< MapNote*> mapnotes;
+ std::vector< Spawn*> spawns;
+ Actor** queue[QUEUE_COUNT];
+ int Qcount[QUEUE_COUNT];
+ unsigned int lastActorCount[QUEUE_COUNT];
+public:
+ Map(void);
+ ~Map(void);
+ static void ReleaseMemory();
+
+ /** prints useful information on console */
+ void DebugDump(bool show_actors=0) const;
+ TileMap *GetTileMap() { return TMap; }
+ /* gets the signal of daylight changes */
+ bool ChangeMap(bool day_or_night);
+ void SeeSpellCast(Scriptable *caster, ieDword spell);
+ /* low level function to perform the daylight changes */
+ void ChangeTileMap(Image* lm, Sprite2D* sm);
+ /* sets all the auxiliary maps and the tileset */
+ void AddTileMap(TileMap* tm, Image* lm, Bitmap* sr, Sprite2D* sm, Bitmap* hm);
+ void UpdateScripts();
+ void ResolveTerrainSound(ieResRef &sound, Point &pos);
+ bool DoStepForActor(Actor *actor, int speed, ieDword time);
+ void UpdateEffects();
+ /* removes empty heaps and returns total itemcount */
+ int ConsolidateContainers();
+ /* transfers all piles (loose items) to another map */
+ void CopyGroundPiles(Map *othermap, const Point &Pos);
+ /* transfers all ever visible piles (loose items) to the specified position */
+ void MoveVisibleGroundPiles(const Point &Pos);
+ /* draws stationary vvc graphics */
+ //void DrawVideocells(Region screen);
+ void DrawHighlightables(Region screen);
+ void DrawMap(Region screen);
+ void PlayAreaSong(int SongType, bool restart = true, bool hard = false);
+ void AddAnimation(AreaAnimation* anim);
+ aniIterator GetFirstAnimation() { return animations.begin(); }
+ AreaAnimation* GetNextAnimation(aniIterator &iter)
+ {
+ if (iter == animations.end()) {
+ return NULL;
+ }
+ return *iter++;
+ }
+ AreaAnimation* GetAnimation(const char* Name);
+ size_t GetAnimationCount() const { return animations.size(); }
+
+ unsigned int GetWallCount() { return WallCount; }
+ Wall_Polygon *GetWallGroup(int i) { return Walls[i]; }
+ void SetWallGroups(unsigned int count, Wall_Polygon **walls)
+ {
+ WallCount = count;
+ Walls = walls;
+ }
+ SpriteCover* BuildSpriteCover(int x, int y, int xpos, int ypos,
+ unsigned int width, unsigned int height, int flag);
+ void ActivateWallgroups(unsigned int baseindex, unsigned int count, int flg);
+ void Shout(Actor* actor, int shoutID, unsigned int radius);
+ void ActorSpottedByPlayer(Actor *actor);
+ void AddActor(Actor* actor);
+ //returns true if an enemy is near P (used in resting/saving)
+ bool AnyEnemyNearPoint(const Point &p);
+ bool GetBlocked(unsigned int x, unsigned int y, unsigned int size);
+ unsigned int GetBlocked(unsigned int x, unsigned int y);
+ unsigned int GetBlocked(const Point &p);
+ Door *GetDoorByGlobalID(ieDword objectID);
+ Container *GetContainerByGlobalID(ieDword objectID);
+ InfoPoint *GetInfoPointByGlobalID(ieDword objectID);
+ Actor* GetActorByGlobalID(ieDword objectID);
+ Actor* GetActor(const Point &p, int flags);
+ Actor* GetActorInRadius(const Point &p, int flags, unsigned int radius);
+ Actor **GetAllActorsInRadius(const Point &p, int flags, unsigned int radius);
+ Actor* GetActor(const char* Name, int flags);
+ Actor* GetActor(int i, bool any);
+ Actor* GetActorByDialog(const char* resref);
+ Actor* GetActorByResource(const char* resref);
+ Actor* GetActorByScriptName(const char* name);
+ bool HasActor(Actor *actor);
+ void RemoveActor(Actor* actor);
+ //returns actors in rect (onlyparty could be more sophisticated)
+ int GetActorInRect(Actor**& actors, Region& rgn, bool onlyparty);
+ int GetActorCount(bool any) const;
+ //fix actors position if required
+ void JumpActors(bool jump);
+ //selects all selectable actors in the area
+ void SelectActors();
+ //if items == true, remove noncritical items from ground piles too
+ void PurgeArea(bool items);
+
+ SongHeaderType SongHeader;
+ RestHeaderType RestHeader;
+
+ //count of all projectiles that are saved
+ size_t GetProjectileCount(proIterator &iter);
+ //get the next projectile
+ Projectile *GetNextProjectile(proIterator &iter);
+ //count of unexploded projectiles that are saved
+ ieDword GetTrapCount(proIterator &iter);
+ //get the next saved projectile
+ Projectile* GetNextTrap(proIterator &iter);
+ //add a projectile to the area
+ void AddProjectile(Projectile* pro, const Point &source, ieWord actorID, bool fake);
+ void AddProjectile(Projectile* pro, const Point &source, const Point &dest);
+
+ //returns the duration of a VVC cell set in the area (point may be set to empty)
+ ieDword HasVVCCell(const ieResRef resource, const Point &p);
+ void AddVVCell(ScriptedAnimation* vvc);
+ bool CanFree();
+ int GetCursor( const Point &p);
+ //adds a sparkle puff of colour to a point in the area
+ //FragAnimID is an optional avatar animation ID (see avatars.2da) for
+ //fragment animation
+ void Sparkle(ieDword duration, ieDword color, ieDword type, const Point &pos, unsigned int FragAnimID = 0, int Zpos = 0);
+ //removes or fades the sparkle puff at a point
+ void FadeSparkle(const Point &pos, bool forced);
+
+ //entrances
+ void AddEntrance(char* Name, int XPos, int YPos, short Face);
+ Entrance* GetEntrance(const char* Name);
+ Entrance* GetEntrance(int i) { return entrances[i]; }
+ int GetEntranceCount() const { return (int) entrances.size(); }
+
+ //containers
+ /* this function returns/creates a pile container at position */
+ Container* AddContainer(const char* Name, unsigned short Type,
+ Gem_Polygon* outline);
+ Container *GetPile(Point position);
+ void AddItemToLocation(const Point &position, CREItem *item);
+
+ int GetWidth() const { return Width; }
+ int GetHeight() const { return Height; }
+ int GetExploredMapSize() const;
+ /*fills the explored bitmap with setreset */
+ void Explore(int setreset);
+ /*fills the visible bitmap with setreset */
+ void SetMapVisibility(int setreset = 0);
+ /* set one fog tile as visible. x, y are tile coordinates */
+ void ExploreTile(const Point &Tile);
+ /* explore map from given point in map coordinates */
+ void ExploreMapChunk(const Point &Pos, int range, int los);
+ /* block or unblock searchmap with value */
+ void BlockSearchMap(const Point &Pos, unsigned int size, unsigned int value);
+ void ClearSearchMapFor(Movable *actor);
+ /* update VisibleBitmap by resolving vision of all explore actors */
+ void UpdateFog();
+ //PathFinder
+ /* Finds the nearest passable point */
+ void AdjustPosition(Point &goal, unsigned int radius=0);
+ /* Finds the path which leads the farthest from d */
+ PathNode* RunAway(const Point &s, const Point &d, unsigned int size, unsigned int PathLen, int flags);
+ /* Returns true if there is no path to d */
+ bool TargetUnreachable(const Point &s, const Point &d, unsigned int size);
+ /* returns true if there is enemy visible */
+ bool AnyPCSeesEnemy();
+ /* Finds straight path from s, length l and orientation o, f=1 passes wall, f=2 rebounds from wall*/
+ PathNode* GetLine(const Point &start, const Point &dest, int flags);
+ PathNode* GetLine(const Point &start, int Steps, int Orientation, int flags);
+ PathNode* GetLine(const Point &start, const Point &dest, int speed, int Orientation, int flags);
+ /* Finds the path which leads to near d */
+ PathNode* FindPathNear(const Point &s, const Point &d, unsigned int size, unsigned int MinDistance = 0, bool sight = true);
+ /* Finds the path which leads to d */
+ PathNode* FindPath(const Point &s, const Point &d, unsigned int size, int MinDistance = 0);
+ /* returns false if point isn't visible on visibility/explored map */
+ bool IsVisible(const Point &s, int explored);
+ /* returns false if point d cannot be seen from point d due to searchmap */
+ bool IsVisible(const Point &s, const Point &d);
+ /* returns edge direction of map boundary, only worldmap regions */
+ int WhichEdge(const Point &s);
+
+ //ambients
+ void AddAmbient(Ambient *ambient) { ambients.push_back(ambient); }
+ void SetupAmbients();
+ Ambient *GetAmbient(int i) { return ambients[i]; }
+ unsigned int GetAmbientCount() { return (unsigned int) ambients.size(); }
+
+ //mapnotes
+ void AddMapNote(const Point &point, int color, char *text, ieStrRef strref);
+ void RemoveMapNote(const Point &point);
+ MapNote *GetMapNote(int i) { return mapnotes[i]; }
+ MapNote *GetMapNote(const Point &point);
+ unsigned int GetMapNoteCount() { return (unsigned int) mapnotes.size(); }
+ //restheader
+ /* May spawn creature(s), returns true in case of an interrupted rest */
+ bool Rest(const Point &pos, int hours, int day);
+ /* Spawns creature(s) in radius of position */
+ void SpawnCreature(const Point &pos, const char *CreName, int radius = 0);
+
+ //spawns
+ void LoadIniSpawn();
+ Spawn *AddSpawn(char* Name, int XPos, int YPos, ieResRef *creatures, unsigned int count);
+ Spawn *GetSpawn(int i) { return spawns[i]; }
+ //returns spawn by name
+ Spawn *GetSpawn(const char *Name);
+ //returns spawn inside circle, checks for schedule and other
+ //conditions as well
+ Spawn *GetSpawnRadius(const Point &point, unsigned int radius);
+ unsigned int GetSpawnCount() { return (unsigned int) spawns.size(); }
+ void TriggerSpawn(Spawn *spawn);
+ //move some or all players to a new area
+ void MoveToNewArea(const char *area, const char *entrance, unsigned int direction, int EveryOne, Actor *actor);
+ bool HasWeather();
+ int GetWeather();
+ void ClearTrap(Actor *actor, ieDword InTrap);
+ void SetTrackString(ieStrRef strref, int flg, int difficulty);
+ //returns true if tracking failed
+ bool DisplayTrackString(Actor *actor);
+ unsigned int GetLightLevel(const Point &Pos);
+ unsigned short GetInternalSearchMap(int x, int y);
+ void SetInternalSearchMap(int x, int y, int value);
+ void SetBackground(const ieResRef &bgResref, ieDword duration);
+private:
+ AreaAnimation *GetNextAreaAnimation(aniIterator &iter, ieDword gametime);
+ Particles *GetNextSpark(spaIterator &iter);
+ ScriptedAnimation *GetNextScriptedAnimation(scaIterator &iter);
+ Actor *GetNextActor(int &q, int &index);
+ void DrawSearchMap(const Region &screen);
+ void GenerateQueues();
+ void SortQueues();
+ //Actor* GetRoot(int priority, int &index);
+ void DeleteActor(int i);
+ void Leveldown(unsigned int px, unsigned int py, unsigned int& level,
+ Point &p, unsigned int& diff);
+ void SetupNode(unsigned int x, unsigned int y, unsigned int size, unsigned int Cost);
+ //actor uses travel region
+ void UseExit(Actor *pc, InfoPoint *ip);
+ //separated position adjustment, so their order could be randomised */
+ bool AdjustPositionX(Point &goal, unsigned int radius);
+ bool AdjustPositionY(Point &goal, unsigned int radius);
+ void DrawPortal(InfoPoint *ip, int enable);
+};
+
+#endif
diff --git a/gemrb/core/MapMgr.cpp b/gemrb/core/MapMgr.cpp
new file mode 100644
index 0000000..dba36b2
--- /dev/null
+++ b/gemrb/core/MapMgr.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 "MapMgr.h"
+
+MapMgr::MapMgr(void)
+{
+}
+
+MapMgr::~MapMgr(void)
+{
+}
diff --git a/gemrb/core/MapMgr.h b/gemrb/core/MapMgr.h
new file mode 100644
index 0000000..c709e70
--- /dev/null
+++ b/gemrb/core/MapMgr.h
@@ -0,0 +1,52 @@
+/* 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 MapMgr.h
+ * Declares MapMgr class, loader for Map objects
+ * @author The GemRB Project
+ */
+
+#ifndef MAPMGR_H
+#define MAPMGR_H
+
+#include "Map.h"
+#include "Plugin.h"
+#include "Scriptable/Scriptable.h"
+#include "System/DataStream.h"
+
+/**
+ * @class MapMgr
+ * Abstract loader for Map objects
+ */
+
+class GEM_EXPORT MapMgr : public Plugin {
+public:
+ MapMgr(void);
+ virtual ~MapMgr(void);
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual bool ChangeMap(Map *map, bool day_or_night) = 0;
+ virtual Map* GetMap(const char* ResRef, bool day_or_night) = 0;
+
+ virtual int GetStoredFileSize(Map *map) = 0;
+ virtual int PutArea(DataStream* stream, Map *map) = 0;
+};
+
+#endif
diff --git a/gemrb/core/MoviePlayer.cpp b/gemrb/core/MoviePlayer.cpp
new file mode 100644
index 0000000..f171681
--- /dev/null
+++ b/gemrb/core/MoviePlayer.cpp
@@ -0,0 +1,31 @@
+/* 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 "MoviePlayer.h"
+
+const TypeID MoviePlayer::ID = { "MoviePlayer" };
+
+MoviePlayer::MoviePlayer(void)
+{
+}
+
+MoviePlayer::~MoviePlayer(void)
+{
+}
diff --git a/gemrb/core/MoviePlayer.h b/gemrb/core/MoviePlayer.h
new file mode 100644
index 0000000..b52a3fc
--- /dev/null
+++ b/gemrb/core/MoviePlayer.h
@@ -0,0 +1,50 @@
+/* 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 MoviePlayer.h
+ * Declares MoviePlayer class, abstract loader and player for videos
+ * @author The GemRB Project
+ */
+
+
+#ifndef MOVIEPLAYER_H
+#define MOVIEPLAYER_H
+
+#include "globals.h"
+#include "win32def.h"
+
+#include "Resource.h"
+
+/**
+ * @class MoviePlayer
+ * Abstract loader and player for videos
+ */
+
+class GEM_EXPORT MoviePlayer : public Resource {
+public:
+ static const TypeID ID;
+ MoviePlayer(void);
+ virtual ~MoviePlayer(void);
+ virtual int Play() = 0;
+ virtual void CallBackAtFrames(ieDword cnt, ieDword *frames, ieDword *strrefs) = 0;
+};
+
+#endif
diff --git a/gemrb/core/MusicMgr.cpp b/gemrb/core/MusicMgr.cpp
new file mode 100644
index 0000000..592b84b
--- /dev/null
+++ b/gemrb/core/MusicMgr.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 "MusicMgr.h"
+
+MusicMgr::MusicMgr()
+{
+}
+MusicMgr::~MusicMgr()
+{
+}
+/** Initializes the PlayList Manager */
+bool MusicMgr::Init()
+{
+ return true;
+}
diff --git a/gemrb/core/MusicMgr.h b/gemrb/core/MusicMgr.h
new file mode 100644
index 0000000..0386500
--- /dev/null
+++ b/gemrb/core/MusicMgr.h
@@ -0,0 +1,49 @@
+/* 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 MUSICMGR_H
+#define MUSICMGR_H
+
+#include "Plugin.h"
+
+class GEM_EXPORT MusicMgr : public Plugin {
+public:
+ MusicMgr();
+ virtual ~MusicMgr();
+ /** Ends the Current PlayList Execution */
+ virtual void End(void) = 0;
+ virtual void HardEnd(void) = 0;
+ /** Start the PlayList Music Execution */
+ virtual void Start(void) = 0;
+ /** Initializes the PlayList Manager */
+ virtual bool Init();
+ /** Loads a PlayList for playing */
+ virtual bool OpenPlaylist(const char* name) = 0;
+ /** Switches the current PlayList while playing the current one, return nonzero on error */
+ virtual int SwitchPlayList(const char* name, bool Hard) = 0;
+ /** Plays the Next Entry */
+ virtual void PlayNext() = 0;
+ /** Returns whether music is currently playing */
+ virtual bool IsPlaying() = 0;
+ /** Returns whether given playlist is currently loaded */
+ virtual bool CurrentPlayList(const char* name) = 0;
+};
+
+#endif
diff --git a/gemrb/core/Palette.cpp b/gemrb/core/Palette.cpp
new file mode 100644
index 0000000..11690f1
--- /dev/null
+++ b/gemrb/core/Palette.cpp
@@ -0,0 +1,243 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 "Palette.h"
+
+#include "Interface.h"
+
+#define MINCOL 2
+#define MUL 2
+
+void Palette::CreateShadedAlphaChannel()
+{
+ for (int i = 0; i < 256; ++i) {
+ unsigned int r = col[i].r;
+ unsigned int g = col[i].g;
+ unsigned int b = col[i].b;
+ unsigned int m = (r + g + b) / 3;
+ if (m > MINCOL) {
+ if (( r == 0 ) && ( g == 0xff ) && ( b == 0 )) {
+ col[i].a = 0xff;
+ } else {
+ int tmp = m * MUL;
+ col[i].a = ( tmp > 0xff ) ? 0xff : (unsigned char) tmp;
+ }
+ }
+ else {
+ col[i].a = 0;
+ }
+ }
+ alpha = true;
+}
+
+Palette* Palette::Copy()
+{
+ Palette* pal = new Palette(col, alpha);
+ Release();
+ return pal;
+}
+
+void Palette::SetupPaperdollColours(const ieDword* Colors, unsigned int type)
+{
+ unsigned int s = 8*type;
+ //metal
+ core->GetPalette( (Colors[0]>>s)&0xFF, 12, &col[0x04]);
+ //minor
+ core->GetPalette( (Colors[1]>>s)&0xFF, 12, &col[0x10]);
+ //major
+ core->GetPalette( (Colors[2]>>s)&0xFF, 12, &col[0x1c]);
+ //skin
+ core->GetPalette( (Colors[3]>>s)&0xFF, 12, &col[0x28]);
+ //leather
+ core->GetPalette( (Colors[4]>>s)&0xFF, 12, &col[0x34]);
+ //armor
+ core->GetPalette( (Colors[5]>>s)&0xFF, 12, &col[0x40]);
+ //hair
+ core->GetPalette( (Colors[6]>>s)&0xFF, 12, &col[0x4c]);
+
+ //minor
+ memcpy( &col[0x58], &col[0x11], 8 * sizeof( Color ) );
+ //major
+ memcpy( &col[0x60], &col[0x1d], 8 * sizeof( Color ) );
+ //minor
+ memcpy( &col[0x68], &col[0x11], 8 * sizeof( Color ) );
+ //metal
+ memcpy( &col[0x70], &col[0x05], 8 * sizeof( Color ) );
+ //leather
+ memcpy( &col[0x78], &col[0x35], 8 * sizeof( Color ) );
+ //leather
+ memcpy( &col[0x80], &col[0x35], 8 * sizeof( Color ) );
+ //minor
+ memcpy( &col[0x88], &col[0x11], 8 * sizeof( Color ) );
+
+ int i;
+ for (i = 0x90; i < 0xA8; i += 0x08)
+ //leather
+ memcpy( &col[i], &col[0x35], 8 * sizeof( Color ) );
+
+ //skin
+ memcpy( &col[0xB0], &col[0x29], 8 * sizeof( Color ) );
+
+ for (i = 0xB8; i < 0xFF; i += 0x08)
+ //leather
+ memcpy( &col[i], &col[0x35], 8 * sizeof( Color ) );
+}
+
+
+static inline void applyMod(const Color& src, Color& dest,
+ const RGBModifier& mod) {
+ if (mod.speed == -1) {
+ if (mod.type == RGBModifier::TINT) {
+ dest.r = ((unsigned int)src.r * mod.rgb.r)>>8;
+ dest.g = ((unsigned int)src.g * mod.rgb.g)>>8;
+ dest.b = ((unsigned int)src.b * mod.rgb.b)>>8;
+ } else if (mod.type == RGBModifier::BRIGHTEN) {
+ unsigned int r = (unsigned int)src.r * mod.rgb.r;
+ if (r & (~0x7FF)) r = 0x7FF;
+ dest.r = r >> 3;
+
+ unsigned int g = (unsigned int)src.g * mod.rgb.g;
+ if (g & (~0x7FF)) g = 0x7FF;
+ dest.g = g >> 3;
+
+ unsigned int b = (unsigned int)src.b * mod.rgb.b;
+ if (b & (~0x7FF)) b = 0x7FF;
+ dest.b = b >> 3;
+ } else if (mod.type == RGBModifier::ADD) {
+ unsigned int r = (unsigned int)src.r + mod.rgb.r;
+ if (r & (~0xFF)) r = 0xFF;
+ dest.r = r;
+
+ unsigned int g = (unsigned int)src.g + mod.rgb.g;
+ if (g & (~0xFF)) g = 0xFF;
+ dest.g = g;
+
+ unsigned int b = (unsigned int)src.b + mod.rgb.b;
+ if (b & (~0xFF)) b = 0xFF;
+ dest.b = b;
+ } else {
+ dest = src;
+ }
+ } else if (mod.speed > 0) {
+
+ // TODO: a sinewave will probably look better
+ int phase = (mod.phase % (2*mod.speed));
+ if (phase > mod.speed) {
+ phase = 512 - (256*phase)/mod.speed;
+ } else {
+ phase = (256*phase)/mod.speed;
+ }
+
+ if (mod.type == RGBModifier::TINT) {
+ dest.r = ((unsigned int)src.r * (256*256 + phase*mod.rgb.r - 256*phase))>>16;
+ dest.g = ((unsigned int)src.g * (256*256 + phase*mod.rgb.g - 256*phase))>>16;
+ dest.b = ((unsigned int)src.b * (256*256 + phase*mod.rgb.b - 256*phase))>>16;
+ } else if (mod.type == RGBModifier::BRIGHTEN) {
+ unsigned int r = src.r + (256*256 + phase*mod.rgb.r - 256*phase);
+ if (r & (~0x7FFFF)) r = 0x7FFFF;
+ dest.r = r >> 11;
+
+ unsigned int g = src.g * (256*256 + phase*mod.rgb.g - 256*phase);
+ if (g & (~0x7FFFF)) g = 0x7FFFF;
+ dest.g = g >> 11;
+
+ unsigned int b = src.b * (256*256 + phase*mod.rgb.b - 256*phase);
+ if (b & (~0x7FFFF)) b = 0x7FFFF;
+ dest.b = b >> 11;
+ } else if (mod.type == RGBModifier::ADD) {
+ unsigned int r = src.r + ((phase*mod.rgb.r)>>8);
+ if (r & (~0xFF)) r = 0xFF;
+ dest.r = r;
+
+ unsigned int g = src.g + ((phase*mod.rgb.g)>>8);
+ if (g & (~0xFF)) g = 0xFF;
+ dest.g = g;
+
+ unsigned int b = src.b + ((phase*mod.rgb.b)>>8);
+ if (b & (~0xFF)) b = 0xFF;
+ dest.b = b;
+ } else {
+ dest = src;
+ }
+ } else {
+ dest = src;
+ }
+}
+
+void Palette::SetupRGBModification(const Palette* src, const RGBModifier* mods,
+ unsigned int type)
+{
+ const RGBModifier* tmods = mods+(8*type);
+ int i;
+
+ for (i = 0; i < 4; ++i)
+ col[i] = src->col[i];
+
+ for (i = 0; i < 12; ++i)
+ applyMod(src->col[0x04+i],col[0x04+i],tmods[0]);
+ for (i = 0; i < 12; ++i)
+ applyMod(src->col[0x10+i],col[0x10+i],tmods[1]);
+ for (i = 0; i < 12; ++i)
+ applyMod(src->col[0x1c+i],col[0x1c+i],tmods[2]);
+ for (i = 0; i < 12; ++i)
+ applyMod(src->col[0x28+i],col[0x28+i],tmods[3]);
+ for (i = 0; i < 12; ++i)
+ applyMod(src->col[0x34+i],col[0x34+i],tmods[4]);
+ for (i = 0; i < 12; ++i)
+ applyMod(src->col[0x40+i],col[0x40+i],tmods[5]);
+ for (i = 0; i < 12; ++i)
+ applyMod(src->col[0x4c+i],col[0x4c+i],tmods[6]);
+ for (i = 0; i < 8; ++i)
+ applyMod(src->col[0x58+i],col[0x58+i],tmods[1]);
+ for (i = 0; i < 8; ++i)
+ applyMod(src->col[0x60+i],col[0x60+i],tmods[2]);
+ for (i = 0; i < 8; ++i)
+ applyMod(src->col[0x68+i],col[0x68+i],tmods[1]);
+ for (i = 0; i < 8; ++i)
+ applyMod(src->col[0x70+i],col[0x70+i],tmods[0]);
+ for (i = 0; i < 8; ++i)
+ applyMod(src->col[0x78+i],col[0x78+i],tmods[4]);
+ for (i = 0; i < 8; ++i)
+ applyMod(src->col[0x80+i],col[0x80+i],tmods[4]);
+ for (i = 0; i < 8; ++i)
+ applyMod(src->col[0x88+i],col[0x88+i],tmods[1]);
+ for (i = 0; i < 24; ++i)
+ applyMod(src->col[0x90+i],col[0x90+i],tmods[4]);
+
+ for (i = 0; i < 8; ++i)
+ col[0xA8+i] = src->col[0xA8+i];
+
+ for (i = 0; i < 8; ++i)
+ applyMod(src->col[0xB0+i],col[0xB0+i],tmods[3]);
+ for (i = 0; i < 72; ++i)
+ applyMod(src->col[0xB8+i],col[0xB8+i],tmods[4]);
+}
+
+void Palette::SetupGlobalRGBModification(const Palette* src,
+ const RGBModifier& mod)
+{
+ int i;
+ // don't modify the transparency and shadow colour
+ for (i = 0; i < 2; ++i)
+ col[i] = src->col[i];
+
+ for (i = 2; i < 256; ++i)
+ applyMod(src->col[i],col[i],mod);
+}
diff --git a/gemrb/core/Palette.h b/gemrb/core/Palette.h
new file mode 100644
index 0000000..ba857d1
--- /dev/null
+++ b/gemrb/core/Palette.h
@@ -0,0 +1,102 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2005-2006 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 PALETTE_H
+#define PALETTE_H
+
+#include "RGBAColor.h"
+#include "exports.h"
+#include "ie_types.h"
+
+#include <cassert>
+
+enum PaletteType {
+ PAL_MAIN,
+ PAL_WEAPON,
+ PAL_OFFHAND,
+ PAL_HELMET
+};
+
+struct RGBModifier {
+ Color rgb;
+ int speed;
+ int phase;
+ enum Type {
+ NONE,
+ ADD,
+ TINT,
+ BRIGHTEN
+ } type;
+ bool locked;
+};
+
+
+class GEM_EXPORT Palette {
+public:
+ Palette(const Color* colours, bool alpha_=false) {
+ for (int i = 0; i < 256; ++i) {
+ col[i] = colours[i];
+ }
+ alpha = alpha_;
+ refcount = 1;
+ named = false;
+ }
+ Palette() {
+ alpha = false;
+ refcount = 1;
+ named = false;
+ }
+ ~Palette() { }
+
+ Color col[256]; //< RGB or RGBA 8 bit palette
+ bool alpha; //< true if this is a RGBA palette
+ bool named; //< true if the palette comes from a bmp and cached
+ Color front; // Original colors used by core->CreatePalette()
+ Color back;
+
+ void IncRef() {
+ refcount++;
+ }
+
+ void Release() {
+ assert(refcount > 0);
+ if (!--refcount)
+ delete this;
+ }
+
+ bool IsShared() const {
+ return (refcount > 1);
+ }
+
+ void CreateShadedAlphaChannel();
+
+ void SetupPaperdollColours(const ieDword* Colors, unsigned int type);
+ void SetupRGBModification(const Palette* src, const RGBModifier* mods,
+ unsigned int type);
+ void SetupGlobalRGBModification(const Palette* src,
+ const RGBModifier& mod);
+
+ Palette* Copy();
+
+private:
+ unsigned int refcount;
+
+};
+
+#endif
diff --git a/gemrb/core/PalettedImageMgr.cpp b/gemrb/core/PalettedImageMgr.cpp
new file mode 100644
index 0000000..8851760
--- /dev/null
+++ b/gemrb/core/PalettedImageMgr.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 "PalettedImageMgr.h"
+
+const TypeID PalettedImageMgr::ID = { "PalettedImageMgr" };
+
+PalettedImageMgr::PalettedImageMgr(void)
+{
+}
+
+PalettedImageMgr::~PalettedImageMgr(void)
+{
+}
diff --git a/gemrb/core/PalettedImageMgr.h b/gemrb/core/PalettedImageMgr.h
new file mode 100644
index 0000000..f6ea798
--- /dev/null
+++ b/gemrb/core/PalettedImageMgr.h
@@ -0,0 +1,49 @@
+/* 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 PALETTEDIMAGEMGR_H
+#define PALETTEDIMAGEMGR_H
+
+#include "Resource.h"
+#include "Sprite2D.h"
+#include "System/DataStream.h"
+
+class ImageFactory;
+
+/**
+ * Base class for Paletted Imqge plugins
+ *
+ * This represents a false color image that is combined with
+ * color data to generate a real image.
+ */
+class GEM_EXPORT PalettedImageMgr : public Resource {
+public:
+ static const TypeID ID;
+public:
+ PalettedImageMgr(void);
+ virtual ~PalettedImageMgr(void);
+ /**
+ * Returns a @ref{Sprite2D} that has been colored with the given palette.
+ *
+ * @param[in] type Type of palette to use.
+ * @param[in] paletteIndex Array of palettes to use.
+ */
+ virtual Sprite2D* GetSprite2D(unsigned int type, ieDword paletteIndex[8]) = 0;
+};
+
+#endif
diff --git a/gemrb/core/Particles.cpp b/gemrb/core/Particles.cpp
new file mode 100644
index 0000000..ce3195f
--- /dev/null
+++ b/gemrb/core/Particles.cpp
@@ -0,0 +1,387 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 "Particles.h"
+
+#include "Interface.h"
+#include "Video.h"
+#include "Game.h"
+
+Color sparkcolors[MAX_SPARK_COLOR][MAX_SPARK_PHASE];
+bool inited = false;
+
+#define SPARK_COUNT 13
+
+static int spark_color_indices[SPARK_COUNT]={12,5,0,6,1,8,2,7,9,3,4,10,11};
+
+void TranslateColor(const char *value, Color &color)
+{
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ //if not RGB then try to interpret it as a dword
+ if (strnicmp(value,"RGB(",4)) {
+ r = strtol(value,NULL,0);
+ color.r = r&0xff;
+ color.g = (r>>8)&0xff;
+ color.b = (r>>16)&0xff;
+ color.a = (r>>24)&0xff;
+ }
+ sscanf(value+4,"%d,%d,%d)", &r, &g, &b);
+ color.r=r;
+ color.g=g;
+ color.b=b;
+}
+
+void InitSparks()
+{
+ int i,j;
+ AutoTable tab("sprklclr");
+ if (!tab)
+ return;
+
+ memset(sparkcolors,0,sizeof(sparkcolors));
+ for (i=0;i<MAX_SPARK_COLOR;i++) {
+ for (j=0;j<MAX_SPARK_PHASE;j++) {
+ sparkcolors[i][j].a=0xff;
+ }
+ }
+ i = tab->GetRowCount();
+ if (i>MAX_SPARK_COLOR) {
+ i = MAX_SPARK_COLOR;
+ }
+ while (i--) {
+ for (int j=0;j<MAX_SPARK_PHASE;j++) {
+ int idx;
+
+ if (i<SPARK_COUNT) {
+ idx = spark_color_indices[i];
+ } else {
+ idx = i;
+ }
+ const char *value = tab->QueryField(idx,j);
+ TranslateColor(value, sparkcolors[i][j]);
+ }
+ }
+ inited = true;
+}
+
+Particles::Particles(int s)
+{
+ points = (Element *) malloc(s*sizeof(Element) );
+ memset(points, -1, s*sizeof(Element) );
+ /*
+ for (int i=0;i<MAX_SPARK_PHASE;i++) {
+ bitmap[i]=NULL;
+ }
+ */
+ fragments = NULL;
+ if (!inited) {
+ InitSparks();
+ }
+ size = last_insert = s;
+ color = 0;
+ phase = P_FADE;
+ owner = NULL;
+ type = SP_TYPE_POINT;
+ path = SP_PATH_FALL;
+ spawn_type = SP_SPAWN_NONE;
+ timetolive = 0;
+}
+
+Particles::~Particles()
+{
+ if (points) {
+ free(points);
+ }
+ /*
+ for (int i=0;i<MAX_SPARK_PHASE;i++) {
+ delete( bitmap[i]);
+ }
+ */
+ delete fragments;
+}
+
+void Particles::SetBitmap(unsigned int FragAnimID)
+{
+ //int i;
+
+ delete fragments;
+
+ fragments = new CharAnimations(FragAnimID, 0);
+/*
+ for (i=0;i<MAX_SPARK_PHASE;i++) {
+ delete( bitmap[i] );
+ }
+
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( BAM, IE_BAM_CLASS_ID );
+
+ if (af == NULL) {
+ return;
+ }
+
+ for (i=0;i<MAX_SPARK_PHASE; i++) {
+ bitmap[i] = af->GetCycle( i );
+ }
+
+*/
+}
+
+bool Particles::AddNew(const Point &point)
+{
+ int st;
+
+ switch(path)
+ {
+ case SP_PATH_EXPL:
+ st = pos.h+last_insert%15;
+ break;
+ case SP_PATH_RAIN:
+ case SP_PATH_FLIT:
+ st = core->Roll(3,5,MAX_SPARK_PHASE)<<4;
+ break;
+ case SP_PATH_FOUNT:
+ st =(MAX_SPARK_PHASE + 2*pos.h);
+ break;
+ case SP_PATH_FALL:
+ default:
+ st =(MAX_SPARK_PHASE + pos.h)<<4;
+ break;
+ }
+ int i = last_insert;
+ while (i--) {
+ if (points[i].state == -1) {
+ points[i].state = st;
+ points[i].pos = point;
+ last_insert = i;
+ return false;
+ }
+ }
+ i = size;
+ while (i--!=last_insert) {
+ if (points[i].state == -1) {
+ points[i].state = st;
+ points[i].pos = point;
+ last_insert = i;
+ return false;
+ }
+ }
+ return true;
+}
+
+void Particles::Draw(const Region &screen)
+{
+ int length; //used only for raindrops
+
+ Video *video=core->GetVideoDriver();
+ Region region = video->GetViewport();
+ if (owner) {
+ region.x-=pos.x;
+ region.y-=pos.y;
+ }
+ int i = size;
+ while (i--) {
+ if (points[i].state == -1) {
+ continue;
+ }
+ int state;
+
+ switch(path) {
+ case SP_PATH_FLIT:
+ case SP_PATH_RAIN:
+ state = points[i].state>>4;
+ break;
+ default:
+ state = points[i].state;
+ break;
+ }
+
+ if (state>=MAX_SPARK_PHASE) {
+ length = 6-abs(state-MAX_SPARK_PHASE-6);
+ state = 0;
+ } else {
+ state=MAX_SPARK_PHASE-state-1;
+ length=0;
+ }
+ Color clr = sparkcolors[color][state];
+ switch (type) {
+ case SP_TYPE_BITMAP:
+ /*
+ if (bitmap[state]) {
+ Sprite2D *frame = bitmap[state]->GetFrame(points[i].state&255);
+ video->BlitGameSprite(frame,
+ points[i].pos.x+screen.x,
+ points[i].pos.y+screen.y, 0, clr,
+ NULL, NULL, &screen);
+ }
+ */
+ if (fragments) {
+ //IE_ANI_CAST stance has a simple looping animation
+ Animation** anims = fragments->GetAnimation( IE_ANI_CAST, i );
+ if (anims) {
+ Animation* anim = anims[0];
+ Sprite2D* nextFrame = anim->GetFrame(anim->GetCurrentFrame());
+ video->BlitGameSprite( nextFrame, points[i].pos.x - region.x, points[i].pos.y - region.y,
+ 0, clr, NULL, fragments->GetPartPalette(0), &screen);
+ }
+ }
+ break;
+ case SP_TYPE_CIRCLE:
+ video->DrawCircle (points[i].pos.x-region.x,
+ points[i].pos.y-region.y, 2, clr, true);
+ break;
+ case SP_TYPE_POINT:
+ default:
+ video->SetPixel (points[i].pos.x-region.x,
+ points[i].pos.y-region.y, clr, true);
+ break;
+ // this is more like a raindrop
+ case SP_TYPE_LINE:
+ if (length) {
+ video->DrawLine (points[i].pos.x+region.x,
+ points[i].pos.y+region.y,
+ points[i].pos.x+region.x+(i&1),
+ points[i].pos.y+region.y+length, clr, true);
+ }
+ break;
+ }
+ }
+}
+
+void Particles::AddParticles(int count)
+{
+ while (count--) {
+ Point p;
+
+ switch (path) {
+ case SP_PATH_EXPL:
+ p.x = pos.w/2+core->Roll(1,pos.w/2,pos.w/4);
+ p.y = pos.h/2+(last_insert&7);
+ break;
+ case SP_PATH_FALL:
+ default:
+ p.x = core->Roll(1,pos.w,0);
+ p.y = core->Roll(1,pos.h/2,0);
+ break;
+ case SP_PATH_RAIN:
+ case SP_PATH_FLIT:
+ p.x = core->Roll(1,pos.w,0);
+ p.y = core->Roll(1,pos.h,0);
+ break;
+ case SP_PATH_FOUNT:
+ p.x = core->Roll(1,pos.w/2,pos.w/4);
+ p.y = core->Roll(1,pos.h/2,0);
+ break;
+ }
+ if (AddNew(p) ) {
+ break;
+ }
+ }
+}
+
+int Particles::Update()
+{
+ int drawn=false;
+ int i;
+ int grow;
+
+ if (phase==P_EMPTY) {
+ return drawn;
+ }
+
+ if (timetolive) {
+ if (timetolive<core->GetGame()->GameTime) {
+ spawn_type = SP_SPAWN_NONE;
+ phase = P_FADE;
+ }
+ }
+
+ switch(spawn_type) {
+ case SP_SPAWN_NONE:
+ grow = 0;
+ break;
+ case SP_SPAWN_FULL:
+ grow = size;
+ spawn_type=SP_SPAWN_NONE;
+ break;
+ case SP_SPAWN_SOME:
+ default:
+ grow = size/10;
+ }
+ for(i=0;i<size;i++) {
+ if (points[i].state==-1) {
+ continue;
+ }
+ drawn=true;
+ if (!points[i].state) {
+ grow++;
+ }
+ points[i].state--;
+
+ switch (path) {
+ case SP_PATH_FALL:
+ points[i].pos.y+=3+((i>>2)&3);
+ points[i].pos.y%=pos.h;
+ break;
+ case SP_PATH_RAIN:
+ points[i].pos.x+=pos.w+(i&1);
+ points[i].pos.x%=pos.w;
+ points[i].pos.y+=3+((i>>2)&3);
+ points[i].pos.y%=pos.h;
+ break;
+ case SP_PATH_FLIT:
+ if (points[i].state<=MAX_SPARK_PHASE<<4) {
+ break;
+ }
+ points[i].pos.x+=core->Roll(1,3,pos.w-2);
+ points[i].pos.x%=pos.w;
+ points[i].pos.y+=(i&3)+1;
+ break;
+ case SP_PATH_EXPL:
+ points[i].pos.y+=1;
+ break;
+ case SP_PATH_FOUNT:
+ if (points[i].state<=MAX_SPARK_PHASE) {
+ break;
+ }
+ if (points[i].state<(MAX_SPARK_PHASE+pos.h)) {
+ if ( (points[i].state&7) == 7) {
+ points[i].pos.x+=(i&3)-1;
+ }
+ points[i].pos.y+=2;
+ } else {
+ if ( (points[i].state&7) == 7) {
+ points[i].pos.x+=(i&3)-1;
+ }
+ points[i].pos.y-=2;
+ }
+ break;
+ }
+ }
+ if (phase==P_GROW) {
+ AddParticles(grow);
+ drawn=true;
+ }
+ if (!drawn) {
+ phase = P_EMPTY;
+ }
+ return drawn;
+}
diff --git a/gemrb/core/Particles.h b/gemrb/core/Particles.h
new file mode 100644
index 0000000..fb9cf8a
--- /dev/null
+++ b/gemrb/core/Particles.h
@@ -0,0 +1,137 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 Particles.h
+ * Declares Particles class implementing weather and spark effects
+ * and related defines
+ */
+
+#ifndef PARTICLES_H
+#define PARTICLES_H
+
+#include "exports.h"
+#include "ie_types.h"
+
+#include "Region.h"
+
+class CharAnimations;
+class Scriptable;
+
+//global phase for the while spark structure
+#define P_GROW 0
+#define P_FADE 1
+#define P_EMPTY 2
+
+// this structure holds data for a single particle element
+struct Element {
+ int state;
+ Point pos;
+};
+
+/**
+ * @class Particles
+ * Class holding information about particles and rendering them.
+ */
+
+#define SP_TYPE_POINT 0
+#define SP_TYPE_LINE 1
+#define SP_TYPE_CIRCLE 2
+#define SP_TYPE_BITMAP 3
+
+#define SP_PATH_FALL 0 //free falling
+#define SP_PATH_FOUNT 1 //going up and down
+#define SP_PATH_FLIT 2 //flitting
+#define SP_PATH_RAIN 3 //falling and vanishing quickly
+#define SP_PATH_EXPL 4 //explosion (mostly used with fragments)
+
+#define SP_SPAWN_NONE 0 //don't create new sparks
+#define SP_SPAWN_FULL 1 //fill all at setup, then switch to none
+#define SP_SPAWN_SOME 2 //add some new elements regularly
+
+#define SPARK_COLOR_BLUE 0
+#define SPARK_COLOR_GOLD 1
+#define SPARK_COLOR_PURPLE 2
+#define SPARK_COLOR_ICE 3
+#define SPARK_COLOR_STONE 4
+#define SPARK_COLOR_BLACK 5
+#define SPARK_COLOR_CHROM 6
+#define SPARK_COLOR_RED 7
+#define SPARK_COLOR_GREEN 8
+#define SPARK_COLOR_WHITE 9
+#define SPARK_COLOR_MAGENTA 10
+#define SPARK_COLOR_ORANGE 11
+#define SPARK_COLOR_CUSTOM 12
+
+#define MAX_SPARK_COLOR 13
+#define MAX_SPARK_PHASE 5
+
+class GEM_EXPORT Particles {
+public:
+ Particles(int s);
+ ~Particles();
+
+ void SetBitmap(unsigned int FragAnimID);
+ void SetPhase(ieByte ph) { phase = ph; }
+ int GetPhase() const { return phase; }
+ bool MatchPos(const Point &p) const { return pos.x==p.x && pos.y==p.y; }
+ void SetType(ieByte t, ieByte p=SP_PATH_FALL, ieByte st=SP_SPAWN_NONE)
+ {
+ type=t;
+ path=p;
+ spawn_type=st;
+ }
+ void SetRegion(int x, int y, int w, int h)
+ {
+ pos.x = x;
+ pos.y = y;
+ pos.w = w;
+ pos.h = h;
+ }
+ void SetTimeToLive(int ttl) { timetolive = ttl; }
+ void SetColor(ieByte c) { color=c; }
+ void SetOwner(Scriptable *o) { owner=o; }
+ /* returns true if it cannot add new elements */
+ bool AddNew(const Point &point);
+ void Draw(const Region &screen);
+ void AddParticles(int count);
+ /* returns true if it could be destroyed (didn't draw anything) */
+ int Update();
+ int GetHeight() const { return pos.y+pos.h; }
+private:
+ Element *points;
+ ieDword timetolive;
+ ieDword target; //could be 0, in that case target is pos
+ ieWord size; //spark number
+ ieWord last_insert;//last spark idx added
+ Scriptable *owner; //could be area or game or actor
+ Region pos;
+ ieByte phase; //global phase
+ ieByte type; //draw type (snow, rain)
+ ieByte path; //path type
+ ieByte color; //general spark color
+ ieByte spawn_type;
+ //use char animations for the fragment animations
+ //1. the cycles are loaded only when needed
+ //2. the fragments ARE avatar animations in the original IE (for some unknown reason)
+ CharAnimations *fragments;
+};
+
+#endif // ! PARTICLES_H
diff --git a/gemrb/core/PathFinder.h b/gemrb/core/PathFinder.h
new file mode 100644
index 0000000..b8b6ca7
--- /dev/null
+++ b/gemrb/core/PathFinder.h
@@ -0,0 +1,52 @@
+/* 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 PATHFINDER_H
+#define PATHFINDER_H
+
+//searchmap conversion bits
+
+enum {
+ PATH_MAP_IMPASSABLE = 0,
+ PATH_MAP_PASSABLE = 1,
+ PATH_MAP_TRAVEL = 2,
+ PATH_MAP_NO_SEE = 4,
+ PATH_MAP_SIDEWALL = 8,
+ PATH_MAP_AREAMASK = 15,
+ PATH_MAP_FREE = 0,
+ PATH_MAP_DOOR_OPAQUE = 16,
+ PATH_MAP_DOOR_IMPASSABLE = 32,
+ PATH_MAP_PC = 64,
+ PATH_MAP_NPC = 128,
+ PATH_MAP_ACTOR = (PATH_MAP_PC|PATH_MAP_NPC),
+ PATH_MAP_DOOR = (PATH_MAP_DOOR_OPAQUE|PATH_MAP_DOOR_IMPASSABLE),
+ PATH_MAP_NOTAREA = (PATH_MAP_ACTOR|PATH_MAP_DOOR),
+ PATH_MAP_NOTDOOR = (PATH_MAP_ACTOR|PATH_MAP_AREAMASK),
+ PATH_MAP_NOTACTOR = (PATH_MAP_DOOR|PATH_MAP_AREAMASK)
+};
+
+struct PathNode {
+ PathNode* Parent;
+ PathNode* Next;
+ unsigned short x;
+ unsigned short y;
+ unsigned int orient;
+};
+
+#endif
diff --git a/gemrb/core/Plugin.cpp b/gemrb/core/Plugin.cpp
new file mode 100644
index 0000000..97aa567
--- /dev/null
+++ b/gemrb/core/Plugin.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 "Plugin.h"
+
+Plugin::Plugin(void)
+{
+}
+
+Plugin::~Plugin(void)
+{
+}
diff --git a/gemrb/core/Plugin.h b/gemrb/core/Plugin.h
new file mode 100644
index 0000000..1212103
--- /dev/null
+++ b/gemrb/core/Plugin.h
@@ -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.
+ *
+ *
+ */
+
+/**
+ * @file Plugin.h
+ * Declares Plugin class, base class for all plugins
+ * @author The GemRB Project
+ */
+
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+#include "SClassID.h" // FIXME
+#include "exports.h"
+
+#include "Holder.h"
+#include "PluginMgr.h"
+#include "TypeID.h"
+
+#include <cstddef>
+
+/**
+ * @class Plugin
+ * Base class for all GemRB plugins
+ */
+
+class GEM_EXPORT Plugin : public Held<Plugin> {
+public:
+ Plugin(void);
+ virtual ~Plugin(void);
+};
+
+template <class T>
+class PluginHolder : public Holder<T> {
+public:
+ PluginHolder()
+ {
+ }
+ PluginHolder(PluginID id)
+ : Holder<T>(static_cast<T*>(PluginMgr::Get()->GetPlugin(id)))
+ {
+ }
+ ~PluginHolder()
+ {
+ }
+};
+
+#endif
diff --git a/gemrb/core/PluginMgr.cpp b/gemrb/core/PluginMgr.cpp
new file mode 100644
index 0000000..8b31b15
--- /dev/null
+++ b/gemrb/core/PluginMgr.cpp
@@ -0,0 +1,321 @@
+/* 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 "PluginMgr.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+#include "Plugin.h"
+#include "ResourceDesc.h"
+#include "Variables.h" // FIXME: this should be in Interface.h instead
+
+#include <cstdio>
+#include <cstdlib>
+#ifdef WIN32
+#include <io.h>
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#include <dlfcn.h>
+#endif
+
+typedef const char* (*Version_t)(void);
+typedef const char* (*Description_t)(void);
+typedef PluginID (*ID_t)();
+typedef bool (* Register_t)(PluginMgr*);
+
+#ifdef HAVE_FORBIDDEN_OBJECT_TO_FUNCTION_CAST
+#include <assert.h>
+typedef void *(* voidvoid)(void);
+inline voidvoid my_dlsym(void *handle, const char *symbol)
+{
+ void *value = dlsym(handle,symbol);
+ voidvoid ret;
+ assert(sizeof(ret)==sizeof(value) );
+ memcpy(&ret, &value, sizeof(ret) );
+ return ret;
+}
+#else
+#define my_dlsym dlsym
+#endif
+
+#ifdef WIN32
+#define FREE_PLUGIN( handle ) FreeLibrary( handle )
+#define GET_PLUGIN_SYMBOL( handle, name ) GetProcAddress( handle, name )
+#define PRINT_DLERROR
+#else
+#define FREE_PLUGIN( handle ) dlclose( handle )
+#define GET_PLUGIN_SYMBOL( handle, name ) my_dlsym( handle, name )
+#define PRINT_DLERROR printf( "%s\n", dlerror() )
+#endif
+
+PluginMgr *PluginMgr::Get()
+{
+ static PluginMgr mgr;
+ return &mgr;
+}
+
+PluginMgr::PluginMgr()
+{
+}
+
+void PluginMgr::LoadPlugins(char* pluginpath)
+{
+ printMessage( "PluginMgr", "Loading Plugins from ", WHITE );
+ printf( "%s\n", pluginpath );
+
+ char path[_MAX_PATH];
+ strcpy( path, pluginpath );
+
+ std::list< char * > files;
+ if (! FindFiles( path, files ))
+ return;
+
+ //Iterate through all the available modules to load
+ int file_count = files.size (); // keeps track of first-pass files
+ while (! files.empty()) {
+ char* file = files.front();
+ files.pop_front();
+ file_count--;
+
+ PathJoin( path, pluginpath, file, NULL );
+ printBracket( "PluginMgr", LIGHT_WHITE );
+ printf( ": Loading: " );
+ textcolor( LIGHT_WHITE );
+ printf( "%s", path );
+ textcolor( WHITE );
+ printf( "..." );
+
+
+ ieDword flags = 0;
+ core->plugin_flags->Lookup (file, flags);
+
+ // module is sent to the back
+ if ((flags == PLF_DELAY) && (file_count >= 0)) {
+ printStatus( "DELAYING", YELLOW );
+ files.push_back( file );
+ continue;
+ }
+
+ // We do not need the basename anymore now
+ free( file );
+
+ // module is skipped
+ if (flags == PLF_SKIP) {
+ printStatus( "SKIPPING", YELLOW );
+ continue;
+ }
+
+
+
+ // Try to load the Module
+#ifdef WIN32
+ HMODULE hMod = LoadLibrary( path );
+#else
+ // Note: the RTLD_GLOBAL is necessary to export symbols to modules
+ // which python may have to dlopen (-wjp, 20060716)
+ // (to reproduce, try 'import bz2' or another .so module)
+ void* hMod = dlopen( path, RTLD_NOW | RTLD_GLOBAL );
+#endif
+ if (hMod == NULL) {
+ printBracket( "ERROR", LIGHT_RED );
+ printf( "\nCannot Load Module, Skipping...\n" );
+ PRINT_DLERROR;
+ continue;
+ }
+
+ //printStatus( "OK", LIGHT_GREEN );
+ //using C bindings, so we don't need to jump through extra hoops
+ //with the symbol name
+ Version_t LibVersion = ( Version_t ) GET_PLUGIN_SYMBOL( hMod, "GemRBPlugin_Version" );
+ Description_t Description = ( Description_t ) GET_PLUGIN_SYMBOL( hMod, "GemRBPlugin_Description" );
+ ID_t ID = ( ID_t ) GET_PLUGIN_SYMBOL( hMod, "GemRBPlugin_ID" );
+ Register_t Register = ( Register_t ) GET_PLUGIN_SYMBOL( hMod, "GemRBPlugin_Register" );
+
+ //printMessage( "PluginMgr", "Checking Plugin Version...", WHITE );
+ if (LibVersion==NULL) {
+ printStatus( "ERROR", LIGHT_RED );
+ printf( "Invalid Plug-in, Skipping...\n" );
+ FREE_PLUGIN( hMod );
+ continue;
+ }
+ if (strcmp(LibVersion(), VERSION_GEMRB) ) {
+ printStatus( "ERROR", LIGHT_RED );
+ printf( "Plug-in Version not valid, Skipping...\n" );
+ FREE_PLUGIN( hMod );
+ continue;
+ }
+
+ PluginDesc desc = { hMod, ID(), Description(), Register };
+
+ //printStatus( "OK", LIGHT_GREEN );
+ //printMessage( "PluginMgr", "Loading Exports for ", WHITE );
+ printf( " " );
+ textcolor( LIGHT_WHITE );
+ printf( "%s", desc.Description );
+ textcolor( WHITE );
+ printf( "..." );
+ printStatus( "OK", LIGHT_GREEN );
+ if (libs.find(desc.ID) != libs.end()) {
+ printMessage( "PluginMgr", "Plug-in Already Loaded! ", WHITE );
+ printStatus( "SKIPPING", YELLOW );
+ FREE_PLUGIN( hMod );
+ continue;
+ }
+ if (desc.Register != NULL) {
+ if (!desc.Register(this)) {
+ printMessage( "PluginMgr", "Plug-in Registration Failed! Perhaps a duplicate? ", WHITE );
+ printStatus( "SKIPPING", YELLOW );
+ FREE_PLUGIN( hMod );
+ }
+ }
+ libs[desc.ID] = desc;
+ }
+}
+
+PluginMgr::~PluginMgr(void)
+{
+//don't free the shared libraries in debug mode, so valgrind can resolve the stack trace
+#ifndef _DEBUG
+ for (unsigned int i = 0; i < libs.size(); i++) {
+#ifdef WIN32
+ FreeLibrary(libs[i].handle);
+#else
+ // dlclose(libs[i].handle);
+#endif
+ }
+#endif
+}
+
+#ifdef WIN32
+bool
+PluginMgr::FindFiles( char* path, std::list<char*> &files )
+{
+ //The windows _findfirst/_findnext functions allow the use of wildcards so we'll use them :)
+ struct _finddata_t c_file;
+ long hFile;
+ strcat( path, "*.dll" );
+ if (( hFile = ( long ) _findfirst( path, &c_file ) ) == -1L) //If there is no file matching our search
+ return false;
+
+ do {
+ files.push_back( strdup( c_file.name ));
+ } while (_findnext( hFile, &c_file ) == 0);
+
+ _findclose( hFile );
+ return true;
+}
+
+#else // ! WIN32
+
+bool
+PluginMgr::FindFiles( char* path, std::list<char*> &files )
+{
+ DirectoryIterator dir(path);
+ if (!dir) //If we cannot open the Directory
+ return false;
+
+ do {
+ const char *name = dir.GetName();
+ if (fnmatch( "*.so", name, 0 ) != 0) //If the current file has no ".so" extension, skip it
+ continue;
+ files.push_back( strdup( name ));
+ } while (++dir);
+
+ return true;
+}
+#endif // ! WIN32
+
+bool PluginMgr::IsAvailable(SClass_ID plugintype) const
+{
+ return plugins.find(plugintype) != plugins.end();
+}
+
+Plugin* PluginMgr::GetPlugin(SClass_ID plugintype) const
+{
+ std::map<SClass_ID, PluginFunc>::const_iterator iter = plugins.find(plugintype);
+ if (iter != plugins.end())
+ return iter->second();
+ return NULL;
+}
+
+const std::vector<ResourceDesc>& PluginMgr::GetResourceDesc(const TypeID* type)
+{
+ return resources[type];
+}
+
+bool PluginMgr::RegisterPlugin(SClass_ID id, PluginFunc create)
+{
+ if (plugins.find(id) != plugins.end())
+ return false;
+ plugins[id] = create;
+ return true;
+}
+
+void PluginMgr::RegisterResource(const TypeID* type, ResourceFunc create, const char *ext, ieWord keyType)
+{
+ resources[type].push_back(ResourceDesc(type,create,ext,keyType));
+}
+
+void PluginMgr::RegisterInitializer(void (*func)(void))
+{
+ intializerFunctions.push_back(func);
+}
+
+void PluginMgr::RegisterCleanup(void (*func)(void))
+{
+ cleanupFunctions.push_back(func);
+}
+
+void PluginMgr::RunInitializers() const
+{
+ for (size_t i = 0; i < intializerFunctions.size(); i++)
+ intializerFunctions[i]();
+}
+
+void PluginMgr::RunCleanup() const
+{
+ for (size_t i = 0; i < cleanupFunctions.size(); i++)
+ cleanupFunctions[i]();
+}
+
+bool PluginMgr::RegisterDriver(const TypeID* type, const char* name, PluginFunc create)
+{
+ driver_map &map = drivers[type];
+ driver_map::const_iterator iter = map.find(name);
+ if (iter != map.end())
+ return false;
+ map[name] = create;
+ return true;
+}
+
+Plugin* PluginMgr::GetDriver(const TypeID* type, const char* name)
+{
+ driver_map &map = drivers[type];
+ if (map.begin() == map.end())
+ return NULL;
+ driver_map::const_iterator iter = map.find(name);
+ if (iter != map.end())
+ return iter->second();
+ return map.begin()->second();
+}
diff --git a/gemrb/core/PluginMgr.h b/gemrb/core/PluginMgr.h
new file mode 100644
index 0000000..ff0d88c
--- /dev/null
+++ b/gemrb/core/PluginMgr.h
@@ -0,0 +1,153 @@
+/* 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 PluginMgr.h
+ * Declares PluginMgr, loader for GemRB plugins
+ * @author The GemRB Project
+ */
+
+#ifndef PLUGINMGR_H
+#define PLUGINMGR_H
+
+#include "SClassID.h" // For PluginID
+#include "exports.h"
+#include "globals.h"
+#include "iless.h"
+#include "win32def.h"
+
+#include "ResourceDesc.h"
+
+#include <cstring>
+#include <list>
+#include <map>
+#include <vector>
+
+#ifdef WIN32
+typedef HINSTANCE LibHandle;
+#else
+typedef void *LibHandle;
+#endif
+
+class Plugin;
+class Resource;
+class TypeID;
+
+/**
+ * @class PluginMgr
+ * Class for loading GemRB plugins from shared libraries or DLLs.
+ * It goes over all appropriately named files in PluginPath directory
+ * and tries to load them one after another.
+ */
+
+class GEM_EXPORT PluginMgr {
+public:
+ typedef Resource* (*ResourceFunc)(DataStream*);
+ typedef Plugin* (*PluginFunc)();
+public:
+ /** Return global instance of PluginMgr */
+ static PluginMgr* Get();
+ void LoadPlugins(char* pluginpath);
+private:
+ PluginMgr();
+public: // HACK: MSVC6 is buggy.
+ ~PluginMgr(void);
+private:
+ struct PluginDesc {
+ LibHandle handle;
+ PluginID ID;
+ const char *Description;
+ bool (*Register)(PluginMgr*);
+ };
+ std::map< PluginID, PluginDesc> libs;
+ std::map< SClass_ID, PluginFunc> plugins;
+ std::map< const TypeID*, std::vector<ResourceDesc> > resources;
+ /** Array of initializer functions */
+ std::vector<void (*)(void)> intializerFunctions;
+ /** Array of cleanup functions */
+ std::vector<void (*)(void)> cleanupFunctions;
+ typedef std::map<const char*, PluginFunc, iless> driver_map;
+ std::map<const TypeID*, driver_map> drivers;
+public:
+ /** Return names of all *.so or *.dll files in the given directory */
+ bool FindFiles( char* path, std::list< char* > &files);
+ bool IsAvailable(SClass_ID plugintype) const;
+ Plugin* GetPlugin(SClass_ID plugintype) const;
+
+ size_t GetPluginCount() const { return plugins.size(); }
+
+ /**
+ * Register class plugin.
+ *
+ * @param[in] id ID used to access plugin.
+ * @param[in] create Function to create instance of plugin.
+ */
+ bool RegisterPlugin(SClass_ID id, PluginFunc create);
+ /**
+ * Register resource.
+ *
+ * @param[in] type Base class for resource.
+ * @param[in] create Function to create resource from a stream.
+ * @param[in] ext Extension used for resource files.
+ * @param[in] keyType \iespecific Type identifier used in key/biff files.
+ */
+ void RegisterResource(const TypeID* type, ResourceFunc create, const char *ext, ieWord keyType = 0);
+
+ const std::vector<ResourceDesc>& GetResourceDesc(const TypeID*);
+
+ /**
+ * Registers a static intializer.
+ *
+ * @param[in] init Function to call on startup.
+ */
+ void RegisterInitializer(void (*init)(void));
+ /**
+ * Registers a static cleanup.
+ *
+ * @param[in] cleanup Function to call on shutdown.
+ */
+ void RegisterCleanup(void (*cleanup)(void));
+
+ /** Run intializer functions. */
+ void RunInitializers() const;
+ /** Run cleanup functions */
+ void RunCleanup() const;
+
+ /**
+ * Registers a driver plugin
+ *
+ * @param[in] type Base class for driver.
+ * @param[in] name Name of driver.
+ * @param[in] create Function to create instance of plugin.
+ */
+ bool RegisterDriver(const TypeID* type, const char* name, PluginFunc create);
+
+ /**
+ * Gets driver of specified type.
+ *
+ * @param[in] type Base class for driver.
+ * @param[in] name Name of driver.
+ *
+ * Tries to get driver associated to name, or falls back to a random one.
+ */
+ Plugin* GetDriver(const TypeID* type, const char* name);
+};
+
+#endif
diff --git a/gemrb/core/Polygon.cpp b/gemrb/core/Polygon.cpp
new file mode 100644
index 0000000..2128a1d
--- /dev/null
+++ b/gemrb/core/Polygon.cpp
@@ -0,0 +1,387 @@
+/* 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 "Polygon.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+
+#include <algorithm>
+#include <cstring>
+#include <vector>
+
+Gem_Polygon::Gem_Polygon(Point* points, unsigned int cnt, Region *bbox)
+{
+ if (cnt) {
+ this->points = ( Point * ) malloc( cnt * sizeof( Point ) );
+ memcpy( this->points, points, cnt * sizeof( Point ) );
+ } else {
+ this->points = NULL;
+ }
+ count = cnt;
+ if(bbox) BBox=*bbox;
+ else RecalcBBox();
+
+ ComputeTrapezoids();
+}
+
+Gem_Polygon::~Gem_Polygon(void)
+{
+ if (points) {
+ free( points );
+ }
+}
+
+void Gem_Polygon::RecalcBBox()
+{
+ if(!count) {
+ BBox.x=BBox.y=BBox.w=BBox.h=0;
+ return;
+ }
+ BBox.x=points[0].x;
+ BBox.y=points[0].y;
+ BBox.w=points[0].x;
+ BBox.h=points[0].y;
+ unsigned int i;
+ for(i=1; i<count; i++) {
+ if(points[i].x<BBox.x) {
+ BBox.x=points[i].x;
+ }
+ if(points[i].x>BBox.w) {
+ BBox.w=points[i].x;
+ }
+ if(points[i].y<BBox.y) {
+ BBox.y=points[i].y;
+ }
+ if(points[i].y>BBox.h) {
+ BBox.h=points[i].y;
+ }
+ }
+ BBox.w-=BBox.x;
+ BBox.h-=BBox.y;
+}
+
+bool Gem_Polygon::PointIn(const Point &p) const
+{
+ if(!BBox.PointInside(p) ) return false;
+ return PointIn(p.x, p.y);
+}
+
+bool Gem_Polygon::PointIn(int tx, int ty) const
+{
+ register int j, yflag0, yflag1, xflag0 , index;
+ bool inside_flag = false;
+ Point* vtx0, * vtx1;
+
+ if (count<3) {
+ return false;
+ }
+ index = 0;
+
+ vtx0 = &points[count - 1];
+ yflag0 = ( vtx0->y >= ty );
+ vtx1 = &points[index];
+
+ for (j = count + 1; --j ;) {
+ yflag1 = ( vtx1->y >= ty );
+ if (yflag0 != yflag1) {
+ xflag0 = ( vtx0->x >= tx );
+ if (xflag0 == ( vtx1->x >= tx )) {
+ if (xflag0)
+ inside_flag = !inside_flag;
+ } else {
+ if (( vtx1->x -
+ ( vtx1->y - ty ) * ( vtx0->x - vtx1->x ) /
+ ( vtx0->y - vtx1->y ) ) >= tx) {
+ inside_flag = !inside_flag;
+ }
+ }
+ }
+ yflag0 = yflag1;
+ vtx0 = vtx1;
+ vtx1 = &points[++index];
+ }
+ return inside_flag;
+}
+
+// returns twice the area of triangle a, b, c.
+// (can also be negative depending on orientation of a,b,c)
+inline int area2(const Point& a, const Point& b, const Point& c)
+{
+ return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
+}
+
+
+// return (c is to the left of a-b)
+inline bool left(const Point& a, const Point& b, const Point& c)
+{
+ return (area2(a, b, c) > 0);
+}
+
+// { return (c is collinear with a-b)
+inline bool collinear(const Point& a, const Point& b, const Point& c)
+{
+ return (area2(a, b, c) == 0);
+}
+
+// Find the intersection of two segments, if any.
+// If the intersection is one of the endpoints, or the lines are
+// parallel, false is returned.
+// The point returned has the actual intersection coordinates rounded down
+// to integers
+static bool intersectSegments(Point& a, Point& b, Point& c, Point& d, Point& s)
+{
+ if (collinear(a, b, c) || collinear(a, b, d) ||
+ collinear(c, d, a) || collinear(c, d, b))
+ return false;
+
+ if (!((left(a, b, c) != left(a, b, d)) &&
+ (left(c, d, a) != left(c, d, b))))
+ return false;
+
+ __int64 A1 = area2(c, d, a);
+ __int64 A2 = area2(d, c, b);
+
+ s.x = (short) ((b.x*A1 + a.x*A2) / (A1 + A2));
+ s.y = (short) ((b.y*A1 + a.y*A2) / (A1 + A2));
+
+ return true;
+}
+
+// find the intersection of a segment with a horizontal scanline, if any
+static bool intersectSegmentScanline(Point& a, Point& b, int y, int& x)
+{
+ int y1 = a.y - y;
+ int y2 = b.y - y;
+
+ if (y1 * y2 > 0) return false;
+ if (y1 == 0 && y2 == 0) return false;
+
+ x = a.x + ((b.x - a.x)*y1)/(y1-y2);
+ return true;
+}
+
+
+struct ScanlineInt {
+ int x;
+ int pi;
+ Gem_Polygon* p;
+
+ bool operator<(const ScanlineInt& i2) const
+ {
+ if (x < i2.x)
+ return true;
+
+ if (x > i2.x)
+ return false;
+
+ Point& a = p->points[pi];
+ Point& b = p->points[(pi+1)%(p->count)];
+ Point& c = p->points[i2.pi];
+ Point& d = p->points[(i2.pi+1)%(p->count)];
+
+ int dx1 = a.x - b.x;
+ int dx2 = c.x - d.x;
+ int dy1 = a.y - b.y;
+ int dy2 = c.y - d.y;
+
+ if (dy1 < 0) {
+ dy1 *= -1;
+ dx1 *= -1;
+ }
+
+ if (dy2 < 0) {
+ dy2 *= -1;
+ dx2 *= -1;
+ }
+
+ if (dx1 * dy2 > dx2 * dy1) return true;
+
+ return false;
+ }
+
+};
+
+
+void Gem_Polygon::ComputeTrapezoids()
+{
+ if (count < 3) return;
+ //the loader never should load such a large polygon,
+ //because the polygon count is supposed to be a 16 bit value
+ if (count > 65535) {
+ printMessage("Polygon", "Invalid Polygon!\n", LIGHT_RED);
+ abort();
+ }
+
+ trapezoids.clear();
+ std::vector<int> ys;
+ ys.reserve(2*count);
+
+ // y coords of vertices
+ unsigned int i;
+
+ for (i = 0; i < count; ++i)
+ ys.push_back(points[i].y);
+
+ Point p;
+ // y coords of self-intersections
+ for (unsigned int i1 = 0; i1 < count; ++i1) {
+ Point& a = points[i1];
+ Point& b = points[(i1+1)%count];
+
+ // intersections with horizontal lines don't matter
+ if (a.y == b.y) continue;
+
+ for (unsigned int i2 = i1+2; i2 < count; ++i2) {
+ Point& c = points[i2];
+ Point& d = points[(i2+1)%count];
+
+ // intersections with horizontal lines don't matter
+ if (c.y == d.y) continue;
+
+ if (intersectSegments(a, b, c, d, p)) {
+ ys.push_back(p.y);
+ }
+ }
+ }
+
+ std::sort(ys.begin(), ys.end());
+
+ std::vector<ScanlineInt> ints;
+ ints.reserve(count);
+
+ Trapezoid t;
+ ScanlineInt is;
+ is.p = this;
+ std::list<Trapezoid>::iterator iter;
+
+ unsigned int yi = 0;
+ int cury = ys[0];
+
+ // TODO: it's possible to keep a set of 'active' edges and only check
+ // scanline intersections of those edges.
+
+
+ while (yi < ys.size() - 1) {
+ while (yi < ys.size() && ys[yi] == cury) ++yi;
+ if (yi == ys.size()) break;
+ int nexty = ys[yi];
+
+ t.y1 = cury;
+ t.y2 = nexty;
+
+ // Determine all scanline intersections at level nexty.
+ // This includes edges which have their lower vertex at nexty,
+ // but excludes edges with their upper vertex at nexty.
+ // (We're taking the intersections along the 'upper' edge of
+ // the nexty scanline.)
+ ints.clear();
+ for (i = 0; i < count; ++i) {
+ Point& a = points[i];
+ Point& b = points[(i+1)%count];
+
+ if (a.y == b.y) continue;
+
+ if (a.y == nexty) {
+ if (b.y - nexty < 0) {
+ is.x = a.x;
+ is.pi = i;
+ ints.push_back(is);
+ }
+ } else if (b.y == nexty) {
+ if (a.y - nexty < 0) {
+ is.x = b.x;
+ is.pi = i;
+ ints.push_back(is);
+ }
+ } else {
+ int x;
+ if (intersectSegmentScanline(a, b, nexty, x)) {
+ is.x = x;
+ is.pi = i;
+ ints.push_back(is);
+ }
+ }
+ }
+
+ std::sort(ints.begin(), ints.end());
+ unsigned int newtcount = (unsigned int) (ints.size() / 2);
+
+ for (i = 0; i < newtcount; ++i) {
+ t.left_edge = ints[2*i].pi;
+ t.right_edge = ints[2*i+1].pi;
+
+
+ bool found = false;
+
+ // merge trapezoids with old one if it's just a continuation
+ for (iter = trapezoids.begin(); iter != trapezoids.end(); ++iter) {
+ Trapezoid& oldt = *iter;
+ if (oldt.y2 == cury &&
+ oldt.left_edge == t.left_edge &&
+ oldt.right_edge == t.right_edge)
+ {
+ oldt.y2 = nexty;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ trapezoids.push_back(t);
+ }
+
+ // Done with this strip
+ cury = nexty;
+ }
+}
+
+
+// wall polygons
+void Wall_Polygon::SetBaseline(const Point &a, const Point &b)
+{
+ if ((a.x<b.x) || ((a.x==b.x) && (a.y<b.y)) ) {
+ base0=a;
+ base1=b;
+ return;
+ }
+ base0=b;
+ base1=a;
+}
+
+bool Wall_Polygon::PointCovered(const Point &p) const
+{
+ if (wall_flag&WF_DISABLED)
+ return false;
+ if (wall_flag&WF_BASELINE) {
+ if (base0.x > base1.x)
+ return left(base0, base1, p);
+ else
+ return left(base1, base0, p);
+ }
+ return true;
+}
+
+bool Wall_Polygon::PointCovered(int tx, int ty) const
+{
+ Point p((short) tx, (short) ty);
+ return PointCovered(p);
+}
+
diff --git a/gemrb/core/Polygon.h b/gemrb/core/Polygon.h
new file mode 100644
index 0000000..505a91f
--- /dev/null
+++ b/gemrb/core/Polygon.h
@@ -0,0 +1,81 @@
+/* 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 POLYGON_H
+#define POLYGON_H
+
+#include "RGBAColor.h"
+#include "exports.h"
+#include "globals.h"
+
+#include "Region.h"
+
+#include <list>
+
+class GEM_EXPORT Trapezoid {
+public:
+ int y1, y2;
+ int left_edge, right_edge;
+};
+
+class GEM_EXPORT Gem_Polygon {
+public:
+ Gem_Polygon(Point* points, unsigned int count, Region *bbox = NULL);
+ ~Gem_Polygon(void);
+ Region BBox;
+ Point* points;
+ unsigned int count;
+ std::list<Trapezoid> trapezoids;
+ bool PointIn(const Point &p) const;
+ bool PointIn(int x, int y) const;
+ void RecalcBBox();
+ void ComputeTrapezoids();
+};
+
+// wall polygons are used to render area wallgroups
+// wall polygons never create a surface
+//ALWAYSCOVER wallpolygon blocks everything that it covers
+//BASELINE means there is a baseline, the loader makes the distinction
+//between first edge is base line/separate baseline
+//DITHER means the polygon only dithers what it covers
+
+#define WF_ALWAYSCOVER 0
+#define WF_BASELINE 1
+#define WF_DITHER 2
+//this is used only externally, but converted to baseline on load time
+#define WF_HOVER 4
+// cover animations
+#define WF_COVERANIMS 8
+// door polygons are not always drawn
+#define WF_DISABLED 0x80
+
+class GEM_EXPORT Wall_Polygon: public Gem_Polygon {
+public:
+ Wall_Polygon(Point *points,int count,Region *bbox) : Gem_Polygon(points,count,bbox) {}
+ //is the point above the baseline
+ bool PointCovered(const Point &p) const;
+ bool PointCovered(int x, int y) const;
+ ieDword GetPolygonFlag() const { return wall_flag; }
+ void SetPolygonFlag(ieDword flg) { wall_flag=flg; }
+ void SetBaseline(const Point &a, const Point &b);
+public:
+ ieDword wall_flag;
+ Point base0, base1;
+};
+
+#endif
diff --git a/gemrb/core/PolymorphCache.h b/gemrb/core/PolymorphCache.h
new file mode 100644
index 0000000..b97c11b
--- /dev/null
+++ b/gemrb/core/PolymorphCache.h
@@ -0,0 +1,34 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2010 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 POLYMORPHCACHE_H
+#define POLYMORPHCACHE_H
+
+#include "ie_types.h"
+
+struct GEM_EXPORT PolymorphCache {
+ ieResRef Resource;
+ ieDword *stats;
+
+ PolymorphCache() : stats(NULL) { }
+ ~PolymorphCache() {
+ delete[] stats;
+ }
+};
+
+#endif
diff --git a/gemrb/core/Projectile.cpp b/gemrb/core/Projectile.cpp
new file mode 100644
index 0000000..b63f075
--- /dev/null
+++ b/gemrb/core/Projectile.cpp
@@ -0,0 +1,1763 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 "Projectile.h"
+
+#include "win32def.h"
+
+#include "Audio.h"
+#include "DisplayMessage.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "ProjectileServer.h"
+#include "Video.h"
+
+#include <cmath>
+#include <cstdlib>
+
+//to get gradient color
+//apparently pst doesn't have the small palette correctly
+#define PALSIZE 32
+
+static const ieByte SixteenToNine[MAX_ORIENT]={0,1,2,3,4,5,6,7,8,7,6,5,4,3,2,1};
+static const ieByte SixteenToFive[MAX_ORIENT]={0,1,2,3,4,3,2,1,0,1,2,3,4,3,2,1};
+
+static ProjectileServer *server = NULL;
+
+Projectile::Projectile()
+{
+ autofree = false;
+ Extension = NULL;
+ area = NULL;
+ palette = NULL;
+ Pos.empty();
+ Destination = Pos;
+ Orientation = 0;
+ NewOrientation = 0;
+ path = NULL;
+ step = NULL;
+ timeStartStep = 0;
+ phase = P_UNINITED;
+ effects = NULL;
+ children = NULL;
+ child_size = 0;
+ memset(travel, 0, sizeof(travel)) ;
+ memset(shadow, 0, sizeof(shadow)) ;
+ memset(PaletteRes,0,sizeof(PaletteRes));
+ memset(smokebam, 0, sizeof(smokebam));
+ light = NULL;
+ pathcounter = 0x7fff;
+ FakeTarget = 0;
+ bend = 0;
+ drawSpark = 0;
+ ZPos = 0;
+ extension_delay = 0;
+ if (!server)
+ server = core->GetProjectileServer();
+}
+
+Projectile::~Projectile()
+{
+ int i;
+
+ if (autofree) {
+ free(Extension);
+ }
+ delete effects;
+
+ gamedata->FreePalette(palette, PaletteRes);
+ ClearPath();
+
+ if (travel_handle) {
+ //allow an explosion sound to finish completely
+ travel_handle->StopLooping();
+ }
+
+ if (phase != P_UNINITED) {
+ for (i = 0; i < MAX_ORIENT; ++i) {
+ if(travel[i])
+ delete travel[i];
+ if(shadow[i])
+ delete shadow[i];
+ }
+ core->GetVideoDriver()->FreeSprite(light);
+ }
+
+ if(children) {
+ for(i=0;i<child_size;i++) {
+ delete children[i];
+ }
+ free (children);
+ }
+}
+
+void Projectile::InitExtension()
+{
+ autofree = false;
+ if (!Extension) {
+ Extension = (ProjectileExtension *) calloc( 1, sizeof(ProjectileExtension));
+ }
+}
+
+void Projectile::CreateAnimations(Animation **anims, const ieResRef bamres, int Seq)
+{
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( bamres,
+ IE_BAM_CLASS_ID, IE_NORMAL );
+
+ if (!af) {
+ return;
+ }
+
+ int Max = af->GetCycleCount();
+ if (!Max) {
+ return;
+ }
+
+ if((ExtFlags&PEF_CYCLE) && !Seq) {
+ Seq=rand()%Max;
+ }
+
+ //this hack is needed because bioware .pro files are sometimes
+ //reporting bigger face count than possible by the animation
+ if (Aim>Max) Aim=Max;
+
+ if(ExtFlags&PEF_PILLAR) {
+ CreateCompositeAnimation(anims, af, Seq);
+ } else {
+ CreateOrientedAnimations(anims, af, Seq);
+ }
+}
+
+//Seq is the first cycle to use in the composite
+//Aim is the number of cycles
+void Projectile::CreateCompositeAnimation(Animation **anims, AnimationFactory *af, int Seq)
+{
+ for (int Cycle = 0; Cycle<Aim; Cycle++) {
+ int c = Cycle+Seq;
+ Animation* a = af->GetCycle( c );
+ anims[Cycle] = a;
+ if (!a) continue;
+ //animations are started at a random frame position
+ //Always start from 0, unless set otherwise
+ if (!(ExtFlags&PEF_RANDOM)) {
+ a->SetPos(0);
+ }
+
+ a->gameAnimation = true;
+ }
+}
+
+//Seq is the cycle to use in case of single orientations
+//Aim is the number of Orientations
+void Projectile::CreateOrientedAnimations(Animation **anims, AnimationFactory *af, int Seq)
+{
+ for (int Cycle = 0; Cycle<MAX_ORIENT; Cycle++) {
+ bool mirror = false, mirrorvert = false;
+ int c;
+ switch(Aim) {
+ default:
+ c = Seq;
+ break;
+ case 5:
+ c = SixteenToFive[Cycle];
+ // orientations go counter-clockwise, starting south
+ if (Cycle <= 4) {
+ // bottom-right quadrant
+ mirror = false; mirrorvert = false;
+ } else if (Cycle <= 8) {
+ // top-right quadrant
+ mirror = false; mirrorvert = true;
+ } else if (Cycle < 12) {
+ // top-left quadrant
+ mirror = true; mirrorvert = true;
+ } else {
+ // bottom-left quadrant
+ mirror = true; mirrorvert = false;
+ }
+ break;
+ case 9:
+ c = SixteenToNine[Cycle];
+ if (Cycle>8) mirror=true;
+ break;
+ case 16:
+ c=Cycle;
+ break;
+ }
+ Animation* a = af->GetCycle( c );
+ anims[Cycle] = a;
+ if (!a) continue;
+ //animations are started at a random frame position
+ //Always start from 0, unless set otherwise
+ if (!(ExtFlags&PEF_RANDOM)) {
+ a->SetPos(0);
+ }
+
+ if (mirror) {
+ a->MirrorAnimation();
+ }
+ if (mirrorvert) {
+ a->MirrorAnimationVert();
+ }
+ a->gameAnimation = true;
+ }
+}
+
+//apply gradient colors
+void Projectile::SetupPalette(Animation *anim[], Palette *&pal, const ieByte *gradients)
+{
+ ieDword Colors[7];
+
+ for (int i=0;i<7;i++) {
+ Colors[i]=gradients[i];
+ }
+ GetPaletteCopy(anim, pal);
+ if (pal) {
+ pal->SetupPaperdollColours(Colors, 0);
+ }
+}
+
+void Projectile::GetPaletteCopy(Animation *anim[], Palette *&pal)
+{
+ if (pal)
+ return;
+ for (unsigned int i=0;i<MAX_ORIENT;i++) {
+ if (anim[i]) {
+ Sprite2D* spr = anim[i]->GetFrame(0);
+ if (spr) {
+ pal = spr->GetPalette()->Copy();
+ break;
+ }
+ }
+ }
+}
+
+void Projectile::SetBlend()
+{
+ GetPaletteCopy(travel, palette);
+ if (!palette)
+ return;
+ if (!palette->alpha) {
+ palette->CreateShadedAlphaChannel();
+ }
+}
+
+//create another projectile with type-1 (iterate magic missiles and call lightning)
+void Projectile::CreateIteration()
+{
+ Projectile *pro = server->GetProjectileByIndex(type-1);
+ pro->SetEffectsCopy(effects);
+ pro->SetCaster(Caster, Level);
+ if (ExtFlags&PEF_CURVE) {
+ pro->bend=bend+1;
+ }
+
+ if (FakeTarget) {
+ area->AddProjectile(pro, Pos, FakeTarget, true);
+ } else {
+ area->AddProjectile(pro, Pos, Target, false);
+ }
+
+ // added by fuzzie, to make magic missiles instant, maybe wrong place
+ pro->Setup();
+}
+
+void Projectile::GetSmokeAnim()
+{
+ int AvatarsRowNum=CharAnimations::GetAvatarsCount();
+
+ SmokeAnimID&=0xfff0; //this is a hack, i'm too lazy to figure out the subtypes
+
+ for(int i=0;i<AvatarsRowNum;i++) {
+ AvatarStruct *as = CharAnimations::GetAvatarStruct(i);
+ if (as->AnimID==SmokeAnimID) {
+ memcpy(smokebam, as->Prefixes, sizeof(ieResRef) );
+ return;
+ }
+ }
+ //turn off smoke animation if its animation was not found
+ //you might want to issue some warning here
+ TFlags&=PTF_SMOKE;
+}
+// load animations, start sound
+void Projectile::Setup()
+{
+ tint.r=128;
+ tint.g=128;
+ tint.b=128;
+ tint.a=255;
+
+ ieDword time = core->GetGame()->Ticks;
+ timeStartStep = time;
+
+ if(ExtFlags&PEF_TEXT) {
+ Actor *act = area->GetActorByGlobalID(Caster);
+ if(act) {
+ displaymsg->DisplayStringName(StrRef,0xd7d7be,act,0);
+ }
+ }
+
+ //falling = vertical
+ //incoming = right side
+ //both = left side
+ if(ExtFlags&(PEF_FALLING|PEF_INCOMING) ) {
+ if (ExtFlags&PEF_INCOMING) {
+ if (ExtFlags&PEF_FALLING) {
+ Pos.x=Destination.x-200;
+ } else {
+ Pos.x=Destination.x+200;
+ }
+ }
+ else {
+ Pos.x=Destination.x;
+ }
+ Pos.y=Destination.y-200;
+ NextTarget(Destination);
+ }
+
+ if(ExtFlags&PEF_WALL) {
+ SetupWall();
+ }
+
+ //cone area of effect always disables the travel flag
+ //but also makes the caster immune to the effect
+ if (Extension) {
+ if (Extension->AFlags&PAF_CONE) {
+ NewOrientation = Orientation = GetOrient(Destination, Pos);
+ Destination=Pos;
+ ExtFlags|=PEF_NO_TRAVEL;
+ }
+
+ //this flag says the first explosion is delayed
+ //(works for delaying triggers too)
+ //getting the explosion count here, so an absent caster won't cut short
+ //on the explosion count
+ if(Extension->AFlags&PAF_DELAY) {
+ extension_delay=Extension->Delay;
+ } else {
+ extension_delay=0;
+ }
+ extension_explosioncount=CalculateExplosionCount();
+ }
+
+ //set any static tint
+ if(ExtFlags&PEF_TINT) {
+ Color tmpColor[PALSIZE];
+
+ core->GetPalette( Gradients[0], PALSIZE, tmpColor );
+ StaticTint(tmpColor[PALSIZE/2]);
+ }
+
+ CreateAnimations(travel, BAMRes1, Seq1);
+
+ if (TFlags&PTF_SHADOW) {
+ CreateAnimations(shadow, BAMRes2, Seq2);
+ }
+
+ if (TFlags&PTF_SMOKE) {
+ GetSmokeAnim();
+ }
+
+ //there is no travel phase, create the projectile right at the target
+ if (ExtFlags&PEF_NO_TRAVEL) {
+ Pos = Destination;
+
+ //the travel projectile should linger after explosion
+ if(ExtFlags&PEF_POP) {
+ //the explosion consists of a pop in/hold/pop out of the travel projectile (dimension door)
+ if(travel[0] && shadow[0]) {
+ extension_delay = travel[0]->GetFrameCount()*2+shadow[0]->GetFrameCount();
+ //SetDelay( travel[0]->GetFrameCount()*2+shadow[0]->GetFrameCount());
+ travel[0]->Flags|=A_ANI_PLAYONCE;
+ shadow[0]->Flags|=A_ANI_PLAYONCE;
+ }
+ } else {
+ if(travel[0]) {
+ extension_delay = travel[0]->GetFrameCount();
+ travel[0]->Flags|=A_ANI_PLAYONCE;
+ //SetDelay(travel[0]->GetFrameCount() );
+ }
+ }
+ }
+
+ if (TFlags&PTF_COLOUR) {
+ SetupPalette(travel, palette, Gradients);
+ } else {
+ gamedata->FreePalette(palette, PaletteRes);
+ palette=gamedata->GetPalette(PaletteRes);
+ }
+
+ if (TFlags&PTF_LIGHT) {
+ light = core->GetVideoDriver()->CreateLight(LightX, LightZ);
+ }
+ if (TFlags&PTF_BLEND) {
+ SetBlend();
+ }
+ if (SFlags&PSF_FLYING) {
+ ZPos = FLY_HEIGHT;
+ }
+ phase = P_TRAVEL;
+ travel_handle = core->GetAudioDrv()->Play(SoundRes1, Pos.x, Pos.y, (SFlags & PSF_LOOPING ? GEM_SND_LOOPING : 0));
+
+ //create more projectiles
+ if(ExtFlags&PEF_ITERATION) {
+ CreateIteration();
+ }
+}
+
+Actor *Projectile::GetTarget()
+{
+ Actor *target;
+
+ if (Target) {
+ target = area->GetActorByGlobalID(Target);
+ if (!target) return NULL;
+ Actor *original = area->GetActorByGlobalID(Caster);
+ if (original==target) {
+ effects->SetOwner(target);
+ return target;
+ }
+ int res = effects->CheckImmunity ( target );
+ //resisted
+ if (!res) {
+ return NULL;
+ }
+ if (res==-1) {
+ Target = original->GetGlobalID();
+ return NULL;
+ }
+ effects->SetOwner(original);
+ return target;
+ }
+ target = area->GetActorByGlobalID(Caster);
+ if (target) {
+ effects->SetOwner(target);
+ }
+ return target;
+}
+
+void Projectile::SetDelay(int delay)
+{
+ extension_delay=delay;
+ ExtFlags|=PEF_FREEZE;
+}
+
+//copied from Actor.cpp
+#define ATTACKROLL 20
+#define WEAPON_FIST 0
+
+bool Projectile::FailedIDS(Actor *target) const
+{
+ bool fail = !EffectQueue::match_ids( target, IDSType, IDSValue);
+ if (ExtFlags&PEF_NOTIDS) {
+ fail = !fail;
+ }
+ if (ExtFlags&PEF_BOTH) {
+ if (!fail) {
+ fail = !EffectQueue::match_ids( target, IDSType2, IDSValue2);
+ if (ExtFlags&PEF_NOTIDS2) {
+ fail = !fail;
+ }
+ }
+ }
+ else
+ {
+ if (fail && IDSType2) {
+ fail = !EffectQueue::match_ids( target, IDSType2, IDSValue2);
+ if (ExtFlags&PEF_NOTIDS2) {
+ fail = !fail;
+ }
+ }
+ }
+
+ if (!fail) {
+ if(ExtFlags&PEF_TOUCH) {
+ Actor *caster = core->GetGame()->GetActorByGlobalID(Caster);
+ if (caster) {
+ //TODO move this to Actor
+ //TODO some projectiles use melee attack (fist), others use projectile attack
+ //this apparently depends on the spell's spell form (normal vs. projectile)
+ int roll = caster->LuckyRoll(1, ATTACKROLL, 0);
+ if (roll==1) {
+ return true; //critical failure
+ }
+
+ if (!(target->GetStat(IE_STATE_ID)&STATE_CRIT_PROT)) {
+ if (roll >= (ATTACKROLL - (int) caster->GetStat(IE_CRITICALHITBONUS))) {
+ return false; //critical success
+ }
+ }
+
+ //handle attack type here, weapon depends on it too?
+ int tohit = caster->GetToHit(0, WEAPON_FIST, target);
+ //damage type, should be generic?
+ int defense = target->GetDefense(0, caster);
+ if(target->IsReverseToHit()) {
+ fail = roll + defense < tohit;
+ } else {
+ fail = tohit + roll < defense;
+ }
+ }
+ }
+ }
+
+ return fail;
+}
+
+void Projectile::Payload()
+{
+ Actor *target;
+
+ if(Shake) {
+ core->timer->SetScreenShake( Shake, Shake, Shake);
+ Shake = 0;
+ }
+
+ //allow area affecting projectile with a spell
+ if(!(effects || (!Target && FailSpell[0]))) {
+ return;
+ }
+
+ if (Target) {
+ target = GetTarget();
+ if (!target && (Target==Caster)) {
+ //projectile rebounced
+ return;
+ }
+ } else {
+ //the target will be the original caster
+ //in case of single point area target (dimension door)
+ if (FakeTarget) {
+ target = area->GetActorByGlobalID(FakeTarget);
+ if (!target) {
+ target = core->GetGame()->GetActorByGlobalID(FakeTarget);
+ }
+ } else {
+ target = area->GetActorByGlobalID(Caster);
+ }
+ Actor *source = area->GetActorByGlobalID(Caster);
+ if (effects) {
+ if (source) {
+ effects->SetOwner(source);
+ } else {
+ effects->SetOwner(target);
+ }
+ }
+ }
+
+ if (target) {
+ //apply this spell on target when the projectile fails
+ if (FailedIDS(target)) {
+ if (FailSpell[0]) {
+ if (Target) {
+ core->ApplySpell(FailSpell, target, effects->GetOwner(), Level);
+ } else {
+ //no Target, using the fake target as owner
+ core->ApplySpellPoint(FailSpell, area, Destination, target, Level);
+ }
+ }
+ } else {
+ //apply this spell on the target when the projectile succeeds
+ if (SuccSpell[0]) {
+ core->ApplySpell(SuccSpell, target, effects->GetOwner(), Level);
+ }
+
+ if(ExtFlags&PEF_RGB) {
+ target->SetColorMod(0xff, RGBModifier::ADD, ColorSpeed,
+ RGB >> 8, RGB >> 16, RGB >> 24);
+ }
+
+ effects->AddAllEffects(target, Destination);
+ }
+ }
+
+ delete effects;
+ effects = NULL;
+}
+
+void Projectile::ApplyDefault()
+{
+ Actor *actor = area->GetActorByGlobalID(Caster);
+ if (actor) {
+ //name is the projectile's name
+ //for simplicity, we apply a spell of the same name
+ core->ApplySpell(name, actor, actor, Level);
+ }
+}
+
+void Projectile::StopSound()
+{
+ if (travel_handle) {
+ travel_handle->Stop();
+ travel_handle.release();
+ }
+}
+
+void Projectile::UpdateSound()
+{
+ if (!(SFlags&PSF_SOUND2)) {
+ StopSound();
+ }
+ if (!travel_handle || !travel_handle->Playing()) {
+ travel_handle = core->GetAudioDrv()->Play(SoundRes2, Pos.x, Pos.y, (SFlags & PSF_LOOPING2 ? GEM_SND_LOOPING : 0));
+ SFlags|=PSF_SOUND2;
+ }
+}
+
+//control the phase change when the projectile reached its target
+//possible actions: vanish, hover over point, explode
+//depends on the area extension
+//play explosion sound
+void Projectile::ChangePhase()
+{
+ if (Target) {
+ Actor *target = area->GetActorByGlobalID(Target);
+ if (!target) {
+ phase = P_EXPIRED;
+ return;
+ }
+ }
+
+ if (phase == P_TRAVEL) {
+ if ((ExtFlags&PEF_DELAY) && extension_delay) {
+ extension_delay--;
+ UpdateSound();
+ return;
+ }
+ }
+
+ //reached target, and explodes now
+ if (!Extension) {
+ //there are no-effect projectiles, like missed arrows
+ //Payload can redirect the projectile in case of projectile reflection
+ if (phase ==P_TRAVEL) {
+ if(ExtFlags&PEF_DEFSPELL) {
+ ApplyDefault();
+ }
+ StopSound();
+ Payload();
+ phase = P_TRAVEL2;
+ }
+ //freeze on target, this is recommended only for child projectiles
+ //as the projectile won't go away on its own
+ if(ExtFlags&PEF_FREEZE) {
+ if(extension_delay) {
+ if (extension_delay>0) {
+ extension_delay--;
+ UpdateSound();
+ }
+ return;
+ }
+ }
+
+ if (phase == P_TRAVEL2) {
+ if (extension_delay) {
+ extension_delay--;
+ return;
+ }
+ }
+
+ if(ExtFlags&PEF_FADE) {
+ TFlags &= ~PTF_TINT; //turn off area tint
+ tint.a--;
+ if(tint.a>0) {
+ return;
+ }
+ }
+ }
+
+ EndTravel();
+}
+
+//Call this only if Extension exists!
+int Projectile::CalculateExplosionCount()
+{
+ int count = 0;
+ Actor *act = area->GetActorByGlobalID(Caster);
+ if(act) {
+ if (Extension->AFlags&PAF_LEV_MAGE) {
+ count = act->GetMageLevel();
+ }
+ else if (Extension->AFlags&PAF_LEV_CLERIC) {
+ count = act->GetClericLevel();
+ }
+ }
+
+ if (!count) {
+ count = Extension->ExplosionCount;
+ }
+ if (!count) {
+ count = 1;
+ }
+ return count;
+}
+
+void Projectile::EndTravel()
+{
+ StopSound();
+ UpdateSound();
+ if(!Extension) {
+ phase = P_EXPIRED;
+ return;
+ }
+
+ //this flag says that the explosion should occur only when triggered
+ if (Extension->AFlags&PAF_TRIGGER) {
+ phase = P_TRIGGER;
+ return;
+ } else {
+ phase = P_EXPLODING1;
+ }
+}
+
+int Projectile::AddTrail(ieResRef BAM, const ieByte *pal)
+{
+ ScriptedAnimation *sca=gamedata->GetScriptedAnimation(BAM,0);
+ if (!sca) return 0;
+ if(pal) {
+ if (ExtFlags & PEF_TINT) {
+ Color tmpColor[PALSIZE];
+
+ core->GetPalette( pal[0], PALSIZE, tmpColor );
+ sca->Tint = tmpColor[PALSIZE/2];
+ sca->Transparency |= BLIT_TINTED;
+ } else {
+ for(int i=0;i<7;i++) {
+ sca->SetPalette(pal[i], 4+i*PALSIZE);
+ }
+ }
+ }
+ sca->SetOrientation(Orientation);
+ sca->PlayOnce();
+ sca->SetBlend();
+ sca->XPos += Pos.x;
+ sca->YPos += Pos.y;
+ area->AddVVCell(sca);
+ return sca->GetSequenceDuration(AI_UPDATE_TIME);
+}
+
+void Projectile::DoStep(unsigned int walk_speed)
+{
+ if(pathcounter) {
+ pathcounter--;
+ } else {
+ ClearPath();
+ }
+
+ //intro trailing, drawn only once at the beginning
+ if (pathcounter==0x7ffe) {
+ for(int i=0;i<3;i++) {
+ if(!TrailSpeed[i] && TrailBAM[i][0]) {
+ extension_delay = AddTrail(TrailBAM[i], (ExtFlags&PEF_TINT)?Gradients:NULL);
+ }
+ }
+ }
+
+ if (!path) {
+ ChangePhase();
+ return;
+ }
+
+ if (Pos==Destination) {
+ ClearPath();
+ ChangePhase();
+ return;
+ }
+
+ //don't bug out on 0 smoke frequency like the original IE
+ if ((TFlags&PTF_SMOKE) && SmokeSpeed) {
+ if(!(pathcounter%SmokeSpeed)) {
+ AddTrail(smokebam, SmokeGrad);
+ }
+ }
+
+ for(int i=0;i<3;i++) {
+ if(TrailSpeed[i] && !(pathcounter%TrailSpeed[i])) {
+ AddTrail(TrailBAM[i], (ExtFlags&PEF_TINT)?Gradients:NULL);
+ }
+ }
+
+ if (ExtFlags&PEF_LINE) {
+ if(Extension) {
+ //transform into an explosive line
+ EndTravel();
+ } else {
+ if(!(ExtFlags&PEF_FREEZE) && travel[0]) {
+ //switch to 'fading' phase
+ //SetDelay(travel[0]->GetFrameCount());
+ SetDelay(100);
+ }
+ ChangePhase();
+ }
+ //don't change position
+ return;
+ }
+
+ //path won't be calculated if speed==0
+ walk_speed=1500/walk_speed;
+ ieDword time = core->GetGame()->Ticks;
+ if (!step) {
+ step = path;
+ }
+ while (step->Next && (( time - timeStartStep ) >= walk_speed)) {
+ step = step->Next;
+ if (!walk_speed) {
+ timeStartStep = time;
+ break;
+ }
+ timeStartStep = timeStartStep + walk_speed;
+ }
+
+ SetOrientation (step->orient, false);
+
+ Pos.x=step->x;
+ Pos.y=step->y;
+ if (travel_handle) {
+ travel_handle->SetPos(Pos.x, Pos.y);
+ }
+ if (!step->Next) {
+ ClearPath();
+ NewOrientation = Orientation;
+ ChangePhase();
+ return;
+ }
+ if (!walk_speed) {
+ return;
+ }
+
+ if (SFlags&PSF_SPARKS) {
+ drawSpark = 1;
+ }
+
+ if (step->Next->x > step->x)
+ Pos.x += ( unsigned short )
+ ( ( step->Next->x - Pos.x ) * ( time - timeStartStep ) / walk_speed );
+ else
+ Pos.x -= ( unsigned short )
+ ( ( Pos.x - step->Next->x ) * ( time - timeStartStep ) / walk_speed );
+ if (step->Next->y > step->y)
+ Pos.y += ( unsigned short )
+ ( ( step->Next->y - Pos.y ) * ( time - timeStartStep ) / walk_speed );
+ else
+ Pos.y -= ( unsigned short )
+ ( ( Pos.y - step->Next->y ) * ( time - timeStartStep ) / walk_speed );
+
+}
+
+void Projectile::SetCaster(ieDword caster, int level)
+{
+ Caster=caster;
+ Level=level;
+}
+
+ieDword Projectile::GetCaster() const
+{
+ return Caster;
+}
+
+void Projectile::NextTarget(const Point &p)
+{
+ ClearPath();
+ Destination = p;
+ //call this with destination
+ if (path) {
+ return;
+ }
+ if (!Speed) {
+ Pos = Destination;
+ return;
+ }
+ NewOrientation = Orientation = GetOrient(Destination, Pos);
+
+ //this hack ensures that the projectile will go away after its time
+ //by the time it reaches this part, it was already expired, so Target
+ //needs to be cleared.
+ if(ExtFlags&PEF_NO_TRAVEL) {
+ Target = 0;
+ Destination = Pos;
+ return;
+ }
+ path = area->GetLine( Pos, Destination, Speed, Orientation, GL_PASS );
+}
+
+void Projectile::SetTarget(const Point &p)
+{
+ Target = 0;
+ NextTarget(p);
+}
+
+void Projectile::SetTarget(ieDword tar, bool fake)
+{
+ Actor *target = NULL;
+
+ if (fake) {
+ Target = 0;
+ FakeTarget = tar;
+ return;
+ } else {
+ Target = tar;
+ target = area->GetActorByGlobalID(tar);
+ }
+
+ if (!target) {
+ phase = P_EXPIRED;
+ return;
+ }
+ //replan the path in case the target moved
+ if(target->Pos!=Destination) {
+ NextTarget(target->Pos);
+ return;
+ }
+
+ //replan the path in case the source moved (only for line projectiles)
+ if(ExtFlags&PEF_LINE) {
+ Actor *c = area->GetActorByGlobalID(Caster);
+ if(c && c->Pos!=Pos) {
+ Pos=c->Pos;
+ NextTarget(target->Pos);
+ }
+ }
+}
+
+void Projectile::MoveTo(Map *map, const Point &Des)
+{
+ area = map;
+ Origin = Des;
+ Pos = Des;
+ Destination = Des;
+}
+
+void Projectile::ClearPath()
+{
+ PathNode* thisNode = path;
+ while (thisNode) {
+ PathNode* nextNode = thisNode->Next;
+ delete( thisNode );
+ thisNode = nextNode;
+ }
+ path = NULL;
+ step = NULL;
+}
+
+int Projectile::CalculateTargetFlag()
+{
+ //if there are any, then change phase to exploding
+ int flags = GA_NO_DEAD;
+
+ if (Extension->AFlags&PAF_NO_WALL) {
+ flags|=GA_NO_LOS;
+ }
+
+ //projectiles don't affect dead/inanimate normally
+ if (Extension->AFlags&PAF_INANIMATE) {
+ flags&=~GA_NO_DEAD;
+ }
+
+ //affect only enemies or allies
+ switch (Extension->AFlags&PAF_TARGET) {
+ case PAF_ENEMY:
+ flags|=GA_NO_NEUTRAL|GA_NO_ALLY;
+ break;
+ case PAF_PARTY: //this doesn't exist in IE
+ flags|=GA_NO_ENEMY;
+ break;
+ case PAF_TARGET:
+ flags|=GA_NO_NEUTRAL|GA_NO_ENEMY;
+ break;
+ default:
+ return flags;
+ }
+
+ //this is the only way to affect neutrals and enemies
+ if (Extension->APFlags&APF_INVERT_TARGET) {
+ flags^=(GA_NO_ALLY|GA_NO_ENEMY);
+ }
+
+ Actor *caster = area->GetActorByGlobalID(Caster);
+ if (caster && ((Actor *) caster)->GetStat(IE_EA)<EA_GOODCUTOFF) {
+ return flags;
+ }
+
+ return flags^(GA_NO_ALLY|GA_NO_ENEMY);
+}
+
+//get actors covered in area of trigger radius
+void Projectile::CheckTrigger(unsigned int radius)
+{
+ if (phase == P_TRIGGER) {
+ //special trigger flag, explode only if the trigger animation has
+ //passed a hardcoded sequence number
+ if (Extension->AFlags&PAF_TRIGGER_D) {
+ if (travel[Orientation]) {
+ int anim = travel[Orientation]->GetCurrentFrame();
+ if (anim<30)
+ return;
+ }
+ }
+ }
+ if (area->GetActorInRadius(Pos, CalculateTargetFlag(), radius)) {
+ if (phase == P_TRIGGER) {
+ phase = P_EXPLODING1;
+ extension_delay = Extension->Delay;
+ }
+ } else if (phase == P_EXPLODING1) {
+ //the explosion is revoked
+ if (Extension->AFlags&PAF_SYNC) {
+ phase = P_TRIGGER;
+ }
+ }
+}
+
+void Projectile::SetEffectsCopy(EffectQueue *eq)
+{
+ if(effects) delete effects;
+ if(!eq) {
+ effects=NULL;
+ return;
+ }
+ effects = eq->CopySelf();
+}
+
+void Projectile::LineTarget()
+{
+ if(!effects) {
+ return;
+ }
+
+ Actor *original = area->GetActorByGlobalID(Caster);
+ Actor *prev = NULL;
+ PathNode *iter = path;
+ while(iter) {
+ Point pos(iter->x,iter->y);
+ Actor *target = area->GetActorInRadius(pos, CalculateTargetFlag(), 1);
+ if (target && target->GetGlobalID()!=Caster && prev!=target) {
+ prev = target;
+ int res = effects->CheckImmunity ( target );
+ if (res>0) {
+ EffectQueue *eff = effects->CopySelf();
+ eff->SetOwner(original);
+ if(ExtFlags&PEF_RGB) {
+ target->SetColorMod(0xff, RGBModifier::ADD, ColorSpeed,
+ RGB >> 8, RGB >> 16, RGB >> 24);
+ }
+
+ eff->AddAllEffects(target, target->Pos);
+ }
+ }
+ iter = iter->Next;
+ }
+}
+
+//secondary projectiles target all in the explosion radius
+void Projectile::SecondaryTarget()
+{
+ //fail will become true if the projectile utterly failed to find a target
+ //if the spell was already applied on explosion, ignore this
+ bool fail= !!(Extension->APFlags&APF_SPELLFAIL) && !(ExtFlags&PEF_DEFSPELL);
+ int mindeg = 0;
+ int maxdeg = 0;
+
+ //the AOE (area of effect) is cone shaped
+ if (Extension->AFlags&PAF_CONE) {
+ mindeg=(Orientation*45-Extension->ConeWidth)/2;
+ maxdeg=mindeg+Extension->ConeWidth;
+ }
+
+ int radius = Extension->ExplosionRadius;
+ Actor **actors = area->GetAllActorsInRadius(Pos, CalculateTargetFlag(), radius);
+ Actor **poi=actors;
+
+ if (Extension->DiceCount) {
+ //precalculate the maximum affected target count in case of PAF_AFFECT_ONE
+ extension_targetcount = core->Roll(Extension->DiceCount, Extension->DiceSize, 0);
+ } else {
+ //this is the default case (for original engine)
+ extension_targetcount = 1;
+ }
+
+ while(*poi) {
+ ieDword Target = (*poi)->GetGlobalID();
+
+ //this flag is actually about ignoring the caster (who is at the center)
+ if ((SFlags & PSF_IGNORE_CENTER) && (Caster==Target)) {
+ poi++;
+ continue;
+ }
+
+ //IDS targeting for area projectiles
+ if (FailedIDS(*poi)) {
+ poi++;
+ continue;
+ }
+
+ if (Extension->AFlags&PAF_CONE) {
+ //cone never affects the caster
+ if(Caster==Target) {
+ poi++;
+ continue;
+ }
+ double xdiff = (*poi)->Pos.x-Pos.x;
+ double ydiff = Pos.y-(*poi)->Pos.y;
+ int deg;
+
+ //fixme: a dragon will definitely be easier to hit than a mouse
+ //nothing checks on the personal space of the possible target
+
+ //unsigned int dist = (unsigned int) sqrt(xdiff*xdiff+ydiff*ydiff);
+ //int width = (*poi)->GetAnims()->GetCircleSize();
+
+ if (ydiff) {
+ deg = (int) (atan(xdiff/ydiff)*180/M_PI);
+ if(ydiff>0) deg+=180;
+ } else {
+ if (xdiff<0) deg=90;
+ else deg = 270;
+ }
+
+ //not in the right sector of circle
+ if (mindeg>deg || maxdeg<deg) {
+ poi++;
+ continue;
+ }
+ }
+
+ Projectile *pro = server->GetProjectileByIndex(Extension->ExplProjIdx);
+ pro->SetEffectsCopy(effects);
+ //copy the additional effects reference to the child projectile
+ //but only when there is a spell to copy
+ if (SuccSpell[0])
+ memcpy(pro->SuccSpell, SuccSpell, sizeof(ieResRef) );
+ pro->SetCaster(Caster, Level);
+ //this is needed to apply the success spell on the center point
+ pro->SetTarget(Pos);
+ //TODO:actually some of the splash projectiles are a good example of faketarget
+ //projectiles (that don't follow the target, but still hit)
+ area->AddProjectile(pro, Pos, Target, false);
+
+ poi++;
+ fail=false;
+
+ //we already got one target affected in the AOE, this flag says
+ //that was enough (the GemRB extension can repeat this a random time (x d y)
+ if(Extension->AFlags&PAF_AFFECT_ONE) {
+ if (extension_targetcount<=0) {
+ break;
+ }
+ //if target counting is per HD and this target is an actor, use the xp level field
+ //otherwise count it as one
+ if ((Extension->APFlags&APF_COUNT_HD) && ((*poi)->Type==ST_ACTOR) ) {
+ Actor *actor = (Actor *) *poi;
+ extension_targetcount-= actor->GetXPLevel(true);
+ } else {
+ extension_targetcount--;
+ }
+ }
+ }
+ free(actors);
+
+ //In case of utter failure, apply a spell of the same name on the caster
+ //this feature is used by SCHARGE, PRTL_OP and PRTL_CL in the HoW pack
+ if(fail) {
+ ApplyDefault();
+ }
+}
+
+int Projectile::Update()
+{
+ //if reached target explode
+ //if target doesn't exist expire
+ if (phase == P_EXPIRED) {
+ return 0;
+ }
+ if (phase == P_UNINITED) {
+ Setup();
+ }
+
+ int pause = core->IsFreezed();
+ if (pause) {
+ return 1;
+ }
+ //recreate path if target has moved
+ if(Target) {
+ SetTarget(Target, false);
+ }
+
+ if (phase == P_TRAVEL || phase == P_TRAVEL2) {
+ DoStep(Speed);
+ }
+ return 1;
+}
+
+void Projectile::Draw(const Region &screen)
+{
+ switch (phase) {
+ case P_UNINITED:
+ return;
+ case P_TRIGGER: case P_EXPLODING1:case P_EXPLODING2:
+ //This extension flag is to enable the travel projectile at
+ //trigger/explosion time
+ if (Extension->AFlags&PAF_VISIBLE) {
+ //if (!Extension || (Extension->AFlags&PAF_VISIBLE)) {
+ DrawTravel(screen);
+ }
+ /*
+ if (!Extension) {
+ return;
+ }*/
+ CheckTrigger(Extension->TriggerRadius);
+ if (phase == P_EXPLODING1 || phase == P_EXPLODING2) {
+ DrawExplosion(screen);
+ }
+ break;
+ case P_TRAVEL: case P_TRAVEL2:
+ //There is no Extension for simple traveling projectiles!
+ DrawTravel(screen);
+ return;
+ default:
+ DrawExploded(screen);
+ return;
+ }
+}
+
+bool Projectile::DrawChildren(const Region &screen)
+{
+ bool drawn = false;
+
+ if (children) {
+ for(int i=0;i<child_size;i++){
+ if(children[i]) {
+ if (children[i]->Update()) {
+ children[i]->DrawTravel(screen);
+ drawn = true;
+ } else {
+ delete children[i];
+ children[i]=NULL;
+ }
+ }
+ }
+ }
+
+ return drawn;
+}
+
+//draw until all children expire
+void Projectile::DrawExploded(const Region &screen)
+{
+ if (DrawChildren(screen)) {
+ return;
+ }
+ phase = P_EXPIRED;
+}
+
+void Projectile::SpawnFragment(Point &dest)
+{
+ Projectile *pro = server->GetProjectileByIndex(Extension->FragProjIdx);
+ if (pro) {
+ if (Extension->AFlags&PAF_SECONDARY) {
+ pro->SetEffectsCopy(effects);
+ }
+ pro->SetCaster(Caster, Level);
+ if (pro->ExtFlags&PEF_RANDOM) {
+ dest.x+=core->Roll(1,Extension->TileX, -Extension->TileX/2);
+ dest.y+=core->Roll(1,Extension->TileY, -Extension->TileY/2);
+ }
+ area->AddProjectile(pro, dest, dest);
+ }
+}
+
+void Projectile::DrawExplosion(const Region &screen)
+{
+ //This seems to be a needless safeguard
+ if (!Extension) {
+ phase = P_EXPIRED;
+ return;
+ }
+
+ StopSound();
+ DrawChildren(screen);
+
+ int pause = core->IsFreezed();
+ if (pause) {
+ return;
+ }
+
+ //Delay explosion, it could even be revoked with PAF_SYNC (see skull trap)
+ if (extension_delay) {
+ extension_delay--;
+ return;
+ }
+
+ //0 and 1 have the same effect (1 explosion)
+ if (extension_explosioncount) {
+ extension_explosioncount--;
+ }
+
+ //Line targets are actors between source and destination point
+ if(ExtFlags&PEF_LINE) {
+ if (Target) {
+ SetTarget(Target, false);
+ }
+ LineTarget();
+ }
+
+ int apflags = Extension->APFlags;
+ int aoeflags = Extension->AFlags;
+
+ //no idea what is PAF_SECONDARY
+ //probably it is to alter some behaviour in the secondary
+ //projectile generation
+ //In trapskul.pro it isn't set, yet it has a secondary (invisible) projectile
+ //All area effects are created by secondary projectiles
+
+ //the secondary projectile will target everyone in the area of effect
+ SecondaryTarget();
+
+ //draw fragment graphics animation at the explosion center
+ if (aoeflags&PAF_FRAGMENT) {
+ //there is a character animation in the center of the explosion
+ //which will go towards the edges (flames, etc)
+ //Extension->ExplColor fake color for single shades (blue,green,red flames)
+ //Extension->FragAnimID the animation id for the character animation
+ //This color is not used in the original game
+ Point pos = Pos;
+ pos.x+=screen.x;
+ pos.y+=screen.y;
+ area->Sparkle(0, Extension->ExplColor, SPARKLE_EXPLOSION, pos, Extension->FragAnimID, GetZPos());
+ }
+
+ if(Shake) {
+ core->timer->SetScreenShake( Shake, Shake, Shake);
+ Shake = 0;
+ }
+
+ //the center of the explosion could be another projectile played over the target
+ //warning: this projectile doesn't inherit any effects, so its payload function
+ //won't be doing anything (any effect of PAF_SECONDARY?)
+
+ if (Extension->FragProjIdx) {
+ if (apflags&APF_TILED) {
+ int i,j;
+ int radius = Extension->ExplosionRadius;
+
+ for (i=-radius;i<radius;i+=Extension->TileX) {
+ for(j=-radius;j<radius;j+=Extension->TileY) {
+ if (i*i+j*j<radius*radius) {
+ Point p(Pos.x+i, Pos.y+j);
+ SpawnFragment(p);
+ }
+ }
+ }
+ } else {
+ SpawnFragment(Pos);
+ }
+ }
+
+ //the center of the explosion is based on hardcoded explosion type (this is fireball.cpp in the original engine)
+ //these resources are listed in areapro.2da and served by ProjectileServer.cpp
+
+ //draw it only once, at the time of explosion
+ if (phase==P_EXPLODING1) {
+ core->GetAudioDrv()->Play(Extension->SoundRes, Pos.x, Pos.y);
+ //play VVC in center
+ if (aoeflags&PAF_VVC) {
+ ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(Extension->VVCRes, false);
+ if (vvc) {
+ if (apflags & APF_VVCPAL) {
+ //if the palette is used as tint (as opposed to clown colorset) tint the vvc
+ if (apflags & APF_TINT) {
+ Color tmpColor[PALSIZE];
+
+ core->GetPalette( Extension->ExplColor, PALSIZE, tmpColor );
+ vvc->Tint = tmpColor[PALSIZE/2];
+ vvc->Transparency |= BLIT_TINTED;
+ } else {
+ vvc->SetPalette(Extension->ExplColor);
+ }
+ }
+ //if the trail oriented, then the center is oriented too
+ if (ExtFlags&PEF_TRAIL) {
+ vvc->SetOrientation(Orientation);
+ }
+ vvc->XPos+=Pos.x;
+ vvc->YPos+=Pos.y;
+ vvc->PlayOnce();
+ vvc->SetBlend();
+ area->AddVVCell(vvc);
+ }
+ }
+
+ phase=P_EXPLODING2;
+ } else {
+ core->GetAudioDrv()->Play(Extension->AreaSound, Pos.x, Pos.y);
+ }
+
+ //the spreading animation is in the first column
+ const char *tmp = Extension->Spread;
+ if(tmp[0]) {
+ //i'm unsure about the need of this
+ //returns if the explosion animation is fake coloured
+ if (!children) {
+ child_size = (Extension->ExplosionRadius+15)/16;
+ //more sprites if the whole area needs to be filled
+ if (apflags&APF_FILL) child_size*=2;
+ if (apflags&APF_SPREAD) child_size*=2;
+ if (apflags&APF_BOTH) child_size/=2; //intentionally decreases
+ if (apflags&APF_MORE) child_size*=2;
+ children = (Projectile **) calloc(sizeof(Projectile *), child_size);
+ }
+
+ //zero cone width means single line area of effect
+ if((aoeflags&PAF_CONE) && !Extension->ConeWidth) {
+ child_size = 1;
+ }
+
+ int initial = child_size;
+
+ for(int i=0;i<initial;i++) {
+ //leave this slot free, it is residue from the previous flare up
+ if (children[i])
+ continue;
+ if(apflags&APF_BOTH) {
+ if(rand()&1) {
+ tmp = Extension->Secondary;
+ } else {
+ tmp = Extension->Spread;
+ }
+ }
+ //create a custom projectile with single traveling effect
+ Projectile *pro = server->CreateDefaultProjectile((unsigned int) ~0);
+ strnlwrcpy(pro->BAMRes1, tmp, 8);
+ if (ExtFlags&PEF_TRAIL) {
+ pro->Aim = Aim;
+ }
+ pro->SetEffects(NULL);
+ //calculate the child projectile's target point, it is either
+ //a perimeter or an inside point of the explosion radius
+ int rad = Extension->ExplosionRadius;
+ Point newdest;
+
+ if (apflags&APF_FILL) {
+ rad=core->Roll(1,rad,0);
+ }
+ int max = 360;
+ int add = 0;
+ if (aoeflags&PAF_CONE) {
+ max=Extension->ConeWidth;
+ add=(Orientation*45-max)/2;
+ }
+ max=core->Roll(1,max,add);
+ double degree=max*M_PI/180;
+ newdest.x = (int) -(rad * sin(degree) );
+ newdest.y = (int) (rad * cos(degree) );
+
+ //these fields and flags are always inherited by all children
+ pro->Speed = Speed;
+ pro->ExtFlags = ExtFlags&(PEF_HALFTRANS|PEF_CYCLE|PEF_RGB);
+ pro->RGB = RGB;
+ pro->ColorSpeed = ColorSpeed;
+
+ if (apflags&APF_FILL) {
+ int delay;
+
+ //a bit of difference in case crowding is needed
+ //make this a separate flag if speed difference
+ //is not always wanted
+ pro->Speed-=rand()&7;
+
+ delay = Extension->Delay*extension_explosioncount;
+ if(apflags&APF_BOTH) {
+ if (delay) {
+ delay = rand()%delay;
+ }
+ }
+ //this needs to be commented out for ToB horrid wilting
+ //if(ExtFlags&PEF_FREEZE) {
+ delay += Extension->Delay;
+ //}
+ pro->SetDelay(delay);
+ }
+
+ newdest.x+=Destination.x;
+ newdest.y+=Destination.y;
+
+ if (apflags&APF_SCATTER) {
+ pro->MoveTo(area, newdest);
+ } else {
+ pro->MoveTo(area, Pos);
+ }
+ pro->SetTarget(newdest);
+ pro->autofree=true;
+
+ //sets up the gradient color for the explosion animation
+ if (apflags&(APF_PALETTE|APF_TINT) ) {
+ pro->SetGradient(Extension->ExplColor, !(apflags&APF_PALETTE));
+ }
+ //i'm unsure if we need blending for all anims or just the tinted ones
+ pro->TFlags|=PTF_BLEND;
+ //random frame is needed only for some of these, make it an areapro flag?
+ if( !(ExtFlags&PEF_CYCLE) || (ExtFlags&PEF_RANDOM) ) {
+ pro->ExtFlags|=PEF_RANDOM;
+ }
+ pro->Setup();
+ children[i]=pro;
+ }
+ }
+
+ if (extension_explosioncount) {
+ extension_delay=Extension->Delay;
+ } else {
+ phase = P_EXPLODED;
+ }
+}
+
+int Projectile::GetTravelPos(int face) const
+{
+ if (travel[face]) {
+ return travel[face]->GetCurrentFrame();
+ }
+ return 0;
+}
+
+int Projectile::GetShadowPos(int face) const
+{
+ if (shadow[face]) {
+ return shadow[face]->GetCurrentFrame();
+ }
+ return 0;
+}
+
+void Projectile::SetPos(int face, int frame1, int frame2)
+{
+ if (travel[face]) {
+ travel[face]->SetPos(frame1);
+ }
+ if (shadow[face]) {
+ shadow[face]->SetPos(frame2);
+ }
+}
+
+//recalculate target and source points (perpendicular bisector)
+void Projectile::SetupWall()
+{
+ Point p1, p2;
+
+ p1.x=(Pos.x+Destination.x)/2;
+ p1.y=(Pos.y+Destination.y)/2;
+
+ p2.x=p1.x+(Pos.y-Destination.y);
+ p2.y=p1.y+(Pos.x-Destination.x);
+ Pos=p1;
+ SetTarget(p2);
+}
+
+void Projectile::DrawLine(const Region &screen, int face, ieDword flag)
+{
+ Video *video = core->GetVideoDriver();
+ PathNode *iter = path;
+ Sprite2D *frame = travel[face]->NextFrame();
+ while(iter) {
+ Point pos(iter->x, iter->y);
+
+ if (SFlags&PSF_FLYING) {
+ pos.y-=FLY_HEIGHT;
+ }
+ pos.x+=screen.x;
+ pos.y+=screen.y;
+
+ video->BlitGameSprite( frame, pos.x, pos.y, flag, tint, NULL, palette, &screen);
+ iter = iter->Next;
+ }
+}
+
+void Projectile::DrawTravel(const Region &screen)
+{
+ Video *video = core->GetVideoDriver();
+ ieDword flag;
+
+ if(ExtFlags&PEF_HALFTRANS) {
+ flag=BLIT_HALFTRANS;
+ } else {
+ flag = 0;
+ }
+
+ //static tint (use the tint field)
+ if(ExtFlags&PEF_TINT) {
+ flag|=BLIT_TINTED;
+ }
+
+ //Area tint
+ if (TFlags&PTF_TINT) {
+ tint = area->LightMap->GetPixel( Pos.x / 16, Pos.y / 12);
+ flag |= BLIT_TINTED;
+ }
+
+ unsigned int face = GetNextFace();
+ if (face!=Orientation) {
+ SetPos(face, GetTravelPos(face), GetShadowPos(face));
+ }
+ Point pos = Pos;
+ pos.x+=screen.x;
+ pos.y+=screen.y;
+
+ if(bend && phase == P_TRAVEL && Origin != Destination) {
+ double total_distance = Distance(Origin, Destination);
+ double travelled_distance = Distance(Origin, Pos);
+
+ // distance travelled along the line, from 0.0 to 1.0
+ double travelled = travelled_distance / total_distance;
+ assert(travelled <= 1.0);
+
+ // input to sin(): 0 to pi gives us an arc
+ double arc_angle = travelled * M_PI;
+
+ // calculate the distance between the arc and the current pos
+ // (this could use travelled and a larger constant multiplier,
+ // to make the arc size fixed rather than relative to the total
+ // distance to travel)
+ double length_of_normal = travelled_distance * sin(arc_angle) * 0.3 * ((bend / 2) + 1);
+ if (bend % 2) length_of_normal = -length_of_normal;
+
+ // adjust the to-be-rendered point by that distance
+ double x_vector = (Destination.x - Origin.x) / total_distance,
+ y_vector = (Destination.y - Origin.y) / total_distance;
+ Point newpos = pos;
+ newpos.x += (short)(y_vector*length_of_normal);
+ newpos.y -= (short)(x_vector*length_of_normal);
+ pos = newpos;
+ }
+
+ if (light) {
+ video->BlitGameSprite( light, pos.x, pos.y, 0, tint, NULL, NULL, &screen);
+ }
+
+ if (ExtFlags&PEF_POP) {
+ //draw pop in/hold/pop out animation sequences
+ Sprite2D *frame;
+
+ if(ExtFlags&PEF_UNPOP) {
+ frame = shadow[0]->NextFrame();
+ if(shadow[0]->endReached) {
+ ExtFlags&=~PEF_UNPOP;
+ }
+ } else {
+ frame = travel[0]->NextFrame();
+ if(travel[0]->endReached) {
+ travel[0]->playReversed=true;
+ travel[0]->SetPos(0);
+ ExtFlags|=PEF_UNPOP;
+ frame = shadow[0]->NextFrame();
+ }
+ }
+
+ video->BlitGameSprite( frame, pos.x, pos.y, flag, tint, NULL, palette, &screen);
+ return;
+ }
+
+ if (ExtFlags&PEF_LINE) {
+ DrawLine(screen, face, flag);
+ return;
+ }
+
+ if (shadow[face]) {
+ Sprite2D *frame = shadow[face]->NextFrame();
+ video->BlitGameSprite( frame, pos.x, pos.y, flag, tint, NULL, palette, &screen);
+ }
+
+ pos.y-=GetZPos();
+
+ if (ExtFlags&PEF_PILLAR) {
+ //draw all frames simultaneously on top of each other
+ for(int i=0;i<Aim;i++) {
+ if (travel[i]) {
+ Sprite2D *frame = travel[i]->NextFrame();
+ video->BlitGameSprite( frame, pos.x, pos.y, flag, tint, NULL, palette, &screen);
+ pos.y-=frame->YPos;
+ }
+ }
+ } else {
+ if (travel[face]) {
+ Sprite2D *frame = travel[face]->NextFrame();
+ video->BlitGameSprite( frame, pos.x, pos.y, flag, tint, NULL, palette, &screen);
+ }
+ }
+
+ if (drawSpark) {
+ area->Sparkle(0,SparkColor, SPARKLE_EXPLOSION, pos, 0, GetZPos() );
+ drawSpark = 0;
+ }
+
+}
+
+int Projectile::GetZPos() const
+{
+ return ZPos;
+}
+
+void Projectile::SetIdentifiers(const char *resref, ieWord id)
+{
+ strnuprcpy(name, resref, 8);
+ type=id;
+}
+
+bool Projectile::PointInRadius(const Point &p) const
+{
+ switch(phase) {
+ //better not trigger on projectiles unset/expired
+ case P_EXPIRED:
+ case P_UNINITED: return false;
+ case P_TRAVEL:
+ if(p.x==Pos.x && p.y==Pos.y) return true;
+ return false;
+ default:
+ if(p.x==Pos.x && p.y==Pos.y) return true;
+ if (!Extension) return false;
+ if (Distance(p,Pos)<Extension->ExplosionRadius) return true;
+ }
+ return false;
+}
+
+//Set gradient color, if type is true then it is static tint, otherwise it is paletted color
+void Projectile::SetGradient(int gradient, bool type)
+{
+ //gradients are unsigned chars, so this works
+ memset(Gradients, gradient, 7);
+ if (type) {
+ ExtFlags|=PEF_TINT;
+ } else {
+ TFlags |= PTF_COLOUR;
+ }
+}
+
+void Projectile::StaticTint(const Color &newtint)
+{
+ tint = newtint;
+ TFlags &= ~PTF_TINT; //turn off area tint
+}
+
+void Projectile::Cleanup()
+{
+ //neutralise the payload
+ delete effects;
+ effects = NULL;
+ //diffuse the projectile
+ phase=P_EXPIRED;
+}
+
diff --git a/gemrb/core/Projectile.h b/gemrb/core/Projectile.h
new file mode 100644
index 0000000..16e899d
--- /dev/null
+++ b/gemrb/core/Projectile.h
@@ -0,0 +1,406 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 Projectile.h
+ * Declares Projectile, class for supporting functionality of spell/item projectiles
+ * @author The GemRB Project
+ */
+
+
+#ifndef PROJECTILE_H
+#define PROJECTILE_H
+
+#include "exports.h"
+#include "ie_types.h"
+
+#include "CharAnimations.h" //contains MAX_ORIENT
+#include "Map.h"
+#include "Palette.h"
+#include "PathFinder.h"
+#include "Audio.h"
+
+//this is the height of the projectile when Spark Flag Fly = 1
+#define FLY_HEIGHT 50
+//this is supposed to move the projectile to the background
+#define BACK_DEPTH 50
+
+//projectile phases
+#define P_UNINITED -1
+#define P_TRAVEL 0 //projectile moves to target
+#define P_TRAVEL2 1 //projectile hit target
+#define P_TRIGGER 2 //projectile hovers over target, waits for trigger
+#define P_EXPLODING1 3 //projectile explosion spreads
+#define P_EXPLODING2 4 //projectile explosion repeats
+#define P_EXPLODED 5 //projectile spread over area
+#define P_EXPIRED 99 //projectile scheduled for removal (existing parts are still drawn)
+
+//projectile spark flags
+#define PSF_SPARKS 1
+#define PSF_FLYING 2
+#define PSF_LOOPING 4 //looping sound
+#define PSF_LOOPING2 8 //looping second sound
+#define PSF_IGNORE_CENTER 16
+//gemrb specific internal flag
+#define PSF_SOUND2 0x80000000//already started sound2
+
+//projectile travel flags
+#define PTF_COLOUR 1 //fake colours
+#define PTF_SMOKE 2 //has smoke
+#define PTF_TINT 8 //tint projectile
+#define PTF_SHADOW 32 //has shadow bam
+#define PTF_LIGHT 64 //has light shadow
+#define PTF_BLEND 128 //blend colours
+
+//projectile extended travel flags (gemrb specific)
+#define PEF_BOUNCE 1 //bounce from walls (lightning bolt)
+#define PEF_CONTINUE 2 //continue as a travel projectile after trigger (lightning bolt)
+//TODO: This can probably be replaced by an area projectile trigger (like skull trap, glyph)
+#define PEF_FREEZE 4 //stay around (ice dagger)
+#define PEF_NO_TRAVEL 8 //all instant projectiles (draw upon holy might, finger of death)
+#define PEF_TRAIL 16 //trail bams facing value uses the same field as the travel projectile (otherwise it defaults to 9) (shout in iwd)
+#define PEF_CURVE 32 //curved path (magic missile)
+#define PEF_RANDOM 64 //random starting frame for animation (?)
+#define PEF_PILLAR 128 //draw all cycles simultaneously on top of each other (call lightning, flamestrike)
+#define PEF_HALFTRANS 256 //half-transparency (holy might)
+#define PEF_TINT 512 //use palette gradient as tint
+#define PEF_ITERATION 1024 //create another projectile of type-1 (magic missiles)
+#define PEF_DEFSPELL 2048 //always apply the default spell on the caster
+#define PEF_FALLING 4096 //projectile falls down vertically (cow)
+#define PEF_INCOMING 8192 //projectile falls in on trajectory (comet)
+#define PEF_LINE 16384 //solid line between source and target (agannazar's scorcher)
+#define PEF_WALL 32768 //solid line in front of source, crossing target (wall of fire)
+#define PEF_BACKGROUND 0x10000 //draw under target,overrides flying (dimension door)
+#define PEF_POP 0x20000 //draw travel bam, then shadow, then travel bam backwards
+#define PEF_UNPOP 0x40000 //draw shadow, then travel bam (this is an internal flag)
+//TODO: The next flag is probably not needed, it is done by a separate area hit animation
+#define PEF_FADE 0x80000 //gradually fade on spot if used with PEF_FREEZE (ice dagger)
+#define PEF_TEXT 0x100000//display text during setup
+#define PEF_WANDERING 0x200000//random movement (no real path)
+#define PEF_CYCLE 0x400000//random cycle
+#define PEF_RGB 0x800000//rgb pulse on hit
+#define PEF_TOUCH 0x1000000//successful to hit roll needed
+#define PEF_NOTIDS 0x2000000//negate IDS check
+#define PEF_NOTIDS2 0x4000000//negate secondary IDS check
+#define PEF_BOTH 0x8000000//both IDS check must succeed
+#define PEF_DELAY 0x10000000//delay payload until travel projectile cycle ends
+
+//projectile area flags
+#define PAF_VISIBLE 1 //the travel projectile is visible until explosion
+#define PAF_INANIMATE 2 //target inanimates
+#define PAF_TRIGGER 4 //explosion needs to be triggered
+#define PAF_SYNC 8 //one explosion at a time
+#define PAF_SECONDARY 16 //secondary projectiles at explosion
+#define PAF_FRAGMENT 32 //fragments (charanimation) at explosion
+#define PAF_ENEMY 64 //target party or not party
+#define PAF_PARTY 128 //target party
+#define PAF_TARGET (64|128)
+#define PAF_LEV_MAGE 256
+#define PAF_LEV_CLERIC 512
+#define PAF_VVC 1024 //
+#define PAF_CONE 2048 //enable cone shape
+#define PAF_NO_WALL 0x1000 //pass through walls
+#define PAF_TRIGGER_D 0x2000 //delayed trigger (only if animation is over 30)
+#define PAF_DELAY 0x4000 //
+#define PAF_AFFECT_ONE 0x8000 //
+
+//area projectile flags (in areapro.2da)
+//this functionality was hardcoded in the original engine, so the bit flags are
+//completely arbitrary (i assign them as need arises)
+//child projectiles need to be tinted (example: stinking cloud, counter example: fireball)
+#define APF_TINT 1
+//child projectiles fill the whole area (example: stinking cloud, counter example: fireball)
+#define APF_FILL 2
+//child projectiles start in their destination (example: icestorm, counter example: fireball)
+#define APF_SCATTER 4
+//the explosion vvc has gradient (example: icestorm, counter example: fireball)
+#define APF_VVCPAL 8
+//there is an additional added scatter after the initial spreading ring
+#define APF_SPREAD 16
+//the spread projectile needs gradient colouring,not tint (example:web, counter example: stinking cloud)
+#define APF_PALETTE 32
+//use both animations in the spread
+#define APF_BOTH 64
+//more child projectiles
+#define APF_MORE 128
+//apply spell on caster if failed to find target
+#define APF_SPELLFAIL 256
+//multiple directions
+#define APF_MULTIDIR 512
+//target HD counting
+#define APF_COUNT_HD 1024
+//target flag enemy ally switched
+#define APF_INVERT_TARGET 2048
+//tiled AoE animation
+#define APF_TILED 4096
+
+struct ProjectileExtension
+{
+ ieDword AFlags;
+ ieWord TriggerRadius;
+ ieWord ExplosionRadius;
+ ieResRef SoundRes; //used for areapro.2da explosion sound
+ ieWord Delay;
+ ieWord FragAnimID;
+ ieWord FragProjIdx;
+ ieByte ExplosionCount;
+ ieByte ExplType;
+ ieWord ExplColor;
+ ieWord ExplProjIdx;
+ ieResRef VVCRes; //used for areapro.2da second resref (center animation)
+ ieWord ConeWidth;
+ //these are GemRB specific (from areapro.2da)
+ ieDword APFlags; //areapro.2da flags
+ ieResRef Spread; //areapro.2da first resref
+ ieResRef Secondary; //areapro.2da third resref
+ ieResRef AreaSound; //areapro.2da second sound resource
+ //used for target or HD counting
+ ieWord DiceCount;
+ ieWord DiceSize;
+ ieWord TileX;
+ ieWord TileY;
+};
+
+class GEM_EXPORT Projectile
+{
+public:
+ Projectile();
+ ~Projectile();
+ void InitExtension();
+
+ ieWord Speed;
+ ieDword SFlags;
+ ieResRef SoundRes1;
+ ieResRef SoundRes2;
+ ieResRef TravelVVC;
+ ieDword SparkColor;
+ ieDword ExtFlags;
+ ieDword StrRef;
+ ieDword RGB;
+ ieWord ColorSpeed;
+ ieWord Shake;
+ ieWord IDSType;
+ ieWord IDSValue;
+ ieWord IDSType2;
+ ieWord IDSValue2;
+ ieResRef FailSpell;
+ ieResRef SuccSpell;
+ ////// gap
+ ieDword TFlags;
+ ieResRef BAMRes1;
+ ieResRef BAMRes2;
+ ieByte Seq1, Seq2;
+ ieWord LightX;
+ ieWord LightY;
+ ieWord LightZ;
+ ieResRef PaletteRes;
+ ieByte Gradients[7];
+ ieByte SmokeSpeed;
+ ieByte SmokeGrad[7];
+ ieByte Aim;
+ ieWord SmokeAnimID;
+ ieResRef TrailBAM[3];
+ ieWord TrailSpeed[3];
+ //these are public but not in the .pro file
+ ProjectileExtension* Extension;
+ bool autofree;
+ Palette* palette;
+ //internals
+protected:
+ ieResRef smokebam;
+ ieDword timeStartStep;
+ //attributes from moveable object
+ unsigned char Orientation, NewOrientation;
+ PathNode* path; //whole path
+ PathNode* step; //actual step
+ //similar to normal actors
+ Map *area;
+ Point Pos;
+ int ZPos;
+ Point Destination;
+ Point Origin;
+ ieDword Caster; //the globalID of the caster actor
+ int Level; //the caster's level
+ ieDword Target; //the globalID of target actor
+ ieDword FakeTarget; //a globalID for target that isn't followed
+ int phase;
+ //saved in area
+ ieResRef name;
+ ieWord type;
+ //these come from the extension area
+ int extension_delay;
+ int extension_explosioncount;
+ int extension_targetcount;
+ Color tint;
+
+ //special (not using char animations)
+ Animation* travel[MAX_ORIENT];
+ Animation* shadow[MAX_ORIENT];
+ Sprite2D* light;//this is just a round/halftrans sprite, has no animation
+ EffectQueue* effects;
+ Projectile **children;
+ int child_size;
+ int pathcounter;
+ int bend;
+ int drawSpark;
+ Holder<SoundHandle> travel_handle;
+public:
+ void SetCaster(ieDword t, int level);
+ ieDword GetCaster() const;
+ bool FailedIDS(Actor *target) const;
+ void SetTarget(ieDword t, bool fake);
+ void SetTarget(const Point &p);
+ bool PointInRadius(const Point &p) const;
+ void Cleanup();
+
+ //inliners to protect data consistency
+ inline PathNode * GetNextStep() {
+ if (!step) {
+ DoStep((unsigned int) ~0);
+ }
+ return step;
+ }
+
+ inline Point GetDestination() const { return Destination; }
+ inline const char * GetName() const { return name; }
+ inline ieWord GetType() const { return type; }
+ //This assumes that the effect queue cannot be bigger than 65535
+ //which is a sane expectation
+ inline EffectQueue *GetEffects() const {
+ return effects;
+ }
+
+ inline unsigned char GetOrientation() const {
+ return Orientation;
+ }
+ //no idea if projectiles got height, using y
+ inline int GetHeight() const {
+ //if projectile is drawn behind target
+ if (ExtFlags&PEF_BACKGROUND) {
+ return Pos.y-BACK_DEPTH;
+ }
+
+ //if projectile is flying
+ if (SFlags&PSF_FLYING) {
+ return Pos.y+FLY_HEIGHT;
+ }
+ return Pos.y;
+ }
+
+ void SetIdentifiers(const char *name, ieWord type);
+
+ void SetEffectsCopy(EffectQueue *eq);
+
+ //don't forget to set effects to NULL when the projectile discharges
+ //unexploded projectiles are responsible to destruct their payload
+
+ inline void SetEffects(EffectQueue *fx) {
+ effects = fx;
+ }
+
+ inline unsigned char GetNextFace() {
+ //slow turning
+ if (Orientation != NewOrientation) {
+ if ( ( (NewOrientation-Orientation) & (MAX_ORIENT-1) ) <= MAX_ORIENT/2) {
+ Orientation++;
+ } else {
+ Orientation--;
+ }
+ Orientation = Orientation&(MAX_ORIENT-1);
+ }
+
+ return Orientation;
+ }
+
+ inline void SetOrientation(int value, bool slow) {
+ //MAX_ORIENT == 16, so we can do this
+ NewOrientation = (unsigned char) (value&(MAX_ORIENT-1));
+ if (!slow) {
+ Orientation = NewOrientation;
+ }
+ }
+
+ void Setup();
+ //sets how long a created travel projectile will hover over a spot
+ //before vanishing (without the need of area extension)
+ void SetDelay(int delay);
+ void MoveTo(Map *map, const Point &Des);
+ void ClearPath();
+ //handle phases, return 0 when expired
+ int Update();
+ //draw object
+ void Draw(const Region &screen);
+ void SetGradient(int gradient, bool tint);
+ void StaticTint(const Color &newtint);
+private:
+ //creates a child projectile with current_projectile_id - 1
+ void CreateIteration();
+ void CreateAnimations(Animation **anims, const ieResRef bam, int Seq);
+ //pillar type animations
+ void CreateCompositeAnimation(Animation **anims, AnimationFactory *af, int Seq);
+ //oriented animations (also simple ones)
+ void CreateOrientedAnimations(Animation **anims, AnimationFactory *af, int Seq);
+ void GetPaletteCopy(Animation *anim[], Palette *&pal);
+ void GetSmokeAnim();
+ void SetBlend();
+ //apply spells and effects on the target, only in single travel mode
+ //area effect projectiles call a separate single travel projectile for each affected target
+ void Payload();
+ //if there is an extension, convert to exploding or wait for trigger
+ void EndTravel();
+ //apply default spell
+ void ApplyDefault();
+ //stops the current sound
+ void StopSound();
+ //kickstarts the secondary sound
+ void UpdateSound();
+ //reached end of single travel missile, explode or expire now
+ void ChangePhase();
+ //drop a BAM or VVC on the trail path, return the length of the animation
+ int AddTrail(ieResRef BAM, const ieByte *pal);
+ void DoStep(unsigned int walk_speed);
+ void LineTarget(); //line projectiles (walls, scorchers)
+ void SecondaryTarget(); //area projectiles (circles, cones)
+ void CheckTrigger(unsigned int radius);
+ //calculate target and destination points for a firewall
+ void SetupWall();
+ void DrawLine(const Region &screen, int face, ieDword flag);
+ void DrawTravel(const Region &screen);
+ bool DrawChildren(const Region &screen);
+ void DrawExplosion(const Region &screen);
+ void SpawnFragment(Point &pos);
+ void DrawExploded(const Region &screen);
+ int GetTravelPos(int face) const;
+ int GetShadowPos(int face) const;
+ void SetPos(int face, int frame1, int frame2);
+ inline int GetZPos() const;
+
+ //logic to resolve target when single projectile hit destination
+ int CalculateTargetFlag();
+ //logic to resolve the explosion count (may be based on caster level)
+ int CalculateExplosionCount();
+
+ Actor *GetTarget();
+ void NextTarget(const Point &p);
+ void SetupPalette(Animation *anim[], Palette *&pal, const ieByte *gradients);
+};
+
+#endif // PROJECTILE_H
+
diff --git a/gemrb/core/ProjectileMgr.cpp b/gemrb/core/ProjectileMgr.cpp
new file mode 100644
index 0000000..d732a1d
--- /dev/null
+++ b/gemrb/core/ProjectileMgr.cpp
@@ -0,0 +1,29 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 "ProjectileMgr.h"
+
+ProjectileMgr::ProjectileMgr(void)
+{
+}
+
+ProjectileMgr::~ProjectileMgr(void)
+{
+}
diff --git a/gemrb/core/ProjectileMgr.h b/gemrb/core/ProjectileMgr.h
new file mode 100644
index 0000000..2233d1f
--- /dev/null
+++ b/gemrb/core/ProjectileMgr.h
@@ -0,0 +1,36 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 PROJECTILEMGR_H
+#define PROJECTILEMGR_H
+
+#include "Plugin.h"
+#include "Projectile.h"
+#include "System/DataStream.h"
+
+class GEM_EXPORT ProjectileMgr : public Plugin {
+public:
+ ProjectileMgr(void);
+ virtual ~ProjectileMgr(void);
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual Projectile* GetProjectile( Projectile *) = 0;
+};
+
+#endif
diff --git a/gemrb/core/ProjectileServer.cpp b/gemrb/core/ProjectileServer.cpp
new file mode 100644
index 0000000..c3a2e80
--- /dev/null
+++ b/gemrb/core/ProjectileServer.cpp
@@ -0,0 +1,325 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 "ProjectileServer.h"
+
+#include "GameData.h"
+#include "Interface.h"
+#include "ProjectileMgr.h"
+#include "SymbolMgr.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+#define MAX_PROJ_IDX 0x1fff
+
+ProjectileServer::ProjectileServer()
+{
+ projectilecount = -1;
+ projectiles = NULL;
+ explosioncount = -1;
+ explosions = NULL;
+}
+
+ProjectileServer::~ProjectileServer()
+{
+ if (projectiles) {
+ delete[] projectiles;
+ }
+ if (explosions) {
+ delete[] explosions;
+ }
+}
+
+Projectile *ProjectileServer::CreateDefaultProjectile(unsigned int idx)
+{
+ Projectile *pro = new Projectile();
+ //int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Type);
+ //memset(&pro->Type, 0, strlength );
+ int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Speed);
+ memset(&pro->Speed, 0, strlength );
+
+ //take care, this projectile is not freed up by the server
+ if(idx==(unsigned int) ~0 ) {
+ return pro;
+ }
+
+ projectiles[idx].projectile = pro;
+ pro->SetIdentifiers(projectiles[idx].resname, idx);
+ return ReturnCopy(idx);
+}
+
+//this function can return only projectiles listed in projectl.ids
+Projectile *ProjectileServer::GetProjectileByName(const ieResRef resname)
+{
+ if (!core->IsAvailable(IE_PRO_CLASS_ID)) {
+ return NULL;
+ }
+ unsigned int idx=GetHighestProjectileNumber();
+ while(idx--) {
+ if (!strnicmp(resname, projectiles[idx].resname,8) ) {
+ return GetProjectile(idx);
+ }
+ }
+ return NULL;
+}
+
+Projectile *ProjectileServer::GetProjectileByIndex(unsigned int idx)
+{
+ if (!core->IsAvailable(IE_PRO_CLASS_ID)) {
+ return NULL;
+ }
+ if (idx>=GetHighestProjectileNumber()) {
+ return GetProjectile(0);
+ }
+
+ return GetProjectile(idx);
+}
+
+Projectile *ProjectileServer::ReturnCopy(unsigned int idx)
+{
+ Projectile *pro = new Projectile();
+ Projectile *old = projectiles[idx].projectile;
+ //int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Type);
+ //memcpy(&pro->Type, &old->Type, strlength );
+ int strlength = (ieByte *) (&pro->Extension)-(ieByte *) (&pro->Speed);
+ memcpy(&pro->Speed, &old->Speed, strlength );
+ //FIXME: copy extension data too, or don't alter the extension
+ if (old->Extension) {
+ pro->Extension = old->Extension;
+ }
+ pro->SetIdentifiers(projectiles[idx].resname, idx);
+ return pro;
+}
+
+Projectile *ProjectileServer::GetProjectile(unsigned int idx)
+{
+ if (projectiles[idx].projectile) {
+ return ReturnCopy(idx);
+ }
+ DataStream* str = gamedata->GetResource( projectiles[idx].resname, IE_PRO_CLASS_ID );
+ PluginHolder<ProjectileMgr> sm(IE_PRO_CLASS_ID);
+ if (!sm) {
+ delete ( str );
+ return CreateDefaultProjectile(idx);
+ }
+ if (!sm->Open( str, true )) {
+ return CreateDefaultProjectile(idx);
+ }
+ Projectile *pro = new Projectile();
+ projectiles[idx].projectile = pro;
+ pro->SetIdentifiers(projectiles[idx].resname, idx);
+
+ sm->GetProjectile( pro );
+ int Type = 0xff;
+
+ if(pro->Extension) {
+ Type = pro->Extension->ExplType;
+ }
+ if(Type<0xff) {
+ ieResRef const *res;
+
+ //fill the spread field according to the hardcoded explosion type
+ res = GetExplosion(Type,0);
+ if(res) {
+ strnuprcpy(pro->Extension->Spread,*res,sizeof(ieResRef)-1);
+ }
+
+ //if the hardcoded explosion type has a center animation
+ //override the VVC animation field with it
+ res = GetExplosion(Type,1);
+ if(res) {
+ pro->Extension->AFlags|=PAF_VVC;
+ strnuprcpy(pro->Extension->VVCRes,*res,sizeof(ieResRef)-1);
+ }
+
+ //fill the secondary spread field out
+ res = GetExplosion(Type,2);
+ if(res) {
+ strnuprcpy(pro->Extension->Secondary,*res,sizeof(ieResRef)-1);
+ }
+
+ //the explosion sound, used for the first explosion
+ //(overrides an original field)
+ res = GetExplosion(Type,3);
+ if(res) {
+ strnuprcpy(pro->Extension->SoundRes,*res,sizeof(ieResRef)-1);
+ }
+
+ //the area sound (used for subsequent explosions)
+ res = GetExplosion(Type,4);
+ if(res) {
+ strnuprcpy(pro->Extension->AreaSound,*res,sizeof(ieResRef)-1);
+ }
+
+ //fill the explosion/spread animation flags
+ pro->Extension->APFlags = GetExplosionFlags(Type);
+ }
+
+ pro->autofree = true;
+ return ReturnCopy(idx);
+}
+
+int ProjectileServer::InitExplosion()
+{
+ if (explosioncount>=0) {
+ return explosioncount;
+ }
+
+ AutoTable explist("areapro");
+ if (explist) {
+ explosioncount = 0;
+
+ unsigned int rows = (unsigned int) explist->GetRowCount();
+ //cannot handle 0xff and it is easier to set up the fields
+ //without areapro.2da anyway. So this isn't a restriction
+ if(rows>254) {
+ rows=254;
+ }
+ explosioncount = rows;
+ explosions = new ExplosionEntry[rows];
+
+ while(rows--) {
+ int i;
+
+ for(i=0;i<AP_RESCNT;i++) {
+ strnuprcpy(explosions[rows].resources[i], explist->QueryField(rows, i), 8);
+ }
+ //using i so the flags field will always be after the resources
+ explosions[rows].flags = atoi(explist->QueryField(rows,i));
+ }
+ }
+ return explosioncount;
+}
+
+unsigned int ProjectileServer::PrepareSymbols(Holder<SymbolMgr> projlist) {
+ unsigned int count = 0;
+
+ unsigned int rows = (unsigned int) projlist->GetSize();
+ while(rows--) {
+ unsigned int value = projlist->GetValueIndex(rows);
+ if (value>MAX_PROJ_IDX) {
+ //value = MAX_PROJ_IDX;
+ printMessage("ProjectileServer","Too high projectilenumber\n", YELLOW);
+ continue; // ignore
+ }
+ if (value>(unsigned int) count) {
+ count = (unsigned int) value;
+ }
+ }
+
+ return count;
+}
+
+void ProjectileServer::AddSymbols(Holder<SymbolMgr> projlist) {
+ unsigned int rows = (unsigned int) projlist->GetSize();
+ while(rows--) {
+ unsigned int value = projlist->GetValueIndex(rows);
+ if (value>MAX_PROJ_IDX) {
+ continue;
+ }
+ if (value >= (unsigned int)projectilecount) {
+ // this should never happen!
+ printMessage("ProjectileServer","Too high projectilenumber while adding projectiles\n", RED);
+ abort();
+ }
+ strnuprcpy(projectiles[value].resname, projlist->GetStringIndex(rows), 8);
+ }
+}
+
+unsigned int ProjectileServer::GetHighestProjectileNumber()
+{
+ if (projectilecount>=0) {
+ // already read the projectiles
+ return (unsigned int) projectilecount;
+ }
+
+ // built-in gemrb projectiles and game/mod-provided projectiles
+ unsigned int gemresource = core->LoadSymbol("gemprjtl");
+ Holder<SymbolMgr> gemprojlist = core->GetSymbol(gemresource);
+ unsigned int resource = core->LoadSymbol("projectl");
+ Holder<SymbolMgr> projlist = core->GetSymbol(resource);
+
+ // first, we must calculate how many projectiles we have
+ if (gemprojlist) {
+ projectilecount = PrepareSymbols(gemprojlist) + 1;
+ }
+ if (projlist) {
+ unsigned int temp = PrepareSymbols(projlist) + 1;
+ if (projectilecount == -1 || temp > (unsigned int)projectilecount)
+ projectilecount = temp;
+ }
+
+ // then, allocate space for them all
+ if (projectilecount == -1) {
+ // no valid projectiles files..
+ projectilecount = 1;
+ }
+ projectiles = new ProjectileEntry[projectilecount];
+
+ // finally, we must read the projectile resrefs
+ if (projlist) {
+ AddSymbols(projlist);
+ core->DelSymbol(resource);
+ }
+ // gemprojlist is second because it always overrides game/mod-supplied projectiles
+ if (gemprojlist) {
+ AddSymbols(gemprojlist);
+ core->DelSymbol(gemresource);
+ }
+
+ return (unsigned int) projectilecount;
+}
+
+//return various flags for the explosion type
+int ProjectileServer::GetExplosionFlags(unsigned int idx)
+{
+ if (explosioncount==-1) {
+ if (InitExplosion()<0) {
+ printMessage("ProjectileServer","Problem with explosions\n", RED);
+ explosioncount=0;
+ }
+ }
+ if (idx>=(unsigned int) explosioncount) {
+ return 0;
+ }
+
+ return explosions[idx].flags;
+}
+
+ieResRef const *ProjectileServer::GetExplosion(unsigned int idx, int type)
+{
+ if (explosioncount==-1) {
+ if (InitExplosion()<0) {
+ printMessage("ProjectileServer","Problem with explosions\n", RED);
+ explosioncount=0;
+ }
+ }
+ if (idx>=(unsigned int) explosioncount) {
+ return NULL;
+ }
+ ieResRef const *ret = NULL;
+
+ ret = &explosions[idx].resources[type];
+ if (ret && *ret[0]=='*') ret = NULL;
+
+ return ret;
+}
diff --git a/gemrb/core/ProjectileServer.h b/gemrb/core/ProjectileServer.h
new file mode 100644
index 0000000..d53f99c
--- /dev/null
+++ b/gemrb/core/ProjectileServer.h
@@ -0,0 +1,97 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 PROJSERVER_H
+#define PROJSERVER_H
+
+#include "exports.h"
+
+#include "PluginMgr.h"
+#include "Projectile.h"
+
+class SymbolMgr;
+
+//the number of resrefs in areapro.2da (before the flags field)
+#define AP_RESCNT 5
+
+//this represents a line of projectl.ids
+class ProjectileEntry
+{
+public:
+ ProjectileEntry()
+ {
+ memset(this,0,sizeof(ProjectileEntry));
+ }
+ ~ProjectileEntry()
+ {
+ if (projectile)
+ delete projectile;
+ }
+
+ ieResRef resname;
+ Projectile *projectile;
+};
+
+class ExplosionEntry
+{
+public:
+ ExplosionEntry()
+ {
+ memset(this,0,sizeof(ExplosionEntry));
+ }
+ ieResRef resources[AP_RESCNT];
+ int flags;
+};
+
+//this singleton object serves the projectile objects
+class GEM_EXPORT ProjectileServer
+{
+public:
+ ProjectileServer();
+ ~ProjectileServer();
+
+ Projectile *GetProjectileByIndex(unsigned int idx);
+ //it is highly unlikely we need this function
+ Projectile *GetProjectileByName(const ieResRef resname);
+ //returns the highest projectile id
+ unsigned int GetHighestProjectileNumber();
+ int InitExplosion();
+ int GetExplosionFlags(unsigned int idx);
+ ieResRef const *GetExplosion(unsigned int idx, int type);
+ //creates an empty projectile on the fly
+ Projectile *CreateDefaultProjectile(unsigned int idx);
+private:
+ ProjectileEntry *projectiles; //this is the list of projectiles
+ int projectilecount;
+ ExplosionEntry *explosions; //this is the list of explosion resources
+ int explosioncount;
+ // internal function: what is max valid projectile id?
+ unsigned int PrepareSymbols(Holder<SymbolMgr> projlist);
+ // internal function: read projectiles
+ void AddSymbols(Holder<SymbolMgr> projlist);
+ //this method is used internally
+ Projectile *GetProjectile(unsigned int idx);
+ //creates a clone from the cached projectiles
+ Projectile *ReturnCopy(unsigned int idx);
+ //returns one of the resource names
+};
+
+#endif // PROJSERVER_H
+
diff --git a/gemrb/core/Region.cpp b/gemrb/core/Region.cpp
new file mode 100644
index 0000000..0ab46be
--- /dev/null
+++ b/gemrb/core/Region.cpp
@@ -0,0 +1,224 @@
+/* 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 "Region.h"
+
+/*************** point ****************************/
+Point::Point(void)
+{
+ x = y = 0;
+ //memset(this, 0, sizeof(*this));
+}
+
+Point::~Point(void)
+{
+}
+
+Point::Point(const Point& pnt)
+{
+ x=pnt.x;
+ y=pnt.y;
+ //memcpy(this, &pnt, sizeof(*this));
+}
+
+Point& Point::operator=(const Point& pnt)
+{
+ x = pnt.x;
+ y = pnt.y;
+ //memcpy(this, &pnt, sizeof(*this));
+ return *this;
+}
+
+bool Point::operator==(const Point& pnt)
+{
+ if (( x == pnt.x ) && ( y == pnt.y )) {
+ return true;
+ }
+ return false;
+ //return !memcmp( this, &pnt, sizeof(*this));
+}
+
+bool Point::operator!=(const Point& pnt)
+{
+ if (( x == pnt.x ) && ( y == pnt.y )) {
+ return false;
+ }
+ return true;
+}
+
+Point::Point(short x, short y)
+{
+ this->x = x;
+ this->y = y;
+}
+
+ieDword Point::asDword() const
+{
+ return ((y & 0xFFFF) << 16) | (x & 0xFFFF);
+}
+
+void Point::fromDword(ieDword val)
+{
+ x = val & 0xFFFF;
+ y = val >> 16;
+}
+
+bool Point::isnull() const
+{
+ if (x==0 && y==0) {
+ return true;
+ }
+ return false;
+}
+
+bool Point::isempty() const
+{
+ if (x==-1 && y==-1) {
+ return true;
+ }
+ return false;
+}
+
+bool Point::PointInside(const Point &p) const
+{
+ if (( p.x < 0 ) || ( p.x > x )) {
+ return false;
+ }
+ if (( p.y < 0 ) || ( p.y > y )) {
+ return false;
+ }
+ return true;
+}
+
+/*************** region ****************************/
+Region::Region(void)
+{
+ x = y = w = h = 0;
+}
+
+Region::~Region(void)
+{
+}
+
+Region::Region(const Region& rgn)
+{
+ x = rgn.x;
+ y = rgn.y;
+ w = rgn.w;
+ h = rgn.h;
+}
+
+Region& Region::operator=(const Region& rgn)
+{
+ x = rgn.x;
+ y = rgn.y;
+ w = rgn.w;
+ h = rgn.h;
+ return *this;
+}
+
+bool Region::operator==(const Region& rgn)
+{
+ if (( x == rgn.x ) && ( y == rgn.y ) && ( w == rgn.w ) && ( h == rgn.h )) {
+ return true;
+ }
+ return false;
+}
+
+bool Region::operator!=(const Region& rgn)
+{
+ if (( x != rgn.x ) || ( y != rgn.y ) || ( w != rgn.w ) || ( h != rgn.h )) {
+ return true;
+ }
+ return false;
+}
+
+Region::Region(int x, int y, int w, int h)
+{
+ this->x = x;
+ this->y = y;
+ this->w = w;
+ this->h = h;
+}
+
+Region::Region(const Point &p, int w, int h)
+{
+ this->x = p.x;
+ this->y = p.y;
+ this->w = w;
+ this->h = h;
+}
+
+bool Region::PointInside(const Point &p) const
+{
+ if (( p.x < x ) || ( p.x > ( x + w ) )) {
+ return false;
+ }
+ if (( p.y < y ) || ( p.y > ( y + h ) )) {
+ return false;
+ }
+ return true;
+}
+
+bool Region::PointInside(unsigned short XPos, unsigned short YPos) const
+{
+ if (( XPos < x ) || ( XPos > ( x + w ) )) {
+ return false;
+ }
+ if (( YPos < y ) || ( YPos > ( y + h ) )) {
+ return false;
+ }
+ return true;
+}
+
+bool Region::InsideRegion(const Region& rgn) const
+{
+ if (x > ( rgn.x + rgn.w )) {
+ return false;
+ }
+ if (( x + w ) < rgn.x) {
+ return false;
+ }
+ if (y > ( rgn.y + rgn.h )) {
+ return false;
+ }
+ if (( y + h ) < rgn.y) {
+ return false;
+ }
+ return true;
+}
+
+void Region::Normalize()
+{
+ if (x > w) {
+ int tmp = x;
+ x = w;
+ w = tmp - x;
+ } else {
+ w -= x;
+ }
+ if (y > h) {
+ int tmp = y;
+ y = h;
+ h = tmp - y;
+ } else {
+ h -= y;
+ }
+}
diff --git a/gemrb/core/Region.h b/gemrb/core/Region.h
new file mode 100644
index 0000000..675663b
--- /dev/null
+++ b/gemrb/core/Region.h
@@ -0,0 +1,96 @@
+/* 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 Region.h
+ * Declares Point and Region, 2d-space primitive types
+ * @author The GemRB Project
+ */
+
+
+#ifndef REGION_H
+#define REGION_H
+
+#include "exports.h"
+#include "ie_types.h"
+
+/**
+ * @class Point
+ * Point in 2d-space: [x, y]
+ */
+
+class GEM_EXPORT Point {
+public:
+ Point(void);
+ Point(const Point& pnt);
+ Point(short x, short y);
+ ~Point(void);
+
+ Point& operator=(const Point& pnt);
+ bool operator==(const Point &pnt);
+ bool operator!=(const Point &pnt);
+
+ /** if it is [-1.-1] */
+ bool isempty() const;
+ /** if it is [0.0] */
+ bool isnull() const;
+ inline void empty() {
+ x=-1;
+ y=-1;
+ }
+ inline void null() {
+ x=0;
+ y=0;
+ }
+ bool PointInside(const Point &p) const;
+
+ ieDword asDword() const; // store coordinates in uint32 ((y << 16) | x)
+ void fromDword(ieDword val); // extract coordinates from uint32
+
+ short x,y;
+};
+
+
+/**
+ * @class Region
+ * Rectangular area in 2d-space [x, y] - [x+w, y+h]
+ */
+
+class GEM_EXPORT Region {
+public:
+ Region(void);
+ ~Region(void);
+ int x;
+ int y;
+ int w;
+ int h;
+ Region(const Region& rgn);
+ Region(const Point& p, int w, int h);
+ Region& operator=(const Region& rgn);
+ bool operator==(const Region& rgn);
+ bool operator!=(const Region& rgn);
+ Region(int x, int y, int w, int h);
+ bool PointInside(unsigned short XPos, unsigned short YPos) const;
+ bool PointInside(const Point &p) const;
+ bool InsideRegion(const Region& rgn) const;
+ void Normalize();
+};
+
+#endif // ! REGION_H
diff --git a/gemrb/core/Resource.cpp b/gemrb/core/Resource.cpp
new file mode 100644
index 0000000..5e9bac6
--- /dev/null
+++ b/gemrb/core/Resource.cpp
@@ -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.
+ *
+ *
+ */
+
+#include "Resource.h"
+
+#include "System/DataStream.h"
+
+Resource::Resource(void)
+{
+ str = NULL;
+}
+
+Resource::~Resource(void)
+{
+ if (str) {
+ delete( str );
+ }
+}
diff --git a/gemrb/core/Resource.h b/gemrb/core/Resource.h
new file mode 100644
index 0000000..a5f81f8
--- /dev/null
+++ b/gemrb/core/Resource.h
@@ -0,0 +1,57 @@
+/* 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 Resource.h
+ * Declares Resource class, base class for all resources
+ * @author The GemRB Project
+ */
+
+#ifndef RESOURCE_H
+#define RESOURCE_H
+
+#include "Plugin.h"
+
+#include <cstddef>
+
+class DataStream;
+
+/**
+ * Base class for all GemRB resources
+ */
+
+class GEM_EXPORT Resource : public Plugin {
+protected:
+ DataStream* str;
+public:
+ Resource();
+ virtual ~Resource();
+ /**
+ * Reads the resource from the given stream.
+ *
+ * This should only be called once for a given resource object.
+ * @param[in] stream Non-NULL Stream containg the resource
+ * @retval true stream contains the given resource.
+ * @retval false stream does not contain valid resource.
+ */
+ virtual bool Open(DataStream* stream) = 0;
+};
+
+#endif
diff --git a/gemrb/core/ResourceDesc.cpp b/gemrb/core/ResourceDesc.cpp
new file mode 100644
index 0000000..309ecc8
--- /dev/null
+++ b/gemrb/core/ResourceDesc.cpp
@@ -0,0 +1,50 @@
+/* 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 "ResourceDesc.h"
+
+ResourceDesc::ResourceDesc(const TypeID* type, CreateFunc create, const char *ext, ieWord keyType)
+ : type(type), ext(ext), keyType(keyType), create(create)
+{
+}
+
+ResourceDesc::~ResourceDesc(void)
+{
+}
+
+const char* ResourceDesc::GetExt() const
+{
+ return ext;
+}
+
+const TypeID* ResourceDesc::GetType() const
+{
+ return type;
+}
+
+ieWord ResourceDesc::GetKeyType() const
+{
+ return keyType;
+}
+
+Resource* ResourceDesc::Create(DataStream *stream) const
+{
+ return create(stream);
+}
diff --git a/gemrb/core/ResourceDesc.h b/gemrb/core/ResourceDesc.h
new file mode 100644
index 0000000..74a75c1
--- /dev/null
+++ b/gemrb/core/ResourceDesc.h
@@ -0,0 +1,60 @@
+/* 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 RESOURCEDESC_H
+#define RESOURCEDESC_H
+
+#include "SClassID.h"
+#include "exports.h"
+#include "ie_types.h"
+
+class DataStream;
+class Resource;
+class TypeID;
+
+/**
+ * Class that describes a plugin resource type.
+ */
+class GEM_EXPORT ResourceDesc {
+public:
+ typedef Resource* (*CreateFunc)(DataStream*);
+private:
+ const TypeID *type;
+ const char *ext;
+ ieWord keyType; // IE Specific
+ CreateFunc create;
+public:
+ /**
+ * Create resource descriptor.
+ *
+ * @param[in] type Base class for resource.
+ * @param[in] create Function to create resource from a stream.
+ * @param[in] ext Extension used for resource files.
+ * @param[in] keyType \iespecific Type identifier used in key/biff files.
+ */
+ ResourceDesc(const TypeID* type, CreateFunc create, const char *ext, ieWord keyType = 0);
+ ~ResourceDesc(void);
+ const char* GetExt() const;
+ const TypeID* GetType() const;
+ ieWord GetKeyType() const;
+ Resource* Create(DataStream*) const;
+};
+
+#endif
diff --git a/gemrb/core/ResourceManager.cpp b/gemrb/core/ResourceManager.cpp
new file mode 100644
index 0000000..95c131e
--- /dev/null
+++ b/gemrb/core/ResourceManager.cpp
@@ -0,0 +1,160 @@
+/* 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 "ResourceManager.h"
+
+#include "Interface.h"
+#include "PluginMgr.h"
+#include "Resource.h"
+#include "ResourceDesc.h"
+#include "ResourceSource.h"
+
+ResourceManager::ResourceManager()
+{
+}
+
+
+ResourceManager::~ResourceManager()
+{
+}
+
+bool ResourceManager::AddSource(const char *path, const char *description, PluginID type, int flags)
+{
+ PluginHolder<ResourceSource> source(type);
+ if (!source->Open(path, description)) {
+ return false;
+ }
+
+ if (flags & RM_REPLACE_SAME_SOURCE) {
+ for (size_t i = 0; i < searchPath.size(); i++) {
+ if (!stricmp(description, searchPath[i]->GetDescription())) {
+ searchPath[i] = source;
+ break;
+ }
+ }
+ } else {
+ searchPath.push_back(source);
+ }
+ return true;
+}
+
+static void PrintPossibleFiles(const char* ResRef, const TypeID *type)
+{
+ const std::vector<ResourceDesc>& types = PluginMgr::Get()->GetResourceDesc(type);
+ for (size_t j = 0; j < types.size(); j++) {
+ printf("%s%s ", ResRef, types[j].GetExt());
+ }
+}
+
+bool ResourceManager::Exists(const char *ResRef, SClass_ID type, bool silent) const
+{
+ if (ResRef[0] == '\0')
+ return false;
+ // TODO: check various caches
+ for (size_t i = 0; i < searchPath.size(); i++) {
+ if (searchPath[i]->HasResource( ResRef, type )) {
+ return true;
+ }
+ }
+ if (!silent) {
+ printMessage( "ResourceManager", "Searching for ", WHITE );
+ printf( "%s%s...", ResRef, core->TypeExt( type ) );
+ printStatus( "NOT FOUND", YELLOW );
+ }
+ return false;
+}
+
+bool ResourceManager::Exists(const char *ResRef, const TypeID *type, bool silent) const
+{
+ if (ResRef[0] == '\0')
+ return false;
+ // TODO: check various caches
+ const std::vector<ResourceDesc> &types = PluginMgr::Get()->GetResourceDesc(type);
+ for (size_t j = 0; j < types.size(); j++) {
+ for (size_t i = 0; i < searchPath.size(); i++) {
+ if (searchPath[i]->HasResource(ResRef, types[j])) {
+ return true;
+ }
+ }
+ }
+ if (!silent) {
+ printMessage( "ResourceManager", "Searching for ", WHITE );
+ printf( "%s... ", ResRef );
+ printf("Tried ");
+ PrintPossibleFiles(ResRef,type);
+ printStatus( "NOT FOUND", YELLOW );
+ }
+ return false;
+}
+
+DataStream* ResourceManager::GetResource(const char* ResRef, SClass_ID type, bool silent) const
+{
+ if (ResRef[0] == '\0')
+ return NULL;
+ if (!silent) {
+ printMessage( "ResourceManager", "Searching for ", WHITE );
+ printf( "%s%s...", ResRef, core->TypeExt( type ) );
+ }
+ for (size_t i = 0; i < searchPath.size(); i++) {
+ DataStream *ds = searchPath[i]->GetResource(ResRef, type);
+ if (ds) {
+ if (!silent) {
+ printStatus( searchPath[i]->GetDescription(), GREEN );
+ }
+ return ds;
+ }
+ }
+ if (!silent) {
+ printStatus( "ERROR", LIGHT_RED );
+ }
+ return NULL;
+}
+
+Resource* ResourceManager::GetResource(const char* ResRef, const TypeID *type, bool silent) const
+{
+ if (ResRef[0] == '\0')
+ return NULL;
+ if (!silent) {
+ printMessage( "ResourceManager", "Searching for ", WHITE );
+ printf( "%s... ", ResRef );
+ }
+ const std::vector<ResourceDesc> &types = PluginMgr::Get()->GetResourceDesc(type);
+ for (size_t j = 0; j < types.size(); j++) {
+ for (size_t i = 0; i < searchPath.size(); i++) {
+ DataStream *str = searchPath[i]->GetResource(ResRef, types[j]);
+ if (str) {
+ Resource *res = types[j].Create(str);
+ if (res) {
+ if (!silent) {
+ printf( "%s%s...", ResRef, types[j].GetExt() );
+ printStatus( searchPath[i]->GetDescription(), GREEN );
+ }
+ return res;
+ }
+ }
+ }
+ }
+ if (!silent) {
+ printf("Tried ");
+ PrintPossibleFiles(ResRef,type);
+ printStatus( "ERROR", LIGHT_RED );
+ }
+ return NULL;
+}
diff --git a/gemrb/core/ResourceManager.h b/gemrb/core/ResourceManager.h
new file mode 100644
index 0000000..7fcbe2d
--- /dev/null
+++ b/gemrb/core/ResourceManager.h
@@ -0,0 +1,70 @@
+/* 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 RESOURCEMANGER_H
+#define RESOURCEMANGER_H
+
+#include "SClassID.h"
+#include "exports.h"
+
+#include "Holder.h"
+
+#include <vector>
+
+#ifdef _MSC_VER // No SFINAE
+#include "ResourceSource.h"
+#endif
+
+#define RM_REPLACE_SAME_SOURCE 1
+
+class DataStream;
+class Resource;
+class ResourceSource;
+class TypeID;
+
+class GEM_EXPORT ResourceManager {
+public:
+ ResourceManager();
+ ~ResourceManager();
+
+ /**
+ * Add ResourceSource to search path
+ * @param[in] path Path to be used for source.
+ * Note: This is modified by ResolveFilePath.
+ * @param[in] description Description of the source.
+ * @param[in] type Plugin type used for source.
+ **/
+ bool AddSource(const char *path, const char *description, PluginID type, int flags=0);
+
+ /** returns true if resource exists */
+ bool Exists(const char *ResRef, SClass_ID type, bool silent=false) const;
+ /** returns true if resource exists */
+ bool Exists(const char *ResRef, const TypeID *type, bool silent=false) const;
+
+ /** Returns stream associated to given resource */
+ DataStream* GetResource(const char* resname, SClass_ID type, bool silent = false) const;
+ /** Returns Resource object associated to given resource */
+ Resource* GetResource(const char* resname, const TypeID *type, bool silent = false) const;
+
+private:
+ std::vector<Holder<ResourceSource> > searchPath;
+};
+
+#endif
diff --git a/gemrb/core/ResourceSource.cpp b/gemrb/core/ResourceSource.cpp
new file mode 100644
index 0000000..2fae650
--- /dev/null
+++ b/gemrb/core/ResourceSource.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 "ResourceSource.h"
+
+ResourceSource::ResourceSource(void)
+{
+}
+
+ResourceSource::~ResourceSource(void)
+{
+}
diff --git a/gemrb/core/ResourceSource.h b/gemrb/core/ResourceSource.h
new file mode 100644
index 0000000..11608b9
--- /dev/null
+++ b/gemrb/core/ResourceSource.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 RESOURCESOURCE_H
+#define RESOURCESOURCE_H
+
+#include "SClassID.h"
+#include "exports.h"
+#include "globals.h"
+
+#include "Plugin.h"
+
+class DataStream;
+class ResourceDesc;
+
+class GEM_EXPORT ResourceSource : public Plugin {
+public:
+ ResourceSource(void);
+ virtual ~ResourceSource(void);
+ virtual bool Open(const char *filename, const char *description) = 0;
+ virtual bool HasResource(const char* resname, SClass_ID type) = 0;
+ virtual bool HasResource(const char* resname, const ResourceDesc &type) = 0;
+ virtual DataStream* GetResource(const char* resname, SClass_ID type) = 0;
+ virtual DataStream* GetResource(const char* resname, const ResourceDesc &type) = 0;
+ const char *GetDescription() const { return description; }
+protected:
+ char *description;
+};
+
+#endif
diff --git a/gemrb/core/SaveGame.h b/gemrb/core/SaveGame.h
new file mode 100644
index 0000000..ce7460d
--- /dev/null
+++ b/gemrb/core/SaveGame.h
@@ -0,0 +1,85 @@
+/* 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 SAVEGAME_H
+#define SAVEGAME_H
+
+#include "exports.h"
+
+#include "Holder.h"
+#include "ResourceManager.h"
+#include "System/FileStream.h"
+
+class ImageMgr;
+
+class GEM_EXPORT SaveGame : public Held<SaveGame> {
+public:
+ static const TypeID ID;
+public:
+ SaveGame(const char* path, const char* name, const char* prefix, const char* slotname, int pCount, int saveID);
+ ~SaveGame();
+ int GetPortraitCount() const
+ {
+ return PortraitCount;
+ }
+ int GetSaveID() const
+ {
+ return SaveID;
+ }
+ const char* GetName() const
+ {
+ return Name;
+ }
+ const char* GetPrefix() const
+ {
+ return Prefix;
+ }
+ const char* GetPath() const
+ {
+ return Path;
+ }
+ const char* GetDate() const
+ {
+ return Date;
+ }
+ const char* GetGameDate() const;
+ const char* GetSlotName() const
+ {
+ return SlotName;
+ }
+
+ Sprite2D* GetPortrait(int index) const;
+ Sprite2D* GetPreview() const;
+ DataStream* GetGame() const;
+ DataStream* GetWmap(int idx) const;
+ DataStream* GetSave() const;
+private:
+ char Path[_MAX_PATH];
+ char Prefix[10];
+ char Name[_MAX_PATH];
+ char Date[_MAX_PATH];
+ mutable char GameDate[_MAX_PATH];
+ char SlotName[_MAX_PATH];
+ int PortraitCount;
+ int SaveID;
+ ResourceManager manager;
+};
+
+#endif
diff --git a/gemrb/core/SaveGameIterator.cpp b/gemrb/core/SaveGameIterator.cpp
new file mode 100644
index 0000000..ced734a
--- /dev/null
+++ b/gemrb/core/SaveGameIterator.cpp
@@ -0,0 +1,634 @@
+/* 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 "SaveGameIterator.h"
+
+#include "iless.h"
+#include "strrefs.h"
+#include "win32def.h"
+
+#include "DisplayMessage.h"
+#include "GameData.h" // For ResourceHolder
+#include "ImageMgr.h"
+#include "ImageWriter.h"
+#include "Interface.h"
+#include "SaveGameMgr.h"
+#include "Video.h"
+#include "GUI/GameControl.h"
+
+#if defined(__HAIKU__)
+#include <unistd.h>
+#endif
+
+#include <cassert>
+#include <set>
+#include <time.h>
+
+const TypeID SaveGame::ID = { "SaveGame" };
+
+/** Extract date from save game ds into Date. */
+static void ParseGameDate(DataStream *ds, char *Date)
+{
+ Date[0] = '\0';
+
+ char Signature[8];
+ ieDword GameTime;
+ ds->Read(Signature, 8);
+ ds->ReadDword(&GameTime);
+ delete ds;
+ if (memcmp(Signature,"GAME",4) ) {
+ strcpy(Date, "ERROR");
+ return;
+ }
+
+ int hours = ((int)GameTime)/300;
+ int days = hours/24;
+ hours -= days*24;
+ char *a=NULL,*b=NULL,*c=NULL;
+
+ core->GetTokenDictionary()->SetAtCopy("GAMEDAYS", days);
+ if (days) {
+ if (days==1) a=core->GetString(10698);
+ else a=core->GetString(10697);
+ }
+ core->GetTokenDictionary()->SetAtCopy("HOUR", hours);
+ if (hours || !a) {
+ if (a) b=core->GetString(10699);
+ if (hours==1) c=core->GetString(10701);
+ else c=core->GetString(10700);
+ }
+ if (b) {
+ strcat(Date, a);
+ strcat(Date, " ");
+ strcat(Date, b);
+ strcat(Date, " ");
+ if (c)
+ strcat(Date, c);
+ } else {
+ if (a)
+ strcat(Date, a);
+ if (c)
+ strcat(Date, c);
+ }
+ core->FreeString(a);
+ core->FreeString(b);
+ core->FreeString(c);
+}
+
+SaveGame::SaveGame(const char* path, const char* name, const char* prefix, const char* slotname, int pCount, int saveID)
+{
+ strncpy( Prefix, prefix, sizeof( Prefix ) );
+ strncpy( Path, path, sizeof( Path ) );
+ strncpy( Name, name, sizeof( Name ) );
+ strncpy( SlotName, slotname, sizeof( SlotName ) );
+ PortraitCount = pCount;
+ SaveID = saveID;
+ char nPath[_MAX_PATH];
+ struct stat my_stat;
+ PathJoinExt(nPath, Path, Prefix, "bmp");
+ memset(&my_stat,0,sizeof(my_stat));
+ stat( nPath, &my_stat );
+ strftime( Date, _MAX_PATH, "%c", localtime( (time_t*)&my_stat.st_mtime ) );
+ manager.AddSource(Path, Name, PLUGIN_RESOURCE_DIRECTORY);
+ GameDate[0] = '\0';
+}
+
+SaveGame::~SaveGame()
+{
+}
+
+Sprite2D* SaveGame::GetPortrait(int index) const
+{
+ if (index > PortraitCount) {
+ return NULL;
+ }
+ char nPath[_MAX_PATH];
+ sprintf( nPath, "PORTRT%d", index );
+ ResourceHolder<ImageMgr> im(nPath, manager, true);
+ if (!im)
+ return NULL;
+ return im->GetSprite2D();
+}
+
+Sprite2D* SaveGame::GetPreview() const
+{
+ ResourceHolder<ImageMgr> im(Prefix, manager, true);
+ if (!im)
+ return NULL;
+ return im->GetSprite2D();
+}
+
+DataStream* SaveGame::GetGame() const
+{
+ return manager.GetResource(Prefix, IE_GAM_CLASS_ID, true);
+}
+
+DataStream* SaveGame::GetWmap(int idx) const
+{
+ return manager.GetResource(core->WorldMapName[idx], IE_WMP_CLASS_ID, true);
+}
+
+DataStream* SaveGame::GetSave() const
+{
+ return manager.GetResource(Prefix, IE_SAV_CLASS_ID, true);
+}
+
+const char* SaveGame::GetGameDate() const
+{
+ if (GameDate[0] == '\0')
+ ParseGameDate(GetGame(), GameDate);
+ return GameDate;
+}
+
+SaveGameIterator::SaveGameIterator(void)
+{
+}
+
+SaveGameIterator::~SaveGameIterator(void)
+{
+}
+
+/* mission pack save */
+static const char* SaveDir()
+{
+ ieDword playmode = 0;
+ core->GetDictionary()->Lookup( "SaveDir", playmode );
+ if (playmode == 1) {
+ return "mpsave";
+ }
+ return "save";
+}
+
+#define FormatQuickSavePath(destination, i) \
+ snprintf(destination,sizeof(destination),"%s%s%s%09d-%s", \
+ core->SavePath,SaveDir(), SPathDelimiter,i,folder);
+
+/*
+ * Returns the first 0 bit position of an integer
+ */
+static int GetHole(int n)
+{
+ int mask = 1;
+ int value = 0;
+ while(n&mask) {
+ mask<<=1;
+ value++;
+ }
+ return value;
+}
+
+/*
+ * Returns the age of a quickslot entry. Returns 0 if it isn't a quickslot
+ */
+static int IsQuickSaveSlot(const char* match, const char* slotname)
+{
+ char savegameName[_MAX_PATH];
+ int savegameNumber = 0;
+ int cnt = sscanf( slotname, SAVEGAME_DIRECTORY_MATCHER, &savegameNumber, savegameName );
+ if (cnt != 2) {
+ return 0;
+ }
+ if (stricmp(savegameName, match) )
+ {
+ return 0;
+ }
+ return savegameNumber;
+}
+/*
+ * Return true if directory Path/slotname is a potential save game
+ * slot, otherwise return false.
+ */
+static bool IsSaveGameSlot(const char* Path, const char* slotname)
+{
+ char savegameName[_MAX_PATH];
+ int savegameNumber = 0;
+
+ if (slotname[0] == '.')
+ return false;
+
+ int cnt = sscanf( slotname, SAVEGAME_DIRECTORY_MATCHER, &savegameNumber, savegameName );
+ if (cnt != 2) {
+ //The matcher didn't match: either this is not a valid dir
+ //or the SAVEGAME_DIRECTORY_MATCHER needs updating.
+ printMessage( "SaveGameIterator", " ", LIGHT_RED );
+ printf( "Invalid savegame directory '%s' in %s.\n", slotname, Path );
+ return false;
+ }
+
+ //The matcher got matched correctly.
+ char dtmp[_MAX_PATH];
+ PathJoin(dtmp, Path, slotname, NULL);
+
+ char ftmp[_MAX_PATH];
+ PathJoinExt(ftmp, dtmp, core->GameNameResRef, "bmp");
+
+ if (access( ftmp, R_OK )) {
+ printMessage("SaveGameIterator"," ",YELLOW);
+ printf("Ignoring slot %s because of no appropriate preview!\n", dtmp);
+ return false;
+ }
+
+ PathJoinExt(ftmp, dtmp, core->WorldMapName[0], "wmp");
+ if (access( ftmp, R_OK )) {
+ printMessage("SaveGameIterator"," ",YELLOW);
+ printf("Ignoring slot %s because of no appropriate worldmap!\n", dtmp);
+ return false;
+ }
+
+ /* we might need something here as well
+ PathJoinExt(ftmp, dtmp, core->WorldMapName[1], "wmp");
+ if (access( ftmp, R_OK )) {
+ printMessage("SaveGameIterator"," ",YELLOW);
+ printf("Ignoring slot %s because of no appropriate worldmap!\n", dtmp);
+ return false;
+ }
+ */
+
+ return true;
+}
+
+bool SaveGameIterator::RescanSaveGames()
+{
+ // delete old entries
+ save_slots.clear();
+
+ char Path[_MAX_PATH];
+ PathJoin(Path, core->SavePath, SaveDir(), NULL);
+
+ DirectoryIterator dir(Path);
+ // create the save game directory at first access
+ if (!dir) {
+ mkdir(Path,S_IWRITE|S_IREAD|S_IEXEC);
+ chmod(Path,S_IWRITE|S_IREAD|S_IEXEC);
+ dir.Rewind();
+ }
+ if (!dir) { //If we cannot open the Directory
+ return false;
+ }
+
+ std::set<char*,iless> slots;
+ do {
+ const char *name = dir.GetName();
+ if (dir.IsDirectory() && IsSaveGameSlot( Path, name )) {
+ slots.insert(strdup(name));
+ }
+ } while (++dir);
+
+ for (std::set<char*,iless>::iterator i = slots.begin(); i != slots.end(); i++) {
+ save_slots.push_back(BuildSaveGame(*i));
+ free(*i);
+ }
+
+ return true;
+}
+
+const std::vector<Holder<SaveGame> >& SaveGameIterator::GetSaveGames()
+{
+ RescanSaveGames();
+
+ return save_slots;
+}
+
+Holder<SaveGame> SaveGameIterator::GetSaveGame(const char *name)
+{
+ RescanSaveGames();
+
+ for (std::vector<Holder<SaveGame> >::iterator i = save_slots.begin(); i != save_slots.end(); i++) {
+ if (strcmp(name, (*i)->GetName()) == 0)
+ return *i;
+ }
+ return NULL;
+}
+
+Holder<SaveGame> SaveGameIterator::BuildSaveGame(const char *slotname)
+{
+ if (!slotname) {
+ return NULL;
+ }
+
+ int prtrt = 0;
+ char Path[_MAX_PATH];
+ //lets leave space for the filenames
+ PathJoin(Path, core->SavePath, SaveDir(), slotname, NULL);
+
+ char savegameName[_MAX_PATH]={0};
+ int savegameNumber = 0;
+
+ int cnt = sscanf( slotname, SAVEGAME_DIRECTORY_MATCHER, &savegameNumber, savegameName );
+ //maximum pathlength == 240, without 8+3 filenames
+ if ( (cnt != 2) || (strlen(Path)>240) ) {
+ printf( "Invalid savegame directory '%s' in %s.\n", slotname, Path );
+ return NULL;
+ }
+
+ DirectoryIterator dir(Path);
+ if (!dir) {
+ return NULL;
+ }
+ do {
+ if (strnicmp( dir.GetName(), "PORTRT", 6 ) == 0)
+ prtrt++;
+ } while (++dir);
+
+ SaveGame* sg = new SaveGame( Path, savegameName, core->GameNameResRef, slotname, prtrt, savegameNumber );
+ return sg;
+}
+
+void SaveGameIterator::PruneQuickSave(const char *folder)
+{
+ char from[_MAX_PATH];
+ char to[_MAX_PATH];
+
+ //storing the quicksave ages in an array
+ std::vector<int> myslots;
+ for (charlist::iterator m = save_slots.begin();m!=save_slots.end();m++) {
+ int tmp = IsQuickSaveSlot(folder, (*m)->GetSlotName() );
+ if (tmp) {
+ size_t pos = myslots.size();
+ while(pos-- && myslots[pos]>tmp) ;
+ myslots.insert(myslots.begin()+pos+1,tmp);
+ }
+ }
+ //now we got an integer array in myslots
+ size_t size = myslots.size();
+
+ if (!size) {
+ return;
+ }
+
+ int n=myslots[size-1];
+ size_t hole = GetHole(n);
+ size_t i;
+ if (hole<size) {
+ //prune second path
+ FormatQuickSavePath(from, myslots[hole]);
+ myslots.erase(myslots.begin()+hole);
+ core->DelTree(from, false);
+ rmdir(from);
+ }
+ //shift paths, always do this, because they are aging
+ size = myslots.size();
+ for(i=size;i--;) {
+ FormatQuickSavePath(from, myslots[i]);
+ FormatQuickSavePath(to, myslots[i]+1);
+ rename(from,to);
+ }
+}
+
+/** Save game to given directory */
+static bool DoSaveGame(const char *Path)
+{
+ Game *game = core->GetGame();
+ //saving areas to cache currently in memory
+ unsigned int mc = (unsigned int) game->GetLoadedMapCount();
+ while (mc--) {
+ Map *map = game->GetMap(mc);
+ if (core->SwapoutArea(map)) {
+ return false;
+ }
+ }
+
+ //compress files in cache named: .STO and .ARE
+ //no .CRE would be saved in cache
+ if (core->CompressSave(Path)) {
+ return false;
+ }
+
+ //Create .gam file from Game() object
+ if (core->WriteGame(Path)) {
+ return false;
+ }
+
+ //Create .wmp file from WorldMap() object
+ if (core->WriteWorldMap(Path)) {
+ return false;
+ }
+
+ PluginHolder<ImageWriter> im(PLUGIN_IMAGE_WRITER_BMP);
+ if (!im) {
+ printMessage( "SaveGameIterator", "Couldn't create the BMPWriter!\n", LIGHT_RED );
+ return false;
+ }
+
+ //Create portraits
+ for (int i = 0; i < game->GetPartySize( false ); i++) {
+ Sprite2D* portrait = core->GetGameControl()->GetPortraitPreview( i );
+ if (portrait) {
+ char FName[_MAX_PATH];
+ snprintf( FName, sizeof(FName), "PORTRT%d", i );
+ FileStream outfile;
+ outfile.Create( Path, FName, IE_BMP_CLASS_ID );
+ im->PutImage( &outfile, portrait );
+ }
+ }
+
+ // Create area preview
+ Sprite2D* preview = core->GetGameControl()->GetPreview();
+ FileStream outfile;
+ outfile.Create( Path, core->GameNameResRef, IE_BMP_CLASS_ID );
+ im->PutImage( &outfile, preview );
+
+ return true;
+}
+
+int CanSave()
+{
+ //some of these restrictions might not be needed
+ Store * store = core->GetCurrentStore();
+ if (store) {
+ return 1; //can't save while store is open
+ }
+ GameControl *gc = core->GetGameControl();
+ if (!gc) {
+ return -1; //no gamecontrol!!!
+ }
+ if (gc->GetDialogueFlags()&DF_IN_DIALOG) {
+ return 2; //can't save while in dialog?
+ }
+
+ //TODO: can't save while in combat
+ Game *game = core->GetGame();
+ if (!game) {
+ return -1;
+ }
+ if (game->CombatCounter) {
+ return 3;
+ }
+
+ Map *map = game->GetCurrentArea();
+ if (!map) {
+ return -1;
+ }
+
+ if (map->AreaFlags&AF_SAVE) {
+ //cannot save in area
+ return 4;
+ }
+
+ int i = game->GetPartySize(true);
+ while(i--) {
+ Actor *actor = game->GetPC(i, true);
+ //TODO: can't save while (party) actors are in helpless states
+ if (actor->GetStat(IE_STATE_ID) & STATE_NOSAVE) {
+ //some actor is in nosave state
+ return 5;
+ }
+ if (actor->GetCurrentArea()!=map) {
+ //scattered
+ return 6;
+ }
+ }
+
+ //TODO: can't save while AOE spells are in effect
+ //TODO: can't save while IF_NOINT is set on any actor
+
+ return 0;
+}
+
+static void CreateSavePath(char *Path, int index, const char *slotname)
+{
+ PathJoin( Path, core->SavePath, SaveDir(), NULL );
+
+ //if the path exists in different case, don't make it again
+ mkdir(Path,S_IWRITE|S_IREAD|S_IEXEC);
+ chmod(Path,S_IWRITE|S_IREAD|S_IEXEC);
+ //keep the first part we already determined existing
+
+ char dir[_MAX_PATH];
+ snprintf( dir, _MAX_PATH, "%09d-%s", index, slotname );
+ PathJoin(Path, Path, dir, NULL);
+ //this is required in case the old slot wasn't recognised but still there
+ core->DelTree(Path, false);
+ mkdir(Path,S_IWRITE|S_IREAD|S_IEXEC);
+ chmod(Path,S_IWRITE|S_IREAD|S_IEXEC);
+}
+
+int SaveGameIterator::CreateSaveGame(int index, bool mqs)
+{
+ AutoTable tab("savegame");
+ const char *slotname = NULL;
+ int qsave = 0;
+
+ if (tab) {
+ slotname = tab->QueryField(index);
+ qsave = atoi(tab->QueryField(index, 1));
+ }
+
+ if (mqs) {
+ assert(qsave);
+ PruneQuickSave(slotname);
+ }
+
+ if (int cansave = CanSave())
+ return cansave;
+
+ //if index is not an existing savegame, we create a unique slotname
+ for (size_t i = 0; i < save_slots.size(); ++i) {
+ Holder<SaveGame> save = save_slots[i];
+ if (save->GetSaveID() == index) {
+ DeleteSaveGame(save);
+ break;
+ }
+ }
+ char Path[_MAX_PATH];
+ CreateSavePath(Path, index, slotname);
+ GameControl *gc = core->GetGameControl();
+
+ if (!DoSaveGame(Path)) {
+ displaymsg->DisplayConstantString(STR_CANTSAVE, 0xbcefbc);
+ if (gc) {
+ gc->SetDisplayText(STR_CANTSAVE, 30);
+ }
+ return -1;
+ }
+
+ // Save succesful / Quick-save succesful
+ if (qsave) {
+ displaymsg->DisplayConstantString(STR_QSAVESUCCEED, 0xbcefbc);
+ if (gc) {
+ gc->SetDisplayText(STR_QSAVESUCCEED, 30);
+ }
+ } else {
+ displaymsg->DisplayConstantString(STR_SAVESUCCEED, 0xbcefbc);
+ if (gc) {
+ gc->SetDisplayText(STR_SAVESUCCEED, 30);
+ }
+ }
+ return 0;
+}
+
+int SaveGameIterator::CreateSaveGame(Holder<SaveGame> save, const char *slotname)
+{
+ if (!slotname) {
+ return -1;
+ }
+
+ if (int cansave = CanSave())
+ return cansave;
+
+ GameControl *gc = core->GetGameControl();
+ int index;
+
+ if (save) {
+ index = save->GetSaveID();
+
+ DeleteSaveGame(save);
+ save.release();
+ } else {
+ //leave space for autosaves
+ //probably the hardcoded slot names should be read by this object
+ //in that case 7 == size of hardcoded slot names array (savegame.2da)
+ index = 7;
+ for (size_t i = 0; i < save_slots.size(); ++i) {
+ Holder<SaveGame> save = save_slots[i];
+ if (save->GetSaveID() >= index) {
+ index = save->GetSaveID() + 1;
+ }
+ }
+ }
+
+ char Path[_MAX_PATH];
+ CreateSavePath(Path, index, slotname);
+
+ if (!DoSaveGame(Path)) {
+ displaymsg->DisplayConstantString(STR_CANTSAVE, 0xbcefbc);
+ if (gc) {
+ gc->SetDisplayText(STR_CANTSAVE, 30);
+ }
+ return -1;
+ }
+
+ // Save succesful
+ displaymsg->DisplayConstantString(STR_SAVESUCCEED, 0xbcefbc);
+ if (gc) {
+ gc->SetDisplayText(STR_SAVESUCCEED, 30);
+ }
+ return 0;
+}
+
+void SaveGameIterator::DeleteSaveGame(Holder<SaveGame> game)
+{
+ if (!game) {
+ return;
+ }
+
+ core->DelTree( game->GetPath(), false ); //remove all files from folder
+ rmdir( game->GetPath() );
+}
diff --git a/gemrb/core/SaveGameIterator.h b/gemrb/core/SaveGameIterator.h
new file mode 100644
index 0000000..765ad9e
--- /dev/null
+++ b/gemrb/core/SaveGameIterator.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 SAVEGAMEITERATOR_H
+#define SAVEGAMEITERATOR_H
+
+#include "exports.h"
+
+#include "SaveGame.h"
+
+#include <vector>
+
+#define SAVEGAME_DIRECTORY_MATCHER "%d - %[A-Za-z0-9- _]"
+
+class GEM_EXPORT SaveGameIterator {
+private:
+ typedef std::vector<Holder<SaveGame> > charlist;
+ charlist save_slots;
+
+public:
+ SaveGameIterator(void);
+ ~SaveGameIterator(void);
+ const charlist& GetSaveGames();
+ void DeleteSaveGame(Holder<SaveGame>);
+ int CreateSaveGame(Holder<SaveGame>, const char *slotname);
+ int CreateSaveGame(int index, bool mqs = false);
+ Holder<SaveGame> GetSaveGame(const char *slotname);
+private:
+ bool RescanSaveGames();
+ static Holder<SaveGame> BuildSaveGame(const char *slotname);
+ void PruneQuickSave(const char *folder);
+};
+
+#endif
diff --git a/gemrb/core/SaveGameMgr.cpp b/gemrb/core/SaveGameMgr.cpp
new file mode 100644
index 0000000..f7e6164
--- /dev/null
+++ b/gemrb/core/SaveGameMgr.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 "SaveGameMgr.h"
+
+SaveGameMgr::SaveGameMgr(void)
+{
+}
+
+SaveGameMgr::~SaveGameMgr(void)
+{
+}
diff --git a/gemrb/core/SaveGameMgr.h b/gemrb/core/SaveGameMgr.h
new file mode 100644
index 0000000..20cb911
--- /dev/null
+++ b/gemrb/core/SaveGameMgr.h
@@ -0,0 +1,38 @@
+/* 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 SAVEGAMEMGR_H
+#define SAVEGAMEMGR_H
+
+#include "Game.h"
+#include "Plugin.h"
+#include "System/DataStream.h"
+
+class GEM_EXPORT SaveGameMgr : public Plugin {
+public:
+ SaveGameMgr(void);
+ virtual ~SaveGameMgr(void);
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual Game* LoadGame(Game *newGame, int ver_override = 0) = 0;
+
+ virtual int GetStoredFileSize(Game *game) = 0;
+ virtual int PutGame(DataStream* stream, Game *game) = 0;
+};
+
+#endif
diff --git a/gemrb/core/ScriptEngine.cpp b/gemrb/core/ScriptEngine.cpp
new file mode 100644
index 0000000..9c3bdb6
--- /dev/null
+++ b/gemrb/core/ScriptEngine.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 "ScriptEngine.h"
+
+ScriptEngine::ScriptEngine(void)
+{
+}
+
+ScriptEngine::~ScriptEngine(void)
+{
+}
diff --git a/gemrb/core/ScriptEngine.h b/gemrb/core/ScriptEngine.h
new file mode 100644
index 0000000..94ec6b6
--- /dev/null
+++ b/gemrb/core/ScriptEngine.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 SCRIPTENGINE_H
+#define SCRIPTENGINE_H
+
+#include "Plugin.h"
+
+class GEM_EXPORT ScriptEngine : public Plugin {
+public:
+ ScriptEngine(void);
+ virtual ~ScriptEngine(void);
+ /** Initialization Routine */
+ virtual bool Init(void) = 0;
+ /** Load Script */
+ virtual bool LoadScript(const char* filename) = 0;
+ /** Run Function */
+ virtual bool RunFunction(const char *ModuleName, const char* FunctionName, bool error=true, int intparam=-1) = 0;
+ /** Exec a single String */
+ virtual void ExecString(const char* string) = 0;
+};
+
+#endif
diff --git a/gemrb/core/Scriptable/Actor.cpp b/gemrb/core/Scriptable/Actor.cpp
new file mode 100644
index 0000000..9704647
--- /dev/null
+++ b/gemrb/core/Scriptable/Actor.cpp
@@ -0,0 +1,7078 @@
+/* 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 the .cre (creature) files.
+//Any player or non-player character is a creature.
+//Actor is a scriptable object (Scriptable). See Scriptable.cpp
+
+#include "Scriptable/Actor.h"
+
+#include "ie_feats.h"
+#include "overlays.h"
+#include "strrefs.h"
+#include "opcode_params.h"
+#include "win32def.h"
+
+#include "Audio.h" //pst (react to death sounds)
+#include "DialogHandler.h" // checking for dialog
+#include "Game.h"
+#include "DisplayMessage.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "Item.h"
+#include "PolymorphCache.h" // stupid polymorph cache hack
+#include "Projectile.h"
+#include "ProjectileServer.h"
+#include "ScriptEngine.h"
+#include "Spell.h"
+#include "TableMgr.h"
+#include "Video.h"
+#include "damages.h"
+#include "GameScript/GSUtils.h" //needed for DisplayStringCore
+#include "GameScript/GameScript.h"
+#include "GUI/GameControl.h"
+
+#include <cassert>
+
+//configurable?
+ieDword ref_lightness = 43;
+
+static const Color white = {
+ 0xff, 0xff, 0xff, 0xff
+};
+static const Color green = {
+ 0x00, 0xff, 0x00, 0xff
+};
+static const Color red = {
+ 0xff, 0x00, 0x00, 0xff
+};
+static const Color yellow = {
+ 0xff, 0xff, 0x00, 0xff
+};
+static const Color cyan = {
+ 0x00, 0xff, 0xff, 0xff
+};
+static const Color magenta = {
+ 0xff, 0x00, 0xff, 0xff
+};
+
+static int sharexp = SX_DIVIDE;
+static int classcount = -1;
+static int extraslots = -1;
+static char **clericspelltables = NULL;
+static char **druidspelltables = NULL;
+static char **wizardspelltables = NULL;
+static char **classabilities = NULL;
+static int *turnlevels = NULL;
+static int *booktypes = NULL;
+static int *xpbonus = NULL;
+static int xpbonustypes = -1;
+static int xpbonuslevels = -1;
+static int **levelslots = NULL;
+static int *dualswap = NULL;
+static int *maxhpconbon = NULL;
+static int *skillstats = NULL;
+static int *skillabils = NULL;
+static int skillcount = -1;
+static ieVariable CounterNames[4]={"GOOD","LAW","LADY","MURDER"};
+
+static int FistRows = -1;
+static int *wmlevels[20];
+typedef ieResRef FistResType[MAX_LEVEL+1];
+
+static FistResType *fistres = NULL;
+static int *fistresclass = NULL;
+static ieResRef DefaultFist = {"FIST"};
+
+static int VCMap[VCONST_COUNT];
+
+//item usability array
+struct ItemUseType {
+ ieResRef table; //which table contains the stat usability flags
+ ieByte stat; //which actor stat we talk about
+ ieByte mcol; //which column should be matched against the stat
+ ieByte vcol; //which column has the bit value for it
+ ieByte which; //which item dword should be used (1 = kit)
+};
+
+static ItemUseType *itemuse = NULL;
+static int usecount = -1;
+static bool pstflags = false;
+static bool nocreate = false;
+//used in many places, but different in engines
+static ieDword state_invisible = STATE_INVISIBLE;
+
+//item animation override array
+struct ItemAnimType {
+ ieResRef itemname;
+ ieByte animation;
+};
+
+static ItemAnimType *itemanim = NULL;
+static int animcount = -1;
+
+static int fiststat = IE_CLASS;
+
+//conversion for 3rd ed
+static int isclass[11]={0,0,0,0,0,0,0,0,0,0,0};
+
+static const int mcwasflags[11] = {
+ MC_WAS_FIGHTER, MC_WAS_MAGE, MC_WAS_THIEF, 0, 0, MC_WAS_CLERIC,
+ MC_WAS_DRUID, 0, 0, MC_WAS_RANGER, 0};
+static const char *isclassnames[11] = {
+ "FIGHTER", "MAGE", "THIEF", "BARBARIAN", "BARD", "CLERIC",
+ "DRUID", "MONK", "PALADIN", "RANGER", "SORCERER" };
+
+//fighter is the default level here
+//fixme, make this externalized
+static const int levelslotsbg[21]={ISFIGHTER, ISMAGE, ISFIGHTER, ISCLERIC, ISTHIEF,
+ ISBARD, ISPALADIN, 0, 0, 0, 0, ISDRUID, ISRANGER, 0,0,0,0,0,0,ISSORCERER, ISMONK};
+static const int levelslotsiwd2[11]={IE_LEVELFIGHTER,IE_LEVELMAGE,IE_LEVELTHIEF,
+ IE_LEVELBARBARIAN,IE_LEVELBARD,IE_LEVELCLERIC,IE_LEVELDRUID,IE_LEVELMONK,
+ IE_LEVELPALADIN,IE_LEVELRANGER,IE_LEVELSORCEROR};
+static const unsigned int classesiwd2[ISCLASSES]={5, 11, 9, 1, 2, 3, 4, 6, 7, 8, 10};
+
+//stat values are 0-255, so a byte is enough
+static ieByte featstats[MAX_FEATS]={0
+};
+
+//holds the wspecial table for weapon prof bonuses
+#define WSPECIAL_COLS 3
+static int wspecial_max = 0;
+static int wspattack_rows = 0;
+static int wspattack_cols = 0;
+static int **wspecial = NULL;
+static int **wspattack = NULL;
+
+//holds the weapon style bonuses
+#define STYLE_MAX 3
+static int **wsdualwield = NULL;
+static int **wstwohanded = NULL;
+static int **wsswordshield = NULL;
+static int **wssingle = NULL;
+
+//unhardcoded monk bonuses
+static int **monkbon = NULL;
+static unsigned int monkbon_cols = 0;
+static unsigned int monkbon_rows = 0;
+
+// reputation modifiers
+#define CLASS_PCCUTOFF 32
+#define CLASS_INNOCENT 155
+#define CLASS_FLAMINGFIST 156
+
+static ActionButtonRow *GUIBTDefaults = NULL; //qslots row count
+static ActionButtonRow2 *OtherGUIButtons = NULL;
+ActionButtonRow DefaultButtons = {ACT_TALK, ACT_WEAPON1, ACT_WEAPON2,
+ ACT_QSPELL1, ACT_QSPELL2, ACT_QSPELL3, ACT_CAST, ACT_USE, ACT_QSLOT1, ACT_QSLOT2,
+ ACT_QSLOT3, ACT_INNATE};
+static int QslotTranslation = false;
+static int DeathOnZeroStat = true;
+static ieDword TranslucentShadows = 0;
+static int ProjectileSize = 0; //the size of the projectile immunity bitfield (dwords)
+
+static const char iwd2gemrb[32] = {
+ 0,0,20,2,22,25,0,14,
+ 15,23,13,0,1,24,8,21,
+ 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0
+};
+static const char gemrb2iwd[32] = {
+ 11,12,3,71,72,73,0,0, //0
+ 14,80,83,82,81,10,7,8, //8
+ 0,0,0,0,2,15,4,9, //16
+ 13,5,0,0,0,0,0,0 //24
+};
+
+//letters for char sound resolution bg1/bg2
+static char csound[VCONST_COUNT];
+
+static void InitActorTables();
+
+#define DAMAGE_LEVELS 19
+#define ATTACKROLL 20
+#define SAVEROLL 20
+#define DEFAULTAC 10
+
+//TODO: externalise
+#define TURN_PANIC_LVL_MOD 3
+#define TURN_DEATH_LVL_MOD 7
+
+static ieResRef d_main[DAMAGE_LEVELS] = {
+ //slot 0 is not used in the original engine
+ "BLOODCR","BLOODS","BLOODM","BLOODL", //blood
+ "SPFIRIMP","SPFIRIMP","SPFIRIMP", //fire
+ "SPSHKIMP","SPSHKIMP","SPSHKIMP", //spark
+ "SPFIRIMP","SPFIRIMP","SPFIRIMP", //ice
+ "SHACID","SHACID","SHACID", //acid
+ "SPDUSTY2","SPDUSTY2","SPDUSTY2" //disintegrate
+};
+static ieResRef d_splash[DAMAGE_LEVELS] = {
+ "","","","",
+ "SPBURN","SPBURN","SPBURN", //flames
+ "SPSPARKS","SPSPARKS","SPSPARKS", //sparks
+ "","","",
+ "","","",
+ "","",""
+};
+
+#define BLOOD_GRADIENT 19
+#define FIRE_GRADIENT 19
+#define ICE_GRADIENT 71
+#define STONE_GRADIENT 93
+
+static int d_gradient[DAMAGE_LEVELS] = {
+ BLOOD_GRADIENT,BLOOD_GRADIENT,BLOOD_GRADIENT,BLOOD_GRADIENT,
+ FIRE_GRADIENT,FIRE_GRADIENT,FIRE_GRADIENT,
+ -1,-1,-1,
+ ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT,
+ -1,-1,-1,
+ -1,-1,-1
+};
+
+static ieResRef hc_overlays[OVERLAY_COUNT]={"SANCTRY","SPENTACI","SPMAGGLO","SPSHIELD",
+"GREASED","WEBENTD","MINORGLB","","","","","","","","","","","","","","",
+"","","","SPTURNI2","SPTURNI","","","","","",""};
+static ieDword hc_locations=0x2ba80030;
+static int hc_flags[OVERLAY_COUNT];
+#define HC_INVISIBLE 1
+
+static int *mxsplwis = NULL;
+static int spllevels;
+
+//for every game except IWD2 we need to reverse TOHIT
+static int ReverseToHit=true;
+static int CheckAbilities=false;
+
+//internal flags for calculating to hit
+#define WEAPON_FIST 0
+#define WEAPON_MELEE 1
+#define WEAPON_RANGED 2
+#define WEAPON_STYLEMASK 15
+#define WEAPON_LEFTHAND 16
+#define WEAPON_USESTRENGTH 32
+
+/* counts the on bits in a number */
+ieDword bitcount (ieDword n)
+{
+ ieDword count=0;
+ while (n) {
+ count += n & 0x1u;
+ n >>= 1;
+ }
+ return count;
+}
+
+void ReleaseMemoryActor()
+{
+ if (mxsplwis) {
+ //calloc'd x*y integer matrix
+ free (mxsplwis);
+ mxsplwis = NULL;
+ }
+
+ if (fistres) {
+ delete [] fistres;
+ fistres = NULL;
+ delete [] fistresclass;
+ fistresclass = NULL;
+ }
+
+ if (itemuse) {
+ delete [] itemuse;
+ itemuse = NULL;
+ }
+
+ if (itemanim) {
+ delete [] itemanim;
+ itemanim = NULL;
+ }
+ FistRows = -1;
+}
+
+Actor::Actor()
+ : Movable( ST_ACTOR )
+{
+ int i;
+
+ for (i = 0; i < MAX_STATS; i++) {
+ BaseStats[i] = 0;
+ Modified[i] = 0;
+ }
+ PrevStats = NULL;
+
+ SmallPortrait[0] = 0;
+ LargePortrait[0] = 0;
+
+ anims = NULL;
+ ShieldRef[0]=0;
+ HelmetRef[0]=0;
+ WeaponRef[0]=0;
+ for (i = 0; i < EXTRA_ACTORCOVERS; ++i)
+ extraCovers[i] = NULL;
+
+ LongName = NULL;
+ ShortName = NULL;
+ LongStrRef = (ieStrRef) -1;
+ ShortStrRef = (ieStrRef) -1;
+
+ LastProtected = 0;
+ LastFollowed = 0;
+ LastCommander = 0;
+ LastHelp = 0;
+ LastSeen = 0;
+ LastMarked = 0;
+ LastMarkedSpell = 0;
+ LastHeard = 0;
+ PCStats = NULL;
+ LastCommand = 0; //used by order
+ LastShout = 0; //used by heard
+ LastDamage = 0;
+ LastDamageType = 0;
+ LastTurner = 0;
+ LastExit = 0;
+ HotKey = 0;
+ attackcount = 0;
+ secondround = 0;
+ attacksperround = 0;
+ nextattack = 0;
+ nextWalk = 0;
+ lastattack = 0;
+ InTrap = 0;
+ PathTries = 0;
+ TargetDoor = 0;
+ attackProjectile = NULL;
+ lastInit = 0;
+ roundTime = 0;
+ modalTime = 0;
+ modalSpellLingering = 0;
+ panicMode = PANIC_NONE;
+
+ inventory.SetInventoryType(INVENTORY_CREATURE);
+ Equipped = 0;
+ EquippedHeader = 0;
+
+ fxqueue.SetOwner( this );
+ inventory.SetOwner( this );
+ if (classcount<0) {
+ //This block is executed only once, when the first actor is loaded
+ InitActorTables();
+
+ TranslucentShadows = 0;
+ core->GetDictionary()->Lookup("Translucent Shadows", TranslucentShadows);
+ //get the needed size to store projectile immunity bitflags in Dwords
+ ProjectileSize = (core->GetProjectileServer()->GetHighestProjectileNumber()+31)/32;
+ //allowing 1024 bits (1024 projectiles ought to be enough for everybody)
+ //the rest of the projectiles would still work, but couldn't be resisted
+ if (ProjectileSize>32) {
+ ProjectileSize=32;
+ }
+ }
+ projectileImmunity = (ieDword *) calloc(ProjectileSize,sizeof(ieDword));
+ AppearanceFlags = 0;
+ SetDeathVar = IncKillCount = UnknownField = 0;
+ memset( DeathCounters, 0, sizeof(DeathCounters) );
+ InParty = 0;
+ TalkCount = 0;
+ InteractCount = 0; //numtimesinteracted depends on this
+ appearance = 0xffffff; //might be important for created creatures
+ RemovalTime = ~0;
+ HomeLocation.x = 0;
+ HomeLocation.y = 0;
+ version = 0;
+ //these are used only in iwd2 so we have to default them
+ for(i=0;i<7;i++) {
+ BaseStats[IE_HATEDRACE2+i]=0xff;
+ }
+ //this one is saved only for PC's
+ ModalState = 0;
+ //set it to a neutral value
+ ModalSpell[0] = '*';
+ LingeringModalSpell[0] = '*';
+ //this one is not saved
+ GotLUFeedback = false;
+ RollSaves();
+ WMLevelMod = 0;
+
+ polymorphCache = NULL;
+ memset(&wildSurgeMods, 0, sizeof(wildSurgeMods));
+}
+
+Actor::~Actor(void)
+{
+ unsigned int i;
+
+ delete anims;
+
+ core->FreeString( LongName );
+ core->FreeString( ShortName );
+
+ delete PCStats;
+
+ for (i = 0; i < vvcOverlays.size(); i++) {
+ if (vvcOverlays[i]) {
+ delete vvcOverlays[i];
+ vvcOverlays[i] = NULL;
+ }
+ }
+ for (i = 0; i < vvcShields.size(); i++) {
+ if (vvcShields[i]) {
+ delete vvcShields[i];
+ vvcShields[i] = NULL;
+ }
+ }
+ for (i = 0; i < EXTRA_ACTORCOVERS; i++)
+ delete extraCovers[i];
+
+ delete attackProjectile;
+ delete polymorphCache;
+
+ free(projectileImmunity);
+}
+
+void Actor::SetFistStat(ieDword stat)
+{
+ fiststat = stat;
+}
+
+void Actor::SetDefaultActions(int qslot, ieByte slot1, ieByte slot2, ieByte slot3)
+{
+ QslotTranslation=qslot;
+ DefaultButtons[0]=slot1;
+ DefaultButtons[1]=slot2;
+ DefaultButtons[2]=slot3;
+}
+
+void Actor::SetName(const char* ptr, unsigned char type)
+{
+ size_t len = strlen( ptr ) + 1;
+ //32 is the maximum possible length of the actor name in the original games
+ if (len>32) len=33;
+ if (type!=2) {
+ LongName = ( char * ) realloc( LongName, len );
+ memcpy( LongName, ptr, len );
+ core->StripLine( LongName, len );
+ }
+ if (type!=1) {
+ ShortName = ( char * ) realloc( ShortName, len );
+ memcpy( ShortName, ptr, len );
+ core->StripLine( ShortName, len );
+ }
+}
+
+void Actor::SetName(int strref, unsigned char type)
+{
+ if (type!=2) {
+ if (LongName) free(LongName);
+ LongName = core->GetString( strref, IE_STR_REMOVE_NEWLINE );
+ LongStrRef = strref;
+ }
+ if (type!=1) {
+ if (ShortName) free(ShortName);
+ ShortName = core->GetString( strref, IE_STR_REMOVE_NEWLINE );
+ ShortStrRef = strref;
+ }
+}
+
+void Actor::SetAnimationID(unsigned int AnimID)
+{
+ //if the palette is locked, then it will be transferred to the new animation
+ Palette *recover = NULL;
+
+ if (anims) {
+ if (anims->lockPalette) {
+ recover = anims->palette[PAL_MAIN];
+ }
+ // Take ownership so the palette won't be deleted
+ if (recover) {
+ recover->IncRef();
+ }
+ delete( anims );
+ }
+ //hacking PST no palette
+ if (core->HasFeature(GF_ONE_BYTE_ANIMID) ) {
+ if ((AnimID&0xf000)==0xe000) {
+ if (BaseStats[IE_COLORCOUNT]) {
+ printMessage("Actor"," ",YELLOW);
+ printf("Animation ID %x is supposed to be real colored (no recoloring), patched creature\n", AnimID);
+ }
+ BaseStats[IE_COLORCOUNT]=0;
+ }
+ }
+ anims = new CharAnimations( AnimID&0xffff, BaseStats[IE_ARMOR_TYPE]);
+ if(anims->ResRef[0] == 0) {
+ delete anims;
+ anims = NULL;
+ printMessage("Actor", " ",LIGHT_RED);
+ printf("Missing animation for %s\n",LongName);
+ return;
+ }
+ anims->SetOffhandRef(ShieldRef);
+ anims->SetHelmetRef(HelmetRef);
+ anims->SetWeaponRef(WeaponRef);
+
+ //if we have a recovery palette, then set it back
+ assert(anims->palette[PAL_MAIN] == 0);
+ anims->palette[PAL_MAIN] = recover;
+ if (recover) {
+ anims->lockPalette = true;
+ }
+ //bird animations are not hindered by searchmap
+ //only animtype==7 (bird) uses this feature
+ //this is a hardcoded hack, but works for all engine type
+ if (anims->GetAnimType()!=IE_ANI_BIRD) {
+ BaseStats[IE_DONOTJUMP]=0;
+ } else {
+ BaseStats[IE_DONOTJUMP]=DNJ_BIRD;
+ }
+ SetCircleSize();
+ anims->SetColors(BaseStats+IE_COLORS);
+
+ //Speed is determined by the number of frames in each cycle of its animation
+ // (beware! GetAnimation has side effects!)
+ // TODO: we should have a more efficient way to look this up
+ Animation** anim = anims->GetAnimation(IE_ANI_WALK, 0);
+ if (anim && anim[0]) {
+ SetBase(IE_MOVEMENTRATE, anim[0]->GetFrameCount()) ;
+ } else {
+ printMessage("Actor", "Unable to determine movement rate for animation ", YELLOW);
+ printf("%04x!\n", AnimID);
+ }
+
+}
+
+CharAnimations* Actor::GetAnims() const
+{
+ return anims;
+}
+
+/** Returns a Stat value (Base Value + Mod) */
+ieDword Actor::GetStat(unsigned int StatIndex) const
+{
+ if (StatIndex >= MAX_STATS) {
+ return 0xdadadada;
+ }
+ return Modified[StatIndex];
+}
+
+/** Always return a final stat value not partially calculated ones */
+ieDword Actor::GetSafeStat(unsigned int StatIndex) const
+{
+ if (StatIndex >= MAX_STATS) {
+ return 0xdadadada;
+ }
+ if (PrevStats) return PrevStats[StatIndex];
+ return Modified[StatIndex];
+}
+
+void Actor::SetCircleSize()
+{
+ const Color *color;
+ int color_index;
+
+ if (!anims)
+ return;
+
+ GameControl *gc = core->GetGameControl();
+ if (UnselectableTimer) {
+ color = &magenta;
+ color_index = 4;
+ } else if (Modified[IE_STATE_ID] & STATE_PANIC) {
+ color = &yellow;
+ color_index = 5;
+ } else if (gc && gc->dialoghandler->targetID == GetGlobalID() && (gc->GetDialogueFlags()&DF_IN_DIALOG)) {
+ color = &white;
+ color_index = 3; //?? made up
+ } else {
+ switch (Modified[IE_EA]) {
+ case EA_PC:
+ case EA_FAMILIAR:
+ case EA_ALLY:
+ case EA_CONTROLLED:
+ case EA_CHARMED:
+ case EA_EVILBUTGREEN:
+ case EA_GOODCUTOFF:
+ color = &green;
+ color_index = 0;
+ break;
+
+ case EA_ENEMY:
+ case EA_GOODBUTRED:
+ case EA_EVILCUTOFF:
+ color = &red;
+ color_index = 1;
+ break;
+ default:
+ color = &cyan;
+ color_index = 2;
+ break;
+ }
+ }
+
+ int csize = anims->GetCircleSize() - 1;
+ if (csize >= MAX_CIRCLE_SIZE)
+ csize = MAX_CIRCLE_SIZE - 1;
+
+ SetCircle( anims->GetCircleSize(), *color, core->GroundCircles[csize][color_index], core->GroundCircles[csize][(color_index == 0) ? 3 : color_index] );
+}
+
+static void ApplyClab_internal(Actor *actor, const char *clab, int level, bool remove)
+{
+ AutoTable table(clab);
+ if (table) {
+ int row = table->GetRowCount();
+ for(int i=0;i<level;i++) {
+ for (int j=0;j<row;j++) {
+ const char *res = table->QueryField(j,i);
+ if (res[0]=='*') continue;
+
+ if (!memcmp(res,"AP_",3)) {
+ if (remove) {
+ actor->fxqueue.RemoveAllEffects(res+3);
+ } else {
+ core->ApplySpell(res+3, actor, actor, 0);
+ }
+ }
+ else if (!memcmp(res,"GA_",3)) {
+ if (remove) {
+ actor->spellbook.RemoveSpell(res+3);
+ } else {
+ actor->LearnSpell(res+3, LS_MEMO);
+ }
+ }
+ else if (!memcmp(res,"FA_",3)) {//iwd2 only
+ //memorize these
+ int x=atoi(res+3);
+ ieResRef resref;
+ ResolveSpellName(resref, x);
+ actor->LearnSpell(resref, LS_MEMO);
+ }
+ else if (!memcmp(res,"FS_",3)) {//iwd2 only
+ //don't memorize these
+ int x=atoi(res+3);
+ ieResRef resref;
+ ResolveSpellName(resref, x);
+ actor->LearnSpell(resref, 0);
+ }
+ else if (!memcmp(res,"RA_",3)) {//iwd2 only
+ //remove ability
+ int x=atoi(res+3);
+ actor->spellbook.RemoveSpell(x);
+ }
+ }
+ }
+ }
+}
+
+#define BG2_KITMASK 0xffffc000
+#define KIT_BASECLASS 0x4000
+
+static ieDword GetKitIndex (ieDword kit, const char *resref="kitlist")
+{
+ int kitindex = 0;
+
+ if ((kit&BG2_KITMASK) == KIT_BASECLASS) {
+ kitindex = kit&0xfff;
+ }
+
+ // carefully looking for kit by the usability flag
+ // since the barbarian kit id clashes with the no-kit value
+ if (kitindex == 0 && kit != KIT_BASECLASS) {
+ Holder<TableMgr> tm = gamedata->GetTable(gamedata->LoadTable(resref) );
+ if (tm) {
+ kitindex = tm->FindTableValue(6, kit);
+ if (kitindex < 0) {
+ kitindex = 0;
+ }
+ }
+ }
+
+ return (ieDword)kitindex;
+}
+
+//applies a kit on the character (only bg2)
+bool Actor::ApplyKit(bool remove)
+{
+ ieDword kit = GetStat(IE_KIT);
+ ieDword kitclass = 0;
+ ieDword row = GetKitIndex(kit);
+ const char *clab = NULL;
+ ieDword max = 0;
+
+ if (row) {
+ //kit abilities
+ Holder<TableMgr> tm = gamedata->GetTable(gamedata->LoadTable("kitlist"));
+ if (tm) {
+ kitclass = (ieDword) atoi(tm->QueryField(row, 7));
+ clab = tm->QueryField(row, 4);
+ }
+ }
+
+ //multi class
+ if (multiclass) {
+ ieDword msk = 1;
+ for(unsigned int i=1;(i<(unsigned int) classcount) && (msk<=multiclass);i++) {
+ if (multiclass & msk) {
+ max = GetClassLevel(levelslotsbg[i]);
+ // don't apply/remove the old kit clab if the kit is disabled
+ if (i==kitclass && !IsDualClassed()) {
+ ApplyClab(clab, max, remove);
+ } else {
+ ApplyClab(classabilities[i], max, remove);
+ }
+ }
+ msk+=msk;
+ }
+ return true;
+ }
+ //single class
+ ieDword cls = GetStat(IE_CLASS);
+ if (cls<(ieDword) classcount) {
+ return false;
+ }
+ max = GetClassLevel(levelslotsbg[cls]);
+ if (kitclass==cls) {
+ ApplyClab(clab, max, remove);
+ } else {
+ ApplyClab(classabilities[cls], max, remove);
+ }
+ return true;
+}
+
+void Actor::ApplyClab(const char *clab, ieDword max, bool remove)
+{
+ if (clab[0]!='*') {
+ if (max) {
+ //singleclass
+ if (remove) {
+ ApplyClab_internal(this, clab, max, true);
+ } else {
+ ApplyClab_internal(this, clab, max, true);
+ ApplyClab_internal(this, clab, max, false);
+ }
+ }
+ }
+}
+
+//call this when morale or moralebreak changed
+//cannot use old or new value, because it is called two ways
+void pcf_morale (Actor *actor, ieDword /*oldValue*/, ieDword /*newValue*/)
+{
+ if ((actor->Modified[IE_MORALE]<=actor->Modified[IE_MORALEBREAK]) && (actor->Modified[IE_MORALEBREAK] != 0) ) {
+ //TODO: current attacker should be passed instead of NULL
+ actor->Panic(NULL, core->Roll(1,3,0) );
+ }
+ //for new colour
+ actor->SetCircleSize();
+}
+
+void pcf_ea (Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ if (actor->Selected && (newValue>EA_GOODCUTOFF) ) {
+ core->GetGame()->SelectActor(actor, false, SELECT_NORMAL);
+ }
+ actor->SetCircleSize();
+}
+
+//this is a good place to recalculate level up stuff
+void pcf_level (Actor *actor, ieDword oldValue, ieDword newValue)
+{
+ ieDword sum =
+ actor->GetFighterLevel()+
+ actor->GetMageLevel()+
+ actor->GetThiefLevel()+
+ actor->GetBarbarianLevel()+
+ actor->GetBardLevel()+
+ actor->GetClericLevel()+
+ actor->GetDruidLevel()+
+ actor->GetMonkLevel()+
+ actor->GetPaladinLevel()+
+ actor->GetRangerLevel()+
+ actor->GetSorcererLevel();
+ actor->SetBase(IE_CLASSLEVELSUM,sum);
+ actor->SetupFist();
+ if (newValue!=oldValue) {
+ actor->ApplyKit(false);
+ }
+ actor->GotLUFeedback = false;
+}
+
+void pcf_class (Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ actor->InitButtons(newValue, true);
+
+ int sorcerer=0;
+ if (newValue<(ieDword) classcount) {
+ switch(booktypes[newValue]) {
+ case 2: sorcerer = 1<<IE_SPELL_TYPE_WIZARD; break;
+ case 3: sorcerer = 1<<IE_SPELL_TYPE_PRIEST; break;
+ default: break;
+ }
+ }
+ actor->spellbook.SetBookType(sorcerer);
+}
+
+void pcf_animid(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ actor->SetAnimationID(newValue);
+}
+
+static const ieDword fullwhite[7]={ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT};
+
+static const ieDword fullstone[7]={STONE_GRADIENT,STONE_GRADIENT,STONE_GRADIENT,STONE_GRADIENT,STONE_GRADIENT,STONE_GRADIENT,STONE_GRADIENT};
+
+void pcf_state(Actor *actor, ieDword /*oldValue*/, ieDword State)
+{
+ if (actor->InParty) core->SetEventFlag(EF_PORTRAIT);
+ if (State & STATE_PETRIFIED) {
+ actor->SetLockedPalette(fullstone);
+ return;
+ }
+ if (State & STATE_FROZEN) {
+ actor->SetLockedPalette(fullwhite);
+ return;
+ }
+ //it is not enough to check the new state
+ core->GetGame()->Infravision();
+ actor->UnlockPalette();
+}
+
+//changes based on extended state bits, right now it is only the seven eyes
+//animation (used in how/iwd2)
+void pcf_extstate(Actor *actor, ieDword oldValue, ieDword State)
+{
+ if ((oldValue^State)&EXTSTATE_SEVEN_EYES) {
+ ieDword mask = EXTSTATE_EYE_MIND;
+ int eyeCount = 7;
+ for (int i=0;i<7;i++)
+ {
+ if (State&mask) eyeCount--;
+ mask<<=1;
+ }
+ ScriptedAnimation *sca = actor->FindOverlay(OV_SEVENEYES);
+ if (sca) {
+ sca->SetOrientation(eyeCount);
+ }
+ sca = actor->FindOverlay(OV_SEVENEYES2);
+ if (sca) {
+ sca->SetOrientation(eyeCount);
+ }
+ }
+}
+
+void pcf_hitpoint(Actor *actor, ieDword /*oldValue*/, ieDword hp)
+{
+ int maxhp = (signed) actor->GetSafeStat(IE_MAXHITPOINTS);
+ if ((signed) hp>maxhp) {
+ hp=maxhp;
+ }
+
+ int minhp = (signed) actor->GetSafeStat(IE_MINHITPOINTS);
+ if (minhp && (signed) hp<minhp) {
+ hp=minhp;
+ }
+ if ((signed) hp<=0) {
+ actor->Die(NULL);
+ }
+ actor->BaseStats[IE_HITPOINTS]=hp;
+ actor->Modified[IE_HITPOINTS]=hp;
+ if (actor->InParty) core->SetEventFlag(EF_PORTRAIT);
+}
+
+void pcf_maxhitpoint(Actor *actor, ieDword /*oldValue*/, ieDword hp)
+{
+ if ((signed) hp<(signed) actor->BaseStats[IE_HITPOINTS]) {
+ actor->BaseStats[IE_HITPOINTS]=hp;
+ //passing 0 because it is ignored anyway
+ pcf_hitpoint(actor, 0, hp);
+ }
+}
+
+void pcf_minhitpoint(Actor *actor, ieDword /*oldValue*/, ieDword hp)
+{
+ if ((signed) hp>(signed) actor->BaseStats[IE_HITPOINTS]) {
+ actor->BaseStats[IE_HITPOINTS]=hp;
+ //passing 0 because it is ignored anyway
+ pcf_hitpoint(actor, 0, hp);
+ }
+}
+
+void pcf_stat(Actor *actor, ieDword newValue, ieDword stat)
+{
+ if ((signed) newValue<=0) {
+ if (DeathOnZeroStat) {
+ actor->Die(NULL);
+ } else {
+ actor->Modified[stat]=1;
+ }
+ }
+}
+
+void pcf_stat_str(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ pcf_stat(actor, newValue, IE_STR);
+}
+
+void pcf_stat_int(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ pcf_stat(actor, newValue, IE_INT);
+}
+
+void pcf_stat_wis(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ pcf_stat(actor, newValue, IE_WIS);
+}
+
+void pcf_stat_dex(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ pcf_stat(actor, newValue, IE_DEX);
+}
+
+void pcf_stat_con(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ pcf_stat(actor, newValue, IE_CON);
+ pcf_hitpoint(actor, 0, actor->BaseStats[IE_HITPOINTS]);
+}
+
+void pcf_stat_cha(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ pcf_stat(actor, newValue, IE_CHR);
+}
+
+void pcf_xp(Actor *actor, ieDword /*oldValue*/, ieDword /*newValue*/)
+{
+ // check if we reached a new level
+ unsigned int pc = actor->InParty;
+ if (pc && !actor->GotLUFeedback) {
+ char varname[16];
+ sprintf(varname, "CheckLevelUp%d", pc);
+ core->GetGUIScriptEngine()->RunFunction("GUICommonWindows", "CheckLevelUp", true, pc);
+ ieDword NeedsLevelUp = 0;
+ core->GetDictionary()->Lookup(varname, NeedsLevelUp);
+ if (NeedsLevelUp == 1) {
+ displaymsg->DisplayConstantStringName(STR_LEVELUP, 0xffffff, actor);
+ actor->GotLUFeedback = true;
+ }
+ }
+}
+
+void pcf_gold(Actor *actor, ieDword /*oldValue*/, ieDword /*newValue*/)
+{
+ //this function will make a party member automatically donate their
+ //gold to the party pool, not the same as in the original engine
+ if (actor->InParty) {
+ Game *game = core->GetGame();
+ game->AddGold ( actor->BaseStats[IE_GOLD] );
+ actor->BaseStats[IE_GOLD]=0;
+ }
+}
+
+static void handle_overlay(Actor *actor, ieDword idx)
+{
+ if (actor->FindOverlay(idx))
+ return;
+ ieDword flag = hc_locations&(1<<idx);
+ ScriptedAnimation *sca = gamedata->GetScriptedAnimation(hc_overlays[idx], false);
+ if (!sca) {
+ return;
+ }
+
+ // always draw it for party members; the rest must not be invisible to have it;
+ // this is just a guess, maybe there are extra conditions (MC_HIDDEN? IE_AVATARREMOVAL?)
+ if (hc_flags[idx] & HC_INVISIBLE && (!actor->InParty && actor->Modified[IE_STATE_ID] & STATE_INVISIBLE)) {
+ return;
+ }
+
+ if (flag) {
+ sca->ZPos=-1;
+ }
+ actor->AddVVCell(sca);
+}
+
+//de/activates the entangle overlay
+void pcf_entangle(Actor *actor, ieDword oldValue, ieDword newValue)
+{
+ if (newValue&1) {
+ handle_overlay(actor, OV_ENTANGLE);
+ }
+ if (oldValue&1) {
+ actor->RemoveVVCell(hc_overlays[OV_ENTANGLE], true);
+ }
+}
+
+//de/activates the sanctuary and other overlays
+//unlike IE, gemrb uses this stat for other overlay fields
+//see the complete list in overlay.2da
+//it loosely follows the internal representation of overlays in IWD2
+void pcf_sanctuary(Actor *actor, ieDword oldValue, ieDword newValue)
+{
+ ieDword changed = newValue^oldValue;
+ ieDword mask = 1;
+ for (int i=0;i<32;i++) {
+ if (changed&mask) {
+ if (newValue&mask) {
+ handle_overlay(actor, i);
+ } else {
+ actor->RemoveVVCell(hc_overlays[i], true);
+ }
+ }
+ mask<<=1;
+ }
+}
+
+//de/activates the prot from missiles overlay
+void pcf_shieldglobe(Actor *actor, ieDword oldValue, ieDword newValue)
+{
+ if (newValue&1) {
+ handle_overlay(actor, OV_SHIELDGLOBE);
+ return;
+ }
+ if (oldValue&1) {
+ actor->RemoveVVCell(hc_overlays[OV_SHIELDGLOBE], true);
+ }
+}
+
+//de/activates the globe of invul. overlay
+void pcf_minorglobe(Actor *actor, ieDword oldValue, ieDword newValue)
+{
+ if (newValue&1) {
+ handle_overlay(actor, OV_MINORGLOBE);
+ return;
+ }
+ if (oldValue&1) {
+ actor->RemoveVVCell(hc_overlays[OV_MINORGLOBE], true);
+ }
+}
+
+//de/activates the grease background
+void pcf_grease(Actor *actor, ieDword oldValue, ieDword newValue)
+{
+ if (newValue&1) {
+ handle_overlay(actor, OV_GREASE);
+ return;
+ }
+ if (oldValue&1) {
+ actor->RemoveVVCell(hc_overlays[OV_GREASE], true);
+ }
+}
+
+//de/activates the web overlay
+//the web effect also immobilizes the actor!
+void pcf_web(Actor *actor, ieDword oldValue, ieDword newValue)
+{
+ if (newValue&1) {
+ handle_overlay(actor, OV_WEB);
+ return;
+ }
+ if (oldValue&1) {
+ actor->RemoveVVCell(hc_overlays[OV_WEB], true);
+ }
+}
+
+//de/activates the spell bounce background
+void pcf_bounce(Actor *actor, ieDword oldValue, ieDword newValue)
+{
+ if (newValue&1) {
+ handle_overlay(actor, OV_BOUNCE);
+ return;
+ }
+ if (oldValue&1) {
+ //it seems we have to remove it abruptly
+ actor->RemoveVVCell(hc_overlays[OV_BOUNCE], false);
+ }
+}
+
+//no separate values (changes are permanent)
+void pcf_fatigue(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ actor->BaseStats[IE_FATIGUE]=newValue;
+}
+
+//no separate values (changes are permanent)
+void pcf_intoxication(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ actor->BaseStats[IE_INTOXICATION]=newValue;
+}
+
+void pcf_color(Actor *actor, ieDword /*oldValue*/, ieDword /*newValue*/)
+{
+ CharAnimations *anims = actor->GetAnims();
+ if (anims) {
+ anims->SetColors(actor->Modified+IE_COLORS);
+ }
+}
+
+void pcf_armorlevel(Actor *actor, ieDword /*oldValue*/, ieDword newValue)
+{
+ CharAnimations *anims = actor->GetAnims();
+ if (anims) {
+ anims->SetArmourLevel(newValue);
+ }
+}
+
+static int maximum_values[MAX_STATS]={
+32767,32767,20,100,100,100,100,25,10,25,25,25,25,25,100,100,//0f
+100,100,100,100,100,100,100,100,100,100,255,255,255,255,100,100,//1f
+200,200,MAX_LEVEL,255,25,100,25,25,25,25,25,999999999,999999999,999999999,25,25,//2f
+200,255,200,100,100,200,200,25,5,100,1,1,100,1,1,0,//3f
+511,1,1,1,MAX_LEVEL,MAX_LEVEL,1,9999,25,100,100,255,1,20,20,25,//4f
+25,1,1,255,25,25,255,255,25,255,255,255,255,255,255,255,//5f
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,//6f
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,//7f
+255,255,255,255,255,255,255,100,100,100,255,5,5,255,1,1,//8f
+1,25,25,30,1,1,1,25,0,100,100,1,255,255,255,255,//9f
+255,255,255,255,255,255,20,255,255,1,20,255,999999999,999999999,1,1,//af
+999999999,999999999,0,0,20,0,0,0,0,0,0,0,0,0,0,0,//bf
+0,0,0,0,0,0,0,25,25,255,255,255,255,65535,0,0,//cf - 207
+0,0,0,0,0,0,0,0,MAX_LEVEL,255,65535,3,255,255,255,255,//df - 223
+255,255,255,255,255,255,255,255,255,255,255,255,65535,65535,15,0,//ef - 239
+MAX_LEVEL,MAX_LEVEL,MAX_LEVEL,MAX_LEVEL, MAX_LEVEL,MAX_LEVEL,MAX_LEVEL,MAX_LEVEL, //0xf7 - 247
+MAX_LEVEL,MAX_LEVEL,0,0,0,0,0,0//ff
+};
+
+typedef void (*PostChangeFunctionType)(Actor *actor, ieDword oldValue, ieDword newValue);
+static PostChangeFunctionType post_change_functions[MAX_STATS]={
+pcf_hitpoint, pcf_maxhitpoint, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, //0f
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, pcf_fatigue, pcf_intoxication, //1f
+NULL,NULL,pcf_level,NULL, pcf_stat_str, NULL, pcf_stat_int, pcf_stat_wis,
+pcf_stat_dex,pcf_stat_con,pcf_stat_cha,NULL, pcf_xp, pcf_gold, pcf_morale, NULL, //2f
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, pcf_entangle, pcf_sanctuary, //3f
+pcf_minorglobe, pcf_shieldglobe, pcf_grease, pcf_web, pcf_level, pcf_level, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, //4f
+NULL,NULL,NULL,pcf_minhitpoint, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, //5f
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, //6f
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, //7f
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, //8f
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, //9f
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, //af
+NULL,NULL,NULL,NULL, pcf_morale, pcf_bounce, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, //bf
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL,
+NULL,NULL,NULL,NULL, NULL, pcf_animid,pcf_state, pcf_extstate, //cf
+pcf_color,pcf_color,pcf_color,pcf_color, pcf_color, pcf_color, pcf_color, NULL,
+NULL,NULL,NULL,pcf_armorlevel, NULL, NULL, NULL, NULL, //df
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL,
+pcf_class,NULL,pcf_ea,NULL, NULL, NULL, NULL, NULL, //ef
+pcf_level,pcf_level,pcf_level,pcf_level, pcf_level, pcf_level, pcf_level, pcf_level,
+NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL //ff
+};
+
+/** call this from ~Interface() */
+void Actor::ReleaseMemory()
+{
+ int i;
+
+ if (classcount>=0) {
+ if (clericspelltables) {
+ for (i=0;i<classcount;i++) {
+ if (clericspelltables[i]) {
+ free (clericspelltables[i]);
+ }
+ }
+ free(clericspelltables);
+ clericspelltables=NULL;
+ }
+ if (druidspelltables) {
+ for (i=0;i<classcount;i++) {
+ if (druidspelltables[i]) {
+ free (druidspelltables[i]);
+ }
+ }
+ free(druidspelltables);
+ druidspelltables=NULL;
+ }
+ if (wizardspelltables) {
+ for (i=0;i<classcount;i++) {
+ if (wizardspelltables[i]) {
+ free(wizardspelltables[i]);
+ }
+ }
+ free(wizardspelltables);
+ wizardspelltables=NULL;
+ }
+ if (classabilities) {
+ for (i=0;i<classcount;i++) {
+ if (classabilities[i]) {
+ free (classabilities[i]);
+ }
+ }
+ free(classabilities);
+ classabilities=NULL;
+ }
+ if (turnlevels) {
+ free(turnlevels);
+ turnlevels=NULL;
+ }
+
+ if (booktypes) {
+ free(booktypes);
+ booktypes=NULL;
+ }
+
+ if (xpbonus) {
+ free(xpbonus);
+ xpbonus=NULL;
+ xpbonuslevels = -1;
+ xpbonustypes = -1;
+ }
+ if (levelslots) {
+ for (i=0; i<classcount; i++) {
+ if (levelslots[i]) {
+ free(levelslots[i]);
+ }
+ }
+ free(levelslots);
+ levelslots=NULL;
+ }
+ if (dualswap) {
+ free(dualswap);
+ dualswap=NULL;
+ }
+ if (maxhpconbon) {
+ free(maxhpconbon);
+ maxhpconbon=NULL;
+ }
+ if (skillstats) {
+ free(skillstats);
+ skillstats=NULL;
+ }
+ if (skillabils) {
+ free(skillabils);
+ skillabils=NULL;
+ }
+
+ if (wspecial) {
+ for (i=0; i<=wspecial_max; i++) {
+ if (wspecial[i]) {
+ free(wspecial[i]);
+ }
+ }
+ free(wspecial);
+ wspecial=NULL;
+ }
+ if (wspattack) {
+ for (i=0; i<wspattack_rows; i++) {
+ if (wspattack[i]) {
+ free(wspattack[i]);
+ }
+ }
+ free(wspattack);
+ wspattack=NULL;
+ }
+ if (wsdualwield) {
+ for (i=0; i<=STYLE_MAX; i++) {
+ if (wsdualwield[i]) {
+ free(wsdualwield[i]);
+ }
+ }
+ free(wsdualwield);
+ wsdualwield=NULL;
+ }
+ if (wstwohanded) {
+ for (i=0; i<=STYLE_MAX; i++) {
+ if (wstwohanded[i]) {
+ free(wstwohanded[i]);
+ }
+ }
+ free(wstwohanded);
+ wstwohanded=NULL;
+ }
+ if (wsswordshield) {
+ for (i=0; i<=STYLE_MAX; i++) {
+ if (wsswordshield[i]) {
+ free(wsswordshield[i]);
+ }
+ }
+ free(wsswordshield);
+ wsswordshield=NULL;
+ }
+ if (wssingle) {
+ for (i=0; i<=STYLE_MAX; i++) {
+ if (wssingle[i]) {
+ free(wssingle[i]);
+ }
+ }
+ free(wssingle);
+ wssingle=NULL;
+ }
+ if (monkbon) {
+ for (unsigned i=0; i<monkbon_rows; i++) {
+ if (monkbon[i]) {
+ free(monkbon[i]);
+ }
+ }
+ free(monkbon);
+ monkbon=NULL;
+ }
+ for(i=0;i<20;i++) {
+ free(wmlevels[i]);
+ wmlevels[i]=NULL;
+ }
+ }
+ if (GUIBTDefaults) {
+ free (GUIBTDefaults);
+ GUIBTDefaults=NULL;
+ }
+ if (OtherGUIButtons) {
+ free (OtherGUIButtons);
+ }
+ classcount = -1;
+}
+
+#define COL_HATERACE 0 //ranger type racial enemy
+#define COL_CLERIC_SPELL 1 //cleric spells
+#define COL_MAGE_SPELL 2 //mage spells
+#define COL_STARTXP 3 //starting xp
+#define COL_BARD_SKILL 4 //bard skills
+#define COL_THIEF_SKILL 5 //thief skills
+
+#define COL_MAIN 0
+#define COL_SPARKS 1
+#define COL_GRADIENT 2
+
+/* returns the ISCLASS for the class based on name */
+int IsClassFromName (const char* name)
+{
+ //TODO: is there a better way of doing this?
+ for (int i=0; i<ISCLASSES; i++) {
+ if (strcmp(name, isclassnames[i]) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static void InitActorTables()
+{
+ int i, j;
+
+ pstflags = core->HasFeature(GF_PST_STATE_FLAGS);
+ nocreate = core->HasFeature(GF_NO_NEW_VARIABLES);
+ if (pstflags) {
+ state_invisible=STATE_PST_INVIS;
+ } else {
+ state_invisible=STATE_INVISIBLE;
+ }
+
+ if (core->HasFeature(GF_CHALLENGERATING)) {
+ sharexp=SX_DIVIDE|SX_CR;
+ } else {
+ sharexp=SX_DIVIDE;
+ }
+ ReverseToHit = core->HasFeature(GF_REVERSE_TOHIT);
+ CheckAbilities = core->HasFeature(GF_CHECK_ABILITIES);
+ DeathOnZeroStat = core->HasFeature(GF_DEATH_ON_ZERO_STAT);
+
+ //this table lists various level based xp bonuses
+ AutoTable tm("xpbonus");
+ if (tm) {
+ xpbonustypes = tm->GetRowCount();
+ if (xpbonustypes == 0) {
+ xpbonuslevels = 0;
+ } else {
+ xpbonuslevels = tm->GetColumnCount(0);
+ xpbonus = (int *) calloc(xpbonuslevels*xpbonustypes, sizeof(int));
+ for (i = 0; i<xpbonustypes; i++) {
+ for(j = 0; j<xpbonuslevels; j++) {
+ xpbonus[i*xpbonuslevels+j] = atoi(tm->QueryField(i,j));
+ }
+ }
+ }
+ } else {
+ xpbonustypes = 0;
+ xpbonuslevels = 0;
+ }
+ //this table lists skill groups assigned to classes
+ //it is theoretically possible to create hybrid classes
+ tm.load("clskills");
+ if (tm) {
+ classcount = tm->GetRowCount();
+ memset (isclass,0,sizeof(isclass));
+ clericspelltables = (char **) calloc(classcount, sizeof(char*));
+ druidspelltables = (char **) calloc(classcount, sizeof(char*));
+ wizardspelltables = (char **) calloc(classcount, sizeof(char*));
+ turnlevels = (int *) calloc(classcount, sizeof(int));
+ booktypes = (int *) calloc(classcount, sizeof(int));
+ classabilities = (char **) calloc(classcount, sizeof(char*));
+
+ ieDword bitmask = 1;
+
+ for(i = 0; i<classcount; i++) {
+ const char *field;
+ int turnlevel = atoi(tm->QueryField( i, 7));
+ turnlevels[i]=turnlevel;
+
+ field = tm->QueryField( i, 0 );
+ if (field[0]!='*') {
+ isclass[ISDRUID] |= bitmask;
+ druidspelltables[i]=strdup(field);
+ }
+ field = tm->QueryField( i, 1 );
+ if (field[0]!='*') {
+ isclass[ISCLERIC] |= bitmask;
+ clericspelltables[i]=strdup(field);
+ }
+
+ field = tm->QueryField( i, 2 );
+ if (field[0]!='*') {
+ isclass[ISMAGE] |= bitmask;
+ wizardspelltables[i]=strdup(field);
+ }
+
+ // field 3 holds the starting xp
+
+ field = tm->QueryField( i, 4 );
+ if (field[0]!='*') {
+ isclass[ISBARD] |= bitmask;
+ }
+
+ field = tm->QueryField( i, 5 );
+ if (field[0]!='*') {
+ isclass[ISTHIEF] |= bitmask;
+ }
+
+ field = tm->QueryField( i, 6 );
+ if (field[0]!='*') {
+ isclass[ISPALADIN] |= bitmask;
+ }
+
+ // field 7 holds the turn undead level
+
+ field = tm->QueryField( i, 8 );
+ booktypes[i]=atoi(field);
+ //if booktype == 3 then it is a 'divine sorceror' class
+ //we shouldn't hardcode iwd2 classes this heavily
+ if (booktypes[i]==2) {
+ isclass[ISSORCERER] |= bitmask;
+ }
+
+ field = tm->QueryField( i, 9 );
+ if (field[0]!='*') {
+ isclass[ISRANGER] |= bitmask;
+ }
+
+ field = tm->QueryField( i, 10 );
+ if (!strnicmp(field, "CLABMO", 6)) {
+ isclass[ISMONK] |= bitmask;
+ }
+ classabilities[i]=strdup(field);
+ bitmask <<=1;
+ }
+ } else {
+ classcount = 0; //well
+ }
+
+ i = core->GetMaximumAbility();
+ maximum_values[IE_STR]=i;
+ maximum_values[IE_INT]=i;
+ maximum_values[IE_DEX]=i;
+ maximum_values[IE_CON]=i;
+ maximum_values[IE_CHR]=i;
+ maximum_values[IE_WIS]=i;
+ if (ReverseToHit) {
+ //all games except iwd2
+ maximum_values[IE_ARMORCLASS]=20;
+ } else {
+ //iwd2
+ maximum_values[IE_ARMORCLASS]=199;
+ }
+
+ //initializing the vvc resource references
+ tm.load("damage");
+ if (tm) {
+ for (i=0;i<DAMAGE_LEVELS;i++) {
+ const char *tmp = tm->QueryField( i, COL_MAIN );
+ strnlwrcpy(d_main[i], tmp, 8);
+ if (d_main[i][0]=='*') {
+ d_main[i][0]=0;
+ }
+ tmp = tm->QueryField( i, COL_SPARKS );
+ strnlwrcpy(d_splash[i], tmp, 8);
+ if (d_splash[i][0]=='*') {
+ d_splash[i][0]=0;
+ }
+ tmp = tm->QueryField( i, COL_GRADIENT );
+ d_gradient[i]=atoi(tmp);
+ }
+ }
+
+ tm.load("overlay");
+ if (tm) {
+ ieDword mask = 1;
+ for (i=0;i<OVERLAY_COUNT;i++) {
+ const char *tmp = tm->QueryField( i, 0 );
+ strnlwrcpy(hc_overlays[i], tmp, 8);
+ if (atoi(tm->QueryField( i, 1))) {
+ hc_locations|=mask;
+ }
+ tmp = tm->QueryField( i, 2 );
+ hc_flags[i] = atoi(tmp);
+ mask<<=1;
+ }
+ }
+
+ //csound for bg1/bg2
+ memset(csound,0,sizeof(csound));
+ if (!core->HasFeature(GF_SOUNDFOLDERS)) {
+ tm.load("csound");
+ if (tm) {
+ for(i=0;i<VCONST_COUNT;i++) {
+ const char *tmp = tm->QueryField( i, 0 );
+ switch(tmp[0]) {
+ case '*': break;
+ //I have no idea what this ! mean
+ case '!': csound[i]=tmp[1]; break;
+ default: csound[i]=tmp[0]; break;
+ }
+ }
+ }
+ }
+
+ tm.load("qslots");
+ GUIBTDefaults = (ActionButtonRow *) calloc( classcount,sizeof(ActionButtonRow) );
+
+ for (i = 0; i < classcount; i++) {
+ memcpy(GUIBTDefaults+i, &DefaultButtons, sizeof(ActionButtonRow));
+ if (tm) {
+ for (int j=0;j<MAX_QSLOTS;j++) {
+ GUIBTDefaults[i][j+3]=(ieByte) atoi( tm->QueryField(i,j) );
+ }
+ }
+ }
+
+ tm.load("qslot2");
+ if (tm) {
+ extraslots = tm->GetRowCount();
+ OtherGUIButtons = (ActionButtonRow2 *) calloc( extraslots, sizeof (ActionButtonRow2) );
+
+ for (i=0; i<extraslots; i++) {
+ long tmp = 0;
+ valid_number( tm->QueryField(i,0), tmp );
+ OtherGUIButtons[i].clss = (ieByte) tmp;
+ memcpy(OtherGUIButtons[i].buttons, &DefaultButtons, sizeof(ActionButtonRow));
+ for (int j=0;j<GUIBT_COUNT;j++) {
+ OtherGUIButtons[i].buttons[j]=(ieByte) atoi( tm->QueryField(i,j+1) );
+ }
+ }
+ }
+
+ tm.load("itemuse");
+ if (tm) {
+ usecount = tm->GetRowCount();
+ itemuse = new ItemUseType[usecount];
+ for (i = 0; i < usecount; i++) {
+ itemuse[i].stat = (ieByte) core->TranslateStat( tm->QueryField(i,0) );
+ strnlwrcpy(itemuse[i].table, tm->QueryField(i,1),8 );
+ itemuse[i].mcol = (ieByte) atoi( tm->QueryField(i,2) );
+ itemuse[i].vcol = (ieByte) atoi( tm->QueryField(i,3) );
+ itemuse[i].which = (ieByte) atoi( tm->QueryField(i,4) );
+ //limiting it to 0 or 1 to avoid crashes
+ if (itemuse[i].which!=1) {
+ itemuse[i].which=0;
+ }
+ }
+ }
+
+ tm.load("itemanim");
+ if (tm) {
+ animcount = tm->GetRowCount();
+ itemanim = new ItemAnimType[animcount];
+ for (i = 0; i < animcount; i++) {
+ strnlwrcpy(itemanim[i].itemname, tm->QueryField(i,0),8 );
+ itemanim[i].animation = (ieByte) atoi( tm->QueryField(i,1) );
+ }
+ }
+
+ tm.load("mxsplwis");
+ if (tm) {
+ spllevels = tm->GetColumnCount(0);
+ int max = core->GetMaximumAbility();
+ mxsplwis = (int *) calloc(max*spllevels, sizeof(int));
+ for (i = 0; i < spllevels; i++) {
+ for(int j = 0; j < max; j++) {
+ int k = atoi(tm->GetRowName(j))-1;
+ if (k>=0 && k<max) {
+ mxsplwis[k*spllevels+i]=atoi(tm->QueryField(j,i));
+ }
+ }
+ }
+ }
+
+ tm.load("featreq");
+ if (tm) {
+ unsigned int tmp;
+
+ for(i=0;i<MAX_FEATS;i++) {
+ //we need the MULTIPLE column only
+ //it stores the FEAT_* stat index, and could be taken multiple
+ //times
+ tmp = core->TranslateStat(tm->QueryField(i,0));
+ if (tmp>=MAX_STATS) {
+ printMessage("Actor","Invalid stat value in featreq.2da",YELLOW);
+ }
+ featstats[i] = (ieByte) tmp;
+ }
+ }
+
+ //default all hp con bonuses to 9; this should be updated below
+ //TODO: check iwd2
+ maxhpconbon = (int *) calloc(classcount, sizeof(int));
+ for (i = 0; i < classcount; i++) {
+ maxhpconbon[i] = 9;
+ }
+ tm.load("classes");
+ if (tm && !core->HasFeature(GF_LEVELSLOT_PER_CLASS)) {
+ AutoTable hptm;
+ //iwd2 just uses levelslotsiwd2 instead
+ printf("Examining classes.2da\n");
+
+ //when searching the levelslots, you must search for
+ //levelslots[BaseStats[IE_CLASS]-1] as there is no class id of 0
+ levelslots = (int **) calloc(classcount, sizeof(int*));
+ dualswap = (int *) calloc(classcount, sizeof(int));
+ ieDword tmpindex;
+ for (i=0; i<classcount; i++) {
+ //make sure we have a valid classid, then decrement
+ //it to get the correct array index
+ tmpindex = atoi(tm->QueryField(i, 5));
+ if (!tmpindex)
+ continue;
+ tmpindex--;
+
+ printf("\tID: %d ", tmpindex);
+ //only create the array if it isn't yet made
+ //i.e. barbarians would overwrite fighters in bg2
+ if (levelslots[tmpindex]) {
+ printf ("Already Found!\n");
+ continue;
+ }
+
+ const char* classname = tm->GetRowName(i);
+ printf("Name: %s ", classname);
+ int classis = 0;
+ //default all levelslots to 0
+ levelslots[tmpindex] = (int *) calloc(ISCLASSES, sizeof(int));
+
+ //single classes only worry about IE_LEVEL
+ ieDword tmpclass = atoi(tm->QueryField(i, 4));
+ if (!tmpclass) {
+ classis = IsClassFromName(classname);
+ if (classis>=0) {
+ printf("Classis: %d ", classis);
+ levelslots[tmpindex][classis] = IE_LEVEL;
+ //get the max hp con bonus
+ hptm.load(tm->QueryField(i, 6));
+ if (hptm) {
+ int tmphp = 0;
+ int rollscolumn = hptm->GetColumnIndex("ROLLS");
+ while (atoi(hptm->QueryField(tmphp, rollscolumn)))
+ tmphp++;
+ printf("TmpHP: %d ", tmphp);
+ if (tmphp) maxhpconbon[tmpindex] = tmphp;
+ }
+ }
+ continue;
+ }
+
+ //we have to account for dual-swap in the multiclass field
+ ieDword numfound = 1;
+ ieDword tmpbits = bitcount (tmpclass);
+
+ //we need all the classnames of the multi to compare with the order we load them in
+ //because the original game set the levels based on name order, not bit order
+ char **classnames = (char **) calloc(tmpbits, sizeof(char *));
+ classnames[0] = (char*)strtok(strdup((char*)classname), "_");
+ while (numfound<tmpbits && (classnames[numfound] = strdup(strtok(NULL, "_")))) {
+ numfound++;
+ }
+ numfound = 0;
+ bool foundwarrior = false;
+ for (int j=0; j<classcount; j++) {
+ //no sense continuing if we've found all to be found
+ if (numfound==tmpbits)
+ break;
+ if ((1<<j)&tmpclass) {
+ //save the IE_LEVEL information
+ const char* currentname = tm->GetRowName((ieDword)(tm->FindTableValue(5, j+1)));
+ classis = IsClassFromName(currentname);
+ if (classis>=0) {
+ //search for the current class in the split of the names to get it's
+ //correct order
+ for (ieDword k=0; k<tmpbits; k++) {
+ if (strcmp(classnames[k], currentname) == 0) {
+ int tmplevel = 0;
+ if (k==0) tmplevel = IE_LEVEL;
+ else if (k==1) tmplevel = IE_LEVEL2;
+ else tmplevel = IE_LEVEL3;
+ levelslots[tmpindex][classis] = tmplevel;
+ }
+ }
+ printf("Classis: %d ", classis);
+
+ //warrior take presedence
+ if (!foundwarrior) {
+ foundwarrior = (classis==ISFIGHTER||classis==ISRANGER||classis==ISPALADIN||
+ classis==ISBARBARIAN);
+ hptm.load(tm->QueryField(currentname, "HP"));
+ if (hptm) {
+ int tmphp = 0;
+ int rollscolumn = hptm->GetColumnIndex("ROLLS");
+ while (atoi(hptm->QueryField(tmphp, rollscolumn)))
+ tmphp++;
+ //make sure we at least set the first class
+ if ((tmphp>maxhpconbon[tmpindex])||foundwarrior||numfound==0)
+ maxhpconbon[tmpindex]=tmphp;
+ }
+ }
+ }
+
+ //save the MC_WAS_ID of the first class in the dual-class
+ if (numfound==0 && tmpbits==2) {
+ if (strcmp(classnames[0], currentname) == 0) {
+ dualswap[tmpindex] = strtol(tm->QueryField(classname, "MC_WAS_ID"), NULL, 0);
+ }
+ } else if (numfound==1 && tmpbits==2 && !dualswap[tmpindex]) {
+ dualswap[tmpindex] = strtol(tm->QueryField(classname, "MC_WAS_ID"), NULL, 0);
+ }
+ numfound++;
+ }
+ }
+ if (classnames) {
+ for (ieDword j=0; j<tmpbits; j++) {
+ if (classnames[j]) {
+ free(classnames[j]);
+ }
+ }
+ free(classnames);
+ classnames = NULL;
+ }
+ printf("HPCON: %d ", maxhpconbon[tmpindex]);
+ printf("DS: %d\n", dualswap[tmpindex]);
+ }
+ /*this could be enabled to ensure all levelslots are filled with at least 0's;
+ *however, the access code should ensure this never happens
+ for (i=0; i<classcount; i++) {
+ if (!levelslots[i]) {
+ levelslots[i] = (int *) calloc(ISCLASSES, sizeof(int *));
+ }
+ }*/
+ }
+ printf("Finished examining classes.2da\n");
+
+ //pre-cache hit/damage/speed bonuses for weapons
+ tm.load("wspecial");
+ if (tm) {
+ //load in the identifiers
+ wspecial_max = tm->GetRowCount()-1;
+ int cols = tm->GetColumnCount();
+ wspecial = (int **) calloc(wspecial_max+1, sizeof(int *));
+
+ for (i=0; i<=wspecial_max; i++) {
+ wspecial[i] = (int *) calloc(WSPECIAL_COLS, sizeof(int));
+ for (int j=0; j<cols; j++) {
+ wspecial[i][j] = atoi(tm->QueryField(i, j));
+ }
+ }
+ }
+
+ //pre-cache attack per round bonuses
+ tm.load("wspatck");
+ if (tm) {
+ wspattack_rows = tm->GetRowCount();
+ wspattack_cols = tm->GetColumnCount();
+ wspattack = (int **) calloc(wspattack_rows, sizeof(int *));
+
+ int tmp = 0;
+ for (i=0; i<wspattack_rows; i++) {
+ wspattack[i] = (int *) calloc(wspattack_cols, sizeof(int));
+ for (int j=0; j<wspattack_cols; j++) {
+ tmp = atoi(tm->QueryField(i, j));
+ //negative values relate to x/2, so we adjust them
+ //positive values relate to x, so we must times by 2
+ if (tmp<0) tmp = -2*tmp-1;
+ else tmp *= 2;
+ wspattack[i][j] = tmp;
+ }
+ }
+ }
+
+ //dual-wielding table
+ tm.load("wstwowpn");
+ if (tm) {
+ wsdualwield = (int **) calloc(STYLE_MAX+1, sizeof(int *));
+ int cols = tm->GetColumnCount();
+ for (i=0; i<=STYLE_MAX; i++) {
+ wsdualwield[i] = (int *) calloc(cols, sizeof(int));
+ for (int j=0; j<cols; j++) {
+ wsdualwield[i][j] = atoi(tm->QueryField(i, j));
+ }
+ }
+ }
+
+ //two-handed table
+ tm.load("wstwohnd");
+ if (tm) {
+ wstwohanded = (int **) calloc(STYLE_MAX+1, sizeof(int *));
+ int cols = tm->GetColumnCount();
+ for (i=0; i<=STYLE_MAX; i++) {
+ wstwohanded[i] = (int *) calloc(cols, sizeof(int));
+ for (int j=0; j<cols; j++) {
+ wstwohanded[i][j] = atoi(tm->QueryField(i, j));
+ }
+ }
+ }
+
+ //two-handed table
+ tm.load("wsshield");
+ if (tm) {
+ wsswordshield = (int **) calloc(STYLE_MAX+1, sizeof(int *));
+ int cols = tm->GetColumnCount();
+ for (i=0; i<=STYLE_MAX; i++) {
+ wsswordshield[i] = (int *) calloc(cols, sizeof(int));
+ for (int j=0; j<cols; j++) {
+ wsswordshield[i][j] = atoi(tm->QueryField(i, j));
+ }
+ }
+ }
+
+ //two-handed table
+ tm.load("wssingle");
+ if (tm) {
+ wssingle = (int **) calloc(STYLE_MAX+1, sizeof(int *));
+ int cols = tm->GetColumnCount();
+ for (i=0; i<=STYLE_MAX; i++) {
+ wssingle[i] = (int *) calloc(cols, sizeof(int));
+ for (int j=0; j<cols; j++) {
+ wssingle[i][j] = atoi(tm->QueryField(i, j));
+ }
+ }
+ }
+
+ //unhardcoded monk bonus table
+ tm.load("monkbon");
+ if (tm) {
+ monkbon_rows = tm->GetRowCount();
+ monkbon_cols = tm->GetColumnCount();
+ monkbon = (int **) calloc(monkbon_rows, sizeof(int *));
+ for (unsigned i=0; i<monkbon_rows; i++) {
+ monkbon[i] = (int *) calloc(monkbon_cols, sizeof(int));
+ for (unsigned j=0; j<monkbon_cols; j++) {
+ monkbon[i][j] = atoi(tm->QueryField(i, j));
+ }
+ }
+ }
+
+ //wild magic level modifiers
+ for(i=0;i<20;i++) {
+ wmlevels[i]=(int *) calloc(MAX_LEVEL,sizeof(int) );
+ }
+ tm.load("lvlmodwm");
+ if (tm) {
+ int maxrow = tm->GetRowCount();
+ for (i=0;i<20;i++) {
+ for(j=0;j<MAX_LEVEL;j++) {
+ int row = maxrow;
+ if (j<row) row=j;
+ wmlevels[i][j]=strtol(tm->QueryField(row,i), NULL, 0);
+ }
+ }
+ }
+
+ // verbal constant remapping, if omitted, it is an 1-1 mapping
+ // TODO: allow disabled VC slots
+ for (i=0;i<VCONST_COUNT;i++) {
+ VCMap[i]=i;
+ }
+ tm.load("vcremap");
+ if (tm) {
+ int rows = tm->GetRowCount();
+
+ for (i=0;i<rows;i++) {
+ int row = atoi(tm->QueryField(i,0));
+ if (row<0 || row>=VCONST_COUNT) continue;
+ int value = atoi(tm->QueryField(i,1));
+ if (value<0 || value>=VCONST_COUNT) continue;
+ VCMap[row]=value;
+ }
+ }
+
+ //initializing the skill->stats conversion table (used in iwd2)
+ tm.load("skillsta");
+ if (tm) {
+ int rowcount = tm->GetRowCount();
+ skillcount = rowcount;
+ if (rowcount) {
+ skillstats = (int *) malloc(rowcount * sizeof(int) );
+ skillabils = (int *) malloc(rowcount * sizeof(int) );
+ while(rowcount--) {
+ skillstats[rowcount]=core->TranslateStat(tm->QueryField(rowcount,0));
+ skillabils[rowcount]=core->TranslateStat(tm->QueryField(rowcount,1));
+ }
+ }
+ }
+}
+
+void Actor::SetLockedPalette(const ieDword *gradients)
+{
+ if (!anims) return; //cannot apply it (yet)
+ anims->LockPalette(gradients);
+}
+
+void Actor::UnlockPalette()
+{
+ if (!anims) return;
+ anims->lockPalette=false;
+ anims->SetColors(Modified+IE_COLORS);
+}
+
+void Actor::AddAnimation(const ieResRef resource, int gradient, int height, int flags)
+{
+ ScriptedAnimation *sca = gamedata->GetScriptedAnimation(resource, false);
+ if (!sca)
+ return;
+ sca->ZPos=height;
+ if (flags&AA_PLAYONCE) {
+ sca->PlayOnce();
+ }
+ if (flags&AA_BLEND) {
+ //pst anims need this?
+ sca->SetBlend();
+ }
+ if (gradient!=-1) {
+ sca->SetPalette(gradient, 4);
+ }
+ AddVVCell(sca);
+}
+
+void Actor::PlayDamageAnimation(int type, bool hit)
+{
+ int i;
+
+ printf("Damage animation type: %d\n", type);
+
+ switch(type) {
+ case 0: case 1: case 2: case 3: //blood
+ i = anims->GetBloodColor();
+ if (!i) i = d_gradient[type];
+ if(hit) {
+ AddAnimation(d_main[type], i, 0, AA_PLAYONCE);
+ }
+ break;
+ case 4: case 5: case 6: //fire
+ if(hit) {
+ AddAnimation(d_main[type], d_gradient[type], 0, AA_PLAYONCE);
+ }
+ for(i=DL_FIRE;i<=type;i++) {
+ AddAnimation(d_splash[i], d_gradient[i], 0, AA_PLAYONCE);
+ }
+ break;
+ case 7: case 8: case 9: //electricity
+ if (hit) {
+ AddAnimation(d_main[type], d_gradient[type], 0, AA_PLAYONCE);
+ }
+ for(i=DL_ELECTRICITY;i<=type;i++) {
+ AddAnimation(d_splash[i], d_gradient[i], 0, AA_PLAYONCE);
+ }
+ break;
+ case 10: case 11: case 12://cold
+ if (hit) {
+ AddAnimation(d_main[type], d_gradient[type], 0, AA_PLAYONCE);
+ }
+ break;
+ case 13: case 14: case 15://acid
+ if (hit) {
+ AddAnimation(d_main[type], d_gradient[type], 0, AA_PLAYONCE);
+ }
+ break;
+ case 16: case 17: case 18://disintegrate
+ if (hit) {
+ AddAnimation(d_main[type], d_gradient[type], 0, AA_PLAYONCE);
+ }
+ break;
+ }
+}
+
+bool Actor::SetStat(unsigned int StatIndex, ieDword Value, int pcf)
+{
+ if (StatIndex >= MAX_STATS) {
+ return false;
+ }
+ if ( (signed) Value<-100) {
+ Value = (ieDword) -100;
+ }
+ else {
+ if ( maximum_values[StatIndex]>0) {
+ if ( (signed) Value>maximum_values[StatIndex]) {
+ Value = (ieDword) maximum_values[StatIndex];
+ }
+ }
+ }
+
+ unsigned int previous = GetSafeStat(StatIndex);
+ if (Modified[StatIndex]!=Value) {
+ Modified[StatIndex] = Value;
+ }
+ if (previous!=Value) {
+ if (pcf) {
+ PostChangeFunctionType f = post_change_functions[StatIndex];
+ if (f) (*f)(this, previous, Value);
+ }
+ }
+ return true;
+}
+
+int Actor::GetMod(unsigned int StatIndex)
+{
+ if (StatIndex >= MAX_STATS) {
+ return 0xdadadada;
+ }
+ return (signed) Modified[StatIndex] - (signed) BaseStats[StatIndex];
+}
+/** Returns a Stat Base Value */
+ieDword Actor::GetBase(unsigned int StatIndex) const
+{
+ if (StatIndex >= MAX_STATS) {
+ return 0xffff;
+ }
+ return BaseStats[StatIndex];
+}
+
+/** Sets a Stat Base Value */
+/** If required, modify the modified value and run the pcf function */
+bool Actor::SetBase(unsigned int StatIndex, ieDword Value)
+{
+ if (StatIndex >= MAX_STATS) {
+ return false;
+ }
+ ieDword diff = Modified[StatIndex]-BaseStats[StatIndex];
+
+ //maximize the base stat
+ if ( maximum_values[StatIndex]) {
+ if ( (signed) Value>maximum_values[StatIndex]) {
+ Value = (ieDword) maximum_values[StatIndex];
+ }
+ }
+
+ BaseStats[StatIndex] = Value;
+
+ //if already initialized, then the modified stats
+ //might need to run the post change function (stat change can kill actor)
+ SetStat (StatIndex, Value+diff, InternalFlags&IF_INITIALIZED);
+ return true;
+}
+
+bool Actor::SetBaseNoPCF(unsigned int StatIndex, ieDword Value)
+{
+ if (StatIndex >= MAX_STATS) {
+ return false;
+ }
+ ieDword diff = Modified[StatIndex]-BaseStats[StatIndex];
+
+ //maximize the base stat
+ if ( maximum_values[StatIndex]) {
+ if ( (signed) Value>maximum_values[StatIndex]) {
+ Value = (ieDword) maximum_values[StatIndex];
+ }
+ }
+
+ BaseStats[StatIndex] = Value;
+
+ //if already initialized, then the modified stats
+ //might need to run the post change function (stat change can kill actor)
+ SetStat (StatIndex, Value+diff, 0);
+ return true;
+}
+
+bool Actor::SetBaseBit(unsigned int StatIndex, ieDword Value, bool setreset)
+{
+ if (StatIndex >= MAX_STATS) {
+ return false;
+ }
+ if (setreset) {
+ BaseStats[StatIndex] |= Value;
+ } else {
+ BaseStats[StatIndex] &= ~Value;
+ }
+ //if already initialized, then the modified stats
+ //need to run the post change function (stat change can kill actor)
+ if (setreset) {
+ SetStat (StatIndex, Modified[StatIndex]|Value, InternalFlags&IF_INITIALIZED);
+ } else {
+ SetStat (StatIndex, Modified[StatIndex]&~Value, InternalFlags&IF_INITIALIZED);
+ }
+ return true;
+}
+
+const unsigned char *Actor::GetStateString() const
+{
+ if (!PCStats) {
+ return NULL;
+ }
+ ieByte *tmp = PCStats->PortraitIconString;
+ ieWord *Icons = PCStats->PortraitIcons;
+ int j=0;
+ for (int i=0;i<MAX_PORTRAIT_ICONS;i++) {
+ if (!(Icons[i]&0xff00)) {
+ tmp[j++]=(ieByte) ((Icons[i]&0xff)+66);
+ }
+ }
+ tmp[j]=0;
+ return tmp;
+}
+
+void Actor::AddPortraitIcon(ieByte icon)
+{
+ if (!PCStats) {
+ return;
+ }
+ ieWord *Icons = PCStats->PortraitIcons;
+
+ for(int i=0;i<MAX_PORTRAIT_ICONS;i++) {
+ if (Icons[i]==0xffff) {
+ Icons[i]=icon;
+ return;
+ }
+ if (icon == (Icons[i]&0xff)) {
+ return;
+ }
+ }
+}
+
+void Actor::DisablePortraitIcon(ieByte icon)
+{
+ if (!PCStats) {
+ return;
+ }
+ ieWord *Icons = PCStats->PortraitIcons;
+ int i;
+
+ for(i=0;i<MAX_PORTRAIT_ICONS;i++) {
+ if (icon == (Icons[i]&0xff)) {
+ Icons[i]=0xff00|icon;
+ return;
+ }
+ }
+}
+
+/** call this after load, to apply effects */
+void Actor::RefreshEffects(EffectQueue *fx)
+{
+ ieDword previous[MAX_STATS];
+
+ //put all special cleanup calls here
+ CharAnimations* anims = GetAnims();
+ if (anims) {
+ anims->CheckColorMod();
+ }
+ spellbook.ClearBonus();
+ /* these apply resrefs should be on a list as a trigger+resref */
+ memset(applyWhenHittingMelee,0,sizeof(ieResRef));
+ memset(applyWhenHittingRanged,0,sizeof(ieResRef));
+ memset(applyWhenNearLiving,0,sizeof(ieResRef));
+ memset(applyWhen50Damage,0,sizeof(ieResRef));
+ memset(applyWhen90Damage,0,sizeof(ieResRef));
+ memset(applyWhenEnemySighted,0,sizeof(ieResRef));
+ memset(applyWhenPoisoned,0,sizeof(ieResRef));
+ memset(applyWhenHelpless,0,sizeof(ieResRef));
+ memset(applyWhenAttacked,0,sizeof(ieResRef));
+ memset(applyWhenBeingHit,0,sizeof(ieResRef));
+ memset(BardSong,0,sizeof(ieResRef));
+ memset(projectileImmunity,0,ProjectileSize*sizeof(ieDword));
+
+ //initialize base stats
+ bool first = !(InternalFlags&IF_INITIALIZED);
+
+ if (first) {
+ InternalFlags|=IF_INITIALIZED;
+ memcpy( previous, BaseStats, MAX_STATS * sizeof( ieDword ) );
+ } else {
+ memcpy( previous, Modified, MAX_STATS * sizeof( ieDword ) );
+ }
+ PrevStats = &previous[0];
+
+ memcpy( Modified, BaseStats, MAX_STATS * sizeof( ieDword ) );
+ if (PCStats) {
+ memset( PCStats->PortraitIcons, -1, sizeof(PCStats->PortraitIcons) );
+ }
+
+ if (fx) {
+ fx->SetOwner(this);
+ fx->AddAllEffects(this, Pos);
+ delete fx;
+ //copy back the original stats, because the effects
+ //will be reapplied in ApplyAllEffects again
+ memcpy( Modified, BaseStats, MAX_STATS * sizeof( ieDword ) );
+ //also clear the spell bonuses just given, they will be
+ //recalculated below again
+ spellbook.ClearBonus();
+ }
+
+ unsigned int i;
+
+ // some VVCs are controlled by stats (and so by PCFs), the rest have 'effect_owned' set
+ for (i = 0; i < vvcOverlays.size(); i++) {
+ if (vvcOverlays[i] && vvcOverlays[i]->effect_owned) vvcOverlays[i]->active = false;
+ }
+ for (i = 0; i < vvcShields.size(); i++) {
+ if (vvcShields[i] && vvcShields[i]->effect_owned) vvcShields[i]->active = false;
+ }
+
+ fxqueue.ApplyAllEffects( this );
+ //move this further down if needed
+ PrevStats = NULL;
+
+ // IE_CLASS is >classcount for non-PCs/NPCs
+ if (BaseStats[IE_CLASS] <= (ieDword)classcount)
+ RefreshPCStats();
+
+ for (i=0;i<MAX_STATS;i++) {
+ if (first || Modified[i]!=previous[i]) {
+ PostChangeFunctionType f = post_change_functions[i];
+ if (f) {
+ (*f)(this, previous[i], Modified[i]);
+ }
+ }
+ }
+ //add wisdom bonus spells
+ if (!spellbook.IsIWDSpellBook() && mxsplwis) {
+ int level = Modified[IE_WIS];
+ if (level--) {
+ spellbook.BonusSpells(IE_SPELL_TYPE_PRIEST, spllevels, mxsplwis+spllevels*level);
+ }
+ }
+
+ // check if any new portrait icon was removed or added
+ if (PCStats) {
+ if (memcmp(PCStats->PreviousPortraitIcons, PCStats->PortraitIcons, sizeof(PCStats->PreviousPortraitIcons))) {
+ core->SetEventFlag(EF_PORTRAIT);
+ memcpy( PCStats->PreviousPortraitIcons, PCStats->PortraitIcons, sizeof(PCStats->PreviousPortraitIcons) );
+ }
+ }
+}
+
+// refresh stats on creatures (PC or NPC) with a valid class (not animals etc)
+// internal use only, and this is maybe a stupid name :)
+void Actor::RefreshPCStats() {
+ //calculate hp bonus
+ int bonus;
+ int bonlevel = GetXPLevel(true);
+ int oldlevel, oldbonus;
+ oldlevel = oldbonus = 0;
+ ieDword bonindex = BaseStats[IE_CLASS]-1;
+
+ //we must limit the levels to the max allowable
+ if (bonlevel>maxhpconbon[bonindex])
+ bonlevel = maxhpconbon[bonindex];
+
+ if (IsDualInactive()) {
+ //we apply the inactive hp bonus if it's better than the new hp bonus, so that we
+ //never lose hp, only gain, on leveling
+ oldlevel = IsDualSwap() ? BaseStats[IE_LEVEL] : BaseStats[IE_LEVEL2];
+ bonlevel = IsDualSwap() ? BaseStats[IE_LEVEL2] : BaseStats[IE_LEVEL];
+ oldlevel = (oldlevel > maxhpconbon[bonindex]) ? maxhpconbon[bonindex] : oldlevel;
+ if (Modified[IE_MC_FLAGS] & (MC_WAS_FIGHTER|MC_WAS_RANGER)) {
+ oldbonus = core->GetConstitutionBonus(STAT_CON_HP_WARRIOR, Modified[IE_CON]);
+ } else {
+ oldbonus = core->GetConstitutionBonus(STAT_CON_HP_NORMAL, Modified[IE_CON]);
+ }
+ }
+
+ // warrior (fighter, barbarian, ranger, or paladin) or not
+ // GetClassLevel now takes into consideration inactive dual-classes
+ bonus = GetHpAdjustment(bonlevel);
+ oldbonus *= oldlevel;
+ bonus = (oldbonus > bonus) ? oldbonus : bonus;
+
+ //morale recovery every xth AI cycle
+ int mrec = GetStat(IE_MORALERECOVERYTIME);
+ if (mrec) {
+ if (!(core->GetGame()->GameTime%mrec)) {
+ NewBase(IE_MORALE,1,MOD_ADDITIVE);
+ }
+ }
+
+ if (bonus<0 && (Modified[IE_MAXHITPOINTS]+bonus)<=0) {
+ bonus=1-Modified[IE_MAXHITPOINTS];
+ }
+
+ //get the wspattack bonuses for proficiencies
+ WeaponInfo wi;
+ ITMExtHeader *header = GetWeapon(wi, false);
+ ieDword stars;
+ int dualwielding = IsDualWielding();
+ if (header && (wi.prof <= MAX_STATS)) {
+ stars = GetStat(wi.prof)&PROFS_MASK;
+ if (stars >= (unsigned)wspattack_rows) {
+ stars = wspattack_rows-1;
+ }
+
+ int tmplevel = GetWarriorLevel();
+ if (tmplevel >= wspattack_cols) {
+ tmplevel = wspattack_cols-1;
+ } else if (tmplevel < 0) {
+ tmplevel = 0;
+ }
+
+ //HACK: attacks per round bonus for monks should only apply to fists
+ if (isclass[ISMONK]&(1<<BaseStats[IE_CLASS])) {
+ unsigned int level = GetMonkLevel()-1;
+ if (level < monkbon_cols) {
+ SetBase(IE_NUMBEROFATTACKS, 2 + monkbon[0][level]);
+ }
+ } else {
+ //wspattack appears to only effect warriors
+ int defaultattacks = 2 + 2*dualwielding;
+ if (stars) {
+ if (tmplevel) {
+ SetBase(IE_NUMBEROFATTACKS, defaultattacks+wspattack[stars][tmplevel]);
+ } else {
+ SetBase(IE_NUMBEROFATTACKS, defaultattacks);
+ }
+ } else {
+ // unproficient user - force defaultattacks
+ SetStat(IE_NUMBEROFATTACKS, defaultattacks, 0);
+ }
+ }
+ }
+
+ //we still apply the maximum bonus to dead characters, but don't apply
+ //to current HP, or we'd have dead characters showing as having hp
+ Modified[IE_MAXHITPOINTS]+=bonus;
+// if(BaseStats[IE_STATE_ID]&STATE_DEAD)
+// bonus = 0;
+// BaseStats[IE_HITPOINTS]+=bonus;
+
+ // apply the intelligence and wisdom bonus to lore
+ Modified[IE_LORE] += core->GetLoreBonus(0, Modified[IE_INT]) + core->GetLoreBonus(0, Modified[IE_WIS]);
+}
+
+void Actor::RollSaves()
+{
+ if (InternalFlags&IF_USEDSAVE) {
+ SavingThrow[0]=(ieByte) core->Roll(1, SAVEROLL, 0);
+ SavingThrow[1]=(ieByte) core->Roll(1, SAVEROLL, 0);
+ SavingThrow[2]=(ieByte) core->Roll(1, SAVEROLL, 0);
+ SavingThrow[3]=(ieByte) core->Roll(1, SAVEROLL, 0);
+ SavingThrow[4]=(ieByte) core->Roll(1, SAVEROLL, 0);
+ InternalFlags&=~IF_USEDSAVE;
+ }
+}
+
+//saving throws:
+//type bits in file order in stats
+//0 spells 1 4
+//1 breath 2 3
+//2 death 4 0
+//3 wands 8 1
+//4 polymorph 16 2
+
+//iwd2 (luckily they use the same bits as it would be with bg2):
+//0 not used
+//1 not used
+//2 fortitude 4 0
+//3 reflex 8 1
+//4 will 16 2
+
+#define SAVECOUNT 5
+static int savingthrows[SAVECOUNT]={IE_SAVEVSSPELL, IE_SAVEVSBREATH, IE_SAVEVSDEATH, IE_SAVEVSWANDS, IE_SAVEVSPOLY};
+
+/** returns true if actor made the save against saving throw type */
+bool Actor::GetSavingThrow(ieDword type, int modifier)
+{
+ assert(type<SAVECOUNT);
+ InternalFlags|=IF_USEDSAVE;
+ int ret = SavingThrow[type];
+ if (ret == 1) return false;
+ if (ret == SAVEROLL) return true;
+ ret += modifier + GetStat(IE_LUCK);
+ return ret > (int) GetStat(savingthrows[type]);
+}
+
+/** implements a generic opcode function, modify modified stats
+returns the change
+*/
+int Actor::NewStat(unsigned int StatIndex, ieDword ModifierValue, ieDword ModifierType)
+{
+ int oldmod = Modified[StatIndex];
+
+ switch (ModifierType) {
+ case MOD_ADDITIVE:
+ //flat point modifier
+ SetStat(StatIndex, Modified[StatIndex]+ModifierValue, 0);
+ break;
+ case MOD_ABSOLUTE:
+ //straight stat change
+ SetStat(StatIndex, ModifierValue, 0);
+ break;
+ case MOD_PERCENT:
+ //percentile
+ SetStat(StatIndex, BaseStats[StatIndex] * ModifierValue / 100, 0);
+ break;
+ }
+ return Modified[StatIndex] - oldmod;
+}
+
+int Actor::NewBase(unsigned int StatIndex, ieDword ModifierValue, ieDword ModifierType)
+{
+ int oldmod = BaseStats[StatIndex];
+
+ switch (ModifierType) {
+ case MOD_ADDITIVE:
+ //flat point modifier
+ SetBase(StatIndex, BaseStats[StatIndex]+ModifierValue);
+ break;
+ case MOD_ABSOLUTE:
+ //straight stat change
+ SetBase(StatIndex, ModifierValue);
+ break;
+ case MOD_PERCENT:
+ //percentile
+ SetBase(StatIndex, BaseStats[StatIndex] * ModifierValue / 100);
+ break;
+ }
+ return BaseStats[StatIndex] - oldmod;
+}
+
+inline int CountElements(const char *s, char separator)
+{
+ int ret = 1;
+ while(*s) {
+ if (*s==separator) ret++;
+ s++;
+ }
+ return ret;
+}
+
+void Actor::Interact(int type)
+{
+ int start;
+ int count;
+
+ switch(type) {
+ case I_INSULT: start=VB_INSULT; count=3; break;
+ case I_COMPLIMENT: start=VB_COMPLIMENT; count=3; break;
+ case I_SPECIAL: start=VB_SPECIAL; count=3; break;
+ default:
+ return;
+ }
+ VerbalConstant(start, count);
+}
+
+ieStrRef Actor::GetVerbalConstant(int index) const
+{
+ if (index<0 || index>=VCONST_COUNT) {
+ return (ieStrRef) -1;
+ }
+
+ int idx = VCMap[index];
+
+ if (idx<0 || idx>=VCONST_COUNT) {
+ return (ieStrRef) -1;
+ }
+ return StrRefs[idx];
+}
+
+void Actor::VerbalConstant(int start, int count)
+{
+ ieStrRef vc;
+
+ count=rand()%count;
+
+ while(count>=0 && (!(vc = GetVerbalConstant(start+count)) || (vc==(ieStrRef) -1)) ) {
+ count--;
+ }
+ if(count>=0) {
+ DisplayStringCore(this, start+count, DS_CONSOLE|DS_CONST );
+ }
+}
+
+void Actor::Response(int type)
+{
+ int start;
+ int count;
+ ieStrRef vc;
+
+ switch(type) {
+ case I_INSULT: start=VB_RESP_INS; count=3; break;
+ case I_COMPLIMENT: start=VB_RESP_COMP; count=3; break;
+ default:
+ return;
+ }
+
+ count=rand()%count;
+ while(count && ((vc = GetVerbalConstant(start+count))!=(ieStrRef) -1)) {
+ count--;
+ }
+ if(count>=0) {
+ DisplayStringCore(this, start+count, DS_CONSOLE|DS_CONST );
+ }
+}
+
+void Actor::ReactToDeath(const char * deadname)
+{
+ AutoTable tm("death");
+ if (!tm) return;
+ // lookup value based on died's scriptingname and ours
+ // if value is 0 - use reactdeath
+ // if value is 1 - use reactspecial
+ // if value is string - use playsound instead (pst)
+ const char *value = tm->QueryField (scriptName, deadname);
+ switch (value[0]) {
+ case '0':
+ DisplayStringCore(this, VB_REACT, DS_CONSOLE|DS_CONST );
+ break;
+ case '1':
+ DisplayStringCore(this, VB_REACT_S, DS_CONSOLE|DS_CONST );
+ break;
+ default:
+ {
+ int count = CountElements(value,',');
+ if (count<=0) break;
+ count = core->Roll(1,count,-1);
+ ieResRef resref;
+ while(count--) {
+ while(*value && *value!=',') value++;
+ if (*value==',') value++;
+ }
+ strncpy(resref, value, 8);
+ for(count=0;count<8 && resref[count]!=',';count++) {};
+ resref[count]=0;
+
+ unsigned int len = 0;
+ core->GetAudioDrv()->Play( resref, &len );
+ ieDword counter = ( AI_UPDATE_TIME * len ) / 1000;
+ if (counter != 0)
+ SetWait( counter );
+ break;
+ }
+ }
+}
+
+//call this only from gui selects
+void Actor::SelectActor()
+{
+ DisplayStringCore(this, VB_SELECT+core->Roll(1,3,-1), DS_CONSOLE|DS_CONST );
+}
+
+void Actor::Panic(Scriptable *attacker, int panicmode)
+{
+ if (GetStat(IE_STATE_ID)&STATE_PANIC) {
+ printf("Already paniced\n");
+ //already in panic
+ return;
+ }
+ if (InParty) core->GetGame()->SelectActor(this, false, SELECT_NORMAL);
+ SetBaseBit(IE_STATE_ID, STATE_PANIC, true);
+ DisplayStringCore(this, VB_PANIC, DS_CONSOLE|DS_CONST );
+
+ Action *action;
+ char Tmp[40];
+ //FIXME: GenerateActionDirect should work on any scriptable
+ //they just need global ID
+ if (panicmode == PANIC_RUNAWAY && (!attacker || attacker->Type!=ST_ACTOR)) {
+ panicmode = PANIC_RANDOMWALK;
+ }
+
+ switch(panicmode) {
+ case PANIC_RUNAWAY:
+ strncpy(Tmp,"RunAwayFromNoInterrupt([-1])", sizeof(Tmp) );
+ action = GenerateActionDirect(Tmp, (Actor *) attacker);
+ break;
+ case PANIC_RANDOMWALK:
+ strncpy(Tmp,"RandomWalk()", sizeof(Tmp) );
+ action = GenerateAction( Tmp );
+ break;
+ case PANIC_BERSERK:
+ if (Modified[IE_EA]<EA_GOODCUTOFF) {
+ strncpy(Tmp,"GroupAttack('[EVILCUTOFF]'", sizeof(Tmp) );
+ } else {
+ strncpy(Tmp,"GroupAttack('[GOODCUTOFF]'", sizeof(Tmp) );
+ }
+ action = GenerateAction( Tmp );
+ break;
+ default:
+ return;
+ }
+ if (action) {
+ AddActionInFront(action);
+ } else {
+ printMessage("Actor","Cannot generate panic action\n", RED);
+ }
+}
+
+void Actor::SetMCFlag(ieDword arg, int op)
+{
+ ieDword tmp = BaseStats[IE_MC_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;
+ }
+ SetBase(IE_MC_FLAGS, tmp);
+}
+
+void Actor::DialogInterrupt()
+{
+ //if dialoginterrupt was set, no verbal constant
+ if ( Modified[IE_MC_FLAGS]&MC_NO_TALK)
+ return;
+
+ /* this part is unsure */
+ if (Modified[IE_EA]>=EA_EVILCUTOFF) {
+ DisplayStringCore(this, VB_HOSTILE, DS_CONSOLE|DS_CONST );
+ } else {
+ DisplayStringCore(this, VB_DIALOG, DS_CONSOLE|DS_CONST );
+ }
+}
+
+static EffectRef fx_cure_sleep_ref = { "Cure:Sleep", -1 };
+
+void Actor::GetHit()
+{
+ SetStance( IE_ANI_DAMAGE );
+ DisplayStringCore(this, VB_DAMAGE, DS_CONSOLE|DS_CONST );
+ if (Modified[IE_STATE_ID]&STATE_SLEEP) {
+ if (Modified[IE_EXTSTATE_ID]&EXTSTATE_NO_WAKEUP) {
+ return;
+ }
+ Effect *fx = EffectQueue::CreateEffect(fx_cure_sleep_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT);
+ fxqueue.AddEffect(fx);
+ }
+}
+
+bool Actor::HandleCastingStance(const ieResRef SpellResRef, bool deplete)
+{
+ if (deplete) {
+ if (! spellbook.HaveSpell( SpellResRef, HS_DEPLETE )) {
+ SetStance(IE_ANI_READY);
+ return true;
+ }
+ }
+ SetStance(IE_ANI_CAST);
+ return false;
+}
+
+static EffectRef fx_sleep_ref = { "State:Helpless", -1 };
+
+//returns actual damage
+int Actor::Damage(int damage, int damagetype, Scriptable *hitter, int modtype)
+{
+ //won't get any more hurt
+ if (InternalFlags & IF_REALLYDIED) {
+ return 0;
+ }
+
+ //add lastdamagetype up ? maybe
+ LastDamageType|=damagetype;
+ if(hitter && hitter->Type==ST_ACTOR) {
+ LastHitter=hitter->GetGlobalID();
+ } else {
+ //Maybe it should be something impossible like 0xffff, and use 'Someone'
+ LastHitter=GetGlobalID();
+ }
+
+ switch(modtype)
+ {
+ case MOD_ADDITIVE:
+ break;
+ case MOD_ABSOLUTE:
+ damage = GetBase(IE_HITPOINTS) - damage;
+ break;
+ case MOD_PERCENT:
+ damage = GetStat(IE_MAXHITPOINTS) * 100 / damage;
+ break;
+ default:
+ //this shouldn't happen
+ printMessage("Actor","Invalid damagetype!\n",RED);
+ return 0;
+ }
+
+ int resisted = 0;
+ ModifyDamage (this, hitter, damage, resisted, damagetype, NULL, false);
+
+ DisplayCombatFeedback(damage, resisted, damagetype, hitter);
+
+ if (BaseStats[IE_HITPOINTS] <= (ieDword) damage) {
+ // common fists do normal damage, but cause sleeping for a round instead of death
+ if ((damagetype & DAMAGE_STUNNING) && Modified[IE_MINHITPOINTS] <= 0) {
+ NewBase(IE_HITPOINTS, 1, MOD_ABSOLUTE);
+ // stack unconsciousness carefully to avoid replaying the stance changing
+ Effect *sleep = fxqueue.HasEffectWithParamPair(fx_sleep_ref, 0, 0);
+ if (sleep) {
+ sleep->Duration += core->Time.round_sec;
+ } else {
+ Effect *fx = EffectQueue::CreateEffect(fx_sleep_ref, 0, 0, FX_DURATION_INSTANT_LIMITED);
+ fx->Duration = core->Time.round_sec; // 1 round
+ core->ApplyEffect(fx, this, this);
+ delete fx;
+ }
+ } else {
+ if (damage) {
+ GetHit();
+ }
+ NewBase(IE_HITPOINTS, (ieDword) -damage, MOD_ADDITIVE);
+ }
+ } else {
+ if (damage) {
+ GetHit();
+ }
+ NewBase(IE_HITPOINTS, (ieDword) -damage, MOD_ADDITIVE);
+
+ // also apply reputation damage if we hurt (but not killed) an innocent
+ if (Modified[IE_CLASS] == CLASS_INNOCENT) {
+ Actor *act=NULL;
+ if (!hitter) {
+ hitter = area->GetActorByGlobalID(LastHitter);
+ }
+
+ if (hitter) {
+ if (hitter->Type==ST_ACTOR) {
+ act = (Actor *) hitter;
+ }
+ }
+
+ if (act && act->GetStat(IE_EA) <= EA_CONTROLLABLE) {
+ core->GetGame()->SetReputation(core->GetGame()->Reputation + core->GetReputationMod(1));
+ }
+ }
+ }
+
+ LastDamage=damage;
+ InternalFlags|=IF_ACTIVE;
+ int chp = (signed) BaseStats[IE_HITPOINTS];
+ int damagelevel = 0;
+ if (damage<10) {
+ damagelevel = 1;
+ } else {
+ NewBase(IE_MORALE, (ieDword) -1, MOD_ADDITIVE);
+ damagelevel = 2;
+ }
+
+ if (damagetype & (DAMAGE_FIRE|DAMAGE_MAGICFIRE) ) {
+ PlayDamageAnimation(DL_FIRE+damagelevel);
+ } else if (damagetype & (DAMAGE_COLD|DAMAGE_MAGICCOLD) ) {
+ PlayDamageAnimation(DL_COLD+damagelevel);
+ } else if (damagetype & (DAMAGE_ELECTRICITY) ) {
+ PlayDamageAnimation(DL_ELECTRICITY+damagelevel);
+ } else if (damagetype & (DAMAGE_ACID) ) {
+ PlayDamageAnimation(DL_ACID+damagelevel);
+ } else if (damagetype & (DAMAGE_MAGIC) ) {
+ PlayDamageAnimation(DL_DISINTEGRATE+damagelevel);
+ } else {
+ if (chp<-10) {
+ PlayDamageAnimation(DL_CRITICAL);
+ } else {
+ PlayDamageAnimation(DL_BLOOD+damagelevel);
+ }
+ }
+
+ if (InParty) {
+ if (chp<(signed) Modified[IE_MAXHITPOINTS]/10) {
+ core->Autopause(AP_WOUNDED);
+ }
+ if (damage>0) {
+ core->Autopause(AP_HIT);
+ core->SetEventFlag(EF_PORTRAIT);
+ }
+ }
+ return damage;
+}
+
+//TODO: handle pst
+void Actor::DisplayCombatFeedback (unsigned int damage, int resisted, int damagetype, Scriptable *hitter)
+{
+ bool detailed = false;
+ const char *type_name = "unknown";
+ if (displaymsg->HasStringReference(STR_DMG_SLASHING)) { // how and iwd2
+ std::multimap<ieDword, DamageInfoStruct>::iterator it;
+ it = core->DamageInfoMap.find(damagetype);
+ if (it != core->DamageInfoMap.end()) {
+ type_name = core->GetString(it->second.strref, 0);
+ }
+ detailed = true;
+ }
+
+ if (damage > 0 && resisted != DR_IMMUNE) {
+ printMessage("Actor", " ", GREEN);
+ printf("%d damage taken.\n", damage);
+
+ if (detailed) {
+ // 3 choices depending on resistance and boni
+ // iwd2 also has two Tortoise Shell (spell) absorption strings
+ core->GetTokenDictionary()->SetAtCopy( "TYPE", type_name);
+ core->GetTokenDictionary()->SetAtCopy( "AMOUNT", damage);
+ if (hitter && hitter->Type == ST_ACTOR) {
+ core->GetTokenDictionary()->SetAtCopy( "DAMAGER", hitter->GetName(1) );
+ } else {
+ core->GetTokenDictionary()->SetAtCopy( "DAMAGER", "trap" );
+ }
+ if (resisted < 0) {
+ //Takes <AMOUNT> <TYPE> damage from <DAMAGER> (<RESISTED> damage bonus)
+ core->GetTokenDictionary()->SetAtCopy( "RESISTED", abs(resisted));
+ displaymsg->DisplayConstantStringName(STR_DAMAGE3, 0xffffff, this);
+ } else if (resisted > 0) {
+ //Takes <AMOUNT> <TYPE> damage from <DAMAGER> (<RESISTED> damage resisted)
+ core->GetTokenDictionary()->SetAtCopy( "RESISTED", abs(resisted));
+ displaymsg->DisplayConstantStringName(STR_DAMAGE2, 0xffffff, this);
+ } else {
+ //Takes <AMOUNT> <TYPE> damage from <DAMAGER>
+ displaymsg->DisplayConstantStringName(STR_DAMAGE1, 0xffffff, this);
+ }
+ } else if (core->HasFeature(GF_ONSCREEN_TEXT) ) {
+ if(0) printf("TODO: pst floating text\n");
+ } else if (!displaymsg->HasStringReference(STR_DAMAGE2) || !hitter || hitter->Type != ST_ACTOR) {
+ // bg1 and iwd
+ // or any traps or self-infliction (also for bg1)
+ // construct an i18n friendly "Damage Taken (damage)", since there's no token
+ char tmp[64];
+ const char* msg = core->GetString(displaymsg->GetStringReference(STR_DAMAGE1), 0);
+ snprintf(tmp, sizeof(tmp), "%s (%d)", msg, damage);
+ displaymsg->DisplayStringName(tmp, 0xffffff, this);
+ } else { //bg2
+ //<DAMAGER> did <AMOUNT> damage to <DAMAGEE>
+ core->GetTokenDictionary()->SetAtCopy( "DAMAGEE", GetName(1) );
+ // wipe the DAMAGER token, so we can color it
+ core->GetTokenDictionary()->SetAtCopy( "DAMAGER", "" );
+ core->GetTokenDictionary()->SetAtCopy( "AMOUNT", damage);
+ displaymsg->DisplayConstantStringName(STR_DAMAGE2, 0xffffff, hitter);
+ }
+ } else {
+ if (resisted == DR_IMMUNE) {
+ printMessage("Actor", " ", GREEN);
+ printf("is immune to damage type: %s.\n", type_name);
+ if (hitter && hitter->Type == ST_ACTOR) {
+ if (detailed) {
+ //<DAMAGEE> was immune to my <TYPE> damage
+ core->GetTokenDictionary()->SetAtCopy( "DAMAGEE", GetName(1) );
+ core->GetTokenDictionary()->SetAtCopy( "TYPE", type_name );
+ displaymsg->DisplayConstantStringName(STR_DAMAGE_IMMUNITY, 0xffffff, hitter);
+ } else if (displaymsg->HasStringReference(STR_DAMAGE_IMMUNITY) && displaymsg->HasStringReference(STR_DAMAGE1)) {
+ // bg2
+ //<DAMAGEE> was immune to my damage.
+ core->GetTokenDictionary()->SetAtCopy( "DAMAGEE", GetName(1) );
+ displaymsg->DisplayConstantStringName(STR_DAMAGE_IMMUNITY, 0xffffff, hitter);
+ } // else: other games don't display anything
+ }
+ } else {
+ // mirror image or stoneskin: no message
+ }
+ }
+
+ //PST hit sounds
+ DataFileMgr *resdata = core->GetResDataINI();
+ if (resdata) {
+ PlayHitSound(resdata, damagetype, false);
+ }
+}
+
+void Actor::PlayWalkSound()
+{
+ ieDword thisTime;
+ ieResRef Sound;
+
+ GetTime(thisTime);
+ if (thisTime<nextWalk) return;
+ int cnt = anims->GetWalkSoundCount();
+ if (!cnt) return;
+
+ cnt=core->Roll(1,cnt,-1);
+ strnuprcpy(Sound, anims->GetWalkSound(), sizeof(ieResRef) );
+ area->ResolveTerrainSound(Sound, Pos);
+ if (cnt) {
+ int len = strlen(Sound);
+ if (len<8) {
+ Sound[len]=cnt+0x60;
+ }
+ }
+ unsigned int len = 0;
+ core->GetAudioDrv()->Play( Sound,Pos.x,Pos.y, 0, &len );
+ nextWalk = thisTime + len;
+}
+
+//Play PST specific hit sounds (HIT_0<dtype><armor>)
+void Actor::PlayHitSound(DataFileMgr *resdata, int damagetype, bool suffix)
+{
+ int type;
+
+ switch(damagetype) {
+ case DAMAGE_SLASHING: type = 1; break; //slashing
+ case DAMAGE_PIERCING: type = 2; break; //piercing
+ case DAMAGE_CRUSHING: type = 3; break; //crushing
+ case DAMAGE_MISSILE: type = 4; break; //missile
+ default: return; //other
+ }
+
+ ieResRef Sound;
+ char section[12];
+ unsigned int animid=BaseStats[IE_ANIMATION_ID];
+ if(core->HasFeature(GF_ONE_BYTE_ANIMID)) {
+ animid&=0xff;
+ }
+
+ snprintf(section,10,"%d", animid);
+
+ int armor = resdata->GetKeyAsInt(section, "armor",0);
+ if (armor<0 || armor>35) return;
+
+ snprintf(Sound,8,"HIT_0%d%c%c",type, armor+'A', suffix?'1':0);
+
+ core->GetAudioDrv()->Play( Sound,Pos.x,Pos.y );
+}
+
+//Just to quickly inspect debug maximum values
+#if 0
+void Actor::DumpMaxValues()
+{
+ int symbol = core->LoadSymbol( "stats" );
+ SymbolMgr *sym = core->GetSymbol( symbol );
+
+ for(int i=0;i<MAX_STATS;i++) {
+ printf("%d (%s) %d\n", i, sym->GetValue(i), maximum_values[i]);
+ }
+}
+#endif
+
+void Actor::DebugDump()
+{
+ unsigned int i;
+
+ printf( "Debugdump of Actor %s (%s, %s):\n", LongName, ShortName, GetName(-1) );
+ printf ("Scripts:");
+ for (i = 0; i < MAX_SCRIPTS; i++) {
+ const char* poi = "<none>";
+ if (Scripts[i]) {
+ poi = Scripts[i]->GetName();
+ }
+ printf( " %.8s", poi );
+ }
+ printf( "\nArea: %.8s ", Area );
+ printf( "Dialog: %.8s\n", Dialog );
+ printf( "Global ID: %d PartySlot: %d\n", GetGlobalID(), InParty);
+ printf( "Script name:%.32s Current action: %d\n", scriptName, CurrentAction ? CurrentAction->actionID : -1);
+ printf( "TalkCount: %d ", TalkCount );
+ printf( "Allegiance: %d current allegiance:%d\n", BaseStats[IE_EA], Modified[IE_EA] );
+ printf( "Class: %d current class:%d\n", BaseStats[IE_CLASS], Modified[IE_CLASS] );
+ printf( "Race: %d current race:%d\n", BaseStats[IE_RACE], Modified[IE_RACE] );
+ printf( "Gender: %d current gender:%d\n", BaseStats[IE_SEX], Modified[IE_SEX] );
+ printf( "Specifics: %d current specifics:%d\n", BaseStats[IE_SPECIFIC], Modified[IE_SPECIFIC] );
+ printf( "Alignment: %x current alignment:%x\n", BaseStats[IE_ALIGNMENT], Modified[IE_ALIGNMENT] );
+ printf( "Morale: %d current morale:%d\n", BaseStats[IE_MORALE], Modified[IE_MORALE] );
+ printf( "Moralebreak:%d Morale recovery:%d\n", Modified[IE_MORALEBREAK], Modified[IE_MORALERECOVERYTIME] );
+ printf( "Visualrange:%d (Explorer: %d)\n", Modified[IE_VISUALRANGE], Modified[IE_EXPLORE] );
+ printf( "Levels: %d/%d/%d (average %d)\n", Modified[IE_LEVEL], Modified[IE_LEVEL2], Modified[IE_LEVEL3], GetXPLevel(true) );
+ printf( "current HP:%d\n", BaseStats[IE_HITPOINTS] );
+ printf( "Mod[IE_ANIMATION_ID]: 0x%04X\n", Modified[IE_ANIMATION_ID] );
+ printf( "Colors: ");
+ if (core->HasFeature(GF_ONE_BYTE_ANIMID) ) {
+ for(i=0;i<Modified[IE_COLORCOUNT];i++) {
+ printf(" %d", Modified[IE_COLORS+i]);
+ }
+ }
+ else {
+ for(i=0;i<7;i++) {
+ printf(" %d", Modified[IE_COLORS+i]);
+ }
+ }
+ printf( "\nWaitCounter: %d\n", (int) GetWait());
+ printf( "LastTarget: %d %s\n", LastTarget, GetActorNameByID(LastTarget));
+ printf( "LastTalked: %d %s\n", LastTalkedTo, GetActorNameByID(LastTalkedTo));
+ inventory.dump();
+ spellbook.dump();
+ fxqueue.dump();
+#if 0
+ DumpMaxValues();
+#endif
+}
+
+const char* Actor::GetActorNameByID(ieDword ID) const
+{
+ Actor *actor = GetCurrentArea()->GetActorByGlobalID(ID);
+ if (!actor) {
+ return "<NULL>";
+ }
+ return actor->GetScriptName();
+}
+
+void Actor::SetMap(Map *map)
+{
+ //Did we have an area?
+ bool effinit=!GetCurrentArea();
+ //now we have an area
+ Scriptable::SetMap(map);
+ //unless we just lost it, in that case clear up some fields and leave
+ if (!map) {
+ //more bits may or may not be needed
+ InternalFlags &=~IF_CLEANUP;
+ return;
+ }
+
+ //These functions are called once when the actor is first put in
+ //the area. It already has all the items (including fist) at this
+ //time and it is safe to call effects.
+ //This hack is to delay the equipping effects until the actor has
+ //an area (and the game object is also existing)
+ if (effinit) {
+ int SlotCount = inventory.GetSlotCount();
+ for (int Slot = 0; Slot<SlotCount;Slot++) {
+ int slottype = core->QuerySlotEffects( Slot );
+ switch (slottype) {
+ case SLOT_EFFECT_NONE:
+ case SLOT_EFFECT_MELEE:
+ break;
+ default:
+ inventory.EquipItem( Slot );
+ break;
+ }
+ }
+ //We need to convert this to signed 16 bits, because
+ //it is actually a 16 bit number.
+ //It is signed to have the correct math
+ //when adding it to the base slot (SLOT_WEAPON) in
+ //case of quivers. (weird IE magic)
+ //The other word is the equipped header.
+ //find a quiver for the bow, etc
+ if (Equipped!=IW_NO_EQUIPPED) {
+ inventory.EquipItem( Equipped+inventory.GetWeaponSlot());
+ SetEquippedQuickSlot( inventory.GetEquipped(), EquippedHeader );
+ }
+ }
+}
+
+void Actor::SetPosition(const Point &position, int jump, int radius)
+{
+ PathTries = 0;
+ ClearPath();
+ Point p;
+ p.x = position.x/16;
+ p.y = position.y/12;
+ if (jump && !(Modified[IE_DONOTJUMP] & DNJ_FIT) && size ) {
+ GetCurrentArea()->AdjustPosition( p, radius );
+ }
+ p.x = p.x * 16 + 8;
+ p.y = p.y * 12 + 6;
+ MoveTo( p );
+}
+
+/* this is returning the level of the character for xp calculations
+ and the average level for dual/multiclass (rounded up),
+ also with iwd2's 3rd ed rules, this is why it is a separate function */
+ieDword Actor::GetXPLevel(int modified) const
+{
+ const ieDword *stats;
+
+ if (modified) {
+ stats = Modified;
+ }
+ else {
+ stats = BaseStats;
+ }
+
+ float classcount = 0;
+ float average = 0;
+ if (core->HasFeature(GF_LEVELSLOT_PER_CLASS)) {
+ // iwd2
+ for (int i=0; i < 11; i++) {
+ if (stats[levelslotsiwd2[i]] > 0) classcount++;
+ }
+ average = stats[IE_CLASSLEVELSUM] / classcount + 0.5;
+ }
+ else {
+ int levels[3]={stats[IE_LEVEL], stats[IE_LEVEL2], stats[IE_LEVEL3]};
+ average = levels[0];
+ classcount = 1;
+ if (IsDualClassed()) {
+ // dualclassed
+ if (levels[1] > 0) {
+ classcount++;
+ average += levels[1];
+ }
+ }
+ else if (IsMultiClassed()) {
+ //classcount is the number of on bits in the MULTI field
+ classcount = bitcount (multiclass);
+ for (int i=1; i<classcount; i++)
+ average += levels[i];
+ } //else single classed
+ average = average / classcount + 0.5;
+ }
+ return ieDword(average);
+}
+
+// returns the guessed caster level by passed spell type
+// FIXME: add iwd2 support (should be more precise, as there are more class types)
+// FIXME: add more logic for cross-type kits (like avengers)?
+ieDword Actor::GetBaseCasterLevel(int spelltype) const
+{
+ int level = 0;
+
+ switch(spelltype)
+ {
+ case IE_SPL_PRIEST:
+ level = GetClericLevel();
+ if (!level) level = GetDruidLevel();
+ if (!level) level = GetPaladinLevel();
+ if (!level) level = GetRangerLevel();
+ break;
+ case IE_SPL_WIZARD:
+ level = GetMageLevel();
+ if (!level) level = GetSorcererLevel();
+ if (!level) level = GetBardLevel();
+ break;
+ }
+ // if nothing was found, use the average level
+ if (!level) level = GetXPLevel(true);
+
+ return level;
+}
+
+int Actor::GetWildMod(int level)
+{
+ if(GetStat(IE_KIT)&0x1e) {
+ // avoid rerolling the mod, since we get called multiple times per each cast
+ if (!WMLevelMod) {
+ if (level>=MAX_LEVEL) level=MAX_LEVEL;
+ if(level<1) level=1;
+ WMLevelMod = wmlevels[core->Roll(1,20,-1)][level-1];
+
+ core->GetTokenDictionary()->SetAtCopy("LEVELDIF", abs(WMLevelMod));
+ if (WMLevelMod > 0) {
+ displaymsg->DisplayConstantStringName(STR_CASTER_LVL_INC, 0xffffff, this);
+ } else if (WMLevelMod < 0) {
+ displaymsg->DisplayConstantStringName(STR_CASTER_LVL_DEC, 0xffffff, this);
+ }
+ }
+ return WMLevelMod;
+ }
+ return 0;
+}
+
+int Actor::CastingLevelBonus(int level, int type)
+{
+ int bonus = 0;
+ switch(type)
+ {
+ case IE_SPL_PRIEST:
+ bonus = GetStat(IE_CASTINGLEVELBONUSCLERIC);
+ break;
+ case IE_SPL_WIZARD:
+ bonus = GetWildMod(level) + GetStat(IE_CASTINGLEVELBONUSMAGE);
+ }
+
+ return bonus;
+}
+
+ieDword Actor::GetCasterLevel(int spelltype)
+{
+ int level = GetBaseCasterLevel(spelltype);
+ return level + CastingLevelBonus(level, spelltype);
+}
+
+/** maybe this would be more useful if we calculate with the strength too
+*/
+int Actor::GetEncumbrance()
+{
+ return inventory.GetWeight();
+}
+
+//bg2 and iwd1
+static EffectRef control_creature_ref = { "ControlCreature", -1 };
+//iwd2
+static EffectRef control_undead_ref = { "ControlUndead2", -1 };
+
+//receive turning
+void Actor::Turn(Scriptable *cleric, ieDword turnlevel)
+{
+ bool evilcleric = false;
+
+ if (!turnlevel) {
+ return;
+ }
+
+ //determine if we see the cleric (distance)
+ if (!CanSee(cleric, this, true, GA_NO_DEAD)) {
+ return;
+ }
+
+ if ((cleric->Type==ST_ACTOR) && GameScript::ID_Alignment((Actor *)cleric,AL_EVIL) ) {
+ evilcleric = true;
+ }
+
+ //a little adjustment of the level to get a slight randomness on who is turned
+ unsigned int level = GetXPLevel(true)-(GetGlobalID()&3);
+
+ //this is safely hardcoded i guess
+ if (Modified[IE_GENERAL]!=GEN_UNDEAD) {
+ level = GetPaladinLevel();
+ if (evilcleric && level) {
+ LastTurner = cleric->GetGlobalID();
+ if (turnlevel >= level+TURN_DEATH_LVL_MOD) {
+ if (gamedata->Exists("panic", IE_SPL_CLASS_ID)) {
+ core->ApplySpell("panic", this, cleric, level);
+ } else {
+ printf("Panic from turning!\n");
+ Panic(cleric, PANIC_RUNAWAY);
+ }
+ }
+ }
+ return;
+ }
+
+ //determine alignment (if equals, then no turning)
+
+ LastTurner = cleric->GetGlobalID();
+
+ //determine panic or destruction/control
+ //we get the modified level
+ if (turnlevel >= level+TURN_DEATH_LVL_MOD) {
+ if (evilcleric) {
+ Effect *fx = fxqueue.CreateEffect(control_creature_ref, GEN_UNDEAD, 3, FX_DURATION_INSTANT_LIMITED);
+ if (!fx) {
+ fx = fxqueue.CreateEffect(control_undead_ref, GEN_UNDEAD, 3, FX_DURATION_INSTANT_LIMITED);
+ }
+ if (fx) {
+ fx->Duration = core->Time.round_sec;
+ fx->Target = FX_TARGET_PRESET;
+ core->ApplyEffect(fx, this, cleric);
+ delete fx;
+ return;
+ }
+ //fallthrough for bg1
+ }
+ Die(cleric);
+ } else if (turnlevel >= level+TURN_PANIC_LVL_MOD) {
+ printf("Panic from turning!\n");
+ Panic(cleric, PANIC_RUNAWAY);
+ }
+}
+
+//TODO: needs a way to respawn at a point
+void Actor::Resurrect()
+{
+ if (!(Modified[IE_STATE_ID ] & STATE_DEAD)) {
+ return;
+ }
+ InternalFlags&=IF_FROMGAME; //keep these flags (what about IF_INITIALIZED)
+ InternalFlags|=IF_ACTIVE|IF_VISIBLE; //set these flags
+ SetBase(IE_STATE_ID, 0);
+ SetBase(IE_MORALE, 10);
+ //resurrect spell sets the hitpoints to maximum in a separate effect
+ //raise dead leaves it at 1 hp
+ SetBase(IE_HITPOINTS, 1);
+ ClearActions();
+ ClearPath();
+ SetStance(IE_ANI_EMERGE);
+ //readjust death variable on resurrection
+ if (core->HasFeature(GF_HAS_KAPUTZ) && (AppearanceFlags&APP_DEATHVAR)) {
+ ieVariable DeathVar;
+
+ snprintf(DeathVar,sizeof(ieVariable),"%s_DEAD",scriptName);
+ Game *game=core->GetGame();
+ ieDword value=0;
+
+ game->kaputz->Lookup(DeathVar, value);
+ if (value>0) {
+ game->kaputz->SetAt(DeathVar, value-1);
+ }
+ }
+ //clear effects?
+}
+
+static EffectRef fx_cure_poisoned_state_ref = { "Cure:Poison", -1 };
+static EffectRef fx_cure_hold_state_ref = { "Cure:Hold", -1 };
+static EffectRef fx_cure_stun_state_ref = { "Cure:Stun", -1 };
+static EffectRef fx_remove_portrait_icon_ref = { "Icon:Remove", -1 };
+static EffectRef fx_unpause_caster_ref = { "Cure:CasterHold", -1 };
+
+void Actor::Die(Scriptable *killer)
+{
+ int i,j;
+
+ if (InternalFlags&IF_REALLYDIED) {
+ return; //can die only once
+ }
+
+ //Can't simply set Selected to false, game has its own little list
+ Game *game = core->GetGame();
+ game->SelectActor(this, false, SELECT_NORMAL);
+ game->OutAttack(GetGlobalID());
+
+ displaymsg->DisplayConstantStringName(STR_DEATH, 0xffffff, this);
+ DisplayStringCore(this, VB_DIE, DS_CONSOLE|DS_CONST );
+
+ // remove poison, hold, casterhold, stun and its icon
+ Effect *newfx;
+ newfx = EffectQueue::CreateEffect(fx_cure_poisoned_state_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, this, this);
+ delete newfx;
+ newfx = EffectQueue::CreateEffect(fx_cure_hold_state_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, this, this);
+ delete newfx;
+ newfx = EffectQueue::CreateEffect(fx_unpause_caster_ref, 0, 100, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, this, this);
+ delete newfx;
+ newfx = EffectQueue::CreateEffect(fx_cure_stun_state_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, this, this);
+ delete newfx;
+ newfx = EffectQueue::CreateEffect(fx_remove_portrait_icon_ref, 0, 37, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, this, this);
+ delete newfx;
+
+ // clearing the search map here means it's not blocked during death animations
+ // this is perhaps not ideal, but matches other searchmap code which uses
+ // GA_NO_DEAD to exclude IF_JUSTDIED actors as well as dead ones
+ if (area)
+ area->ClearSearchMapFor(this);
+
+ //JUSTDIED will be removed when the Die() trigger executed
+ //otherwise it is the same as REALLYDIED
+ InternalFlags|=IF_REALLYDIED|IF_JUSTDIED;
+ SetStance( IE_ANI_DIE );
+
+ Actor *act=NULL;
+ if (!killer) {
+ killer = area->GetActorByGlobalID(LastHitter);
+ }
+
+ if (killer) {
+ if (killer->Type==ST_ACTOR) {
+ act = (Actor *) killer;
+ }
+ }
+
+ if (InParty) {
+ game->PartyMemberDied(this);
+ core->Autopause(AP_DEAD);
+ } else {
+ if (act) {
+ if (act->InParty) {
+ //adjust kill statistics here
+ PCStatsStruct *stat = act->PCStats;
+ if (stat) {
+ stat->NotifyKill(Modified[IE_XPVALUE], ShortStrRef);
+ }
+ InternalFlags|=IF_GIVEXP;
+ }
+
+ // friendly party summons' kills also grant xp
+ if (act->Modified[IE_SEX] == SEX_SUMMON && act->Modified[IE_EA] == EA_CONTROLLED) {
+ InternalFlags|=IF_GIVEXP;
+ } else if (act->Modified[IE_EA] == EA_FAMILIAR) {
+ // familiar's kills also grant xp
+ InternalFlags|=IF_GIVEXP;
+ }
+ }
+ }
+
+ // XP seems to be handed at out at the moment of death
+ if (InternalFlags&IF_GIVEXP) {
+ //give experience to party
+ game->ShareXP(Modified[IE_XPVALUE], sharexp );
+
+ if (!InParty && act && act->GetStat(IE_EA) <= EA_CONTROLLABLE) {
+ // adjust reputation if the corpse was:
+ // an innocent, a member of the Flaming Fist or something evil
+ int repmod = 0;
+ if (Modified[IE_CLASS] == CLASS_INNOCENT) {
+ repmod = core->GetReputationMod(0);
+ } else if (Modified[IE_CLASS] == CLASS_FLAMINGFIST) {
+ repmod = core->GetReputationMod(3);
+ }
+ if (GameScript::ID_Alignment(this,AL_EVIL) ) {
+ repmod += core->GetReputationMod(7);
+ }
+ if (repmod) {
+ game->SetReputation(game->Reputation + repmod);
+ }
+ }
+ }
+
+ //moved clear actions after xp is given
+ //it clears some triggers, including the LastHitter
+ ClearActions();
+ ClearPath();
+ SetModal( MS_NONE );
+
+ ieDword value = 0;
+ ieVariable varname;
+
+ // death variables are updated at the moment of death
+ if (KillVar[0]) {
+ //don't use the raw killVar here
+ if (core->HasFeature(GF_HAS_KAPUTZ) ) {
+ if (AppearanceFlags&APP_DEATHTYPE) {
+ snprintf(varname, 32, "KILL_%s", KillVar);
+ game->kaputz->Lookup(varname, value);
+ game->kaputz->SetAt(varname, value+1, nocreate);
+ }
+ } else {
+ // iwd/iwd2 path *sets* this var, so i changed it, not sure about pst above
+ game->locals->SetAt(KillVar, 1, nocreate);
+ }
+ }
+ if (IncKillVar[0]) {
+ value = 0;
+ game->locals->Lookup(IncKillVar, value);
+ game->locals->SetAt(IncKillVar, value + 1, nocreate);
+ }
+
+ if (scriptName[0]) {
+ value = 0;
+ if (core->HasFeature(GF_HAS_KAPUTZ) ) {
+ if (AppearanceFlags&APP_DEATHVAR) {
+ snprintf(varname, 32, "%s_DEAD", scriptName);
+ game->kaputz->Lookup(varname, value);
+ game->kaputz->SetAt(varname, value+1, nocreate);
+ }
+ } else {
+ snprintf(varname, 32, core->GetDeathVarFormat(), scriptName);
+ game->locals->Lookup(varname, value);
+ game->locals->SetAt(varname, value+1, nocreate);
+ }
+
+ if (SetDeathVar) {
+ value = 0;
+ snprintf(varname, 32, "%s_DEAD", scriptName);
+ game->locals->Lookup(varname, value);
+ game->locals->SetAt(varname, 1, nocreate);
+ if (value) {
+ snprintf(varname, 32, "%s_KILL_CNT", scriptName);
+ value = 1;
+ game->locals->Lookup(varname, value);
+ game->locals->SetAt(varname, value + 1, nocreate);
+ }
+ }
+ }
+
+ if (IncKillCount) {
+ // racial dead count
+ value = 0;
+ int racetable = core->LoadSymbol("race");
+ if (racetable != -1) {
+ Holder<SymbolMgr> race = core->GetSymbol(racetable);
+ const char *raceName = race->GetValue(Modified[IE_RACE]);
+ if (raceName) {
+ // todo: should probably not set this for humans in iwd?
+ snprintf(varname, 32, "KILL_%s_CNT", raceName);
+ game->locals->Lookup(varname, value);
+ game->locals->SetAt(varname, value+1, nocreate);
+ }
+ }
+ }
+
+ //death counters for PST
+ j=APP_GOOD;
+ for(i=0;i<4;i++) {
+ if (AppearanceFlags&j) {
+ ieDword value = 0;
+ game->locals->Lookup(CounterNames[i], value);
+ game->locals->SetAt(CounterNames[i], value+DeathCounters[i], nocreate);
+ }
+ j+=j;
+ }
+
+ // EXTRACOUNT is updated at the moment of death
+ if (Modified[IE_SEX] == SEX_EXTRA || (Modified[IE_SEX] >= SEX_EXTRA2 && Modified[IE_SEX] <= SEX_MAXEXTRA)) {
+ // if gender is set to one of the EXTRA values, then at death, we have to decrease
+ // the relevant EXTRACOUNT area variable. scripts use this to check how many actors
+ // of this extra id are still alive (for example, see the ToB challenge scripts)
+ ieVariable varname;
+ if (Modified[IE_SEX] == SEX_EXTRA) {
+ snprintf(varname, 32, "EXTRACOUNT");
+ } else {
+ snprintf(varname, 32, "EXTRACOUNT%d", 2 + (Modified[IE_SEX] - SEX_EXTRA2));
+ }
+
+ Map *area = GetCurrentArea();
+ if (area) {
+ ieDword value = 0;
+ area->locals->Lookup(varname, value);
+ // i am guessing that we shouldn't decrease below 0
+ if (value > 0) {
+ area->locals->SetAt(varname, value-1);
+ }
+ }
+ }
+
+ //a plot critical creature has died (iwd2)
+ if (BaseStats[IE_MC_FLAGS]&MC_PLOT_CRITICAL) {
+ core->GetGUIScriptEngine()->RunFunction("GUIWORLD", "DeathWindowPlot", false);
+ }
+ //ensure that the scripts of the actor will run as soon as possible
+ ImmediateEvent();
+}
+
+void Actor::SetPersistent(int partyslot)
+{
+ InParty = (ieByte) partyslot;
+ InternalFlags|=IF_FROMGAME;
+ //if an actor is coming from a game, it should have these too
+ CreateStats();
+}
+
+void Actor::DestroySelf()
+{
+ InternalFlags|=IF_CLEANUP;
+ // clear search map so that a new actor can immediately go there
+ // (via ChangeAnimationCore)
+ if (area)
+ area->ClearSearchMapFor(this);
+}
+
+bool Actor::CheckOnDeath()
+{
+ if (InternalFlags&IF_CLEANUP) {
+ return true;
+ }
+ if (InternalFlags&IF_JUSTDIED) {
+ if (lastRunTime == 0 || CurrentAction || GetNextAction()) {
+ return false; //actor is currently dying, let him die first
+ }
+ }
+ if (!(InternalFlags&IF_REALLYDIED) ) {
+ return false;
+ }
+ //don't mess with the already deceased
+ if (BaseStats[IE_STATE_ID]&STATE_DEAD) {
+ return false;
+ }
+ // don't destroy actors currently in a dialog
+ GameControl *gc = core->GetGameControl();
+ if (gc && (GetGlobalID() == gc->dialoghandler->targetID || GetGlobalID() == gc->dialoghandler->speakerID)) {
+ return false;
+ }
+
+ //we need to check animID here, if it has not played the death
+ //sequence yet, then we could return now
+ ClearActions();
+ //missed the opportunity of Died()
+ InternalFlags&=~IF_JUSTDIED;
+
+ // items seem to be dropped at the moment of death
+ // .. but this can't go in Die() because that is called
+ // from effects and dropping items might change effects!
+ DropItem("",0);
+
+ //remove all effects that are not 'permanent after death' here
+ //permanent after death type is 9
+ SetBaseBit(IE_STATE_ID, STATE_DEAD, true);
+
+ // party actors are never removed
+ if (Persistent()) return false;
+
+ if (Modified[IE_MC_FLAGS]&MC_REMOVE_CORPSE) return true;
+ if (Modified[IE_MC_FLAGS]&MC_KEEP_CORPSE) return false;
+ //if chunked death, then return true
+ if (LastDamageType&DAMAGE_CHUNKING) {
+ //play chunky animation
+ //chunks are projectiles
+ return true;
+ }
+ return false;
+}
+
+/* this will create a heap at location, and transfer the item(s) */
+void Actor::DropItem(const ieResRef resref, unsigned int flags)
+{
+ if (inventory.DropItemAtLocation( resref, flags, area, Pos )) {
+ ReinitQuickSlots();
+ }
+}
+
+void Actor::DropItem(int slot , unsigned int flags)
+{
+ if (inventory.DropItemAtLocation( slot, flags, area, Pos )) {
+ ReinitQuickSlots();
+ }
+}
+
+/** returns quick item data */
+/** if header==-1 which is a 'use quickitem' action */
+/** if header is set, then which is the absolute slot index, */
+/** and header is the header index */
+void Actor::GetItemSlotInfo(ItemExtHeader *item, int which, int header)
+{
+ ieWord idx;
+ ieWord headerindex;
+
+ memset(item, 0, sizeof(ItemExtHeader) );
+ if (header<0) {
+ if (!PCStats) return; //not a player character
+ PCStats->GetSlotAndIndex(which,idx,headerindex);
+ if (headerindex==0xffff) return; //headerindex is invalid
+ } else {
+ idx=(ieWord) which;
+ headerindex=(ieWord) header;
+ }
+ const CREItem *slot = inventory.GetSlotItem(idx);
+ if (!slot) return; //quick item slot is empty
+ Item *itm = gamedata->GetItem(slot->ItemResRef);
+ if (!itm) return; //quick item slot contains invalid item resref
+ ITMExtHeader *ext_header = itm->GetExtHeader(headerindex);
+ //item has no extended header, or header index is wrong
+ if (!ext_header) return;
+ memcpy(item->itemname, slot->ItemResRef, sizeof(ieResRef) );
+ item->slot = idx;
+ item->headerindex = headerindex;
+ memcpy(&(item->AttackType), &(ext_header->AttackType),
+ ((char *) &(item->itemname)) -((char *) &(item->AttackType)) );
+ if (headerindex>=CHARGE_COUNTERS) {
+ item->Charges=0;
+ } else {
+ item->Charges=slot->Usages[headerindex];
+ }
+ gamedata->FreeItem(itm,slot->ItemResRef, false);
+}
+
+void Actor::ReinitQuickSlots()
+{
+ if (!PCStats) {
+ return;
+ }
+
+ // Note: (wjp, 20061226)
+ // This function needs some rethinking.
+ // It tries to satisfy two things at the moment:
+ // Fill quickslots when they are empty and an item is placed in the
+ // inventory slot corresponding to the quickslot
+ // Reset quickslots when an item is removed
+ // Currently, it resets all slots when items are removed,
+ // but it only refills the ACT_QSLOTn slots, not the ACT_WEAPONx slots.
+ //
+ // Refilling a weapon slot is possible, but essentially duplicates a lot
+ // of code from Inventory::EquipItem() which performs the same steps for
+ // the Inventory::Equipped slot.
+ // Hopefully, weapons/arrows are never added to inventory slots without
+ // EquipItem being called.
+
+ int i=sizeof(PCStats->QSlots);
+ while (i--) {
+ int slot;
+ int which;
+ if (i<0) which = ACT_WEAPON4+i+1;
+ else which = PCStats->QSlots[i];
+ switch (which) {
+ case ACT_WEAPON1:
+ case ACT_WEAPON2:
+ case ACT_WEAPON3:
+ case ACT_WEAPON4:
+ CheckWeaponQuickSlot(which-ACT_WEAPON1);
+ slot = 0;
+ break;
+ //WARNING:this cannot be condensed, because the symbols don't come in order!!!
+ case ACT_QSLOT1: slot = inventory.GetQuickSlot(); break;
+ case ACT_QSLOT2: slot = inventory.GetQuickSlot()+1; break;
+ case ACT_QSLOT3: slot = inventory.GetQuickSlot()+2; break;
+ case ACT_QSLOT4: slot = inventory.GetQuickSlot()+3; break;
+ case ACT_QSLOT5: slot = inventory.GetQuickSlot()+4; break;
+ default:
+ slot = 0;
+ }
+ if (!slot) continue;
+ //if magic items are equipped the equipping info doesn't change
+ //(afaik)
+
+ // Note: we're now in the QSLOTn case
+ // If slot is empty, reset quickslot to 0xffff/0xffff
+
+ if (!inventory.HasItemInSlot("", slot)) {
+ SetupQuickSlot(which, 0xffff, 0xffff);
+ } else {
+ ieWord idx;
+ ieWord headerindex;
+ PCStats->GetSlotAndIndex(which,idx,headerindex);
+ if (idx != slot || headerindex == 0xffff) {
+ // If slot just became filled, set it to filled
+ SetupQuickSlot(which,slot,0);
+ }
+ }
+ }
+
+ //these are always present
+ CheckWeaponQuickSlot(0);
+ CheckWeaponQuickSlot(1);
+ //disabling quick weapon slots for certain classes
+ for(i=0;i<2;i++) {
+ int which = ACT_WEAPON3+i;
+ // Assuming that ACT_WEAPON3 and 4 are always in the first two spots
+ if (PCStats->QSlots[i+3]!=which) {
+ SetupQuickSlot(which, 0xffff, 0xffff);
+ }
+ }
+}
+
+void Actor::CheckWeaponQuickSlot(unsigned int which)
+{
+ if (!PCStats) return;
+
+ bool empty = false;
+ // If current quickweaponslot doesn't contain an item, reset it to fist
+ int slot = PCStats->QuickWeaponSlots[which];
+ int header = PCStats->QuickWeaponHeaders[which];
+ if (!inventory.HasItemInSlot("", slot) || header == 0xffff) {
+ //a quiver just went dry, falling back to fist
+ empty = true;
+ } else {
+ // If current quickweaponslot contains ammo, and bow not found, reset
+
+ if (core->QuerySlotEffects(slot) == SLOT_EFFECT_MISSILE) {
+ const CREItem *slotitm = inventory.GetSlotItem(slot);
+ assert(slotitm);
+ Item *itm = gamedata->GetItem(slotitm->ItemResRef);
+ assert(itm);
+ ITMExtHeader *ext_header = itm->GetExtHeader(header);
+ if (ext_header) {
+ int type = ext_header->ProjectileQualifier;
+ int weaponslot = inventory.FindTypedRangedWeapon(type);
+ if (weaponslot == inventory.GetFistSlot()) {
+ empty = true;
+ }
+ } else {
+ empty = true;
+ }
+ gamedata->FreeItem(itm,slotitm->ItemResRef, false);
+ }
+ }
+
+ if (empty)
+ SetupQuickSlot(ACT_WEAPON1+which, inventory.GetFistSlot(), 0);
+}
+
+//if dual stuff needs to be handled on load too, improve this method with it
+int Actor::GetHpAdjustment(int multiplier)
+{
+ int val;
+
+ if (IsWarrior()) {
+ val = core->GetConstitutionBonus(STAT_CON_HP_WARRIOR,Modified[IE_CON]);
+ } else {
+ val = core->GetConstitutionBonus(STAT_CON_HP_NORMAL,Modified[IE_CON]);
+ }
+ return val * multiplier;
+}
+
+void Actor::InitStatsOnLoad()
+{
+ //default is 9 in Tob (is this true? or just most anims are 9?)
+ SetBase(IE_MOVEMENTRATE,9);
+
+ ieWord animID = ( ieWord ) BaseStats[IE_ANIMATION_ID];
+ //this is required so the actor has animation already
+ SetAnimationID( animID );
+
+ // Setting up derived stats
+ if (BaseStats[IE_STATE_ID] & STATE_DEAD) {
+ SetStance( IE_ANI_TWITCH );
+ Deactivate();
+ InternalFlags|=IF_REALLYDIED;
+ } else {
+ SetStance( IE_ANI_AWAKE );
+ }
+ inventory.CalculateWeight();
+ CreateDerivedStats();
+ Modified[IE_CON]=BaseStats[IE_CON]; // used by GetHpAdjustment
+ ieDword hp = BaseStats[IE_HITPOINTS] + GetHpAdjustment(GetXPLevel(false));
+ BaseStats[IE_HITPOINTS]=hp;
+ SetupFist();
+ //initial setup of modified stats
+ memcpy(Modified,BaseStats, sizeof(Modified));
+}
+
+void Actor::SetupQuickSlot(unsigned int which, int slot, int headerindex)
+{
+ if (!PCStats) return;
+ PCStats->InitQuickSlot(which, slot, headerindex);
+ //something changed about the quick items
+ core->SetEventFlag(EF_ACTION);
+}
+
+bool Actor::ValidTarget(int ga_flags) const
+{
+ //scripts can still see this type of actor
+
+ if (ga_flags&GA_NO_HIDDEN) {
+ if (Modified[IE_AVATARREMOVAL]) return false;
+ if (Modified[IE_EA]>EA_GOODCUTOFF && Modified[IE_STATE_ID]&state_invisible) {
+ return false;
+ }
+ }
+
+ if (ga_flags&GA_NO_ALLY) {
+ if(InParty) return false;
+ if(Modified[IE_EA]<=EA_GOODCUTOFF) return false;
+ }
+
+ if (ga_flags&GA_NO_ENEMY) {
+ if(!InParty && (Modified[IE_EA]>=EA_EVILCUTOFF) ) return false;
+ }
+
+ if (ga_flags&GA_NO_NEUTRAL) {
+ if((Modified[IE_EA]>EA_GOODCUTOFF) && (Modified[IE_EA]<EA_EVILCUTOFF) ) return false;
+ }
+
+ switch(ga_flags&GA_ACTION) {
+ case GA_PICK:
+ if (Modified[IE_STATE_ID] & STATE_CANTSTEAL) return false;
+ break;
+ case GA_TALK:
+ //can't talk to dead
+ if (Modified[IE_STATE_ID] & STATE_CANTLISTEN) return false;
+ //can't talk to hostile
+ if (Modified[IE_EA]>=EA_EVILCUTOFF) return false;
+ break;
+ }
+ if (ga_flags&GA_NO_DEAD) {
+ if (InternalFlags&IF_JUSTDIED) return false;
+ if (Modified[IE_STATE_ID] & STATE_DEAD) return false;
+ }
+ if (ga_flags&GA_SELECT) {
+ if (UnselectableTimer) return false;
+ if (Immobile()) return false;
+ }
+ return true;
+}
+
+//returns true if it won't be destroyed with an area
+//in this case it shouldn't be saved with the area either
+//it will be saved in the savegame
+bool Actor::Persistent() const
+{
+ if (InParty) return true;
+ if (InternalFlags&IF_FROMGAME) return true;
+ return false;
+}
+
+//this is a reimplementation of cheatkey a/s of bg2
+//cycling through animation/stance
+// a - get next animation, s - get next stance
+
+void Actor::GetNextAnimation()
+{
+ int RowNum = anims->AvatarsRowNum - 1;
+ if (RowNum<0)
+ RowNum = CharAnimations::GetAvatarsCount() - 1;
+ int NewAnimID = CharAnimations::GetAvatarStruct(RowNum)->AnimID;
+ printf ("AnimID: %04X\n", NewAnimID);
+ SetBase( IE_ANIMATION_ID, NewAnimID);
+}
+
+void Actor::GetPrevAnimation()
+{
+ int RowNum = anims->AvatarsRowNum + 1;
+ if (RowNum>=CharAnimations::GetAvatarsCount() )
+ RowNum = 0;
+ int NewAnimID = CharAnimations::GetAvatarStruct(RowNum)->AnimID;
+ printf ("AnimID: %04X\n", NewAnimID);
+ SetBase( IE_ANIMATION_ID, NewAnimID);
+}
+
+//slot is the projectile slot
+//This will return the projectile item.
+ITMExtHeader *Actor::GetRangedWeapon(WeaponInfo &wi) const
+{
+//EquippedSlot is the projectile. To get the weapon, use inventory.GetUsedWeapon()
+ wi.slot = inventory.GetEquippedSlot();
+ const CREItem *wield = inventory.GetSlotItem(wi.slot);
+ if (!wield) {
+ return NULL;
+ }
+ Item *item = gamedata->GetItem(wield->ItemResRef);
+ if (!item) {
+ return NULL;
+ }
+ //The magic of the bow and the arrow add up?
+ wi.enchantment += item->Enchantment;
+ wi.itemflags = wield->Flags;
+ //wi.range is not set, the projectile has no effect on range?
+
+ ITMExtHeader *which = item->GetWeaponHeader(true);
+ gamedata->FreeItem(item, wield->ItemResRef, false);
+ return which;
+}
+
+int Actor::IsDualWielding() const
+{
+ int slot;
+ //if the shield slot is a weapon, we're dual wielding
+ const CREItem *wield = inventory.GetUsedWeapon(true, slot);
+ if (!wield) {
+ return 0;
+ }
+
+ Item *itm = gamedata->GetItem( wield->ItemResRef );
+ if (!itm) {
+ return 0;
+ }
+
+ //if the item is usable in weapon slot, then it is weapon
+ int weapon = core->CanUseItemType( SLOT_WEAPON, itm );
+ gamedata->FreeItem( itm, wield->ItemResRef, false );
+ //is just weapon>0 ok?
+ return (weapon>0)?1:0;
+}
+
+//returns weapon header currently used (bow in case of bow+arrow)
+//if range is nonzero, then the returned header is valid
+ITMExtHeader *Actor::GetWeapon(WeaponInfo &wi, bool leftorright) const
+{
+ //only use the shield slot if we are dual wielding
+ leftorright = leftorright && IsDualWielding();
+
+ const CREItem *wield = inventory.GetUsedWeapon(leftorright, wi.slot);
+ if (!wield) {
+ return 0;
+ }
+ Item *item = gamedata->GetItem(wield->ItemResRef);
+ if (!item) {
+ return 0;
+ }
+
+ wi.enchantment = item->Enchantment;
+ wi.itemflags = wield->Flags;
+ wi.prof = item->WeaProf;
+
+ //select first weapon header
+ ITMExtHeader *which;
+ if (GetAttackStyle() == WEAPON_RANGED) {
+ which = item->GetWeaponHeader(true);
+ if (which) {
+ wi.backstabbing = which->RechargeFlags & IE_ITEM_BACKSTAB;
+ } else {
+ wi.backstabbing = false;
+ }
+ } else {
+ which = item->GetWeaponHeader(false);
+ // any melee weapon usable by a single class thief is game (UAI does not affect this)
+ // but also check a bit in the recharge flags (modder extension)
+ if (which) {
+ wi.backstabbing = !(item->UsabilityBitmask & 0x400000) || (which->RechargeFlags & IE_ITEM_BACKSTAB);
+ } else {
+ wi.backstabbing = !(item->UsabilityBitmask & 0x400000);
+ }
+ }
+
+ //make sure we use 'false' in this freeitem
+ //so 'which' won't point into invalid memory
+ gamedata->FreeItem(item, wield->ItemResRef, false);
+ if (!which) {
+ return 0;
+ }
+ if (which->Location!=ITEM_LOC_WEAPON) {
+ return 0;
+ }
+ wi.range = which->Range+1;
+ return which;
+ //return which->Range+1;
+}
+
+void Actor::GetNextStance()
+{
+ static int Stance = IE_ANI_AWAKE;
+
+ if (--Stance < 0) Stance = MAX_ANIMS-1;
+ printf ("StanceID: %d\n", Stance);
+ SetStance( Stance );
+}
+
+int Actor::LearnSpell(const ieResRef spellname, ieDword flags)
+{
+ //don't fail if the spell is also memorized (for innates)
+ if (! (flags&LS_MEMO)) {
+ if (spellbook.HaveSpell(spellname, 0) ) {
+ return LSR_KNOWN;
+ }
+ }
+ Spell *spell = gamedata->GetSpell(spellname);
+ if (!spell) {
+ return LSR_INVALID; //not existent spell
+ }
+
+ if (flags & LS_STATS) {
+ // chance to learn roll
+ int roll = LuckyRoll(1, 100, 0);
+ // adjust the roll for specialist mages
+ // doesn't work in bg1, since its spells don't have PrimaryType set (0 is NONE)
+ if (GetKitIndex(BaseStats[IE_KIT]) && spell->PrimaryType) {
+ if ((signed)BaseStats[IE_KIT] == 1<<(spell->PrimaryType+5)) { // +5 since the kit values start at 0x40
+ roll += 15;
+ } else {
+ roll -= 15;
+ }
+ }
+
+ if (roll > core->GetIntelligenceBonus(0, GetStat(IE_INT))) {
+ return LSR_FAILED;
+ }
+ }
+
+ int explev = spellbook.LearnSpell(spell, flags&LS_MEMO);
+ int tmp = spell->SpellName;
+ if (flags&LS_LEARN) {
+ core->GetTokenDictionary()->SetAt("SPECIALABILITYNAME", core->GetString(tmp));
+ switch (spell->SpellType) {
+ case IE_SPL_INNATE:
+ tmp = STR_GOTABILITY;
+ break;
+ case IE_SPL_SONG:
+ tmp = STR_GOTSONG;
+ break;
+ default:
+ tmp = STR_GOTSPELL;
+ break;
+ }
+ } else tmp = 0;
+ gamedata->FreeSpell(spell, spellname, false);
+ if (!explev) {
+ return LSR_INVALID;
+ }
+ if (tmp) {
+ displaymsg->DisplayConstantStringName(tmp, 0xbcefbc, this);
+ }
+ if (flags&LS_ADDXP && !(flags&LS_NOXP)) {
+ int xp = CalculateExperience(XP_LEARNSPELL, explev);
+ Game *game = core->GetGame();
+ game->ShareXP(xp, SX_DIVIDE);
+ }
+ return LSR_OK;
+}
+
+const char *Actor::GetDialog(int flags) const
+{
+ if (!flags) {
+ return Dialog;
+ }
+ if (Modified[IE_EA]>=EA_EVILCUTOFF) {
+ return NULL;
+ }
+
+ if ( (InternalFlags & IF_NOINT) && CurrentAction) {
+ if (flags>1) {
+ displaymsg->DisplayConstantString(STR_TARGETBUSY,0xff0000);
+ }
+ return NULL;
+ }
+ return Dialog;
+}
+
+void Actor::CreateStats()
+{
+ if (!PCStats) {
+ PCStats = new PCStatsStruct();
+ }
+}
+
+const char* Actor::GetScript(int ScriptIndex) const
+{
+ return Scripts[ScriptIndex]->GetName();
+}
+
+void Actor::SetModal(ieDword newstate, bool force)
+{
+ switch(newstate) {
+ case MS_NONE:
+ break;
+ case MS_BATTLESONG:
+ break;
+ case MS_DETECTTRAPS:
+ break;
+ case MS_STEALTH:
+ break;
+ case MS_TURNUNDEAD:
+ break;
+ default:
+ return;
+ }
+
+ if (ModalState == MS_BATTLESONG && ModalState != newstate && HasFeat(FEAT_LINGERING_SONG)) {
+ strnlwrcpy(LingeringModalSpell, ModalSpell, 8);
+ modalSpellLingering = 2;
+ }
+
+ if (IsSelected()) {
+ // display the turning-off message
+ if (ModalState != MS_NONE) {
+ displaymsg->DisplayStringName(core->ModalStates[ModalState].leaving_str, 0xffffff, this, IE_STR_SOUND|IE_STR_SPEECH);
+ }
+
+ // when called with the same state twice, toggle to MS_NONE
+ if (!force && ModalState == newstate) {
+ ModalState = MS_NONE;
+ } else {
+ ModalState = newstate;
+ }
+
+ //update the action bar
+ core->SetEventFlag(EF_ACTION);
+ } else {
+ ModalState = newstate;
+ }
+}
+
+void Actor::SetModalSpell(ieDword state, const char *spell)
+{
+ if (spell) {
+ strnlwrcpy(ModalSpell, spell, 8);
+ } else {
+ if (state >= core->ModalStates.size()) {
+ ModalSpell[0] = 0;
+ } else {
+ if (state==MS_BATTLESONG) {
+ if (BardSong[0]) {
+ strnlwrcpy(ModalSpell, BardSong, 8);
+ return;
+ }
+ }
+ strnlwrcpy(ModalSpell, core->ModalStates[state].spell, 8);
+ }
+ }
+}
+
+//this is just a stub function for now, attackstyle could be melee/ranged
+//even spells got this attack style
+int Actor::GetAttackStyle() const
+{
+ WeaponInfo wi;
+ //Non NULL if the equipped slot is a projectile or a throwing weapon
+ //TODO some weapons have both melee and ranged capability
+ if (GetRangedWeapon(wi) != NULL) return WEAPON_RANGED;
+ return WEAPON_MELEE;
+}
+
+void Actor::AttackedBy( Actor *attacker)
+{
+ LastAttacker = attacker->GetGlobalID();
+ Game * game = core->GetGame();
+ game->InAttack(GetGlobalID() );
+ game->InAttack(LastAttacker);
+}
+
+void Actor::SetTarget( Scriptable *target)
+{
+ if (target->Type==ST_ACTOR) {
+ Actor *tar = (Actor *) target;
+ LastTarget = tar->GetGlobalID();
+ tar->AttackedBy(this);
+ }
+ SetOrientation( GetOrient( target->Pos, Pos ), false );
+}
+
+//in case of LastTarget = 0
+void Actor::StopAttack()
+{
+ SetStance(IE_ANI_READY);
+ secondround = 0;
+ core->GetGame()->OutAttack(GetGlobalID());
+ InternalFlags|=IF_TARGETGONE; //this is for the trigger!
+ if (InParty) {
+ core->Autopause(AP_NOTARGET);
+ }
+}
+
+int Actor::Immobile() const
+{
+ if (GetStat(IE_CASTERHOLD)) {
+ return 1;
+ }
+ if (GetStat(IE_HELD)) {
+ return 1;
+ }
+ if (GetStat(IE_STATE_ID) & STATE_STILL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+//calculate how many attacks will be performed
+//in the next round
+//only called when Game thinks we are in attack
+//so it is safe to do cleanup here (it will be called only once)
+void Actor::InitRound(ieDword gameTime)
+{
+ lastInit = gameTime;
+ secondround = !secondround;
+
+ //roundTime will equal 0 if we aren't attacking something
+ if (roundTime) {
+ //only perform calculations at the beginning of the round!
+ if (((gameTime-roundTime)%core->Time.round_size != 0) || \
+ (roundTime == lastInit)) {
+ return;
+ }
+ }
+
+ //reset variables used in PerformAttack
+ attackcount = 0;
+ attacksperround = 0;
+ nextattack = 0;
+ lastattack = 0;
+
+ //we set roundTime to zero on any of the following returns, because this
+ //is guaranteed to be the start of a round, and we only want roundTime
+ //if we are attacking this round
+ if (InternalFlags&IF_STOPATTACK) {
+ core->GetGame()->OutAttack(GetGlobalID());
+ roundTime = 0;
+ return;
+ }
+
+ if (!LastTarget) {
+ StopAttack();
+ roundTime = 0;
+ return;
+ }
+
+ //if held or disabled, etc, then cannot continue attacking
+ ieDword state = GetStat(IE_STATE_ID);
+ if (state&STATE_CANTMOVE) {
+ roundTime = 0;
+ return;
+ }
+ if (Immobile()) {
+ roundTime = 0;
+ return;
+ }
+
+ //add one for second round to get an extra attack only if we
+ //are x/2 attacks per round
+ attackcount = GetStat(IE_NUMBEROFATTACKS);
+ if (secondround) {
+ attackcount++;
+ }
+ //all numbers of attacks are stored at twice their value
+ attackcount >>= 1;
+
+ //make sure we always get at least 1apr
+ if (attackcount < 1) {
+ attackcount = 1;
+ }
+
+ //set our apr and starting round time
+ attacksperround = attackcount;
+ roundTime = gameTime;
+
+ //print a little message :)
+ printMessage("InitRound", " ", WHITE);
+ printf("Name: %s | Attacks: %d | Start: %d\n", ShortName, attacksperround, gameTime);
+
+ // this might not be the right place, but let's give it a go
+ if (attacksperround && InParty) {
+ core->Autopause(AP_ENDROUND);
+ }
+}
+
+bool Actor::GetCombatDetails(int &tohit, bool leftorright, WeaponInfo& wi, ITMExtHeader *&header, ITMExtHeader *&hittingheader, \
+ ieDword &Flags, int &DamageBonus, int &speed, int &CriticalBonus, int &style, Actor *target) const
+{
+ tohit = GetStat(IE_TOHIT);
+ speed = -GetStat(IE_PHYSICALSPEED);
+ bool dualwielding = IsDualWielding();
+ header = GetWeapon(wi, leftorright && dualwielding);
+ if (!header) {
+ return false;
+ }
+ style = 0;
+ CriticalBonus = 0;
+ hittingheader = header;
+ ITMExtHeader *rangedheader = NULL;
+ int THAC0Bonus = hittingheader->THAC0Bonus;
+ DamageBonus = hittingheader->DamageBonus;
+ switch(hittingheader->AttackType) {
+ case ITEM_AT_MELEE:
+ Flags = WEAPON_MELEE;
+ break;
+ case ITEM_AT_PROJECTILE: //throwing weapon
+ Flags = WEAPON_RANGED;
+ break;
+ case ITEM_AT_BOW:
+ rangedheader = GetRangedWeapon(wi);
+ if (!rangedheader) {
+ //display out of ammo verbal constant if there is any???
+ //DisplayStringCore(this, VB_OUTOFAMMO, DS_CONSOLE|DS_CONST );
+ //SetStance(IE_ANI_READY);
+ //set some trigger?
+ return false;
+ }
+ Flags = WEAPON_RANGED;
+ //The bow can give some bonuses, but the core attack is made by the arrow.
+ hittingheader = rangedheader;
+ THAC0Bonus += rangedheader->THAC0Bonus;
+ DamageBonus += rangedheader->DamageBonus;
+ break;
+ default:
+ //item is unsuitable for fight
+ //SetStance(IE_ANI_READY);
+ return false;
+ }//melee or ranged
+ //this flag is set by the bow in case of projectile launcher.
+ if (header->RechargeFlags&IE_ITEM_USESTRENGTH) Flags|=WEAPON_USESTRENGTH;
+
+ // get our dual wielding modifier
+ if (dualwielding) {
+ if (leftorright) {
+ DamageBonus += GetStat(IE_DAMAGEBONUSLEFT);
+ } else {
+ DamageBonus += GetStat(IE_DAMAGEBONUSRIGHT);
+ }
+ }
+ leftorright = leftorright && dualwielding;
+ if (leftorright) Flags|=WEAPON_LEFTHAND;
+
+ //add in proficiency bonuses
+ ieDword stars;
+ if (wi.prof && (wi.prof <= MAX_STATS)) {
+ stars = GetStat(wi.prof)&PROFS_MASK;
+
+ //hit/damage/speed bonuses from wspecial
+ if ((signed)stars > wspecial_max) {
+ stars = wspecial_max;
+ }
+ THAC0Bonus += wspecial[stars][0];
+ DamageBonus += wspecial[stars][1];
+ speed += wspecial[stars][2];
+ // add non-proficiency penalty, which is missing from the table
+ if (stars == 0) THAC0Bonus -= 4;
+ }
+
+ if (IsDualWielding() && wsdualwield) {
+ //add dual wielding penalty
+ stars = GetStat(IE_PROFICIENCY2WEAPON)&PROFS_MASK;
+ if (stars > STYLE_MAX) stars = STYLE_MAX;
+
+ style = 1000*stars + IE_PROFICIENCY2WEAPON;
+ THAC0Bonus += wsdualwield[stars][leftorright?1:0];
+ } else if (wi.itemflags&(IE_INV_ITEM_TWOHANDED) && (Flags&WEAPON_MELEE) && wstwohanded) {
+ //add two handed profs bonus
+ stars = GetStat(IE_PROFICIENCY2HANDED)&PROFS_MASK;
+ if (stars > STYLE_MAX) stars = STYLE_MAX;
+
+ style = 1000*stars + IE_PROFICIENCY2HANDED;
+ DamageBonus += wstwohanded[stars][0];
+ CriticalBonus = wstwohanded[stars][1];
+ speed += wstwohanded[stars][2];
+ } else if (Flags&WEAPON_MELEE) {
+ int slot;
+ CREItem *weapon = inventory.GetUsedWeapon(true, slot);
+ if(wssingle && weapon == NULL) {
+ //NULL return from GetUsedWeapon means no shield slot
+ stars = GetStat(IE_PROFICIENCYSINGLEWEAPON)&PROFS_MASK;
+ if (stars > STYLE_MAX) stars = STYLE_MAX;
+
+ style = 1000*stars + IE_PROFICIENCYSINGLEWEAPON;
+ CriticalBonus = wssingle[stars][1];
+ } else if (wsswordshield && weapon) {
+ stars = GetStat(IE_PROFICIENCYSWORDANDSHIELD)&PROFS_MASK;
+ if (stars > STYLE_MAX) stars = STYLE_MAX;
+
+ style = 1000*stars + IE_PROFICIENCYSWORDANDSHIELD;
+ } else {
+ // no bonus
+ }
+ } else {
+ // ranged - no bonus
+ }
+
+ // TODO: Elves get a racial THAC0 bonus with all swords and bows in BG2 (but not daggers)
+
+ //second parameter is left or right hand flag
+ tohit = GetToHit(THAC0Bonus, Flags, target);
+
+ //pst increased critical hits
+ if (pstflags && (Modified[IE_STATE_ID]&STATE_CRIT_ENH)) {
+ CriticalBonus++;
+ }
+ return true;
+}
+
+int Actor::MeleePenalty() const
+{
+ if (GetMonkLevel()) return 0;
+ if (inventory.GetEquippedSlot()!=IW_NO_EQUIPPED) return 4;
+ return 0;
+}
+
+int Actor::GetToHit(int bonus, ieDword Flags, Actor *target) const
+{
+ int tohit = bonus;
+
+ //get our dual wielding modifier
+ if (IsDualWielding()) {
+ if (Flags&WEAPON_LEFTHAND) {
+ tohit += GetStat(IE_HITBONUSLEFT);
+ } else {
+ tohit += GetStat(IE_HITBONUSRIGHT);
+ }
+ }
+
+ //get attack style (melee or ranged)
+ switch(Flags&WEAPON_STYLEMASK) {
+ case WEAPON_MELEE:
+ tohit += GetStat(IE_MELEETOHIT);
+ break;
+ case WEAPON_FIST:
+ tohit += GetStat(IE_FISTHIT);
+ break;
+ case WEAPON_RANGED:
+ tohit += GetStat(IE_MISSILEHITBONUS);
+ //add dexterity bonus
+ tohit += core->GetDexterityBonus(STAT_DEX_MISSILE, GetStat(IE_DEX));
+ break;
+ }
+
+ //add strength bonus if we need
+ if (Flags&WEAPON_USESTRENGTH) {
+ tohit += core->GetStrengthBonus(0,GetStat(IE_STR), GetStat(IE_STREXTRA) );
+ }
+
+ if (target) {
+ // if the target is using a ranged weapon while we're meleeing, we get a +4 bonus
+ if ((Flags&WEAPON_STYLEMASK) != WEAPON_RANGED) {
+ if (target->GetAttackStyle() == WEAPON_RANGED) {
+ tohit += 4;
+ }
+ }
+
+ // melee vs. unarmed
+ tohit += target->MeleePenalty() - MeleePenalty();
+
+ // add +4 attack bonus vs racial enemies
+ if (GetRangerLevel()) {
+ if (IsRacialEnemy(target)) {
+ tohit += 4;
+ }
+ }
+ }
+
+ if (ReverseToHit) {
+ tohit = (signed)GetStat(IE_TOHIT)-tohit;
+ } else {
+ tohit += GetStat(IE_TOHIT);
+ }
+ return tohit;
+}
+
+static const int weapon_damagetype[] = {DAMAGE_CRUSHING, DAMAGE_PIERCING,
+ DAMAGE_CRUSHING, DAMAGE_SLASHING, DAMAGE_MISSILE, DAMAGE_STUNNING};
+static EffectRef fx_ac_vs_creature_type_ref = { "ACVsCreatureType", -1 };
+
+int Actor::GetDefense(int DamageType, Actor *attacker) const
+{
+ //specific damage type bonus.
+ int defense = 0;
+ if(DamageType > 5)
+ DamageType = 0;
+ switch (weapon_damagetype[DamageType]) {
+ case DAMAGE_CRUSHING:
+ defense += GetStat(IE_ACCRUSHINGMOD);
+ break;
+ case DAMAGE_PIERCING:
+ defense += GetStat(IE_ACPIERCINGMOD);
+ break;
+ case DAMAGE_SLASHING:
+ defense += GetStat(IE_ACSLASHINGMOD);
+ break;
+ case DAMAGE_MISSILE:
+ defense += GetStat(IE_ACMISSILEMOD);
+ break;
+ //What about stunning ?
+ default :
+ break;
+ }
+
+ //check for s/s and single weapon ac bonuses
+ if (!IsDualWielding() && wssingle && wsswordshield) {
+ WeaponInfo wi;
+ ITMExtHeader* header;
+ header = GetWeapon(wi, false);
+ //make sure we're wielding a single melee weapon
+ if (header && (header->AttackType == ITEM_AT_MELEE)) {
+ int slot;
+ ieDword stars;
+ if (inventory.GetUsedWeapon(true, slot) == NULL) {
+ //single-weapon style applies to all ac
+ stars = GetStat(IE_PROFICIENCYSINGLEWEAPON)&PROFS_MASK;
+ if (stars>STYLE_MAX) stars = STYLE_MAX;
+ defense += wssingle[stars][0];
+ } else if (weapon_damagetype[DamageType] == DAMAGE_MISSILE) {
+ //sword-shield style applies only to missile ac
+ stars = GetStat(IE_PROFICIENCYSWORDANDSHIELD)&PROFS_MASK;
+ if (stars>STYLE_MAX) stars = STYLE_MAX;
+ defense += wsswordshield[stars][0];
+ }
+ }
+ }
+
+ if (ReverseToHit) {
+ defense = GetStat(IE_ARMORCLASS)-defense;
+ } else {
+ defense += GetStat(IE_ARMORCLASS);
+ }
+ //Dexterity bonus is stored negative in 2da files.
+ defense += core->GetDexterityBonus(STAT_DEX_AC, GetStat(IE_DEX) );
+ if (attacker) {
+ defense -= fxqueue.BonusAgainstCreature(fx_ac_vs_creature_type_ref,attacker);
+ }
+ return defense;
+}
+
+
+void Actor::PerformAttack(ieDword gameTime)
+{
+ // start a new round if we really don't have one yet
+ if (!roundTime) {
+ printMessage("Actor", "Unregistered attack. We shouldn't be here?\n", RED);
+ secondround = 0;
+ InitRound(gameTime);
+ }
+
+ //only return if we don't have any attacks left this round
+ if (attackcount==0) {
+ // this is also part of the UpdateActorState hack below. sorry!
+ lastattack = gameTime;
+ return;
+ }
+
+ // this check shouldn't be necessary, but it causes a divide-by-zero below,
+ // so i would like it to be clear if it ever happens
+ if (attacksperround==0) {
+ printMessage("Actor", "APR was 0 in PerformAttack!\n", RED);
+ return;
+ }
+
+ //don't continue if we can't make the attack yet
+ //we check lastattack because we will get the same gameTime a few times
+ if ((nextattack > gameTime) || (gameTime == lastattack)) {
+ // fuzzie added the following line as part of the UpdateActorState hack below
+ lastattack = gameTime;
+ return;
+ }
+
+ if (InternalFlags&IF_STOPATTACK) {
+ // this should be avoided by the AF_ALIVE check by all the calling actions
+ printMessage("Actor", "Attack by dead actor!\n", LIGHT_RED);
+ return;
+ }
+
+ if (!LastTarget) {
+ printMessage("Actor", "Attack without valid target ID!\n", LIGHT_RED);
+ return;
+ }
+ //get target
+ Actor *target = area->GetActorByGlobalID(LastTarget);
+
+ if (target && (target->GetStat(IE_STATE_ID)&(STATE_DEAD|state_invisible))) {
+ target = NULL;
+ }
+
+ if (!target) {
+ printMessage("Actor", "Attack without valid target!\n", LIGHT_RED);
+ return;
+ }
+
+ printf("Performattack for %s, target is: %s\n", ShortName, target->ShortName);
+
+ //which hand is used
+ //we do apr - attacksleft so we always use the main hand first
+ bool leftorright = (bool) ((attacksperround-attackcount)&1);
+
+ WeaponInfo wi;
+ ITMExtHeader *header = NULL;
+ ITMExtHeader *hittingheader = NULL;
+ int tohit;
+ ieDword Flags;
+ int DamageBonus, CriticalBonus;
+ int speed, style;
+
+ //will return false on any errors (eg, unusable weapon)
+ if (!GetCombatDetails(tohit, leftorright, wi, header, hittingheader, Flags, DamageBonus, speed, CriticalBonus, style, target)) {
+ return;
+ }
+
+ //if this is the first call of the round, we need to update next attack
+ if (nextattack == 0) {
+ // initiative calculation (lucky 1d6-1 + item speed + speed stat + constant):
+ // speed contains the bonus from the physical speed stat and the proficiency level
+ int spdfactor = hittingheader->Speed + speed;
+ if (spdfactor<0) spdfactor = 0;
+ // -3: k/2 in the original, hardcoded to 6; -1 for the difference in rolls - the original rolled 0-5
+ spdfactor += LuckyRoll(1, 6, -4, LR_NEGATIVE);
+ if (spdfactor<0) spdfactor = 0;
+ if (spdfactor>10) spdfactor = 10;
+
+ //(round_size/attacks_per_round)*(initiative) is the first delta
+ nextattack = core->Time.round_size*spdfactor/(attacksperround*10) + gameTime;
+
+ //we can still attack this round if we have a speed factor of 0
+ if (nextattack > gameTime) {
+ return;
+ }
+ }
+
+ // FIXME: use proper weapon range
+ if((PersonalDistance(this, target) > wi.range*10) || (GetCurrentArea()!=target->GetCurrentArea() ) ) {
+ // this is a temporary double-check, remove when bugfixed
+ printMessage("Actor", "Attack action didn't bring us close enough!", LIGHT_RED);
+ return;
+ }
+
+ SetStance(AttackStance);
+
+ //figure out the time for our next attack since the old time has the initiative
+ //in it, we only have to add the basic delta
+ attackcount--;
+ nextattack += (core->Time.round_size/attacksperround);
+ lastattack = gameTime;
+
+ //debug messages
+ if (leftorright && IsDualWielding()) {
+ printMessage("Attack","(Off) ", YELLOW);
+ } else {
+ printMessage("Attack","(Main) ", GREEN);
+ }
+ if (attacksperround) {
+ printf("Left: %d | ", attackcount);
+ printf("Next: %d ", nextattack);
+ }
+
+ int roll = LuckyRoll(1, ATTACKROLL, 0);
+ if (roll==1) {
+ //critical failure
+ printBracket("Critical Miss", RED);
+ printf("\n");
+ displaymsg->DisplayConstantStringName(STR_CRITICAL_MISS, 0xffffff, this);
+ DisplayStringCore(this, VB_CRITMISS, DS_CONSOLE|DS_CONST );
+ if (Flags&WEAPON_RANGED) {//no need for this with melee weapon!
+ UseItem(wi.slot, (ieDword)-2, target, UI_MISS);
+ } else if (core->HasFeature(GF_BREAKABLE_WEAPONS)) {
+ //break sword
+ // a random roll on-hit (perhaps critical failure too)
+ // in 0,5% (1d20*1d10==1) cases
+ if ((header->RechargeFlags&IE_ITEM_BREAKABLE) && core->Roll(1,10,0) == 1) {
+ inventory.BreakItemSlot(wi.slot);
+ }
+ }
+ ResetState();
+ return;
+ }
+ //damage type is?
+ //modify defense with damage type
+ ieDword damagetype = hittingheader->DamageType;
+ int damage = 0;
+ int resisted = 0;
+
+ if (hittingheader->DiceThrown<256) {
+ damage += LuckyRoll(hittingheader->DiceThrown, hittingheader->DiceSides, 0, LR_CRITICAL);
+ damage += DamageBonus;
+ printf("| Damage %dd%d%+d = %d ",hittingheader->DiceThrown, hittingheader->DiceSides, DamageBonus, damage);
+ } else {
+ printf("| No Damage");
+ damage = 0;
+ }
+
+ if (roll >= (ATTACKROLL - (int) GetStat(IE_CRITICALHITBONUS) - CriticalBonus)) {
+ //critical success
+ printBracket("Critical Hit", GREEN);
+ printf("\n");
+ displaymsg->DisplayConstantStringName(STR_CRITICAL_HIT, 0xffffff, this);
+ DisplayStringCore(this, VB_CRITHIT, DS_CONSOLE|DS_CONST );
+ ModifyDamage (target, this, damage, resisted, weapon_damagetype[damagetype], &wi, true);
+ UseItem(wi.slot, Flags&WEAPON_RANGED?-2:-1, target, 0, damage);
+ ResetState();
+
+ return;
+ }
+
+
+ //get target's defense against attack
+ int defense = target->GetDefense(damagetype, this);
+
+ bool success;
+ if(ReverseToHit) {
+ success = roll + defense > tohit;
+ } else {
+ success = tohit + roll > defense;
+ }
+
+ if (!success) {
+ //hit failed
+ if (Flags&WEAPON_RANGED) {//Launch the projectile anyway
+ UseItem(wi.slot, (ieDword)-2, target, UI_MISS);
+ }
+ ResetState();
+ printBracket("Missed", LIGHT_RED);
+ printf("\n");
+ return;
+ }
+ printBracket("Hit", GREEN);
+ printf("\n");
+ ModifyDamage (target, this, damage, resisted, weapon_damagetype[damagetype], &wi, false);
+ UseItem(wi.slot, Flags&WEAPON_RANGED?-2:-1, target, 0, damage);
+ ResetState();
+}
+
+static EffectRef fx_stoneskin_ref = { "StoneSkinModifier", -1 };
+static EffectRef fx_stoneskin2_ref = { "StoneSkin2Modifier", -1 };
+static EffectRef fx_mirrorimage_ref = { "MirrorImageModifier", -1 };
+static EffectRef fx_aegis_ref = { "Aegis", -1 };
+
+void Actor::ModifyDamage(Actor *target, Scriptable *hitter, int &damage, int &resisted, int damagetype, WeaponInfo *wi, bool critical)
+{
+
+ int mirrorimages = target->Modified[IE_MIRRORIMAGES];
+ if (mirrorimages) {
+ if (LuckyRoll(1,mirrorimages+1,0) != 1) {
+ target->fxqueue.DecreaseParam1OfEffect(fx_mirrorimage_ref, 1);
+ target->Modified[IE_MIRRORIMAGES]--;
+ damage = 0;
+ return;
+ }
+ }
+
+ // only check stone skins if damage type is physical or magical
+ // DAMAGE_CRUSHING is 0, so we can't AND with it to check for its presence
+ if (!(damagetype & ~(DAMAGE_PIERCING|DAMAGE_SLASHING|DAMAGE_MISSILE|DAMAGE_MAGIC))) {
+ int stoneskins = target->Modified[IE_STONESKINS];
+ if (stoneskins) {
+ target->fxqueue.DecreaseParam1OfEffect(fx_stoneskin_ref, 1);
+ target->fxqueue.DecreaseParam1OfEffect(fx_aegis_ref, 1);
+ target->Modified[IE_STONESKINS]--;
+ damage = 0;
+ return;
+ }
+
+ stoneskins = target->Modified[IE_STONESKINSGOLEM];
+ if (stoneskins) {
+ target->fxqueue.DecreaseParam1OfEffect(fx_stoneskin2_ref, 1);
+ target->Modified[IE_STONESKINSGOLEM]--;
+ damage = 0;
+ return;
+ }
+ }
+
+ if (wi) {
+ if (BaseStats[IE_BACKSTABDAMAGEMULTIPLIER] > 1) {
+ if (Modified[IE_STATE_ID] & state_invisible || Modified[IE_ALWAYSBACKSTAB]) {
+ if ( !(core->HasFeature(GF_PROPER_BACKSTAB) && !IsBehind(target)) ) {
+ if (target->Modified[IE_DISABLEBACKSTAB]) {
+ // The backstab seems to have failed
+ displaymsg->DisplayConstantString (STR_BACKSTAB_FAIL, 0xffffff);
+ } else {
+ if (wi->backstabbing) {
+ damage *= Modified[IE_BACKSTABDAMAGEMULTIPLIER];
+ // display a simple message instead of hardcoding multiplier names
+ displaymsg->DisplayConstantStringValue (STR_BACKSTAB, 0xffffff, Modified[IE_BACKSTABDAMAGEMULTIPLIER]);
+ } else {
+ // weapon is unsuitable for backstab
+ displaymsg->DisplayConstantString (STR_BACKSTAB_BAD, 0xffffff);
+ }
+ }
+ }
+ }
+ }
+
+ // add strength bonus; backstab does not affect it
+ // TODO: should actually check WEAPON_USESTRENGTH, since a sling in bg2 has it
+ if (GetAttackStyle() != WEAPON_RANGED) {
+ if (core->HasFeature(GF_3ED_RULES) && wi->itemflags&IE_INV_ITEM_TWOHANDED) {
+ // 150% bonus for twohandlers
+ damage += 150 * core->GetStrengthBonus(1, GetStat(IE_STR), GetStat(IE_STREXTRA)) / 100;
+ } else {
+ damage += core->GetStrengthBonus(1, GetStat(IE_STR), GetStat(IE_STREXTRA) );
+ }
+ }
+ }
+
+ if (wi && target->fxqueue.WeaponImmunity(wi->enchantment, wi->itemflags)) {
+ damage = 0;
+ resisted = DR_IMMUNE; // mark immunity for GetCombatDetails
+ } else {
+ // check damage type immunity / resistance / susceptibility
+ std::multimap<ieDword, DamageInfoStruct>::iterator it;
+ it = core->DamageInfoMap.find(damagetype);
+ if (it == core->DamageInfoMap.end()) {
+ printf("Unhandled damagetype:%d\n", damagetype);
+ } else if (it->second.resist_stat == 0) {
+ // damage type without a resistance stat
+ } else {
+ damage += (signed)target->GetStat(IE_DAMAGEBONUS);
+ resisted = (int) (damage * (signed)target->GetSafeStat(it->second.resist_stat)/100.0);
+ // check for bonuses for specific damage types
+ if (core->HasFeature(GF_SPECIFIC_DMG_BONUS) && hitter && hitter->Type == ST_ACTOR) {
+ int bonus = ((Actor *)hitter)->fxqueue.SpecificDamageBonus(it->second.iwd_mod_type);
+ if (bonus) {
+ resisted -= int (damage * bonus / 100.0);
+ printf("Bonus damage of %d (%+d%%), neto: %d\n", int (damage * bonus / 100.0), bonus, -resisted);
+ }
+ }
+ damage -= resisted;
+ printf("Resisted %d of %d at %d%% resistance to %d\n", resisted, damage+resisted, target->GetSafeStat(it->second.resist_stat), damagetype);
+ if (damage <= 0) resisted = DR_IMMUNE;
+ }
+ }
+
+ //check casting failure
+ if (damage<0) damage = 0;
+ if (!damage) {
+ DisplayStringCore(this, VB_TIMMUNE, DS_CONSOLE|DS_CONST );
+ return;
+ }
+
+ //critical protection a la PST
+ if (pstflags && (Modified[IE_STATE_ID] & (ieDword) STATE_CRIT_PROT )) {
+ critical = 0;
+ }
+
+ if (critical) {
+ if (target->inventory.ProvidesCriticalAversion()) {
+ //critical hit is averted by helmet
+ displaymsg->DisplayConstantStringName(STR_NO_CRITICAL, 0xffffff, target);
+ } else {
+ //a critical surely raises the morale?
+ //only if it is successful
+ NewBase(IE_MORALE, 1, MOD_ADDITIVE);
+ damage <<=1; //critical damage is always double?
+ core->timer->SetScreenShake(16,16,8);
+ }
+ }
+ return;
+}
+
+void Actor::UpdateActorState(ieDword gameTime) {
+ if (modalTime==gameTime) {
+ return;
+ }
+
+ //IWD2 has no autodetect, you actually should 'search'
+ if (InParty && core->HasFeature(GF_AUTOSEARCH_HIDDEN) ) {
+ core->ApplySpell("detect", this, this, 0);
+ }
+
+ // this is a HACK, fuzzie can't work out where else to do this for now
+ // but we shouldn't be resetting rounds/attacks just because the actor
+ // wandered away, the action code should probably be responsible somehow
+ // see also line above (search for comment containing UpdateActorState)!
+ if (LastTarget && lastattack && lastattack < (gameTime - 1)) {
+ Actor *target = area->GetActorByGlobalID(LastTarget);
+ if (!target || target->GetStat(IE_STATE_ID)&STATE_DEAD) {
+ StopAttack();
+ } else {
+ printMessage("Attack","(Leaving attack)", GREEN);
+ core->GetGame()->OutAttack(GetGlobalID());
+ }
+
+ roundTime = 0;
+ lastattack = 0;
+ }
+
+ if (ModalState == MS_NONE && !modalSpellLingering) {
+ return;
+ }
+
+ //apply the modal effect on the beginning of each round
+ if ((((gameTime-roundTime)%core->Time.round_size)==0)) {
+ //we can set this to 0
+ modalTime = gameTime;
+
+ // handle lingering modal spells like bardsong in iwd2
+ if (modalSpellLingering && LingeringModalSpell[0]) {
+ modalSpellLingering--;
+ if (core->ModalStates[ModalState].aoe_spell) {
+ core->ApplySpellPoint(LingeringModalSpell, GetCurrentArea(), Pos, this, 0);
+ } else {
+ core->ApplySpell(LingeringModalSpell, this, this, 0);
+ }
+ }
+ if (ModalState == MS_NONE) {
+ return;
+ }
+
+ if (!ModalSpell[0]) {
+ printMessage("Actor","Modal Spell Effect was not set!\n", YELLOW);
+ ModalSpell[0]='*';
+ } else if(ModalSpell[0]!='*') {
+ if (ModalSpellSkillCheck()) {
+ if (core->ModalStates[ModalState].aoe_spell) {
+ core->ApplySpellPoint(ModalSpell, GetCurrentArea(), Pos, this, 0);
+ } else {
+ core->ApplySpell(ModalSpell, this, this, 0);
+ }
+ if (InParty) {
+ displaymsg->DisplayStringName(core->ModalStates[ModalState].entering_str, 0xffffff, this, IE_STR_SOUND|IE_STR_SPEECH);
+ }
+ } else {
+ if (InParty) {
+ displaymsg->DisplayStringName(core->ModalStates[ModalState].failed_str, 0xffffff, this, IE_STR_SOUND|IE_STR_SPEECH);
+ }
+ ModalState = MS_NONE;
+ // TODO: wait for a round until allowing new states?
+ }
+ }
+ }
+
+}
+
+//idx could be: 0-6, 16-22, 32-38, 48-54
+//the colors are stored in 7 dwords
+//maybe it would be simpler to store them in 28 bytes (without using stats?)
+void Actor::SetColor( ieDword idx, ieDword grd)
+{
+ ieByte gradient = (ieByte) (grd&255);
+ ieByte index = (ieByte) (idx&15);
+ ieByte shift = (ieByte) (idx/16);
+ ieDword value;
+
+ //invalid value, would crash original IE
+ if (index>6) {
+ return;
+ }
+ if (shift == 15) {
+ // put gradient in all four bytes of value
+ value = gradient;
+ value |= (value << 8);
+ value |= (value << 16);
+ for (index=0;index<7;index++) {
+ Modified[IE_COLORS+index] = value;
+ }
+ } else {
+ //invalid value, would crash original IE
+ if (shift>3) {
+ return;
+ }
+ shift *= 8;
+ value = gradient << shift;
+ value |= Modified[IE_COLORS+index] & ~(255<<shift);
+ Modified[IE_COLORS+index] = value;
+ }
+}
+
+void Actor::SetColorMod( ieDword location, RGBModifier::Type type, int speed,
+ unsigned char r, unsigned char g, unsigned char b,
+ int phase)
+{
+ CharAnimations* ca = GetAnims();
+ if (!ca) return;
+
+ if (location == 0xff) {
+ if (phase && ca->GlobalColorMod.locked) return;
+ ca->GlobalColorMod.locked = !phase;
+ ca->GlobalColorMod.type = type;
+ ca->GlobalColorMod.speed = speed;
+ ca->GlobalColorMod.rgb.r = r;
+ ca->GlobalColorMod.rgb.g = g;
+ ca->GlobalColorMod.rgb.b = b;
+ ca->GlobalColorMod.rgb.a = 0;
+ if (phase >= 0)
+ ca->GlobalColorMod.phase = phase;
+ else {
+ if (ca->GlobalColorMod.phase > 2*speed)
+ ca->GlobalColorMod.phase=0;
+ }
+ return;
+ }
+ //00xx0yyy-->000xxyyy
+ if (location&0xffffffc8) return; //invalid location
+ if (phase && ca->ColorMods[location].locked) return;
+ location = (location &7) | ((location>>1)&0x18);
+ ca->ColorMods[location].type = type;
+ ca->ColorMods[location].speed = speed;
+ ca->ColorMods[location].rgb.r = r;
+ ca->ColorMods[location].rgb.g = g;
+ ca->ColorMods[location].rgb.b = b;
+ ca->ColorMods[location].rgb.a = 0;
+ if (phase >= 0)
+ ca->ColorMods[location].phase = phase;
+ else {
+ if (ca->ColorMods[location].phase > 2*speed)
+ ca->ColorMods[location].phase = 0;
+ }
+}
+
+void Actor::SetLeader(Actor *actor, int xoffset, int yoffset)
+{
+ LastFollowed = actor->GetGlobalID();
+ FollowOffset.x = xoffset;
+ FollowOffset.y = yoffset;
+}
+
+//if days == 0, it means full healing
+void Actor::Heal(int days)
+{
+ if (days) {
+ SetBase(IE_HITPOINTS, BaseStats[IE_HITPOINTS]+days*2);
+ } else {
+ SetBase(IE_HITPOINTS, Modified[IE_MAXHITPOINTS]);
+ }
+}
+
+void Actor::AddExperience(int exp)
+{
+ if (core->HasFeature(GF_WISDOM_BONUS)) {
+ exp = (exp * (100 + core->GetWisdomBonus(0, Modified[IE_WIS]))) / 100;
+ }
+ SetBase(IE_XP,BaseStats[IE_XP]+exp);
+}
+
+int Actor::CalculateExperience(int type, int level)
+{
+ if (type>=xpbonustypes) {
+ return 0;
+ }
+ unsigned int l = (unsigned int) (level - 1);
+
+ if (l>=(unsigned int) xpbonuslevels) {
+ l=xpbonuslevels-1;
+ }
+ return xpbonus[type*xpbonuslevels+l];
+}
+
+bool Actor::Schedule(ieDword gametime, bool checkhide)
+{
+ if (checkhide) {
+ if (!(InternalFlags&IF_VISIBLE) ) {
+ return false;
+ }
+ }
+
+ //check for schedule
+ ieDword bit = 1<<((gametime/6)%7200/300);
+ if (appearance & bit) {
+ return true;
+ }
+ return false;
+}
+
+void Actor::NewPath()
+{
+ PathTries++;
+ Point tmp = Destination;
+ ClearPath();
+ if (PathTries>10) {
+ return;
+ }
+ Movable::WalkTo(tmp, size );
+}
+
+void Actor::SetInTrap(ieDword setreset)
+{
+ InTrap = setreset;
+ if (setreset) {
+ InternalFlags |= IF_INTRAP;
+ } else {
+ InternalFlags &= ~IF_INTRAP;
+ }
+}
+
+void Actor::SetRunFlags(ieDword flags)
+{
+ InternalFlags &= ~IF_RUNFLAGS;
+ InternalFlags |= (flags & IF_RUNFLAGS);
+}
+
+void Actor::WalkTo(const Point &Des, ieDword flags, int MinDistance)
+{
+ PathTries = 0;
+ if (InternalFlags&IF_REALLYDIED) {
+ return;
+ }
+ SetRunFlags(flags);
+ // is this true???
+ if (Des.x==-2 && Des.y==-2) {
+ Point p((ieWord) Modified[IE_SAVEDXPOS], (ieWord) Modified[IE_SAVEDYPOS] );
+ Movable::WalkTo(p, MinDistance);
+ } else {
+ Movable::WalkTo(Des, MinDistance);
+ }
+}
+
+//there is a similar function in Map for stationary vvcs
+void Actor::DrawVideocells(const Region &screen, vvcVector &vvcCells, const Color &tint)
+{
+ Map* area = GetCurrentArea();
+
+ for (unsigned int i = 0; i < vvcCells.size(); i++) {
+ ScriptedAnimation* vvc = vvcCells[i];
+/* we don't allow holes anymore
+ if (!vvc)
+ continue;
+*/
+
+ // actually this is better be drawn by the vvc
+ bool endReached = vvc->Draw(screen, Pos, tint, area, WantDither(), GetOrientation());
+ if (endReached) {
+ delete vvc;
+ vvcCells.erase(vvcCells.begin()+i);
+ continue;
+ }
+ }
+}
+
+void Actor::DrawActorSprite(const Region &screen, int cx, int cy, const Region& bbox,
+ SpriteCover*& newsc, Animation** anims,
+ unsigned char Face, const Color& tint)
+{
+ CharAnimations* ca = GetAnims();
+ int PartCount = ca->GetTotalPartCount();
+ Video* video = core->GetVideoDriver();
+ Region vp = video->GetViewport();
+
+ // display current frames in the right order
+ const int* zOrder = ca->GetZOrder(Face);
+ for (int part = 0; part < PartCount; ++part) {
+ int partnum = part;
+ if (zOrder) partnum = zOrder[part];
+ Animation* anim = anims[partnum];
+ Sprite2D* nextFrame = 0;
+ if (anim)
+ nextFrame = anim->GetFrame(anim->GetCurrentFrame());
+ if (nextFrame && bbox.InsideRegion( vp ) ) {
+ if (!newsc || !newsc->Covers(cx, cy, nextFrame->XPos, nextFrame->YPos, nextFrame->Width, nextFrame->Height)) {
+ // the first anim contains the animarea for
+ // the entire multi-part animation
+ newsc = area->BuildSpriteCover(cx,
+ cy, -anims[0]->animArea.x,
+ -anims[0]->animArea.y,
+ anims[0]->animArea.w,
+ anims[0]->animArea.h, WantDither() );
+ }
+ assert(newsc->Covers(cx, cy, nextFrame->XPos, nextFrame->YPos, nextFrame->Width, nextFrame->Height));
+
+ unsigned int flags = TranslucentShadows ? BLIT_TRANSSHADOW : 0;
+
+ if (!ca->lockPalette) flags|=BLIT_TINTED;
+
+ video->BlitGameSprite( nextFrame, cx + screen.x, cy + screen.y,
+ flags, tint, newsc, ca->GetPartPalette(partnum), &screen);
+ }
+ }
+}
+
+
+static const int OrientdX[16] = { 0, -4, -7, -9, -10, -9, -7, -4, 0, 4, 7, 9, 10, 9, 7, 4 };
+static const int OrientdY[16] = { 10, 9, 7, 4, 0, -4, -7, -9, -10, -9, -7, -4, 0, 4, 7, 9 };
+static const unsigned int MirrorImageLocation[8] = { 4, 12, 8, 0, 6, 14, 10, 2 };
+static const unsigned int MirrorImageZOrder[8] = { 2, 4, 6, 0, 1, 7, 5, 3 };
+
+bool Actor::ShouldHibernate() {
+ //finding an excuse why we don't hybernate the actor
+ if (Modified[IE_ENABLEOFFSCREENAI])
+ return false;
+ if (LastTarget) //currently attacking someone
+ return false;
+ if (!lastRunTime) // haven't had a chance to run a script
+ return false;
+ if (CurrentAction)
+ return false;
+ if (GetNextStep())
+ return false;
+ if (GetNextAction())
+ return false;
+ if (GetWait()) //would never stop waiting
+ return false;
+ return true;
+}
+
+void Actor::UpdateAnimations()
+{
+ // TODO: move this
+ if (InTrap) {
+ area->ClearTrap(this, InTrap-1);
+ }
+
+ //make actor unselectable and unselected when it is not moving
+ //dead, petrified, frozen, paralysed or unavailable to player
+ if (Immobile() || !ShouldDrawCircle()) {
+ core->GetGame()->SelectActor(this, false, SELECT_NORMAL);
+ }
+
+ CharAnimations* ca = GetAnims();
+ if (!ca) {
+ return;
+ }
+
+ ca->PulseRGBModifiers();
+
+ unsigned char StanceID = GetStance();
+ unsigned char Face = GetNextFace();
+ Animation** anims = ca->GetAnimation( StanceID, Face );
+ if (!anims) {
+ return;
+ }
+
+ //If you find a better place for it, I'll really be glad to put it there
+ //IN BG1 and BG2, this is at the ninth frame...
+ if(attackProjectile && (anims[0]->GetCurrentFrame() == 8/*anims[0]->GetFramesCount()/2*/)) {
+ GetCurrentArea()->AddProjectile(attackProjectile, Pos, LastTarget, false);
+ attackProjectile = NULL;
+ }
+
+ // advance first (main) animation by one frame (in sync)
+ if (Immobile()) {
+ // update animation, continue last-displayed frame
+ anims[0]->LastFrame();
+ } else {
+ // update animation, maybe advance a frame (if enough time has passed)
+ anims[0]->NextFrame();
+ }
+
+ // update all other animation parts, in sync with the first part
+ int PartCount = ca->GetTotalPartCount();
+ for (int part = 1; part < PartCount; ++part) {
+ if (anims[part])
+ anims[part]->GetSyncedNextFrame(anims[0]);
+ }
+
+ if (anims[0]->endReached) {
+ if (HandleActorStance()) {
+ // restart animation
+ anims[0]->endReached = false;
+ anims[0]->SetPos(0);
+ }
+ } else {
+ //dialog, pause game
+ if (!(core->GetGameControl()->GetDialogueFlags()&(DF_IN_DIALOG|DF_FREEZE_SCRIPTS) ) ) {
+ //stance
+ if (GetStance() == IE_ANI_WALK) {
+ //frame reached 0
+ if (!anims[0]->GetCurrentFrame()) {
+ PlayWalkSound();
+ }
+ }
+ }
+ }
+}
+
+bool Actor::ShouldDrawCircle()
+{
+ if (Modified[IE_NOCIRCLE]) {
+ return false;
+ }
+
+ if (Modified[IE_AVATARREMOVAL]) {
+ return false;
+ }
+
+ int State = Modified[IE_STATE_ID];
+
+ if (State&STATE_DEAD || InternalFlags&IF_JUSTDIED) {
+ return false;
+ }
+
+ //adjust invisibility for enemies
+ if (Modified[IE_EA]>EA_GOODCUTOFF) {
+ if (State&state_invisible) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void Actor::Draw(const Region &screen)
+{
+ Map* area = GetCurrentArea();
+
+ int cx = Pos.x;
+ int cy = Pos.y;
+ int explored = Modified[IE_DONOTJUMP]&DNJ_UNHINDERED;
+ //check the deactivation condition only if needed
+ //this fixes dead actors disappearing from fog of war (they should be permanently visible)
+ if ((!area->IsVisible( Pos, explored) || (InternalFlags&IF_REALLYDIED) ) && (InternalFlags&IF_ACTIVE) ) {
+ //turning actor inactive if there is no action next turn
+ if (ShouldHibernate()) {
+ InternalFlags|=IF_IDLE;
+ }
+ if (!(InternalFlags&IF_REALLYDIED)) {
+ // for a while this didn't return (disable drawing) if about to hibernate;
+ // Avenger said (aa10aaed) "we draw the actor now for the last time".
+ return;
+ }
+ }
+
+ // if an actor isn't visible, should we still draw video cells?
+ // let us assume not, for now..
+ if (!(InternalFlags & IF_VISIBLE)) {
+ return;
+ }
+
+ //visual feedback
+ CharAnimations* ca = GetAnims();
+ if (!ca) {
+ return;
+ }
+
+ //explored or visibilitymap (bird animations are visible in fog)
+ //0 means opaque
+ int Trans = Modified[IE_TRANSLUCENT];
+
+ if (Trans>255) {
+ Trans=255;
+ }
+
+ //iwd has this flag saved in the creature
+ if (Modified[IE_AVATARREMOVAL]) {
+ Trans = 255;
+ }
+
+ int State = Modified[IE_STATE_ID];
+
+ //adjust invisibility for enemies
+ if (Modified[IE_EA]>EA_GOODCUTOFF) {
+ if (State&state_invisible) {
+ Trans = 255;
+ }
+ }
+
+ //can't move this, because there is permanent blur state where
+ //there is no effect (just state bit)
+ if ((State&STATE_BLUR) && Trans < 128) {
+ Trans = 128;
+ }
+ Color tint = area->LightMap->GetPixel( cx / 16, cy / 12);
+ tint.a = (ieByte) (255-Trans);
+
+ unsigned char heightmapindex = area->HeightMap->GetAt( cx / 16, cy / 12);
+ if (heightmapindex > 15) {
+ // there are 8bpp lightmaps (eg, bg2's AR1300) and fuzzie
+ // cannot work out how they work, so here is an incorrect
+ // hack (probably). please fix!
+ heightmapindex = 15;
+ }
+
+ //don't use cy for area map access beyond this point
+ cy -= heightmapindex;
+
+ //draw videocells under the actor
+ DrawVideocells(screen, vvcShields, tint);
+
+ Video* video = core->GetVideoDriver();
+ Region vp = video->GetViewport();
+
+ bool shoulddrawcircle = ShouldDrawCircle();
+ bool drawcircle = shoulddrawcircle;
+ GameControl *gc = core->GetGameControl();
+ if (gc->GetScreenFlags()&SF_CUTSCENE) {
+ // ground circles are not drawn in cutscenes
+ drawcircle = false;
+ }
+ // the speaker should get a circle even in cutscenes
+ if (gc->dialoghandler->targetID == GetGlobalID() && (gc->GetDialogueFlags()&DF_IN_DIALOG)) {
+ drawcircle = true;
+ }
+ bool drawtarget = drawcircle;
+ // we always show circle/target on pause
+ if (drawcircle && !(gc->GetDialogueFlags() & DF_FREEZE_SCRIPTS)) {
+ // check marker feedback level
+ ieDword markerfeedback = 4;
+ core->GetDictionary()->Lookup("GUI Feedback Level", markerfeedback);
+ if (Over) {
+ // picked creature
+ drawcircle = markerfeedback >= 1;
+ } else if (Selected) {
+ // selected creature
+ drawcircle = markerfeedback >= 2;
+ } else if (IsPC()) {
+ // selectable
+ drawcircle = markerfeedback >= 3;
+ } else if (Modified[IE_EA] >= EA_EVILCUTOFF) {
+ // hostile
+ drawcircle = markerfeedback >= 5;
+ } else {
+ // all
+ drawcircle = markerfeedback >= 6;
+ }
+ drawtarget = Selected && markerfeedback >= 4;
+ }
+ if (drawcircle) {
+ DrawCircle(vp);
+ }
+ if (drawtarget) {
+ DrawTargetPoint(vp);
+ }
+
+ unsigned char StanceID = GetStance();
+ unsigned char Face = GetNextFace();
+ Animation** anims = ca->GetAnimation( StanceID, Face );
+ if (anims) {
+ if (Immobile() || !shoulddrawcircle) {
+ //set the last frame if actor is died and deactivated
+ if (!(InternalFlags&(IF_ACTIVE|IF_IDLE)) && (StanceID==IE_ANI_TWITCH) ) {
+ anims[0]->SetPos(anims[0]->GetFrameCount()-1);
+ }
+ }
+
+ int PartCount = ca->GetTotalPartCount();
+ Sprite2D* nextFrame = anims[0]->GetFrame(anims[0]->GetCurrentFrame());
+
+ // update bounding box and such
+ if (nextFrame && lastFrame != nextFrame) {
+ Region newBBox;
+ if (PartCount == 1) {
+ newBBox.x = cx - nextFrame->XPos;
+ newBBox.w = nextFrame->Width;
+ newBBox.y = cy - nextFrame->YPos;
+ newBBox.h = nextFrame->Height;
+ } else {
+ // FIXME: currently using the animarea instead
+ // of the real bounding box of this (multi-part) frame.
+ // Shouldn't matter much, though. (wjp)
+ newBBox.x = cx + anims[0]->animArea.x;
+ newBBox.y = cy + anims[0]->animArea.y;
+ newBBox.w = anims[0]->animArea.w;
+ newBBox.h = anims[0]->animArea.h;
+ }
+ lastFrame = nextFrame;
+ SetBBox( newBBox );
+ }
+
+ // Drawing the actor:
+ // * mirror images:
+ // Drawn without transparency, unless fully invisible.
+ // Order: W, E, N, S, NW, SE, NE, SW
+ // Uses extraCovers 3-10
+ // * blurred copies (3 of them)
+ // Drawn with transparency.
+ // distance between copies depends on IE_MOVEMENTRATE
+ // TODO: actually, the direction is the real movement direction,
+ // not the (rounded) direction given Face
+ // Uses extraCovers 0-2
+ // * actor itself
+ // Uses main spritecover
+ //
+ //comments by Avenger:
+ // currently we don't have a real direction, but the orientation field
+ // could be used with higher granularity. When we need the face value
+ // it could be divided so it will become a 0-15 number.
+ //
+
+ SpriteCover *sc = 0, *newsc = 0;
+ int blurx = cx;
+ int blury = cy;
+ int blurdx = (OrientdX[Face]*(int)Modified[IE_MOVEMENTRATE])/20;
+ int blurdy = (OrientdY[Face]*(int)Modified[IE_MOVEMENTRATE])/20;
+ Color mirrortint = tint;
+ //mirror images are also half transparent when invis
+ //if (mirrortint.a > 0) mirrortint.a = 255;
+
+ int i;
+
+ // mirror images behind the actor
+ for (i = 0; i < 4; ++i) {
+ unsigned int m = MirrorImageZOrder[i];
+ if (m < Modified[IE_MIRRORIMAGES]) {
+ Region sbbox = BBox;
+ int dir = MirrorImageLocation[m];
+ int icx = cx + 3*OrientdX[dir];
+ int icy = cy + 3*OrientdY[dir];
+ Point iPos(icx, icy);
+ if (area->GetBlocked(iPos) & (PATH_MAP_PASSABLE|PATH_MAP_ACTOR)) {
+ sbbox.x += 3*OrientdX[dir];
+ sbbox.y += 3*OrientdY[dir];
+ newsc = sc = extraCovers[3+m];
+ DrawActorSprite(screen, icx, icy, sbbox, newsc,
+ anims, Face, mirrortint);
+ if (newsc != sc) {
+ delete sc;
+ extraCovers[3+m] = newsc;
+ }
+ }
+ } else {
+ delete extraCovers[3+m];
+ extraCovers[3+m] = NULL;
+ }
+ }
+
+ // blur sprites behind the actor
+ if (State & STATE_BLUR) {
+ if (Face < 4 || Face >= 12) {
+ Region sbbox = BBox;
+ sbbox.x -= 4*blurdx; sbbox.y -= 4*blurdy;
+ blurx -= 4*blurdx; blury -= 4*blurdy;
+ for (i = 0; i < 3; ++i) {
+ sbbox.x += blurdx; sbbox.y += blurdy;
+ blurx += blurdx; blury += blurdy;
+ newsc = sc = extraCovers[i];
+ DrawActorSprite(screen, blurx, blury, sbbox, newsc,
+ anims, Face, tint);
+ if (newsc != sc) {
+ delete sc;
+ extraCovers[i] = newsc;
+ }
+ }
+ }
+ }
+
+ //infravision tint
+ if (!(State&(STATE_DEAD|STATE_FROZEN|STATE_PETRIFIED) ) &&
+ (area->GetLightLevel(Pos)<128) &&
+ core->GetGame()->PartyHasInfravision()) {
+ tint.r=255;
+ }
+
+ // actor itself
+ newsc = sc = GetSpriteCover();
+ DrawActorSprite(screen, cx, cy, BBox, newsc, anims, Face, tint);
+ if (newsc != sc) SetSpriteCover(newsc);
+
+ // blur sprites in front of the actor
+ if (State & STATE_BLUR) {
+ if (Face >= 4 && Face < 12) {
+ Region sbbox = BBox;
+ for (i = 0; i < 3; ++i) {
+ sbbox.x -= blurdx; sbbox.y -= blurdy;
+ blurx -= blurdx; blury -= blurdy;
+ newsc = sc = extraCovers[i];
+ DrawActorSprite(screen, blurx, blury, sbbox, newsc,
+ anims, Face, tint);
+ if (newsc != sc) {
+ delete sc;
+ extraCovers[i] = newsc;
+ }
+ }
+ }
+ }
+
+ // mirror images in front of the actor
+ for (i = 4; i < 8; ++i) {
+ unsigned int m = MirrorImageZOrder[i];
+ if (m < Modified[IE_MIRRORIMAGES]) {
+ Region sbbox = BBox;
+ int dir = MirrorImageLocation[m];
+ int icx = cx + 3*OrientdX[dir];
+ int icy = cy + 3*OrientdY[dir];
+ Point iPos(icx, icy);
+ if (area->GetBlocked(iPos) & (PATH_MAP_PASSABLE|PATH_MAP_ACTOR)) {
+ sbbox.x += 3*OrientdX[dir];
+ sbbox.y += 3*OrientdY[dir];
+ newsc = sc = extraCovers[3+m];
+ DrawActorSprite(screen, icx, icy, sbbox, newsc,
+ anims, Face, mirrortint);
+ if (newsc != sc) {
+ delete sc;
+ extraCovers[3+m] = newsc;
+ }
+ }
+ } else {
+ delete extraCovers[3+m];
+ extraCovers[3+m] = NULL;
+ }
+ }
+ }
+
+ //draw videocells over the actor
+ DrawVideocells(screen, vvcOverlays, tint);
+}
+
+/* Handling automatic stance changes */
+bool Actor::HandleActorStance()
+{
+ CharAnimations* ca = GetAnims();
+ int StanceID = GetStance();
+
+ if (ca->autoSwitchOnEnd) {
+ int nextstance = ca->nextStanceID;
+ SetStance( nextstance );
+ ca->autoSwitchOnEnd = false;
+ return true;
+ }
+ int x = rand()%1000;
+ if ((StanceID==IE_ANI_AWAKE) && !x ) {
+ SetStance( IE_ANI_HEAD_TURN );
+ return true;
+ }
+ // added CurrentAction as part of blocking action fixes
+ if ((StanceID==IE_ANI_READY) && !CurrentAction && !GetNextAction()) {
+ SetStance( IE_ANI_AWAKE );
+ return true;
+ }
+ if (StanceID == IE_ANI_ATTACK || StanceID == IE_ANI_ATTACK_JAB ||
+ StanceID == IE_ANI_ATTACK_SLASH || StanceID == IE_ANI_ATTACK_BACKSLASH ||
+ StanceID == IE_ANI_SHOOT)
+ {
+ SetStance( AttackStance );
+ return true;
+ }
+
+ return false;
+}
+
+void Actor::GetSoundFrom2DA(ieResRef Sound, unsigned int index) const
+{
+ if (!anims) return;
+
+ AutoTable tab(anims->ResRef);
+ if (!tab)
+ return;
+
+ switch (index) {
+ case VB_ATTACK:
+ index = 0;
+ break;
+ case VB_DAMAGE:
+ index = 8;
+ break;
+ case VB_DIE:
+ index = 10;
+ break;
+ //TODO: one day we should implement verbal constant groups
+ case VB_SELECT:
+ case VB_SELECT+1:
+ case VB_SELECT+2:
+ index = 36;
+ break;
+ default:
+ printMessage("Actor","TODO:", YELLOW);
+ printf("Cannot determine 2DA rowcount for index: %d\n", index);
+ return;
+ }
+ printMessage("Actor"," ", WHITE);
+ printf("Getting sound 2da %.8s entry: %s\n", anims->ResRef, tab->GetRowName(index) );
+ int col = core->Roll(1,tab->GetColumnCount(index),-1);
+ strnlwrcpy(Sound, tab->QueryField (index, col), 8);
+}
+
+void Actor::GetSoundFromINI(ieResRef Sound, unsigned int index) const
+{
+ const char *resource = "";
+ char section[12];
+ unsigned int animid=BaseStats[IE_ANIMATION_ID];
+ if(core->HasFeature(GF_ONE_BYTE_ANIMID)) {
+ animid&=0xff;
+ }
+ snprintf(section,10,"%d", animid);
+
+ switch(index) {
+ case VB_ATTACK:
+ resource = core->GetResDataINI()->GetKeyAsString(section, "at1sound","");
+ break;
+ case VB_DAMAGE:
+ resource = core->GetResDataINI()->GetKeyAsString(section, "hitsound","");
+ break;
+ case VB_DIE:
+ resource = core->GetResDataINI()->GetKeyAsString(section, "dfbsound","");
+ break;
+ case VB_SELECT:
+ break;
+ }
+ int count = CountElements(resource,',');
+ if (count<=0) return;
+ count = core->Roll(1,count,-1);
+ while(count--) {
+ while(*resource && *resource!=',') resource++;
+ if (*resource==',') resource++;
+ }
+ strncpy(Sound, resource, 8);
+ for(count=0;count<8 && Sound[count]!=',';count++) {};
+ Sound[count]=0;
+}
+
+void Actor::ResolveStringConstant(ieResRef Sound, unsigned int index) const
+{
+ if (PCStats && PCStats->SoundSet[0]) {
+ //resolving soundset (bg1/bg2 style)
+ if (csound[index]) {
+ snprintf(Sound, sizeof(ieResRef), "%s%c", PCStats->SoundSet, csound[index]);
+ return;
+ }
+ //icewind style
+ snprintf(Sound, sizeof(ieResRef), "%s%02d", PCStats->SoundSet, index);
+ return;
+ }
+
+ Sound[0]=0;
+
+ if (core->HasFeature(GF_RESDATA_INI)) {
+ GetSoundFromINI(Sound, index);
+ } else {
+ GetSoundFrom2DA(Sound, index);
+ }
+
+ //Empty resrefs
+ if (Sound[0]=='*') Sound[0]=0;
+ else if(!strncmp(Sound,"nosound",8) ) Sound[0]=0;
+}
+
+void Actor::SetActionButtonRow(ActionButtonRow &ar)
+{
+ for(int i=0;i<GUIBT_COUNT;i++) {
+ ieByte tmp = ar[i];
+ if (QslotTranslation) {
+ tmp=gemrb2iwd[tmp];
+ }
+ PCStats->QSlots[i]=tmp;
+ }
+}
+
+void Actor::GetActionButtonRow(ActionButtonRow &ar)
+{
+ //at this point, we need the stats for the action button row
+ //only controlled creatures (and pcs) get it
+ CreateStats();
+ InitButtons(GetStat(IE_CLASS), false);
+ for(int i=0;i<GUIBT_COUNT;i++) {
+ ieByte tmp=PCStats->QSlots[i];
+ if (QslotTranslation) {
+ if (tmp>=90) { //quick weapons
+ tmp=16+tmp%10;
+ } else if (tmp>=80) { //quick items
+ tmp=9+tmp%10;
+ } else if (tmp>=70) { //quick spells
+ tmp=3+tmp%10;
+ } else {
+ tmp=iwd2gemrb[tmp];
+ }
+ }
+ ar[i]=tmp;
+ }
+}
+
+void Actor::SetPortrait(const char* ResRef, int Which)
+{
+ int i;
+
+ if (ResRef == NULL) {
+ return;
+ }
+ if (InParty) {
+ core->SetEventFlag(EF_PORTRAIT);
+ }
+
+ if(Which!=1) {
+ memset( SmallPortrait, 0, 8 );
+ strncpy( SmallPortrait, ResRef, 8 );
+ }
+ if(Which!=2) {
+ memset( LargePortrait, 0, 8 );
+ strncpy( LargePortrait, ResRef, 8 );
+ }
+ if(!Which) {
+ for (i = 0; i < 8 && ResRef[i]; i++) {};
+ if (SmallPortrait[i-1] != 'S' && SmallPortrait[i-1] != 's') {
+ SmallPortrait[i] = 'S';
+ }
+ if (LargePortrait[i-1] != 'M' && LargePortrait[i-1] != 'm') {
+ LargePortrait[i] = 'M';
+ }
+ }
+}
+
+void Actor::SetSoundFolder(const char *soundset)
+{
+ if (core->HasFeature(GF_SOUNDFOLDERS)) {
+ char filepath[_MAX_PATH];
+
+ strnlwrcpy(PCStats->SoundFolder, soundset, 32);
+ PathJoin(filepath,core->GamePath,"sounds",PCStats->SoundFolder,0);
+ char file[_MAX_PATH];
+ if (FileGlob(file, filepath, "?????01")) {
+ file[5] = '\0';
+ } else if (FileGlob(file, filepath, "????01")) {
+ file[4] = '\0';
+ } else {
+ return;
+ }
+ strnlwrcpy(PCStats->SoundSet, file, 8);
+ } else {
+ strnlwrcpy(PCStats->SoundSet, soundset, 8);
+ PCStats->SoundFolder[0]=0;
+ }
+}
+
+void Actor::GetSoundFolder(char *soundset, int full) const
+{
+ if (core->HasFeature(GF_SOUNDFOLDERS)) {
+ strnlwrcpy(soundset, PCStats->SoundFolder, 32);
+ if (full) {
+ strcat(soundset,"/");
+ strncat(soundset, PCStats->SoundSet, 8);
+ }
+ }
+ else {
+ strnlwrcpy(soundset, PCStats->SoundSet, 8);
+ }
+}
+
+bool Actor::HasVVCCell(const ieResRef resource) const
+{
+ return GetVVCCell(resource) != NULL;
+}
+
+ScriptedAnimation *Actor::GetVVCCell(const ieResRef resource) const
+{
+ int j = true;
+ const vvcVector *vvcCells=&vvcShields;
+retry:
+ size_t i=vvcCells->size();
+ while (i--) {
+ ScriptedAnimation *vvc = (*vvcCells)[i];
+ if (vvc == NULL) {
+ continue;
+ }
+ if ( strnicmp(vvc->ResName, resource, 8) == 0) {
+ return vvc;
+ }
+ }
+ vvcCells=&vvcOverlays;
+ if (j) { j = false; goto retry; }
+ return NULL;
+}
+
+void Actor::RemoveVVCell(const ieResRef resource, bool graceful)
+{
+ bool j = true;
+ vvcVector *vvcCells=&vvcShields;
+retry:
+ size_t i=vvcCells->size();
+ while (i--) {
+ ScriptedAnimation *vvc = (*vvcCells)[i];
+ if (vvc == NULL) {
+ continue;
+ }
+ if ( strnicmp(vvc->ResName, resource, 8) == 0) {
+ if (graceful) {
+ vvc->SetPhase(P_RELEASE);
+ } else {
+ delete vvc;
+ vvcCells->erase(vvcCells->begin()+i);
+ }
+ }
+ }
+ vvcCells=&vvcOverlays;
+ if (j) { j = false; goto retry; }
+}
+
+//this is a faster version of hasvvccell, because it knows where to look
+//for the overlay, it also returns the vvc for further manipulation
+//use this for the seven eyes overlay
+ScriptedAnimation *Actor::FindOverlay(int index) const
+{
+ const vvcVector *vvcCells;
+
+ if (index>31) return NULL;
+
+ if (hc_locations&(1<<index)) vvcCells=&vvcShields;
+ else vvcCells=&vvcOverlays;
+
+ const char *resRef = hc_overlays[index];
+
+ size_t i=vvcCells->size();
+ while (i--) {
+ ScriptedAnimation *vvc = (*vvcCells)[i];
+ if (vvc == NULL) {
+ continue;
+ }
+ if ( strnicmp(vvc->ResName, resRef, 8) == 0) {
+ return vvc;
+ }
+ }
+ return NULL;
+}
+
+void Actor::AddVVCell(ScriptedAnimation* vvc)
+{
+ vvcVector *vvcCells;
+
+ //if the vvc was not created, don't try to add it
+ if (!vvc) {
+ return;
+ }
+ if (vvc->ZPos<0) {
+ vvcCells=&vvcShields;
+ } else {
+ vvcCells=&vvcOverlays;
+ }
+ size_t i=vvcCells->size();
+ while (i--) {
+ if ((*vvcCells)[i] == NULL) {
+ (*vvcCells)[i] = vvc;
+ return;
+ }
+ }
+ vvcCells->push_back( vvc );
+}
+
+//returns restored spell level
+int Actor::RestoreSpellLevel(ieDword maxlevel, ieDword type)
+{
+ int typemask;
+
+ switch (type) {
+ case 0: //allow only mage
+ typemask = ~2;
+ break;
+ case 1: //allow only cleric
+ typemask = ~1;
+ break;
+ default:
+ //allow any (including innates)
+ typemask = ~0;
+ }
+ for (int i=maxlevel;i>0;i--) {
+ CREMemorizedSpell *cms = spellbook.FindUnchargedSpell(typemask, maxlevel);
+ if (cms) {
+ spellbook.ChargeSpell(cms);
+ return i;
+ }
+ }
+ return 0;
+}
+
+//replenishes spells, cures fatigue
+void Actor::Rest(int hours)
+{
+ if (hours) {
+ //do remove effects
+ int remaining = hours*10;
+ //removes hours*10 fatigue points
+ NewStat (IE_FATIGUE, -remaining, MOD_ADDITIVE);
+ NewStat (IE_INTOXICATION, -remaining, MOD_ADDITIVE);
+ //restore hours*10 spell levels
+ //rememorization starts with the lower spell levels?
+ inventory.ChargeAllItems (remaining);
+ for (int level = 1; level<16; level++) {
+ if (level<remaining) {
+ break;
+ }
+ while (remaining>0) {
+ remaining -= RestoreSpellLevel(level,0);
+ }
+ }
+ } else {
+ SetBase (IE_FATIGUE, 0);
+ SetBase (IE_INTOXICATION, 0);
+ inventory.ChargeAllItems (0);
+ spellbook.ChargeAllSpells ();
+ }
+}
+
+//returns the actual slot from the quickslot
+int Actor::GetQuickSlot(int slot) const
+{
+ assert(slot<8);
+ if (inventory.HasItemInSlot("",inventory.GetMagicSlot())) {
+ return inventory.GetMagicSlot();
+ }
+ if (!PCStats) {
+ return slot+inventory.GetWeaponSlot();
+ }
+ return PCStats->QuickWeaponSlots[slot];
+}
+
+//marks the quickslot as equipped
+int Actor::SetEquippedQuickSlot(int slot, int header)
+{
+ if (!PCStats) {
+ if (header<0) header=0;
+ inventory.SetEquippedSlot(slot, header);
+ return 0;
+ }
+
+
+ if ((slot<0) || (slot == IW_NO_EQUIPPED) ) {
+ if (slot == IW_NO_EQUIPPED) {
+ slot = inventory.GetFistSlot();
+ }
+ int i;
+ for(i=0;i<MAX_QUICKWEAPONSLOT;i++) {
+ if(slot+inventory.GetWeaponSlot()==PCStats->QuickWeaponSlots[i]) {
+ slot = i;
+ break;
+ }
+ }
+ if (i==MAX_QUICKWEAPONSLOT) {
+ return 0;
+ }
+ }
+
+ assert(slot<MAX_QUICKWEAPONSLOT);
+ if (header==-1) {
+ header = PCStats->QuickWeaponHeaders[slot];
+ }
+ else {
+ PCStats->QuickWeaponHeaders[slot]=header;
+ }
+ slot = PCStats->QuickWeaponSlots[slot]-inventory.GetWeaponSlot();
+ Equipped = (ieWordSigned) slot;
+ EquippedHeader = (ieWord) header;
+ if (inventory.SetEquippedSlot(slot, header)) {
+ return 0;
+ }
+ return STR_MAGICWEAPON;
+}
+
+//if target is a non living scriptable, then we simply shoot for its position
+//the fx should get a NULL target, and handle itself by using the position
+//(shouldn't crash when target is NULL)
+bool Actor::UseItemPoint(ieDword slot, ieDword header, const Point &target, ieDword flags)
+{
+ CREItem *item = inventory.GetSlotItem(slot);
+ if (!item)
+ return false;
+
+ ieResRef tmpresref;
+ strnuprcpy(tmpresref, item->ItemResRef, sizeof(ieResRef)-1);
+
+ Item *itm = gamedata->GetItem(tmpresref);
+ if (!itm) return false; //quick item slot contains invalid item resref
+ //item is depleted for today
+ if(itm->UseCharge(item->Usages, header, false)==CHG_DAY) {
+ return false;
+ }
+
+ Projectile *pro = itm->GetProjectile(this, header, target, slot, flags&UI_MISS);
+ ChargeItem(slot, header, item, itm, flags&UI_SILENT);
+ gamedata->FreeItem(itm,tmpresref, false);
+ if (pro) {
+ pro->SetCaster(GetGlobalID(), ITEM_CASTERLEVEL);
+ GetCurrentArea()->AddProjectile(pro, Pos, target);
+ return true;
+ }
+ return false;
+}
+
+static EffectRef fx_damage_ref = { "Damage", -1 };
+static EffectRef fx_melee_ref = { "SetMeleeEffect", -1 };
+static EffectRef fx_ranged_ref = { "SetRangedEffect", -1 };
+
+bool Actor::UseItem(ieDword slot, ieDword header, Scriptable* target, ieDword flags, int damage)
+{
+ if (target->Type!=ST_ACTOR) {
+ return UseItemPoint(slot, header, target->Pos, flags);
+ }
+
+ Actor *tar = (Actor *) target;
+ CREItem *item = inventory.GetSlotItem(slot);
+ if (!item)
+ return false;
+
+ ieResRef tmpresref;
+ strnuprcpy(tmpresref, item->ItemResRef, sizeof(ieResRef)-1);
+
+ Item *itm = gamedata->GetItem(tmpresref);
+ if (!itm) return false; //quick item slot contains invalid item resref
+ //item is depleted for today
+ if (itm->UseCharge(item->Usages, header, false)==CHG_DAY) {
+ return false;
+ }
+
+ Projectile *pro = itm->GetProjectile(this, header, target->Pos, slot, flags&UI_MISS);
+ ChargeItem(slot, header, item, itm, flags&UI_SILENT);
+ gamedata->FreeItem(itm,tmpresref, false);
+ if (pro) {
+ //ieDword is unsigned!!
+ pro->SetCaster(GetGlobalID(), ITEM_CASTERLEVEL);
+ if(((int)header < 0) && !(flags&UI_MISS)) { //using a weapon
+ bool ranged = header == (ieDword)-2;
+ ITMExtHeader *which = itm->GetWeaponHeader(ranged);
+ Effect* AttackEffect = EffectQueue::CreateEffect(fx_damage_ref, damage, (weapon_damagetype[which->DamageType])<<16, FX_DURATION_INSTANT_LIMITED);
+ AttackEffect->Projectile = which->ProjectileAnimation;
+ AttackEffect->Target = FX_TARGET_PRESET;
+ pro->GetEffects()->AddEffect(AttackEffect, true);
+ if (ranged)
+ fxqueue.AddWeaponEffects(pro->GetEffects(), fx_ranged_ref);
+ else
+ fxqueue.AddWeaponEffects(pro->GetEffects(), fx_melee_ref);
+ //AddEffect created a copy, the original needs to be scrapped
+ delete AttackEffect;
+ attackProjectile = pro;
+ } else //launch it now as we are not attacking
+ GetCurrentArea()->AddProjectile(pro, Pos, tar->GetGlobalID(), false);
+ return true;
+ }
+ return false;
+}
+
+void Actor::ChargeItem(ieDword slot, ieDword header, CREItem *item, Item *itm, bool silent)
+{
+ if (!itm) {
+ item = inventory.GetSlotItem(slot);
+ if (!item)
+ return;
+ itm = gamedata->GetItem(item->ItemResRef);
+ }
+ if (!itm) return; //quick item slot contains invalid item resref
+
+ if (IsSelected()) {
+ core->SetEventFlag( EF_ACTION );
+ }
+
+ if (!silent) {
+ ieByte stance = AttackStance;
+ for (int i=0;i<animcount;i++) {
+ if ( strnicmp(item->ItemResRef, itemanim[i].itemname, 8) == 0) {
+ stance = itemanim[i].animation;
+ }
+ }
+ if (stance!=0xff) {
+ SetStance(stance);
+ //play only one cycle of animations
+
+ // this was crashing for fuzzie due to NULL anims
+ if (anims) {
+ anims->nextStanceID=IE_ANI_READY;
+ anims->autoSwitchOnEnd=true;
+ }
+ }
+ }
+
+ switch(itm->UseCharge(item->Usages, header, true)) {
+ case CHG_DAY:
+ break;
+ case CHG_BREAK: //both
+ if (!silent) {
+ core->PlaySound(DS_ITEM_GONE);
+ }
+ //fall through
+ case CHG_NOSOUND: //remove item
+ inventory.BreakItemSlot(slot);
+ break;
+ default: //don't do anything
+ break;
+ }
+}
+
+int Actor::IsReverseToHit()
+{
+ return ReverseToHit;
+}
+
+void Actor::InitButtons(ieDword cls, bool forced)
+{
+ if (!PCStats) {
+ return;
+ }
+ if ( (PCStats->QSlots[0]!=0xff) && !forced) {
+ return;
+ }
+
+ ActionButtonRow myrow;
+ if ((int) cls >= classcount) {
+ memcpy(&myrow, &DefaultButtons, sizeof(ActionButtonRow));
+ for (int i=0;i<extraslots;i++) {
+ if (cls==OtherGUIButtons[i].clss) {
+ memcpy(&myrow, &OtherGUIButtons[i].buttons, sizeof(ActionButtonRow));
+ break;
+ }
+ }
+ } else {
+ memcpy(&myrow, GUIBTDefaults+cls, sizeof(ActionButtonRow));
+ }
+ SetActionButtonRow(myrow);
+}
+
+void Actor::SetFeat(unsigned int feat, int mode)
+{
+ if (feat>=MAX_FEATS) {
+ return;
+ }
+ ieDword mask = 1<<(feat&31);
+ ieDword idx = feat>>5;
+ switch (mode) {
+ case BM_SET: case BM_OR:
+ BaseStats[IE_FEATS1+idx]|=mask;
+ break;
+ case BM_NAND:
+ BaseStats[IE_FEATS1+idx]&=~mask;
+ break;
+ case BM_XOR:
+ BaseStats[IE_FEATS1+idx]^=mask;
+ break;
+ }
+}
+
+void Actor::SetUsedWeapon(const char* AnimationType, ieWord* MeleeAnimation, int wt)
+{
+ memcpy(WeaponRef, AnimationType, sizeof(WeaponRef) );
+ if (wt != -1) WeaponType = wt;
+ if (!anims)
+ return;
+ anims->SetWeaponRef(AnimationType);
+ anims->SetWeaponType(WeaponType);
+ SetAttackMoveChances(MeleeAnimation);
+ if (InParty) {
+ //update the paperdoll weapon animation
+ core->SetEventFlag(EF_UPDATEANIM);
+ }
+ WeaponInfo wi;
+ ITMExtHeader *header = GetWeapon(wi);
+
+ if(header && (header->AttackType == ITEM_AT_BOW)) {
+ ITMExtHeader* projHeader = GetRangedWeapon(wi);
+ if (projHeader->ProjectileQualifier == 0) return; /* no ammo yet? */
+ AttackStance = IE_ANI_SHOOT;
+ anims->SetRangedType(projHeader->ProjectileQualifier-1);
+ //bows ARE one handed, from an anim POV at least
+ anims->SetWeaponType(IE_ANI_WEAPON_1H);
+ return;
+ }
+ if(header && (header->AttackType == ITEM_AT_PROJECTILE)) {
+ AttackStance = IE_ANI_ATTACK_SLASH; //That's it!!
+ return;
+ }
+ AttackStance = IE_ANI_ATTACK;
+}
+
+void Actor::SetUsedShield(const char* AnimationType, int wt)
+{
+ memcpy(ShieldRef, AnimationType, sizeof(ShieldRef) );
+ if (wt != -1) WeaponType = wt;
+ if (AnimationType[0] == ' ' || AnimationType[0] == 0)
+ if (WeaponType == IE_ANI_WEAPON_2W)
+ WeaponType = IE_ANI_WEAPON_1H;
+
+ if (!anims)
+ return;
+ anims->SetOffhandRef(AnimationType);
+ anims->SetWeaponType(WeaponType);
+ if (InParty) {
+ //update the paperdoll weapon animation
+ core->SetEventFlag(EF_UPDATEANIM);
+ }
+}
+
+void Actor::SetUsedHelmet(const char* AnimationType)
+{
+ memcpy(HelmetRef, AnimationType, sizeof(HelmetRef) );
+ if (!anims)
+ return;
+ anims->SetHelmetRef(AnimationType);
+ if (InParty) {
+ //update the paperdoll weapon animation
+ core->SetEventFlag(EF_UPDATEANIM);
+ }
+}
+
+// initializes the fist data the first time it is called
+void Actor::SetupFistData()
+{
+ if (FistRows<0) {
+ FistRows=0;
+ AutoTable fist("fistweap");
+ if (fist) {
+ //default value
+ strnlwrcpy( DefaultFist, fist->QueryField( (unsigned int) -1), 8);
+ FistRows = fist->GetRowCount();
+ fistres = new FistResType[FistRows];
+ fistresclass = new int[FistRows];
+ for (int i=0;i<FistRows;i++) {
+ int maxcol = fist->GetColumnCount(i)-1;
+ for (int cols = 0;cols<MAX_LEVEL;cols++) {
+ strnlwrcpy( fistres[i][cols], fist->QueryField( i, cols>maxcol?maxcol:cols ), 8);
+ }
+ fistresclass[i] = atoi(fist->GetRowName(i));
+ }
+ }
+ }
+}
+
+void Actor::SetupFist()
+{
+ int slot = core->QuerySlot( 0 );
+ assert (core->QuerySlotEffects(slot)==SLOT_EFFECT_FIST);
+ int row = GetBase(fiststat);
+ int col = GetXPLevel(false);
+
+ if (col>MAX_LEVEL) col=MAX_LEVEL;
+ if (col<1) col=1;
+
+ SetupFistData();
+
+ const char *ItemResRef = DefaultFist;
+ for (int i = 0;i<FistRows;i++) {
+ if (fistresclass[i] == row) {
+ ItemResRef = fistres[i][col];
+ }
+ }
+ inventory.SetSlotItemRes(ItemResRef, slot);
+}
+
+static ieDword ResolveTableValue(const char *resref, ieDword stat, ieDword mcol, ieDword vcol) {
+ long ret = 0;
+ //don't close this table, it can mess with the guiscripts
+ int table = gamedata->LoadTable(resref);
+ Holder<TableMgr> tm = gamedata->GetTable(table);
+ if (tm) {
+ unsigned int row;
+ if (mcol == 0xff) {
+ row = stat;
+ } else {
+ row = tm->FindTableValue(mcol, stat);
+ if (row==0xffffffff) {
+ return 0;
+ }
+ }
+ if (valid_number(tm->QueryField(row, vcol), ret)) {
+ return (ieDword) ret;
+ }
+ }
+
+ return 0;
+}
+
+int Actor::CheckUsability(Item *item) const
+{
+ ieDword itembits[2]={item->UsabilityBitmask, item->KitUsability};
+
+ for (int i=0;i<usecount;i++) {
+ ieDword itemvalue = itembits[itemuse[i].which];
+ ieDword stat = GetStat(itemuse[i].stat);
+ ieDword mcol = itemuse[i].mcol;
+ //if we have a kit, we just we use it's index for the lookup
+ if (itemuse[i].stat==IE_KIT) {
+ stat = GetKitIndex(stat, itemuse[i].table);
+ mcol = 0xff;
+ }
+ stat = ResolveTableValue(itemuse[i].table, stat, mcol, itemuse[i].vcol);
+ if (stat&itemvalue) {
+ //printf("failed usability: itemvalue %d, stat %d, stat value %d\n", itemvalue, itemuse[i].stat, stat);
+ return STR_CANNOT_USE_ITEM;
+ }
+ }
+
+ return 0;
+}
+
+static EffectRef fx_cant_use_item_ref = { "CantUseItem", -1 };
+static EffectRef fx_cant_use_item_type_ref = { "CantUseItemType", -1 };
+
+//this one is the same, but returns strrefs based on effects
+ieStrRef Actor::Disabled(ieResRef name, ieDword type) const
+{
+ Effect *fx;
+
+ fx = fxqueue.HasEffectWithResource(fx_cant_use_item_ref, name);
+ if (fx) {
+ return fx->Parameter1;
+ }
+
+ fx = fxqueue.HasEffectWithParam(fx_cant_use_item_type_ref, type);
+ if (fx) {
+ return fx->Parameter1;
+ }
+ return 0;
+}
+
+//checks usability only
+int Actor::Unusable(Item *item) const
+{
+ if (!GetStat(IE_CANUSEANYITEM)) {
+ int unusable = CheckUsability(item);
+ if (unusable) {
+ return unusable;
+ }
+ }
+ // iesdp says this is always checked?
+ if (item->MinLevel>GetXPLevel(true)) {
+ return STR_CANNOT_USE_ITEM;
+ }
+
+ if (!CheckAbilities) {
+ return 0;
+ }
+
+ if (item->MinStrength>GetStat(IE_STR)) {
+ return STR_CANNOT_USE_ITEM;
+ }
+
+ if (item->MinStrength==18) {
+ if (GetStat(IE_STR)==18) {
+ if (item->MinStrengthBonus>GetStat(IE_STREXTRA)) {
+ return STR_CANNOT_USE_ITEM;
+ }
+ }
+ }
+
+ if (item->MinIntelligence>GetStat(IE_INT)) {
+ return STR_CANNOT_USE_ITEM;
+ }
+ if (item->MinDexterity>GetStat(IE_DEX)) {
+ return STR_CANNOT_USE_ITEM;
+ }
+ if (item->MinWisdom>GetStat(IE_WIS)) {
+ return STR_CANNOT_USE_ITEM;
+ }
+ if (item->MinConstitution>GetStat(IE_CON)) {
+ return STR_CANNOT_USE_ITEM;
+ }
+ if (item->MinCharisma>GetStat(IE_CHR)) {
+ return STR_CANNOT_USE_ITEM;
+ }
+ //note, weapon proficiencies shouldn't be checked here
+ //missing proficiency causes only attack penalty
+ return 0;
+}
+
+//full palette will be shaded in gradient color
+void Actor::SetGradient(ieDword gradient)
+{
+ gradient |= (gradient <<16);
+ gradient |= (gradient <<8);
+ for(int i=0;i<7;i++) {
+ Modified[IE_COLORS+i]=gradient;
+ }
+}
+
+//sets one bit of the sanctuary stat (used for overlays)
+void Actor::SetOverlay(unsigned int overlay)
+{
+ if (overlay>=32) return;
+ Modified[IE_SANCTUARY]|=1<<overlay;
+}
+
+//returns true if spell state is already set or illegal
+bool Actor::SetSpellState(unsigned int spellstate)
+{
+ if (spellstate>=192) return true;
+ unsigned int pos = IE_SPLSTATE_ID1+(spellstate>>5);
+ unsigned int bit = 1<<(spellstate&31);
+ if (Modified[pos]&bit) return true;
+ Modified[pos]|=bit;
+ return false;
+}
+
+//returns true if spell state is already set
+bool Actor::HasSpellState(unsigned int spellstate) const
+{
+ if (spellstate>=192) return false;
+ unsigned int pos = IE_SPLSTATE_ID1+(spellstate>>5);
+ unsigned int bit = 1<<(spellstate&31);
+ if (Modified[pos]&bit) return true;
+ return false;
+}
+
+//this is a very specific rule that might need an external table later
+int Actor::GetAbilityBonus(unsigned int ability) const
+{
+ return GetStat(ability)/2-5;
+}
+
+int Actor::GetSkill(unsigned int skill) const
+{
+ if (skill>=(unsigned int) skillcount) return -1;
+ int ret = GetStat(skillstats[skill])+GetAbilityBonus(skillabils[skill]);
+ if (ret<0) ret = 0;
+ return ret;
+}
+
+//returns the numeric value of a feat, different from HasFeat
+//for multiple feats
+int Actor::GetFeat(unsigned int feat) const
+{
+ if (feat>=MAX_FEATS) {
+ return -1;
+ }
+ if (Modified[IE_FEATS1+(feat>>5)]&(1<<(feat&31)) ) {
+ //return the numeric stat value, instead of the boolean
+ if (featstats[feat]) {
+ return Modified[featstats[feat]];
+ }
+ return 1;
+ }
+ return 0;
+}
+
+//returns true if the feat exists
+bool Actor::HasFeat(unsigned int featindex) const
+{
+ if (featindex>=MAX_FEATS) return false;
+ unsigned int pos = IE_FEATS1+(featindex>>5);
+ unsigned int bit = 1<<(featindex&31);
+ if (Modified[pos]&bit) return true;
+ return false;
+}
+
+ieDword Actor::ImmuneToProjectile(ieDword projectile) const
+{
+ int idx;
+
+ idx = projectile/32;
+ if (idx>ProjectileSize) {
+ return 0;
+ }
+ return projectileImmunity[idx]&(1<<(projectile&31));
+}
+
+void Actor::AddProjectileImmunity(ieDword projectile)
+{
+ projectileImmunity[projectile/32]|=1<<(projectile&31);
+}
+
+//2nd edition rules
+void Actor::CreateDerivedStatsBG()
+{
+ int turnundeadlevel = 0;
+ int classid = BaseStats[IE_CLASS];
+
+ //this works only for PC classes
+ if (classid>=CLASS_PCCUTOFF) return;
+
+ //recalculate all level based changes
+ pcf_level(this,0,0);
+
+ //even though the original didn't allow a cleric/paladin dual or multiclass
+ //we shouldn't restrict the possibility by using "else if" here
+ if (isclass[ISCLERIC]&(1<<classid)) {
+ turnundeadlevel += GetClericLevel()+1-turnlevels[classid];
+ if (turnundeadlevel<0) turnundeadlevel=0;
+ }
+ if (isclass[ISPALADIN]&(1<<classid)) {
+ turnundeadlevel += GetPaladinLevel()+1-turnlevels[classid];
+ if (turnundeadlevel<0) turnundeadlevel=0;
+ }
+
+ // barbarian immunity to backstab was hardcoded
+ if (GetBarbarianLevel()) {
+ BaseStats[IE_DISABLEBACKSTAB] = 1;
+ }
+
+ ieDword backstabdamagemultiplier=GetThiefLevel();
+ if (backstabdamagemultiplier) {
+ // HACK: swashbucklers can't backstab
+ if ((BaseStats[IE_KIT]&0xfff) == 12) {
+ backstabdamagemultiplier = 1;
+ } else {
+ AutoTable tm("backstab");
+ //fallback to a general algorithm (bg2 backstab.2da version) if we can't find backstab.2da
+ //TODO: AP_SPCL332 (increase backstab by one) seems to not be effecting this at all
+ //for assassins perhaps the effect is being called prior to this, and this overwrites it;
+ //stalkers work correctly, which is even more odd, considering as they use the same
+ //effect and backstabmultiplier would be 0 for them
+ if (tm) {
+ ieDword cols = tm->GetColumnCount();
+ if (backstabdamagemultiplier >= cols) backstabdamagemultiplier = cols;
+ backstabdamagemultiplier = atoi(tm->QueryField(0, backstabdamagemultiplier));
+ } else {
+ backstabdamagemultiplier = (backstabdamagemultiplier+7)/4;
+ }
+ printf("\n");
+ if (backstabdamagemultiplier>7) backstabdamagemultiplier=7;
+ }
+ }
+
+ // monk's level dictated ac and ac vs missiles bonus
+ // attacks per round bonus will be handled elsewhere, since it only applies to fist apr
+ if (isclass[ISMONK]&(1<<classid)) {
+ unsigned int level = GetMonkLevel()-1;
+ if (level < monkbon_cols) {
+ BaseStats[IE_ARMORCLASS] = DEFAULTAC - monkbon[1][level];
+ BaseStats[IE_ACMISSILEMOD] = - monkbon[2][level];
+ }
+ }
+
+ BaseStats[IE_TURNUNDEADLEVEL]=turnundeadlevel;
+ BaseStats[IE_BACKSTABDAMAGEMULTIPLIER]=backstabdamagemultiplier;
+ BaseStats[IE_LAYONHANDSAMOUNT]=GetPaladinLevel()*2;
+}
+
+//3rd edition rules
+void Actor::CreateDerivedStatsIWD2()
+{
+ int i;
+ int turnundeadlevel = 0;
+
+ ieDword backstabdamagemultiplier=GetThiefLevel();
+ if (backstabdamagemultiplier) {
+ AutoTable tm("backstab");
+ if (tm) {
+ ieDword cols = tm->GetColumnCount();
+ if (backstabdamagemultiplier >= cols) backstabdamagemultiplier = cols;
+ backstabdamagemultiplier = atoi(tm->QueryField(0, backstabdamagemultiplier));
+ } else {
+ backstabdamagemultiplier = (BaseStats[IE_LEVELTHIEF]+1)/2;
+ }
+ printf("\n");
+ if (backstabdamagemultiplier>7) backstabdamagemultiplier=7;
+ }
+
+ int layonhandsamount = (int) BaseStats[IE_LEVELPALADIN];
+ if (layonhandsamount) {
+ layonhandsamount *= BaseStats[IE_CHR]/2-5;
+ if (layonhandsamount<1) layonhandsamount = 1;
+ }
+
+ for (i=0;i<11;i++) {
+ int tmp;
+
+ if (turnlevels[i+1]) {
+ tmp = BaseStats[IE_LEVELBARBARIAN+i]+1-turnlevels[i+1];
+ if (tmp<0) tmp=0;
+ if (tmp>turnundeadlevel) turnundeadlevel=tmp;
+ }
+ }
+ BaseStats[IE_TURNUNDEADLEVEL]=turnundeadlevel;
+ BaseStats[IE_BACKSTABDAMAGEMULTIPLIER]=backstabdamagemultiplier;
+ BaseStats[IE_LAYONHANDSAMOUNT]=(ieDword) layonhandsamount;
+}
+
+//set up stuff here, like attack number, turn undead level
+//and similar derived stats that change with level
+void Actor::CreateDerivedStats()
+{
+ //we have to calculate multiclass for further code
+ AutoTable tm("classes");
+ if (tm) {
+ // currently we need only the MULTI value
+ char tmpmulti[8];
+ long tmp;
+ strcpy(tmpmulti, tm->QueryField(tm->FindTableValue(5, BaseStats[IE_CLASS]), 4));
+ if (!valid_number(tmpmulti, tmp))
+ multiclass = 0;
+ else
+ multiclass = (ieDword)tmp;
+ }
+
+ if (core->HasFeature(GF_3ED_RULES)) {
+ CreateDerivedStatsIWD2();
+ } else {
+ CreateDerivedStatsBG();
+ }
+}
+/* Checks if the actor is multiclassed (the MULTI column is positive) */
+bool Actor::IsMultiClassed() const
+{
+ return (multiclass > 0);
+}
+
+/* Checks if the actor is dualclassed */
+bool Actor::IsDualClassed() const
+{
+ return (Modified[IE_MC_FLAGS] & MC_WAS_ANY) > 0;
+}
+
+Actor *Actor::CopySelf(bool mislead) const
+{
+ Actor *newActor = new Actor();
+
+ newActor->SetName(GetName(0),0);
+ newActor->SetName(GetName(1),1);
+ newActor->version = version;
+ memcpy(newActor->BaseStats, BaseStats, sizeof(BaseStats) );
+ // illusions aren't worth any xp and don't explore
+ newActor->BaseStats[IE_XPVALUE] = 0;
+ newActor->BaseStats[IE_EXPLORE] = 0;
+
+ //IF_INITIALIZED shouldn't be set here, yet
+ newActor->SetMCFlag(MC_EXPORTABLE, BM_NAND);
+
+ //the creature importer does this too
+ memcpy(newActor->Modified,newActor->BaseStats, sizeof(Modified) );
+
+ // temporary hack, so we don't get into a loop with CreateDerivedStats
+ newActor->multiclass = 0;
+
+ //copy the inventory, but only if it is not the Mislead illusion
+ if (mislead) {
+ //these need to be called too to have a valid inventory
+ newActor->inventory.SetSlotCount(inventory.GetSlotCount());
+ } else {
+ newActor->inventory.CopyFrom(this);
+ newActor->Equipped = Equipped;
+ newActor->EquippedHeader = EquippedHeader;
+ if (PCStats) {
+ newActor->CreateStats();
+ memcpy(newActor->PCStats, PCStats, sizeof(PCStatsStruct));
+ }
+ }
+
+ //copy the spellbook, if any
+ if (!mislead) {
+ newActor->spellbook.CopyFrom(this);
+ }
+
+ newActor->CreateDerivedStats();
+
+ //copy the running effects
+ EffectQueue *newFXQueue = fxqueue.CopySelf();
+
+ area->AddActor(newActor);
+ newActor->SetPosition( Pos, CC_CHECK_IMPASSABLE, 0 );
+ newActor->SetOrientation(GetOrientation(),0);
+ newActor->SetStance( IE_ANI_READY );
+
+ //and apply them
+ newActor->RefreshEffects(newFXQueue);
+ return newActor;
+}
+
+ieDword Actor::GetClassLevel(const ieDword id) const
+{
+ if (id>=ISCLASSES)
+ return 0;
+
+ //return iwd2 value if appropriate
+ if (version==22)
+ return BaseStats[levelslotsiwd2[id]];
+
+ //houston, we gots a problem!
+ if (!levelslots || !dualswap)
+ return 0;
+
+ //only works with PC's
+ ieDword classid = BaseStats[IE_CLASS]-1;
+ if (classid>=(ieDword)classcount || !levelslots[classid])
+ return 0;
+
+ //handle barbarians specially, since they're kits and not in levelslots
+ if (id == ISBARBARIAN && levelslots[classid][ISFIGHTER] && GetKitIndex(BaseStats[IE_KIT]) == 31) {
+ return BaseStats[IE_LEVEL];
+ }
+
+ //get the levelid (IE_LEVEL,*2,*3)
+ ieDword levelid = levelslots[classid][id];
+ if (!levelid)
+ return 0;
+
+ //do dual-swap
+ if (IsDualClassed()) {
+ //if the old class is inactive, and it is the class
+ //being searched for, return 0
+ if (IsDualInactive() && ((Modified[IE_MC_FLAGS]&MC_WAS_ANY)==(ieDword)mcwasflags[id]))
+ return 0;
+ }
+ return BaseStats[levelid];
+}
+
+bool Actor::IsDualInactive() const
+{
+ if (!IsDualClassed()) return 0;
+
+ //we assume the old class is in IE_LEVEL2, unless swapped
+ ieDword oldlevel = IsDualSwap() ? BaseStats[IE_LEVEL] : BaseStats[IE_LEVEL2];
+
+ //since GetXPLevel returns the average of the 2 levels, oldclasslevel will
+ //only be less than GetXPLevel when the new class surpasses it
+ return oldlevel>=GetXPLevel(false);
+}
+
+bool Actor::IsDualSwap() const
+{
+ //the dualswap[class-1] holds the info
+ if (!IsDualClassed()) return false;
+ ieDword tmpclass = BaseStats[IE_CLASS]-1;
+ if (tmpclass>=(ieDword)classcount) return false;
+ return (ieDword)dualswap[tmpclass]==(Modified[IE_MC_FLAGS]&MC_WAS_ANY);
+}
+
+ieDword Actor::GetWarriorLevel() const
+{
+ if (!IsWarrior()) return 0;
+
+ ieDword warriorlevels[4] = {
+ GetBarbarianLevel(),
+ GetFighterLevel(),
+ GetPaladinLevel(),
+ GetRangerLevel()
+ };
+
+ ieDword highest = 0;
+ for (int i=0; i<4; i++) {
+ if (warriorlevels[i] > highest) {
+ highest = warriorlevels[i];
+ }
+ }
+
+ return highest;
+}
+
+bool Actor::BlocksSearchMap() const
+{
+ return Modified[IE_DONOTJUMP] < 2;
+}
+
+//return true if the actor doesn't want to use an entrance
+bool Actor::CannotPassEntrance(ieDword exitID) const
+{
+ if (LastExit!=exitID) {
+ return true;
+ }
+
+ if (InternalFlags&IF_USEEXIT) {
+ return false;
+ }
+
+ return true;
+}
+
+void Actor::UseExit(ieDword exitID) {
+ if (exitID) {
+ InternalFlags|=IF_USEEXIT;
+ } else {
+ InternalFlags&=~IF_USEEXIT;
+ }
+ LastExit = exitID;
+}
+
+// luck increases the minimum roll per dice, but only up to the number of dice sides;
+// luck does not affect critical hit chances:
+// if critical is set, it will return 1/sides on a critical, otherwise it can never
+// return a critical miss when luck is positive and can return a false critical hit
+int Actor::LuckyRoll(int dice, int size, int add, ieDword flags, Actor* opponent) const
+{
+ assert(this != opponent);
+
+ ieDword stat;
+ if (flags&LR_DAMAGELUCK) {
+ stat = IE_DAMAGELUCK;
+ } else {
+ stat = IE_LUCK;
+ }
+
+ int luck = (signed) GetSafeStat(stat);
+ if (flags&LR_NEGATIVE) {
+ luck = -luck;
+ }
+ if (opponent) luck -= (signed) opponent->GetStat(stat);
+ if (dice < 1 || size < 1) {
+ return add + luck;
+ }
+
+ ieDword critical = flags&LR_CRITICAL;
+
+ if (dice > 100) {
+ int bonus;
+ if (abs(luck) > size) {
+ bonus = luck/abs(luck) * size;
+ } else {
+ bonus = luck;
+ }
+ int roll = core->Roll(1, dice*size, 0);
+ if (critical && (roll == 1 || roll == size)) {
+ return roll;
+ } else {
+ return add + dice * (size + bonus) / 2;
+ }
+ }
+
+ int roll, result = 0, misses = 0, hits = 0;
+ for (int i = 0; i < dice; i++) {
+ roll = core->Roll(1, size, 0);
+ if (roll == 1) {
+ misses++;
+ } else if (roll == size) {
+ hits++;
+ }
+ roll += luck;
+ if (roll > size) {
+ roll = size;
+ } else if (roll < 1) {
+ roll = 1;
+ }
+ result += roll;
+ }
+
+ // ensure we can still return a critical failure/success
+ if (critical && dice == misses) return 1;
+ if (critical && dice == hits) return size;
+
+ return result + add;
+}
+
+static EffectRef fx_remove_invisible_state_ref = { "ForceVisible", -1 };
+
+// removes the (normal) invisibility state
+void Actor::CureInvisibility()
+{
+ if (Modified[IE_STATE_ID] & state_invisible) {
+ Effect *newfx;
+
+ newfx = EffectQueue::CreateEffect(fx_remove_invisible_state_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, this, this);
+
+ delete newfx;
+
+ //not sure, but better than nothing
+ if (! (Modified[IE_STATE_ID]&state_invisible)) {
+ InternalFlags|=IF_BECAMEVISIBLE;
+ }
+ }
+}
+
+static EffectRef fx_remove_sanctuary_ref = { "Cure:Sanctuary", -1 };
+
+// removes the sanctuary effect
+void Actor::CureSanctuary()
+{
+ Effect *newfx;
+
+ newfx = EffectQueue::CreateEffect(fx_remove_sanctuary_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, this, this);
+
+ delete newfx;
+}
+
+void Actor::ResetState()
+{
+ CureInvisibility();
+ CureSanctuary();
+ SetModal(MS_NONE);
+}
+
+// doesn't check the range, but only that the azimuth and the target
+// orientation match with a +/-2 allowed difference
+bool Actor::IsBehind(Actor* target) const
+{
+ unsigned char tar_orient = target->GetOrientation();
+ // computed, since we don't care where we face
+ unsigned char my_orient = GetOrient(target->Pos, Pos);
+
+ signed char diff;
+ for (int i=-2; i <= 2; i++) {
+ diff = my_orient+i;
+ if (diff >= MAX_ORIENT) diff -= MAX_ORIENT;
+ if (diff <= -1) diff += MAX_ORIENT;
+ if (diff == (signed)tar_orient) return true;
+ }
+ return false;
+}
+
+// checks all the actor's stats to see if the target is her racial enemy
+bool Actor::IsRacialEnemy(Actor* target) const
+{
+ if (Modified[IE_HATEDRACE] == target->Modified[IE_RACE]) {
+ return true;
+ } else if (core->HasFeature(GF_3ED_RULES)) {
+ // iwd2 supports multiple racial enemies gained through level progression
+ for (unsigned int i=0; i<7; i++) {
+ if (Modified[IE_HATEDRACE2+i] == target->Modified[IE_RACE]) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool Actor::ModalSpellSkillCheck() {
+ switch(ModalState) {
+ case MS_BATTLESONG:
+ case MS_DETECTTRAPS:
+ case MS_TURNUNDEAD:
+ return true;
+ case MS_STEALTH:
+ return TryToHide();
+ case MS_NONE:
+ default:
+ return false;
+ }
+}
+
+static EffectRef fx_disable_button_ref = { "DisableButton", -1 };
+
+inline void HideFailed(Actor* actor)
+{
+ Effect *newfx;
+ newfx = EffectQueue::CreateEffect(fx_disable_button_ref, 0, ACT_STEALTH, FX_DURATION_INSTANT_LIMITED);
+ newfx->Duration = core->Time.round_sec; // 90 ticks, 1 round
+ core->ApplyEffect(newfx, actor, actor);
+ delete newfx;
+}
+
+bool Actor::TryToHide() {
+ ieDword roll = LuckyRoll(1, 100, 0);
+ if (roll == 1) {
+ HideFailed(this);
+ return false;
+ }
+
+ // check for disabled dualclassed thieves (not sure if we need it)
+
+ if (Modified[IE_DISABLEDBUTTON] & (1<<ACT_STEALTH)) {
+ HideFailed(this);
+ return false;
+ }
+
+ // check if the pc is in combat (seen / heard)
+ Game *game = core->GetGame();
+ if (game->PCInCombat(this)) {
+ HideFailed(this);
+ return false;
+ }
+
+ ieDword skill;
+ if (core->HasFeature(GF_HAS_HIDE_IN_SHADOWS)) {
+ skill = (GetStat(IE_HIDEINSHADOWS) + GetStat(IE_STEALTH))/2;
+ } else {
+ skill = GetStat(IE_STEALTH);
+ }
+
+ // check how bright our spot is
+ ieDword lightness = game->GetCurrentArea()->GetLightLevel(Pos);
+ // seems to be the color overlay at midnight; lightness of a point with rgb (200, 100, 100)
+ // TODO: but our NightTint computes to a higher value, which one is bad?
+ ieDword light_diff = int((lightness - ref_lightness) * 100 / (100 - ref_lightness)) / 2;
+ ieDword chance = (100 - light_diff) * skill/100;
+
+ if (roll > chance) {
+ HideFailed(this);
+ return false;
+ }
+ return true;
+}
+
+bool Actor::InvalidSpellTarget() const
+{
+ if (GetStat(IE_STATE_ID) & (STATE_DEAD)) return true;
+ if (HasSpellState(SS_SANCTUARY)) return true;
+ return false;
+}
+
+bool Actor::InvalidSpellTarget(int spellnum, Actor *caster, int range) const
+{
+ ieResRef spellres;
+
+ //TODO: spell specific state checks
+ if (!range) return false;
+
+ ResolveSpellName(spellres, spellnum);
+ Spell *spl = gamedata->GetSpell(spellres);
+ int srange = spl->GetCastingDistance(caster);
+
+ return srange<range;
+}
+
+bool Actor::PCInDark() const
+{
+ if (!this) return false;
+ unsigned int level = area->GetLightLevel(Pos);
+ if (level<ref_lightness) {
+ return true;
+ }
+ return false;
+}
+
+int Actor::GetClassMask() const
+{
+ int classmask = 0;
+ for (int i=0; i < ISCLASSES; i++) {
+ if (Modified[levelslotsiwd2[i]] > 0) {
+ classmask |= 1<<(classesiwd2[i]-1);
+ }
+ }
+
+ return classmask;
+}
+
diff --git a/gemrb/core/Scriptable/Actor.h b/gemrb/core/Scriptable/Actor.h
new file mode 100644
index 0000000..5b54b5b
--- /dev/null
+++ b/gemrb/core/Scriptable/Actor.h
@@ -0,0 +1,750 @@
+/* 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 ACTOR_H
+#define ACTOR_H
+
+#include "Scriptable/Scriptable.h"
+
+#include "Scriptable/PCStatStruct.h"
+
+#include "exports.h"
+#include "ie_types.h"
+
+#include "Animation.h"
+#include "Audio.h"
+#include "CharAnimations.h"
+#include "EffectQueue.h"
+#include "ScriptedAnimation.h"
+
+#include <cstring>
+#include <vector>
+
+class Map;
+class ScriptedAnimation;
+struct PolymorphCache;
+
+/** USING DEFINITIONS AS DESCRIBED IN STATS.IDS */
+#include "ie_stats.h"
+
+#include "Inventory.h"
+#include "Spellbook.h"
+
+#define MAX_STATS 256
+#define MAX_LEVEL 128
+#define MAX_FEATS 96 //3*sizeof(ieDword)
+
+//lucky roll
+#define LR_CRITICAL 1
+#define LR_DAMAGELUCK 2
+#define LR_NEGATIVE 4
+
+//modal states
+#define MS_NONE 0
+#define MS_BATTLESONG 1
+#define MS_DETECTTRAPS 2
+#define MS_STEALTH 3
+#define MS_TURNUNDEAD 4
+
+//stat modifier type
+#define MOD_ADDITIVE 0
+#define MOD_ABSOLUTE 1
+#define MOD_PERCENT 2
+
+//'do not jump' flags
+#define DNJ_FIT 1
+#define DNJ_UNHINDERED 2
+#define DNJ_JUMP 4
+#define DNJ_BIRD (DNJ_FIT|DNJ_UNHINDERED)
+
+//add_animation flags (override vvc)
+#define AA_PLAYONCE 1
+#define AA_BLEND 2
+
+//GetDialog flags
+#define GD_NORMAL 0
+#define GD_CHECK 1
+#define GD_FEEDBACK 2 //(also check)
+
+//Panic modes
+#define PANIC_NONE 0
+#define PANIC_BERSERK 1
+#define PANIC_RUNAWAY 2
+#define PANIC_RANDOMWALK 3
+
+/** flags for GetActor */
+//default action
+#define GA_DEFAULT 0
+//actor selected for talk
+#define GA_TALK 1
+//actor selected for attack
+#define GA_ATTACK 2
+//actor selected for spell target
+#define GA_SPELL 3
+//actor selected for defend
+#define GA_DEFEND 4
+//actor selected for pick pockets
+#define GA_PICK 5
+//action mask
+#define GA_ACTION 15
+//unselectable actor may not be selected (can still block)
+#define GA_SELECT 16
+//dead actor may not be selected
+#define GA_NO_DEAD 32
+//any point could be selected (area effect)
+#define GA_POINT 64
+//hidden actor may not be selected
+#define GA_NO_HIDDEN 128
+//party members cannot be selected
+#define GA_NO_ALLY 256
+//only party members could be selected
+#define GA_NO_ENEMY 512
+//
+#define GA_NO_NEUTRAL 1024
+//cannot target self
+#define GA_NO_SELF 2048
+//try other areas too
+//(unused and removed, see git history of the commit which
+//added this comment for some clues if you really need it)
+//#define GA_GLOBAL 4096
+
+//line of sight is ignored (for GetAllActorsInRadius)
+#define GA_NO_LOS 4096
+
+// Detect() mode: IDS matching ignores invisibility
+#define GA_DETECT 8192
+
+#define VCONST_COUNT 100
+
+//interact types
+#define I_INSULT 1
+#define I_COMPLIMENT 2
+#define I_SPECIAL 3
+
+// 3 for blur, 8 for mirror images
+#define EXTRA_ACTORCOVERS 11
+
+//flags for UseItem
+#define UI_SILENT 1 //no sound when used up
+#define UI_MISS 2 //ranged miss (projectile has no effects)
+
+//used to mask off current profs
+#define PROFS_MASK 0x07
+
+//locations of classes in the isclass/levelslots arrays
+#define ISFIGHTER 0
+#define ISMAGE 1
+#define ISTHIEF 2
+#define ISBARBARIAN 3
+#define ISBARD 4
+#define ISCLERIC 5
+#define ISDRUID 6
+#define ISMONK 7
+#define ISPALADIN 8
+#define ISRANGER 9
+#define ISSORCERER 10
+#define ISCLASSES 11
+
+//appearance flags
+
+#define APP_HALFTRANS 2 //half transparent
+#define APP_DEATHVAR 16 //set death variable
+#define APP_DEATHTYPE 32 //count creature type deaths
+//64
+#define APP_FACTION 128 //count killed faction
+#define APP_TEAM 0x100 //count killed team
+#define APP_INVULNERABLE 0x200 //invulnerable
+#define APP_GOOD 0x400 //good count
+#define APP_LAW 0x800 //law count
+#define APP_LADY 0x1000 //lady count
+#define APP_MURDER 0x2000 //murder count
+#define APP_NOTURN 0x4000 //doesn't face gabber in dialogue
+#define APP_BUDDY 0x8000 //npcs will turn hostile if this one dies
+#define APP_DEAD 0x40000000 //used by the engine to prevent dying twice
+
+#define DC_GOOD 0
+#define DC_LAW 1
+#define DC_LADY 2
+#define DC_MURDER 3
+
+// used for distinguishing damage immunity from high damage resistance
+#define DR_IMMUNE 999999
+
+// wild surge target change type
+#define WSTC_SETTYPE 1 // change to this target type
+#define WSTC_ADDTYPE 2 // affect also this target type
+#define WSTC_RANDOMIZE 3 // choose a random target
+struct WildSurgeSpellMods {
+ unsigned int num_castings; // number of times to cast
+ unsigned int num_wildrolls; // number of times to roll
+ unsigned int projectile_id; // new projectile id
+ unsigned int target_change_type; // settype, addtype, randomize
+ unsigned int target_type; // type to use when target_change_type is not WSTC_RANDOMIZE
+ unsigned int projectile_speed_mod; // factor in percents
+ int saving_throw_mod;
+};
+
+typedef ieByte ActionButtonRow[GUIBT_COUNT];
+struct ActionButtonRow2 {
+ ActionButtonRow buttons;
+ ieByte clss;
+};
+
+typedef std::vector< ScriptedAnimation*> vvcVector;
+typedef std::list<ieResRef*> resourceList;
+
+struct WeaponInfo {
+ int slot;
+ int enchantment;
+ unsigned int range;
+ ieDword itemflags;
+ ieDword prof;
+ bool backstabbing;
+};
+
+extern void ReleaseMemoryActor();
+
+class GEM_EXPORT Actor : public Movable {
+public:
+ //CRE DATA FIELDS
+ ieDword BaseStats[MAX_STATS];
+ ieDword Modified[MAX_STATS];
+ ieDword *PrevStats;
+ ieByteSigned DeathCounters[4]; //PST specific (good, law, lady, murder)
+
+ ieResRef applyWhenHittingMelee; //set melee effect
+ ieResRef applyWhenHittingRanged; //set ranged effect
+ ieResRef applyWhenNearLiving; //cast spell on condition
+ ieResRef applyWhen50Damage; //cast spell on condition
+ ieResRef applyWhen90Damage; //cast spell on condition
+ ieResRef applyWhenEnemySighted; //cast spell on condition
+ ieResRef applyWhenPoisoned; //cast spell on condition
+ ieResRef applyWhenHelpless; //cast spell on condition
+ ieResRef applyWhenAttacked; //cast spell on condition
+ ieResRef applyWhenBeingHit; //cast spell on condition
+ ieResRef ModalSpell; //apply this spell once per round
+ ieResRef LingeringModalSpell; //apply this spell once per round if the effects are lingering
+ ieResRef BardSong; //custom bard song (updated by fx)
+
+ PCStatsStruct* PCStats;
+ ieResRef SmallPortrait;
+ ieResRef LargePortrait;
+ /** 0: NPC, 1-8 party slot */
+ ieByte InParty;
+ char* LongName, * ShortName;
+ ieStrRef ShortStrRef, LongStrRef;
+ ieStrRef StrRefs[VCONST_COUNT];
+
+ ieDword AppearanceFlags;
+
+ ieVariable KillVar; //this second field is present in pst, iwd1 and iwd2
+ ieVariable IncKillVar; // iwd1, iwd2
+
+ ieByte SetDeathVar, IncKillCount, UnknownField; // boolean fields from iwd1 and iwd2
+
+ Inventory inventory;
+ ieWordSigned Equipped; //the equipped weapon slot
+ ieWord EquippedHeader; //the used extended header
+ Spellbook spellbook;
+ //savefile version (creatures embedded in area)
+ int version;
+ //in game or area actor header
+ ieDword TalkCount;
+ ieDword RemovalTime;
+ //FIXME: this is definitely not the same in bg2, in bg2 there are joinable npcs
+ //which keep a matrix of counters
+ ieDword InteractCount; //this is accessible in iwd2, probably exists in other games too
+ ieDword appearance;
+ ieDword ModalState;
+ int PathTries; //the # of previous tries to pick up a new walkpath
+public:
+ #define LastTarget LastDisarmFailed
+ //ieDword LastTarget; use lastdisarmfailed
+ #define LastAttacker LastDisarmed
+ //ieDword LastAttacker; use lastdisarmed
+ #define LastHitter LastEntered
+ //ieDword LastHitter; use lastentered
+ #define LastSummoner LastTrigger
+ //ieDword LastSummoner; use lasttrigger
+ #define LastTalkedTo LastUnlocked
+ //ieDword LastTalkedTo; use lastunlocked
+ ieDword LastProtected;
+ ieDword LastFollowed;
+ ieDword LastCommander;
+ ieDword LastHelp;
+ ieDword LastSeen;
+ ieDword LastMarked; //no idea if non-actors could mark objects
+ int LastMarkedSpell; //a spell number to cast
+ ieDword LastHeard;
+ ieDword HotKey;
+ ieDword LastExit; //the global ID of the exit to be used
+ char ShieldRef[2];
+ char HelmetRef[2];
+ char WeaponRef[2];
+ int WeaponType;
+ ieDword multiclass;
+ bool GotLUFeedback;
+ int WMLevelMod;
+
+ int LastCommand; //lastcommander
+ int LastShout; //lastheard
+ int LastDamage; //lasthitter
+ int LastDamageType;//lasthitter
+ ieDword LastTurner;
+ Point FollowOffset;//follow lastfollowed at this offset
+ Point HomeLocation;//spawnpoint, return here after rest
+
+ ieDword TargetDoor;
+
+ EffectQueue fxqueue;
+ vvcVector vvcOverlays;
+ vvcVector vvcShields;
+ ieDword *projectileImmunity; //classic bitfield
+ Holder<SoundHandle> casting_sound;
+ ieDword roundTime; //these are timers for attack rounds
+ ieDword modalTime; //last time the modal effect used
+ char modalSpellLingering; //the count of rounds for which the modal spell will be reapplied after the state ends
+ ieDword panicMode; //runaway, berserk or randomwalk
+ ieDword lastInit;
+ bool no_more_steps;
+ int speed;
+
+ PolymorphCache *polymorphCache; // fx_polymorph etc
+ WildSurgeSpellMods wildSurgeMods;
+private:
+ //this stuff doesn't get saved
+ CharAnimations* anims;
+ SpriteCover* extraCovers[EXTRA_ACTORCOVERS];
+ ieByte SavingThrow[5];
+ //how many attacks in this round
+ int attackcount;
+ //true every second round of attack
+ bool secondround;
+ int attacksperround;
+ //time of our next attack
+ ieDword nextattack;
+ ieDword nextWalk;
+ ieDword lastattack;
+ ieDword InTrap;
+ char AttackStance;
+ /*The projectile bringing the current attack*/
+ Projectile* attackProjectile ;
+ /** paint the actor itself. Called internally by Draw() */
+ void DrawActorSprite(const Region &screen, int cx, int cy, const Region& bbox,
+ SpriteCover*& sc, Animation** anims,
+ unsigned char Face, const Color& tint);
+
+ /** fixes the palette */
+ void SetupColors();
+ /** debugging function, gets the scripting name of an actor referenced by a global ID */
+ const char* GetActorNameByID(ieDword ID) const;
+ /* if Lasttarget is gone, call this */
+ void StopAttack();
+ /* checks a weapon quick slot and resets it to fist if it is empty */
+ void CheckWeaponQuickSlot(unsigned int which);
+ /* helper for usability checks */
+ int CheckUsability(Item *item) const;
+ /* Set up all the missing stats on load time, or after level up */
+ void CreateDerivedStatsBG();
+ /* Set up all the missing stats on load time, or after level up */
+ void CreateDerivedStatsIWD2();
+ /* Gets the given ISCLASS level */
+ ieDword GetClassLevel (const ieDword id) const;
+ /* Returns true if the dual class is backwards */
+ bool IsDualSwap() const;
+ /** Re/Inits the Modified vector for PCs/NPCs */
+ void RefreshPCStats();
+ bool ShouldHibernate();
+ void ApplyClassClab(int cls, bool remove);
+ bool ShouldDrawCircle();
+ void SetupFistData();
+public:
+ Actor(void);
+ ~Actor(void);
+ /** releases memory */
+ static void ReleaseMemory();
+ /** sets game specific parameter (which stat should determine the fist weapon type */
+ static void SetFistStat(ieDword stat);
+ /** sets game specific default data about action buttons */
+ static void SetDefaultActions(int qslot, ieByte slot1, ieByte slot2, ieByte slot3);
+ /** prints useful information on console */
+ void DumpMaxValues();
+ void DebugDump();
+ /** fixes the feet circle */
+ void SetCircleSize();
+ /** places the actor on the map */
+ void SetMap(Map *map);
+ /** sets the actor's position, calculating with the nojump flag*/
+ void SetPosition(const Point &position, int jump, int radius=0);
+ /** you better use SetStat, this stuff is only for special cases*/
+ void SetAnimationID(unsigned int AnimID);
+ /** returns the animations */
+ CharAnimations* GetAnims() const;
+ /** Re/Inits the Modified vector */
+ void RefreshEffects(EffectQueue *eqfx);
+ /** gets saving throws */
+ void RollSaves();
+ /** returns a saving throw */
+ bool GetSavingThrow(ieDword type, int modifier);
+ /** Returns true if the actor is targetable */
+ bool ValidTarget(int ga_flags) const;
+ /** Returns a Stat value */
+ ieDword GetStat(unsigned int StatIndex) const;
+ /** Returns a safe Stat value, one, that is not partially computed */
+ ieDword GetSafeStat(unsigned int StatIndex) const;
+ /** Sets a Stat Value (unsaved) */
+ bool SetStat(unsigned int StatIndex, ieDword Value, int pcf);
+ /** Returns the difference */
+ int GetMod(unsigned int StatIndex);
+ /** Returns a Stat Base Value */
+ ieDword GetBase(unsigned int StatIndex) const;
+ /** Sets a Base Stat Value */
+ bool SetBase(unsigned int StatIndex, ieDword Value);
+ bool SetBaseNoPCF(unsigned int StatIndex, ieDword Value);
+ /** set/resets a Base Stat bit */
+ bool SetBaseBit(unsigned int StatIndex, ieDword Value, bool setreset);
+ /** Sets the modified value in different ways, returns difference */
+ int NewStat(unsigned int StatIndex, ieDword ModifierValue, ieDword ModifierType);
+ /** Modifies the base stat value in different ways, returns difference */
+ int NewBase(unsigned int StatIndex, ieDword ModifierValue, ieDword ModifierType);
+ void SetLeader(Actor *actor, int xoffset=0, int yoffset=0);
+ /** Sets the Icon ResRef */
+ //Which - 0 both, 1 Large, 2 Small
+ void SetPortrait(const char* ResRef, int Which=0);
+ void SetSoundFolder(const char *soundset);
+ void GetSoundFolder(char *soundset, int flag) const;
+ /** Gets the Character Long Name/Short Name */
+ char* GetName(int which) const
+ {
+ if(which==-1) which=TalkCount;
+ if (which) {
+ return LongName;
+ }
+ return ShortName;
+ }
+ /** Gets the DeathVariable */
+ const char* GetScriptName(void) const
+ {
+ return scriptName;
+ }
+ /** Gets a Script ResRef */
+ const char* GetScript(int ScriptIndex) const;
+ /** Gets the Character's level for XP calculations */
+ ieDword GetXPLevel(int modified) const;
+ /** Guesses the (base) casting level */
+ ieDword GetCasterLevel(int spelltype);
+ ieDword GetBaseCasterLevel(int spelltype) const;
+ /** Returns the wild mage casting level modifier */
+ int GetWildMod(int level);
+ /** Returns any casting level modifier */
+ int CastingLevelBonus(int level, int type);
+
+ /** Gets the Dialog ResRef */
+ const char* GetDialog(int flags=GD_NORMAL) const;
+ /** Gets the Portrait ResRef */
+ const char* GetPortrait(int which) const
+ {
+ return which ? SmallPortrait : LargePortrait;
+ }
+
+ /** Gets the attack projectile */
+ Projectile* GetAttackProjectile()
+ {
+ return attackProjectile;
+ }
+ void SetName(const char* ptr, unsigned char type);
+ void SetName(int strref, unsigned char type);
+ /* returns carried weight atm, could calculate with strength*/
+ int GetEncumbrance();
+ /* checks on death of actor, returns true if it should be removed*/
+ bool CheckOnDeath();
+ /* receives undead turning message */
+ void Turn(Scriptable *cleric, ieDword turnlevel);
+ /* call this on gui selects */
+ void SelectActor();
+ /* sets the actor in panic (turn/morale break) */
+ void Panic(Scriptable *attacker, int panicmode);
+ /* sets a multi class flag (actually this is a lot of else too) */
+ void SetMCFlag(ieDword bitmask, int op);
+ /* inlined dialogue start */
+ void Interact(int type);
+ /* returns a remapped verbal constant strref */
+ ieStrRef GetVerbalConstant(int index) const;
+ /* displaying a random verbal constant */
+ void VerbalConstant(int start, int count);
+ /* inlined dialogue response */
+ void Response(int type);
+ /* called when someone died in the party */
+ void ReactToDeath(const char *deadname);
+ /* called when someone talks to Actor */
+ void DialogInterrupt();
+ /* called when actor was hit */
+ void GetHit();
+ /* called when actor starts to cast a spell*/
+ bool HandleCastingStance(const ieResRef SpellResRef, bool deplete);
+ /* deals damage to this actor */
+ int Damage(int damage, int damagetype, Scriptable *hitter, int modtype=MOD_ADDITIVE);
+ /* displays the damage taken and other details (depends on the game type) */
+ void DisplayCombatFeedback (unsigned int damage, int resisted, int damagetype, Scriptable *hitter);
+ /* play a random footstep sound */
+ void PlayWalkSound();
+ /* play the proper hit sound (in pst) */
+ void PlayHitSound(DataFileMgr *resdata, int damagetype, bool suffix);
+ /* drops items from inventory to current spot */
+ void DropItem(const ieResRef resref, unsigned int flags);
+ void DropItem(int slot, unsigned int flags);
+ /* returns item information in quickitem slot */
+ void GetItemSlotInfo(ItemExtHeader *item, int which, int header);
+ /* returns spell information in quickspell slot */
+ void GetSpellSlotInfo(SpellExtHeader *spell, int which);
+ /* updates quickslots */
+ void ReinitQuickSlots();
+ /* actor is in trap */
+ void SetInTrap(ieDword tmp);
+ /* sets some of the internal flags */
+ void SetRunFlags(ieDword flags);
+ /* applies the kit abilities, returns false if kit is not applicable */
+ bool ApplyKit(bool remove);
+ /* applies the class abilities*/
+ void ApplyClab(const char *clab, ieDword max, bool remove);
+ /* calls InitQuickSlot in PCStatStruct */
+ void SetupQuickSlot(unsigned int which, int slot, int headerindex);
+ /* returns true if the actor is PC/joinable*/
+ bool Persistent() const;
+ /* assigns actor to party slot, 0 = NPC, areas won't remove it */
+ void SetPersistent(int partyslot);
+ /* resurrects actor */
+ void Resurrect();
+ /* removes actor in the next update cycle */
+ void DestroySelf();
+ /* schedules actor to die */
+ void Die(Scriptable *killer);
+ /* debug function */
+ void GetNextAnimation();
+ /* debug function */
+ void GetPrevAnimation();
+ /* debug function */
+ void GetNextStance();
+ /* learns the given spell, possibly receive XP */
+ int LearnSpell(const ieResRef resref, ieDword flags);
+ /* returns the ranged weapon header associated with the currently equipped projectile */
+ ITMExtHeader *GetRangedWeapon(WeaponInfo &wi) const;
+ /* Returns current weapon range and extended header
+ if range is nonzero, then which is valid */
+ ITMExtHeader* GetWeapon(WeaponInfo &wi, bool leftorright=false) const;
+ /* Creates player statistics */
+ void CreateStats();
+ /* Heals actor by days */
+ void Heal(int days);
+ /* Receive experience (handle dual/multi class) */
+ void AddExperience(int exp);
+ /* Calculate experience bonus */
+ int CalculateExperience(int type, int level);
+ /* Sets the modal state after checks */
+ void SetModal(ieDword newstate, bool force=1);
+ /* Sets the modal spell after checks */
+ void SetModalSpell(ieDword state, const char *spell);
+ /* returns current attack style */
+ int GetAttackStyle() const;
+ /* adds the combatants to the attackers list */
+ void AttackedBy(Actor *actor);
+ /* sets target for immediate attack */
+ void SetTarget( Scriptable *actor);
+ /* starts combat round*/
+ void InitRound(ieDword gameTime);
+ /* returns melee penalty */
+ int MeleePenalty() const;
+ /* gets the to hit value */
+ int GetToHit(int bonus, ieDword Flags, Actor *target) const;
+ /* gets the defense against an attack */
+ int GetDefense(int DamageType, Actor *attacker) const;
+ /* get the current hit bonus */
+ bool GetCombatDetails(int &tohit, bool leftorright, WeaponInfo &wi, ITMExtHeader *&header, ITMExtHeader *&hittingheader,\
+ ieDword &Flags, int &DamageBonus, int &speed, int &CriticalBonus, int &style, Actor *target) const;
+ /* performs attack against target */
+ void PerformAttack(ieDword gameTime);
+ /* ensures we can deal damage to a target */
+ void ModifyDamage(Actor *target, Scriptable *hitter, int &damage, int &resisted, int damagetype, WeaponInfo *wi, bool critical);
+ /* applies modal spell etc, if needed */
+ void UpdateActorState(ieDword gameTime);
+ /* returns the hp adjustment based on constitution */
+ int GetHpAdjustment(int multiplier);
+ /* does all the housekeeping after loading the actor from file */
+ void InitStatsOnLoad();
+ /* sets a colour gradient stat, handles location */
+ void SetColor( ieDword idx, ieDword grd);
+ /* sets an RGB colour modification effect; location 0xff for global */
+ void SetColorMod( ieDword location, RGBModifier::Type type, int speed,
+ unsigned char r, unsigned char g, unsigned char b,
+ int phase=-1 );
+ bool Schedule(ieDword gametime, bool checkhide);
+ /* call this when path needs to be changed */
+ void NewPath();
+ /* overridden method, won't walk if dead */
+ void WalkTo(const Point &Des, ieDword flags, int MinDistance = 0);
+ /* resolve string constant (sound will be altered) */
+ void ResolveStringConstant(ieResRef sound, unsigned int index) const;
+ void GetSoundFromINI(ieResRef Sound, unsigned int index) const;
+ void GetSoundFrom2DA(ieResRef Sound, unsigned int index) const;
+ /* sets the quick slots */
+ void SetActionButtonRow(ActionButtonRow &ar);
+ /* updates the quick slots */
+ void GetActionButtonRow(ActionButtonRow &qs);
+
+ /* Handling automatic stance changes */
+ bool HandleActorStance();
+
+ /* if necessary, advance animation */
+ void UpdateAnimations();
+ /* if necessary, draw actor */
+ void Draw(const Region &screen);
+
+ /* add mobile vvc (spell effects) to actor's list */
+ void AddVVCell(ScriptedAnimation* vvc);
+ /* remove a vvc from the list, graceful means animated removal */
+ void RemoveVVCell(const ieResRef vvcname, bool graceful);
+ /* returns true if actor already has the overlay (slow) */
+ bool HasVVCCell(const ieResRef resource) const;
+ /* returns overlay if actor already has it (slow) */
+ ScriptedAnimation *GetVVCCell(const ieResRef resource) const;
+ /* returns the vvc pointer to a hardcoded overlay */
+ /* if it exists (faster than hasvvccell) */
+ ScriptedAnimation *FindOverlay(int index) const;
+ /* draw videocells */
+ void DrawVideocells(const Region &screen, vvcVector &vvcCells, const Color &tint);
+
+ void SetLockedPalette(const ieDword *gradients);
+ void UnlockPalette();
+ void AddAnimation(const ieResRef resource, int gradient, int height, int flags);
+ /* plays damage animation, if hit is not set, then plays only the splash part */
+ void PlayDamageAnimation(int x, bool hit=true);
+ /* restores a spell of maximum maxlevel level, type is a mask of disabled spells */
+ int RestoreSpellLevel(ieDword maxlevel, ieDword typemask);
+ /* rememorizes spells, cures fatigue, etc */
+ void Rest(int hours);
+ /* returns the portrait icons list */
+ const unsigned char *GetStateString() const;
+ /* adds a state icon to the list */
+ void AddPortraitIcon(ieByte icon);
+ /* disables a state icon in the list, doesn't remove it! */
+ void DisablePortraitIcon(ieByte icon);
+ /* returns which slot belongs to the quickweapon slot */
+ int GetQuickSlot(int slot) const;
+ /* Sets equipped Quick slot, if header is -1, then use the current one */
+ int SetEquippedQuickSlot(int slot, int header);
+ /* Uses an item on the target or point */
+ bool UseItemPoint(ieDword slot, ieDword header, const Point &point, ieDword flags);
+ bool UseItem(ieDword slot, ieDword header, Scriptable *target, ieDword flags, int damage = 0);
+ /* Deducts a charge from an item */
+ void ChargeItem(ieDword slot, ieDword header, CREItem *item, Item *itm, bool silent);
+ /* If it returns true, then default AC=10 and the lesser the better */
+ static int IsReverseToHit();
+ /* initialize the action buttons based on class. If forced, it will override
+ previously customized or set buttons. */
+ void InitButtons(ieDword cls, bool forced);
+ int GetAbilityBonus(unsigned int ability) const;
+ int GetSkill(unsigned int skill) const;
+ int GetFeat(unsigned int feat) const;
+ void SetFeat(unsigned int feat, int mode);
+ void SetUsedWeapon(const char *AnimationType, ieWord *MeleeAnimation,
+ int WeaponType=-1);
+ void SetUsedShield(const char *AnimationType, int WeaponType=-1);
+ void SetUsedHelmet(const char *AnimationType);
+ void SetupFist();
+ /* Returns nonzero if the caster is held */
+ int Immobile() const;
+ /* Returns strref if the item is unusable due to name/type restrictions */
+ ieStrRef Disabled(ieResRef name, ieDword type) const;
+ /* Returns constant string if the item is unusable */
+ int Unusable(Item *item) const;
+ /* Sets all clown colour to the given gradient */
+ void SetGradient(ieDword gradient);
+ /* Enables an overlay */
+ void SetOverlay(unsigned int overlay);
+ /* Checks and sets a spellstate if it wasn't set yet */
+ bool SetSpellState(unsigned int spellstate);
+ /* Checks a spellstate */
+ bool HasSpellState(unsigned int spellstate) const;
+ /* Checks a feat */
+ bool HasFeat(unsigned int featindex) const;
+ /* Reports projectile immunity, nonzero if immune */
+ ieDword ImmuneToProjectile(ieDword projectile) const;
+ /* Sets projectile immunity */
+ void AddProjectileImmunity(ieDword projectile);
+ /* Set up all the missing stats on load time, chargen, or after level up */
+ void CreateDerivedStats();
+ /* Checks if the actor is multiclassed (excluding dualclassed actors)) */
+ bool IsMultiClassed() const;
+ /* Checks if the actor is dualclassed */
+ bool IsDualClassed() const;
+ /* Returns an exact copy of this actor */
+ Actor *CopySelf(bool mislead) const;
+ /* Returns the actor's level of the given class */
+ ieDword GetFighterLevel() const { return GetClassLevel(ISFIGHTER); }
+ ieDword GetMageLevel() const { return GetClassLevel(ISMAGE); }
+ ieDword GetThiefLevel() const { return GetClassLevel(ISTHIEF); }
+ ieDword GetBarbarianLevel() const { return GetClassLevel(ISBARBARIAN); }
+ ieDword GetBardLevel() const { return GetClassLevel(ISBARD); }
+ ieDword GetClericLevel() const { return GetClassLevel(ISCLERIC); }
+ ieDword GetDruidLevel() const { return GetClassLevel(ISDRUID); }
+ ieDword GetMonkLevel() const { return GetClassLevel(ISMONK); }
+ ieDword GetPaladinLevel() const { return GetClassLevel(ISPALADIN); }
+ ieDword GetRangerLevel() const { return GetClassLevel(ISRANGER); }
+ ieDword GetSorcererLevel() const { return GetClassLevel(ISSORCERER); }
+ /* Returns true if the character is a warrior */
+ ieDword GetWarriorLevel() const;
+ bool IsWarrior() const { return (GetFighterLevel()||GetBarbarianLevel()||GetRangerLevel()||GetPaladinLevel()); }
+ /* Returns true if the old class is inactive */
+ bool IsDualInactive() const;
+ /* true if we are dual-wielding */
+ int IsDualWielding() const;
+ bool BlocksSearchMap() const;
+ bool CannotPassEntrance(ieDword exitID) const;
+ void UseExit(ieDword exitID);
+ //int GetReaction() const;
+ /* Similar to Roll, but takes luck into account */
+ int LuckyRoll(int dice, int size, int add, ieDword flags=1, Actor* opponent=NULL) const;
+ /* removes normal invisibility (type 0) */
+ void CureInvisibility();
+ /* removes sanctuary */
+ void CureSanctuary();
+ /* resets the invisibility, sanctuary and modal states */
+ void ResetState();
+ /* checks whether the actor is behind the target */
+ bool IsBehind(Actor* target) const;
+ /* checks whether the target is the actor's racial enemy */
+ bool IsRacialEnemy(Actor* target) const;
+ /* checks whether the actor can stay in the current modal state */
+ bool ModalSpellSkillCheck();
+ /* does all the game logic checks to see if the actor can hide */
+ bool TryToHide();
+ /* checks if the alignment matches one of the masking constants */
+ //bool MatchesAlignmentMask(ieDword mask);
+ /* returns true if this actor is untargetable */
+ bool InvalidSpellTarget() const;
+ /* returns true if the spell is useless to cast on target
+ or the spell's range is smaller than range */
+ bool InvalidSpellTarget(int spellnum, Actor *caster, int range) const;
+ /* returns true if the lightmap under the actor is dark */
+ bool PCInDark() const;
+ /* computes the actor's classmask (iwd2) */
+ int GetClassMask() const;
+};
+#endif
diff --git a/gemrb/core/Scriptable/Container.cpp b/gemrb/core/Scriptable/Container.cpp
new file mode 100644
index 0000000..e2f6757
--- /dev/null
+++ b/gemrb/core/Scriptable/Container.cpp
@@ -0,0 +1,292 @@
+/* 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 "Scriptable/Container.h"
+
+#include "strrefs.h"
+#include "win32def.h"
+
+#include "Audio.h"
+#include "DisplayMessage.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "Item.h"
+#include "Map.h"
+#include "Projectile.h"
+#include "Spell.h"
+#include "SpriteCover.h"
+#include "TileMap.h"
+#include "Video.h"
+#include "GameScript/GSUtils.h"
+#include "GUI/GameControl.h"
+
+#include <cassert>
+#include <cmath>
+
+#define YESNO(x) ( (x)?"Yes":"No")
+
+Container::Container(void)
+ : Highlightable( ST_CONTAINER )
+{
+ Type = 0;
+ LockDifficulty = 0;
+ Flags = 0;
+ TrapDetectionDiff = 0;
+ TrapRemovalDiff = 0;
+ Trapped = 0;
+ TrapDetected = 0;
+ inventory.SetInventoryType(INVENTORY_HEAP);
+ // NULL should be 0 for this
+ memset (groundicons, 0, sizeof(groundicons) );
+ groundiconcover = 0;
+}
+
+void Container::FreeGroundIcons()
+{
+ Video* video = core->GetVideoDriver();
+
+ for (int i = 0;i<MAX_GROUND_ICON_DRAWN;i++) {
+ if (groundicons[i]) {
+ video->FreeSprite( groundicons[i] );
+ groundicons[i]=NULL;
+ }
+ }
+ delete groundiconcover;
+ groundiconcover = 0;
+}
+
+Container::~Container()
+{
+ FreeGroundIcons();
+}
+
+void Container::DrawPile(bool highlight, Region screen, Color tint)
+{
+ Video* video = core->GetVideoDriver();
+ CreateGroundIconCover();
+ for (int i = 0;i<MAX_GROUND_ICON_DRAWN;i++) {
+ if (groundicons[i]) {
+ //draw it with highlight
+ video->BlitGameSprite(groundicons[i],
+ screen.x + Pos.x, screen.y + Pos.y,
+ BLIT_TINTED | (highlight ? 0:BLIT_NOSHADOW),
+ tint, groundiconcover);
+ }
+ }
+}
+
+// create the SpriteCover for the groundicons
+void Container::CreateGroundIconCover()
+{
+ int xpos = 0;
+ int ypos = 0;
+ int width = 0;
+ int height = 0;
+
+ int i; //msvc6.0
+ for (i = 0;i<MAX_GROUND_ICON_DRAWN;i++) {
+ if (groundicons[i]) {
+ Sprite2D& spr = *groundicons[i];
+ if (xpos < spr.XPos) {
+ width += spr.XPos - xpos;
+ xpos = spr.XPos;
+ }
+ if (ypos < spr.YPos) {
+ height += spr.YPos - ypos;
+ ypos = spr.YPos;
+ }
+ if (width-xpos < spr.Width-spr.XPos) {
+ width = spr.Width-spr.XPos+xpos;
+ }
+ if (height-ypos < spr.Height-spr.YPos) {
+ height = spr.Height-spr.YPos+ypos;
+ }
+ }
+ }
+
+ if (!groundiconcover ||
+ !groundiconcover->Covers(Pos.x, Pos.y, xpos, ypos, width, height))
+ {
+ delete groundiconcover;
+ groundiconcover = 0;
+ if (width*height > 0) {
+ groundiconcover = GetCurrentArea()->BuildSpriteCover
+ (Pos.x, Pos.y, xpos, ypos, width, height, WantDither());
+ }
+ }
+
+#ifndef NDEBUG
+ // TODO: remove this checking code eventually
+ for (i = 0;i<MAX_GROUND_ICON_DRAWN;i++) {
+ if (groundicons[i]) {
+ Sprite2D& spr = *groundicons[i];
+ assert(groundiconcover->Covers(Pos.x, Pos.y, spr.XPos, spr.YPos, spr.Width, spr.Height));
+ }
+ }
+#endif
+}
+
+void Container::SetContainerLocked(bool lock)
+{
+ if (lock) {
+ Flags|=CONT_LOCKED;
+ } else {
+ Flags&=~CONT_LOCKED;
+ }
+}
+
+//This function doesn't exist in the original IE, destroys a container
+//turning it to a ground pile
+void Container::DestroyContainer()
+{
+ //it is already a groundpile?
+ if (Type == IE_CONTAINER_PILE)
+ return;
+ Type = IE_CONTAINER_PILE;
+ RefreshGroundIcons();
+ //probably we should stop the script or trigger it, whatever
+}
+
+//Takes an item from the container's inventory and returns its pointer
+CREItem *Container::RemoveItem(unsigned int idx, unsigned int count)
+{
+ CREItem *ret = inventory.RemoveItem(idx, count);
+ //we just took the 3. or less item, groundpile changed
+ if ((Type == IE_CONTAINER_PILE) && (inventory.GetSlotCount()<3)) {
+ RefreshGroundIcons();
+ }
+ return ret;
+}
+
+//Adds an item to the container's inventory
+//containers always have enough capacity (so far), thus we always return 2
+int Container::AddItem(CREItem *item)
+{
+ inventory.AddItem(item);
+ //we just added a 3. or less item, groundpile changed
+ if ((Type == IE_CONTAINER_PILE) && (inventory.GetSlotCount()<4)) {
+ RefreshGroundIcons();
+ }
+ return 2;
+}
+
+void Container::RefreshGroundIcons()
+{
+ int i = inventory.GetSlotCount();
+ if (i>MAX_GROUND_ICON_DRAWN)
+ i = MAX_GROUND_ICON_DRAWN;
+ FreeGroundIcons();
+ while (i--) {
+ CREItem *slot = inventory.GetSlotItem(i); //borrowed reference
+ Item *itm = gamedata->GetItem( slot->ItemResRef ); //cached reference
+ //well, this is required in PST, needs more work if some other
+ //game is broken by not using -1,0
+ groundicons[i] = gamedata->GetBAMSprite( itm->GroundIcon, 0, 0 );
+ gamedata->FreeItem( itm, slot->ItemResRef ); //decref
+ }
+}
+
+//used for ground piles
+int Container::WantDither()
+{
+ //if pile is highlighted, always dither it
+ if (Highlight) {
+ return 2; //dither me if you want
+ }
+ //if pile isn't highlighted, dither it if the polygon wants
+ return 1;
+}
+
+int Container::IsOpen() const
+{
+ if (Flags&CONT_LOCKED) {
+ return false;
+ }
+ return true;
+}
+
+void Container::TryPickLock(Actor *actor)
+{
+ if (LockDifficulty == 100) {
+ if (OpenFail != (ieDword)-1) {
+ displaymsg->DisplayStringName(OpenFail, 0xbcefbc, actor, IE_STR_SOUND|IE_STR_SPEECH);
+ } else {
+ displaymsg->DisplayConstantStringName(STR_CONT_NOPICK, 0xbcefbc, actor);
+ }
+ return;
+ }
+ if (actor->GetStat(IE_LOCKPICKING)<LockDifficulty) {
+ displaymsg->DisplayConstantStringName(STR_LOCKPICK_FAILED, 0xbcefbc, actor);
+ LastPickLockFailed = actor->GetGlobalID();
+ return;
+ }
+ SetContainerLocked(false);
+ displaymsg->DisplayConstantStringName(STR_LOCKPICK_DONE, 0xd7d7be, actor);
+ LastUnlocked = actor->GetGlobalID();
+ ImmediateEvent();
+ int xp = actor->CalculateExperience(XP_LOCKPICK, actor->GetXPLevel(1));
+ Game *game = core->GetGame();
+ game->ShareXP(xp, SX_DIVIDE);
+}
+
+void Container::TryBashLock(Actor *actor)
+{
+ //Get the strength bonus agains lock difficulty
+ int str = actor->GetStat(IE_STR);
+ int strEx = actor->GetStat(IE_STREXTRA);
+ unsigned int bonus = core->GetStrengthBonus(2, str, strEx); //BEND_BARS_LIFT_GATES
+ unsigned int roll = actor->LuckyRoll(1, 10, bonus, 0);
+
+ if(roll < LockDifficulty || LockDifficulty == 100) {
+ displaymsg->DisplayConstantStringName(STR_CONTBASH_FAIL, 0xbcefbc, actor);
+ return;
+ }
+
+ displaymsg->DisplayConstantStringName(STR_CONTBASH_DONE, 0xd7d7be, actor);
+ SetContainerLocked(false);
+ //Is this really useful ?
+ LastUnlocked = actor->GetGlobalID();
+ ImmediateEvent();
+}
+
+void Container::DebugDump() const
+{
+ printf( "Debugdump of Container %s\n", GetScriptName() );
+ printf( "Container Global ID: %d\n", GetGlobalID());
+ printf( "Position: %d.%d\n", Pos.x, Pos.y);
+ printf( "Type: %d, Locked: %s, LockDifficulty: %d\n", Type, YESNO(Flags&CONT_LOCKED), LockDifficulty );
+ printf( "Flags: %d, Trapped: %s, Detected: %d\n", Flags, YESNO(Trapped), TrapDetected );
+ printf( "Trap detection: %d%%, Trap removal: %d%%\n", TrapDetectionDiff,
+ TrapRemovalDiff );
+ const char *name = "NONE";
+ if (Scripts[0]) {
+ name = Scripts[0]->GetName();
+ }
+ printf( "Script: %s, Key: %s\n", name, KeyResRef );
+ // FIXME: const_cast
+ const_cast<Inventory&>(inventory).dump();
+}
+
+bool Container::TryUnlock(Actor *actor) {
+ if (!(Flags&CONT_LOCKED)) return true;
+
+ return Highlightable::TryUnlock(actor, false);
+}
+
diff --git a/gemrb/core/Scriptable/Container.h b/gemrb/core/Scriptable/Container.h
new file mode 100644
index 0000000..39fd7bb
--- /dev/null
+++ b/gemrb/core/Scriptable/Container.h
@@ -0,0 +1,70 @@
+/* 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 CONTAINER_H
+#define CONTAINER_H
+
+#include "Scriptable.h"
+
+//container flags
+#define CONT_LOCKED 1
+#define CONT_RESET 8
+#define CONT_DISABLED 32
+
+class GEM_EXPORT Container : public Highlightable {
+public:
+ Container(void);
+ ~Container(void);
+ void SetContainerLocked(bool lock);
+ //turns the container to a pile
+ void DestroyContainer();
+ //removes an item from the container's inventory
+ CREItem *RemoveItem(unsigned int idx, unsigned int count);
+ //adds an item to the container's inventory
+ int AddItem(CREItem *item);
+ //draws the ground icons
+ void DrawPile(bool highlight, Region screen, Color tint);
+ //returns dithering option
+ int WantDither();
+ int IsOpen() const;
+ void TryPickLock(Actor *actor);
+ void TryBashLock(Actor* actor) ;
+ bool TryUnlock(Actor *actor);
+ void DebugDump() const;
+ int TrapResets() const { return Flags & CONT_RESET; }
+private:
+ //updates the ground icons for a pile
+ void RefreshGroundIcons();
+ void FreeGroundIcons();
+ void CreateGroundIconCover();
+public:
+ Point toOpen;
+ ieWord Type;
+ ieDword Flags;
+ ieWord LockDifficulty;
+ Inventory inventory;
+ ieStrRef OpenFail;
+ //these are not saved
+ Sprite2D *groundicons[3];
+ SpriteCover *groundiconcover;
+ //keyresref is stored in Highlightable
+};
+
+#endif
diff --git a/gemrb/core/Scriptable/Door.cpp b/gemrb/core/Scriptable/Door.cpp
new file mode 100644
index 0000000..33df7bd
--- /dev/null
+++ b/gemrb/core/Scriptable/Door.cpp
@@ -0,0 +1,409 @@
+/* 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 "Scriptable/Door.h"
+
+#include "strrefs.h"
+#include "win32def.h"
+
+#include "Audio.h"
+#include "DisplayMessage.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "Item.h"
+#include "Map.h"
+#include "Projectile.h"
+#include "Spell.h"
+#include "SpriteCover.h"
+#include "TileMap.h"
+#include "Video.h"
+#include "GameScript/GSUtils.h"
+#include "GUI/GameControl.h"
+#include "Scriptable/InfoPoint.h"
+
+#include <cassert>
+#include <cmath>
+
+#define YESNO(x) ( (x)?"Yes":"No")
+
+Door::Door(TileOverlay* Overlay)
+ : Highlightable( ST_DOOR )
+{
+ tiles = NULL;
+ tilecount = 0;
+ Flags = 0;
+ open = NULL;
+ closed = NULL;
+ open_ib = NULL;
+ oibcount = 0;
+ closed_ib = NULL;
+ cibcount = 0;
+ OpenSound[0] = 0;
+ CloseSound[0] = 0;
+ LockSound[0] = 0;
+ UnLockSound[0] = 0;
+ overlay = Overlay;
+ LinkedInfo[0] = 0;
+ OpenStrRef = (ieDword) -1;
+}
+
+Door::~Door(void)
+{
+ if (Flags&DOOR_OPEN) {
+ if (closed) {
+ delete( closed );
+ }
+ } else {
+ if (open) {
+ delete( open );
+ }
+ }
+ if (tiles) {
+ free( tiles );
+ }
+ if (open_ib) {
+ free( open_ib );
+ }
+ if (closed_ib) {
+ free( closed_ib );
+ }
+}
+
+void Door::ImpedeBlocks(int count, Point *points, unsigned char value)
+{
+ for(int i = 0;i<count;i++) {
+ unsigned char tmp = area->GetInternalSearchMap(points[i].x, points[i].y) & PATH_MAP_NOTDOOR;
+ area->SetInternalSearchMap(points[i].x, points[i].y, tmp|value);
+ }
+}
+
+void Door::UpdateDoor()
+{
+ if (Flags&DOOR_OPEN) {
+ outline = open;
+ } else {
+ outline = closed;
+ }
+ // update the Scriptable position
+ Pos.x = outline->BBox.x + outline->BBox.w/2;
+ Pos.y = outline->BBox.y + outline->BBox.h/2;
+
+ unsigned char oval, cval;
+ oval = PATH_MAP_IMPASSABLE;
+ if (Flags & DOOR_TRANSPARENT) {
+ cval = PATH_MAP_DOOR_IMPASSABLE;
+ }
+ else {
+ //both door flags are needed here, one for transparency the other
+ //is for passability
+ cval = PATH_MAP_DOOR_OPAQUE|PATH_MAP_DOOR_IMPASSABLE;
+ }
+ if (Flags &DOOR_OPEN) {
+ ImpedeBlocks(cibcount, closed_ib, 0);
+ ImpedeBlocks(oibcount, open_ib, cval);
+ }
+ else {
+ ImpedeBlocks(oibcount, open_ib, 0);
+ ImpedeBlocks(cibcount, closed_ib, cval);
+ }
+
+ InfoPoint *ip = area->TMap->GetInfoPoint(LinkedInfo);
+ if (ip) {
+ if (Flags&DOOR_OPEN) ip->Flags&=~INFO_DOOR;
+ else ip->Flags|=INFO_DOOR;
+ }
+}
+
+void Door::ToggleTiles(int State, int playsound)
+{
+ int i;
+ int state;
+
+ if (State) {
+ state = !closedIndex;
+ if (playsound && ( OpenSound[0] != '\0' ))
+ core->GetAudioDrv()->Play( OpenSound );
+ } else {
+ state = closedIndex;
+ if (playsound && ( CloseSound[0] != '\0' ))
+ core->GetAudioDrv()->Play( CloseSound );
+ }
+ for (i = 0; i < tilecount; i++) {
+ overlay->tiles[tiles[i]]->tileIndex = (ieByte) state;
+ }
+
+ //set door_open as state
+ Flags = (Flags & ~DOOR_OPEN) | (State == !core->HasFeature(GF_REVERSE_DOOR) );
+}
+
+//this is the short name (not the scripting name)
+void Door::SetName(const char* name)
+{
+ strnlwrcpy( ID, name, 8 );
+}
+
+void Door::SetTiles(unsigned short* Tiles, int cnt)
+{
+ if (tiles) {
+ free( tiles );
+ }
+ tiles = Tiles;
+ tilecount = cnt;
+}
+
+void Door::SetDoorLocked(int Locked, int playsound)
+{
+ if (Locked) {
+ if (Flags & DOOR_LOCKED) return;
+ Flags|=DOOR_LOCKED;
+ if (playsound && ( LockSound[0] != '\0' ))
+ core->GetAudioDrv()->Play( LockSound );
+ }
+ else {
+ if (!(Flags & DOOR_LOCKED)) return;
+ Flags&=~DOOR_LOCKED;
+ if (playsound && ( UnLockSound[0] != '\0' ))
+ core->GetAudioDrv()->Play( UnLockSound );
+ }
+}
+
+int Door::IsOpen() const
+{
+ int ret = core->HasFeature(GF_REVERSE_DOOR);
+ if (Flags&DOOR_OPEN) {
+ ret = !ret;
+ }
+ return ret;
+}
+
+//also mark actors to fix position
+bool Door::BlockedOpen(int Open, int ForceOpen)
+{
+ bool blocked;
+ int count;
+ Point *points;
+
+ blocked = false;
+ if (Open) {
+ count = oibcount;
+ points = open_ib;
+ } else {
+ count = cibcount;
+ points = closed_ib;
+ }
+ //getting all impeded actors flagged for jump
+ Region rgn;
+ rgn.w = 16;
+ rgn.h = 12;
+ for(int i = 0;i<count;i++) {
+ Actor** ab;
+ rgn.x = points[i].x*16;
+ rgn.y = points[i].y*12;
+ unsigned char tmp = area->GetInternalSearchMap(points[i].x, points[i].y) & PATH_MAP_ACTOR;
+ if (tmp) {
+ int ac = area->GetActorInRect(ab, rgn, false);
+ while(ac--) {
+ if (ab[ac]->GetBase(IE_DONOTJUMP)) {
+ continue;
+ }
+ ab[ac]->SetBase(IE_DONOTJUMP, DNJ_JUMP);
+ blocked = true;
+ }
+ if (ab) {
+ free(ab);
+ }
+ }
+ }
+
+ if ((Flags&DOOR_SLIDE) || ForceOpen) {
+ return false;
+ }
+ return blocked;
+}
+
+void Door::SetDoorOpen(int Open, int playsound, ieDword ID)
+{
+ if (playsound) {
+ //the door cannot be blocked when opening,
+ //but the actors will be pushed
+ //BlockedOpen will mark actors to be pushed
+ if (BlockedOpen(Open,0) && !Open) {
+ //clear up the blocking actors
+ area->JumpActors(false);
+ return;
+ }
+ area->JumpActors(true);
+ }
+ if (Open) {
+ LastEntered = ID; //used as lastOpener
+
+ // in PS:T, opening a door does not unlock it
+ if (!core->HasFeature(GF_REVERSE_DOOR)) {
+ SetDoorLocked(false,playsound);
+ }
+ } else {
+ LastTriggerObject = LastTrigger = ID; //used as lastCloser
+ }
+ ToggleTiles(Open, playsound);
+ //synchronising other data with the door state
+ UpdateDoor();
+ area->ActivateWallgroups(open_wg_index, open_wg_count, Flags&DOOR_OPEN);
+ area->ActivateWallgroups(closed_wg_index, closed_wg_count, !(Flags&DOOR_OPEN));
+ core->SetEventFlag(EF_TARGETMODE);
+}
+
+bool Door::TryUnlock(Actor *actor) {
+ if (!(Flags&DOOR_LOCKED)) return true;
+
+ // don't remove key in PS:T!
+ bool removekey = !core->HasFeature(GF_REVERSE_DOOR) && Flags&DOOR_KEY;
+ return Highlightable::TryUnlock(actor, removekey);
+}
+
+void Door::TryDetectSecret(int skill)
+{
+ if (Type != ST_DOOR) return;
+ if (Visible()) return;
+ if (skill > (signed)DiscoveryDiff) {
+ Flags |= DOOR_FOUND;
+ core->PlaySound(DS_FOUNDSECRET);
+ }
+}
+
+// return true if the door isn't secret or if it is, but was already discovered
+bool Door::Visible()
+{
+ return (!(Flags & DOOR_SECRET) || (Flags & DOOR_FOUND));
+}
+
+void Door::SetPolygon(bool Open, Gem_Polygon* poly)
+{
+ if (Open) {
+ if (open)
+ delete( open );
+ open = poly;
+ } else {
+ if (closed)
+ delete( closed );
+ closed = poly;
+ }
+}
+
+void Door::SetNewOverlay(TileOverlay *Overlay) {
+ overlay = Overlay;
+ ToggleTiles(IsOpen(), false);
+}
+
+void Highlightable::SetTrapDetected(int x)
+{
+ if(x == TrapDetected)
+ return;
+ TrapDetected = x;
+ if(TrapDetected) {
+ core->Autopause(AP_TRAP);
+ }
+}
+
+void Highlightable::TryDisarm(Actor *actor)
+{
+ if (!Trapped || !TrapDetected) return;
+
+ LastTriggerObject = LastTrigger = actor->GetGlobalID();
+ int skill = actor->GetStat(IE_TRAPS);
+
+ if (skill/2+core->Roll(1,skill/2,0)>TrapRemovalDiff) {
+ LastDisarmed = actor->GetGlobalID();
+ //trap removed
+ Trapped = 0;
+ displaymsg->DisplayConstantStringName(STR_DISARM_DONE, 0xd7d7be, actor);
+ int xp = actor->CalculateExperience(XP_DISARM, actor->GetXPLevel(1));
+ Game *game = core->GetGame();
+ game->ShareXP(xp, SX_DIVIDE);
+ } else {
+ displaymsg->DisplayConstantStringName(STR_DISARM_FAIL, 0xd7d7be, actor);
+ TriggerTrap(skill, LastTrigger);
+ }
+ ImmediateEvent();
+}
+
+void Door::TryPickLock(Actor *actor)
+{
+ if (LockDifficulty == 100) {
+ if (OpenStrRef != (ieDword)-1) {
+ displaymsg->DisplayStringName(OpenStrRef, 0xbcefbc, actor, IE_STR_SOUND|IE_STR_SPEECH);
+ } else {
+ displaymsg->DisplayConstantStringName(STR_DOOR_NOPICK, 0xbcefbc, actor);
+ }
+ return;
+ }
+ if (actor->GetStat(IE_LOCKPICKING)<LockDifficulty) {
+ displaymsg->DisplayConstantStringName(STR_LOCKPICK_FAILED, 0xbcefbc, actor);
+ LastPickLockFailed = actor->GetGlobalID();
+ return;
+ }
+ SetDoorLocked( false, true);
+ displaymsg->DisplayConstantStringName(STR_LOCKPICK_DONE, 0xd7d7be, actor);
+ LastUnlocked = actor->GetGlobalID();
+ ImmediateEvent();
+ int xp = actor->CalculateExperience(XP_LOCKPICK, actor->GetXPLevel(1));
+ Game *game = core->GetGame();
+ game->ShareXP(xp, SX_DIVIDE);
+}
+
+void Door::TryBashLock(Actor *actor)
+{
+ //Get the strength bonus agains lock difficulty
+ int str = actor->GetStat(IE_STR);
+ int strEx = actor->GetStat(IE_STREXTRA);
+ unsigned int bonus = core->GetStrengthBonus(2, str, strEx); //BEND_BARS_LIFT_GATES
+ unsigned int roll = actor->LuckyRoll(1, 10, bonus, 0);
+
+ if(roll < LockDifficulty || LockDifficulty == 100) {
+ displaymsg->DisplayConstantStringName(STR_DOORBASH_FAIL, 0xbcefbc, actor);
+ return;
+ }
+
+ displaymsg->DisplayConstantStringName(STR_DOORBASH_DONE, 0xd7d7be, actor);
+ SetDoorLocked(false, true);
+ //Is this really useful ?
+ LastUnlocked = actor->GetGlobalID();
+ ImmediateEvent();
+}
+
+void Door::DebugDump() const
+{
+ printf( "Debugdump of Door %s:\n", GetScriptName() );
+ printf( "Door Global ID: %d\n", GetGlobalID());
+ printf( "Position: %d.%d\n", Pos.x, Pos.y);
+ printf( "Door Open: %s\n", YESNO(IsOpen()));
+ printf( "Door Locked: %s\n", YESNO(Flags&DOOR_LOCKED));
+ printf( "Door Trapped: %s\n", YESNO(Trapped));
+ if (Trapped) {
+ printf( "Trap Permanent: %s Detectable: %s\n", YESNO(Flags&DOOR_RESET), YESNO(Flags&DOOR_DETECTABLE) );
+ }
+ printf( "Secret door: %s (Found: %s)\n", YESNO(Flags&DOOR_SECRET),YESNO(Flags&DOOR_FOUND));
+ const char *Key = GetKey();
+ const char *name = "NONE";
+ if (Scripts[0]) {
+ name = Scripts[0]->GetName();
+ }
+ printf( "Script: %s, Key (%s) removed: %s, Dialog: %s\n", name, Key?Key:"NONE", YESNO(Flags&DOOR_KEY), Dialog );
+}
+
diff --git a/gemrb/core/Scriptable/Door.h b/gemrb/core/Scriptable/Door.h
new file mode 100644
index 0000000..5d6e4db
--- /dev/null
+++ b/gemrb/core/Scriptable/Door.h
@@ -0,0 +1,98 @@
+/* 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 DOOR_H
+#define DOOR_H
+
+#include "Scriptable.h"
+
+//door flags
+#define DOOR_OPEN 1
+#define DOOR_LOCKED 2
+#define DOOR_RESET 4 //reset trap
+#define DOOR_DETECTABLE 8 //trap detectable
+#define DOOR_16 16 //unknown
+#define DOOR_32 32 //unknown
+#define DOOR_LINKED 64 //info trigger linked to this door
+#define DOOR_SECRET 128 //door is secret
+#define DOOR_FOUND 256 //secret door found
+#define DOOR_TRANSPARENT 512 //obscures vision
+#define DOOR_KEY 1024 //key removed when used
+#define DOOR_SLIDE 2048 //impeded blocks ignored
+
+class GEM_EXPORT Door : public Highlightable {
+public:
+ Door(TileOverlay* Overlay);
+ ~Door(void);
+public:
+ ieVariable LinkedInfo;
+ ieResRef ID; //WED ID
+ TileOverlay* overlay;
+ unsigned short* tiles;
+ int tilecount;
+ ieDword Flags;
+ int closedIndex;
+ //trigger areas
+ Gem_Polygon* open;
+ Gem_Polygon* closed;
+ //impeded blocks
+ Point* open_ib; //impeded blocks stored in a Point array
+ int oibcount;
+ Point* closed_ib;
+ int cibcount;
+ //wallgroup covers
+ unsigned int open_wg_index;
+ unsigned int open_wg_count;
+ unsigned int closed_wg_index;
+ unsigned int closed_wg_count;
+ Point toOpen[2];
+ ieResRef OpenSound;
+ ieResRef CloseSound;
+ ieResRef LockSound;
+ ieResRef UnLockSound;
+ ieDword DiscoveryDiff;
+ ieDword LockDifficulty; //this is a dword?
+ ieStrRef OpenStrRef;
+ ieStrRef NameStrRef;
+ ieDword Unknown54; //unused in tob
+private:
+ void SetWallgroups(int count, int value);
+ void ImpedeBlocks(int count, Point *points, unsigned char value);
+ void UpdateDoor();
+ bool BlockedOpen(int Open, int ForceOpen);
+public:
+ void ToggleTiles(int State, int playsound = false);
+ void SetName(const char* Name); // sets door ID
+ void SetTiles(unsigned short* Tiles, int count);
+ void SetDoorLocked(int Locked, int playsound);
+ void SetDoorOpen(int Open, int playsound, ieDword ID);
+ void SetPolygon(bool Open, Gem_Polygon* poly);
+ int IsOpen() const;
+ void TryPickLock(Actor *actor);
+ void TryBashLock(Actor* actor) ;
+ bool TryUnlock(Actor *actor);
+ void TryDetectSecret(int skill);
+ bool Visible();
+ void DebugDump() const;
+ int TrapResets() const { return Flags & DOOR_RESET; }
+ void SetNewOverlay(TileOverlay *Overlay);
+};
+
+#endif
diff --git a/gemrb/core/Scriptable/InfoPoint.cpp b/gemrb/core/Scriptable/InfoPoint.cpp
new file mode 100644
index 0000000..0199bc7
--- /dev/null
+++ b/gemrb/core/Scriptable/InfoPoint.cpp
@@ -0,0 +1,272 @@
+/* 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 "Scriptable/InfoPoint.h"
+
+#include "strrefs.h"
+#include "win32def.h"
+
+#include "Audio.h"
+#include "DisplayMessage.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "Item.h"
+#include "Map.h"
+#include "Projectile.h"
+#include "Spell.h"
+#include "SpriteCover.h"
+#include "TileMap.h"
+#include "Video.h"
+#include "GameScript/GSUtils.h"
+#include "GUI/GameControl.h"
+
+#include <cassert>
+#include <cmath>
+
+#define YESNO(x) ( (x)?"Yes":"No")
+
+InfoPoint::InfoPoint(void)
+ : Highlightable( ST_TRIGGER )
+{
+ Destination[0] = 0;
+ EntranceName[0] = 0;
+ Flags = 0;
+ TrapDetectionDiff = 0;
+ TrapRemovalDiff = 0;
+ TrapDetected = 0;
+ TrapLaunch.empty();
+ EnterWav[0] = 0;
+}
+
+InfoPoint::~InfoPoint(void)
+{
+}
+
+void InfoPoint::SetEnter(const char *resref)
+{
+ if (gamedata->Exists(resref, IE_WAV_CLASS_ID) ) {
+ strnuprcpy(EnterWav, resref, 8);
+ }
+}
+
+//checks if the actor may use this travel trigger
+//bit 1 : can use
+//bit 2 : whole team
+int InfoPoint::CheckTravel(Actor *actor)
+{
+ if (Flags&TRAP_DEACTIVATED) return CT_CANTMOVE;
+ if (!actor->InParty && (Flags&TRAVEL_NONPC) ) return CT_CANTMOVE;
+ if (actor->InParty && (Flags&TRAVEL_PARTY) ) {
+ if (core->HasFeature(GF_TEAM_MOVEMENT) || core->GetGame()->EveryoneNearPoint(actor->GetCurrentArea(), actor->Pos, ENP_CANMOVE) ) {
+ return CT_WHOLE;
+ }
+ return CT_GO_CLOSER;
+ }
+ if(actor->IsSelected() ) {
+ if(core->GetGame()->EveryoneNearPoint(actor->GetCurrentArea(), actor->Pos, ENP_CANMOVE|ENP_ONLYSELECT) ) {
+ return CT_MOVE_SELECTED;
+ }
+ return CT_SELECTED;
+ }
+ return CT_ACTIVE;
+}
+
+//detect this trap, using a skill, skill could be set to 256 for 'sure'
+//skill is the all around modified trap detection skill
+//a trapdetectiondifficulty of 100 means impossible detection short of a spell
+void Highlightable::DetectTrap(int skill)
+{
+ if (!CanDetectTrap()) return;
+ if (!Scripts[0]) return;
+ if ((skill>=100) && (skill!=256) ) skill = 100;
+ if (skill/2+core->Roll(1,skill/2,0)>TrapDetectionDiff) {
+ SetTrapDetected(1); //probably could be set to the player #?
+ }
+}
+
+bool Highlightable::PossibleToSeeTrap() const
+{
+ return CanDetectTrap();
+}
+
+bool InfoPoint::PossibleToSeeTrap() const
+{
+ // Only detectable trap-type infopoints.
+ return (CanDetectTrap() && (Type == ST_PROXIMITY) );
+}
+
+bool InfoPoint::CanDetectTrap() const
+{
+ // Traps can be detected on all types of infopoint, as long
+ // as the trap is detectable and isn't deactivated.
+ return ((Flags&TRAP_DETECTABLE) && !(Flags&TRAP_DEACTIVATED));
+}
+
+// returns true if the infopoint is a PS:T portal
+// GF_REVERSE_DOOR is the closest game feature (exists only in PST, and about area objects)
+bool InfoPoint::IsPortal() const
+{
+ if (Type!=ST_TRAVEL) return false;
+ if (Cursor != IE_CURSOR_PORTAL) return false;
+ return core->HasFeature(GF_REVERSE_DOOR);
+}
+
+//trap that is visible on screen (marked by red)
+//if TrapDetected is a bitflag, we could show traps selectively for
+//players, really nice for multiplayer
+bool Highlightable::VisibleTrap(int see_all) const
+{
+ if (!Trapped) return false;
+ if (!PossibleToSeeTrap()) return false;
+ if (!Scripts[0]) return false;
+ if (see_all) return true;
+ if (TrapDetected ) return true;
+ return false;
+}
+
+//trap that will fire now
+bool Highlightable::TriggerTrap(int /*skill*/, ieDword ID)
+{
+ if (!Trapped) {
+ return false;
+ }
+ //actually this could be script name[0]
+ if (!Scripts[0] && !EnterWav[0]) {
+ return false;
+ }
+ LastTriggerObject = LastTrigger = LastEntered = ID;
+ ImmediateEvent();
+ if (!TrapResets()) {
+ Trapped = false;
+ }
+ return true;
+}
+
+//trap that will fire now
+bool InfoPoint::TriggerTrap(int skill, ieDword ID)
+{
+ if (Type!=ST_PROXIMITY) {
+ return true;
+ }
+ if (Flags&TRAP_DEACTIVATED) {
+ return false;
+ }
+ if (!Trapped) {
+ // we have to set Entered somewhere, here seems best..
+ LastEntered = ID;
+ return true;
+ } else if (Highlightable::TriggerTrap(skill, ID)) {
+ if (!Trapped) {
+ Flags|=TRAP_DEACTIVATED;
+ }
+ // ok, so this is a pain. Entered() trigger checks Trapped,
+ // so it needs to be kept set. how to do this right?
+ Trapped = true;
+ return true;
+ }
+ return false;
+}
+
+bool InfoPoint::Entered(Actor *actor)
+{
+ if (outline->PointIn( actor->Pos ) ) {
+ goto check;
+ }
+ // why is this here? actors which aren't *in* a trap get IF_INTRAP
+ // repeatedly unset, so this triggers again and again and again.
+ // i disabled it for ST_PROXIMITY for now..
+ /*if (Type != ST_PROXIMITY && (PersonalDistance(Pos, actor)<MAX_OPERATING_DISTANCE) ) {
+ goto check;
+ }*/
+ // this method is better (fuzzie, 2009) and also works for the iwd ar6002 northeast exit
+ if (Type == ST_TRAVEL && PersonalDistance(TrapLaunch, actor)<MAX_OPERATING_DISTANCE) {
+ goto check;
+ }
+ // fuzzie can't escape pst's ar1405 without this one, maybe we should really be checking
+ // for distance from the outline for travel regions instead?
+ if (Type == ST_TRAVEL && PersonalDistance(TalkPos, actor)<MAX_OPERATING_DISTANCE) {
+ goto check;
+ }
+ if (Flags&TRAP_USEPOINT) {
+ if (PersonalDistance(UsePoint, actor)<MAX_OPERATING_DISTANCE) {
+ goto check;
+ }
+ }
+ return false;
+check:
+ if (Type==ST_TRAVEL) {
+ return true;
+ }
+
+ if (actor->GetInternalFlag()&IF_INTRAP) {
+ return false;
+ }
+
+ if (actor->InParty || (Flags&TRAP_NPC) ) {
+ //no need to avoid a travel trigger
+
+ //skill?
+ if (TriggerTrap(0, actor->GetGlobalID()) ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void InfoPoint::DebugDump() const
+{
+ switch (Type) {
+ case ST_TRIGGER:
+ printf( "Debugdump of InfoPoint Region %s:\n", GetScriptName() );
+ break;
+ case ST_PROXIMITY:
+ printf( "Debugdump of Trap Region %s:\n", GetScriptName() );
+ break;
+ case ST_TRAVEL:
+ printf( "Debugdump of Travel Region %s:\n", GetScriptName() );
+ break;
+ default:
+ printf( "Debugdump of Unsupported Region %s:\n", GetScriptName() );
+ break;
+ }
+ printf( "Region Global ID: %d\n", GetGlobalID());
+ printf( "Position: %d.%d\n", Pos.x, Pos.y);
+ switch(Type) {
+ case ST_TRAVEL:
+ printf( "Destination Area: %s Entrance: %s\n", Destination, EntranceName);
+ break;
+ case ST_PROXIMITY:
+ printf( "TrapDetected: %d, Trapped: %s\n", TrapDetected, YESNO(Trapped));
+ printf( "Trap detection: %d%%, Trap removal: %d%%\n", TrapDetectionDiff,
+ TrapRemovalDiff );
+ break;
+ case ST_TRIGGER:
+ printf ( "InfoString: %s\n", overHeadText );
+ break;
+ default:;
+ }
+ const char *name = "NONE";
+ if (Scripts[0]) {
+ name = Scripts[0]->GetName();
+ }
+ printf( "Script: %s, Key: %s, Dialog: %s\n", name, KeyResRef, Dialog );
+ printf( "Active: %s\n", YESNO(InternalFlags&IF_ACTIVE));
+}
+
diff --git a/gemrb/core/Scriptable/InfoPoint.h b/gemrb/core/Scriptable/InfoPoint.h
new file mode 100644
index 0000000..0eb6282
--- /dev/null
+++ b/gemrb/core/Scriptable/InfoPoint.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 INFOPOINT_H
+#define INFOPOINT_H
+
+#include "Scriptable.h"
+
+//trigger flags
+#define TRAP_INVISIBLE 1
+#define TRAP_RESET 2
+#define TRAVEL_PARTY 4
+#define TRAP_DETECTABLE 8
+//#define TRAP_16 16
+#define TRAP_LOWMEM 32 //special treatment when low on memory ?
+#define TRAP_NPC 64
+//#define TRAP_128 128
+#define TRAP_DEACTIVATED 256
+#define TRAVEL_NONPC 512
+#define TRAP_USEPOINT 1024 //override usage point of travel regions (used for sound in PST traps)
+#define INFO_DOOR 2048 //info trigger blocked by door
+
+class GEM_EXPORT InfoPoint : public Highlightable {
+public:
+ InfoPoint(void);
+ ~InfoPoint(void);
+ //returns true if trap has been triggered, tumble skill???
+ void SetEnter(const char *resref);
+ bool TriggerTrap(int skill, ieDword ID);
+ //call this to check if an actor entered the trigger zone
+ bool Entered(Actor *actor);
+ //checks if the actor may use this travel trigger
+ int CheckTravel(Actor *actor);
+ void DebugDump() const;
+ int TrapResets() const { return Flags & TRAP_RESET; }
+ bool CanDetectTrap() const;
+ bool PossibleToSeeTrap() const;
+ bool IsPortal() const;
+
+public:
+ ieResRef Destination;
+ ieVariable EntranceName;
+ ieDword Flags;
+ //overheadtext contains the string, but we have to save this
+ ieStrRef StrRef;
+ Point UsePoint;
+ Point TalkPos;
+
+};
+
+#endif
diff --git a/gemrb/core/Scriptable/PCStatStruct.cpp b/gemrb/core/Scriptable/PCStatStruct.cpp
new file mode 100644
index 0000000..32338e5
--- /dev/null
+++ b/gemrb/core/Scriptable/PCStatStruct.cpp
@@ -0,0 +1,185 @@
+/* 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 "Scriptable/PCStatStruct.h"
+
+#include "globals.h" //for abort()
+#include "win32def.h"
+
+#include <cstring>
+
+PCStatsStruct::PCStatsStruct()
+{
+ BestKilledName = 0xffffffff;
+ BestKilledXP = 0;
+ AwayTime = 0;
+ JoinDate = 0;
+ unknown10 = 0;
+ KillsChapterXP = 0;
+ KillsChapterCount = 0;
+ KillsTotalXP = 0;
+ KillsTotalCount = 0;
+ memset( FavouriteSpells, 0, sizeof(FavouriteSpells) );
+ memset( FavouriteSpellsCount, 0, sizeof(FavouriteSpellsCount) );
+ memset( FavouriteWeapons, 0, sizeof(FavouriteWeapons) );
+ memset( FavouriteWeaponsCount, 0, sizeof(FavouriteWeaponsCount) );
+ SoundSet[0]=0;
+ SoundFolder[0]=0;
+ QSlots[0]=0xff;
+ memset( QuickSpells, 0, sizeof(QuickSpells) );
+ memset( QuickSpellClass, 0xff, sizeof(QuickSpellClass) );
+ memset( QuickItemSlots, -1, sizeof(QuickItemSlots) );
+ memset( QuickItemHeaders, -1, sizeof(QuickItemHeaders) );
+ memset( QuickWeaponSlots, -1, sizeof(QuickWeaponSlots) );
+ memset( QuickWeaponHeaders, -1, sizeof(QuickWeaponHeaders) );
+ memset( PortraitIcons, -1, sizeof(PortraitIcons) );
+ memset( PreviousPortraitIcons, -1, sizeof(PreviousPortraitIcons) );
+ memset( PortraitIconString, 0, sizeof(PortraitIconString) );
+ LastLeft = 0;
+ LastJoined = 0;
+}
+
+void PCStatsStruct::IncrementChapter()
+{
+ KillsChapterXP = 0;
+ KillsChapterCount = 0;
+}
+
+void PCStatsStruct::NotifyKill(ieDword xp, ieStrRef name)
+{
+ if (BestKilledXP<=xp) {
+ BestKilledXP = xp;
+ BestKilledName = name;
+ }
+
+ KillsTotalXP += xp;
+ KillsChapterXP += xp;
+ KillsTotalCount ++;
+ KillsChapterCount ++;
+}
+
+//init quick weapon/item slots
+void PCStatsStruct::SetQuickItemSlot(int idx, int slot, int headerindex)
+{
+ if (slot>=0) {
+ QuickItemSlots[idx]=(ieWord) slot;
+ }
+ QuickItemHeaders[idx]=(ieWord) headerindex;
+}
+
+void PCStatsStruct::InitQuickSlot(unsigned int which, int slot, int headerindex)
+{
+ if (!which) {
+ int i;
+
+ for(i=0;i<MAX_QUICKITEMSLOT;i++) {
+ if (slot==QuickItemSlots[i]) {
+ QuickItemHeaders[i]=headerindex;
+ return;
+ }
+ }
+
+ for(i=0;i<MAX_QUICKWEAPONSLOT;i++) {
+ if (slot==QuickWeaponSlots[i]) {
+ QuickWeaponHeaders[i]=headerindex;
+ return;
+ }
+ }
+ return;
+ }
+
+ //precalculate field values. Empty slots will get their ability header
+ //initialized to the invalid value of 0xffff to stay binary compatible
+ //with original
+ int slot2, header;
+
+ if (slot==0xffff) {
+ slot2 = 0xffff;
+ header = 0xffff;
+ }
+ else {
+ slot2 = slot+1;
+ header = 0;
+ }
+ switch(which) {
+ case ACT_QSLOT1: SetQuickItemSlot(0,slot,headerindex); break;
+ case ACT_QSLOT2: SetQuickItemSlot(1,slot,headerindex); break;
+ case ACT_QSLOT3: SetQuickItemSlot(2,slot,headerindex); break;
+ case ACT_QSLOT4: SetQuickItemSlot(3,slot,headerindex); break;
+ case ACT_QSLOT5: SetQuickItemSlot(4,slot,headerindex); break;
+ case ACT_WEAPON1:
+ QuickWeaponSlots[0]=slot;
+ QuickWeaponHeaders[0]=header;
+ QuickWeaponSlots[4]=slot2;
+ QuickWeaponHeaders[4]=header;
+ break;
+ case ACT_WEAPON2:
+ QuickWeaponSlots[1]=slot;
+ QuickWeaponHeaders[1]=header;
+ QuickWeaponSlots[5]=slot2;
+ QuickWeaponHeaders[5]=header;
+ break;
+ case ACT_WEAPON3:
+ QuickWeaponSlots[2]=slot;
+ QuickWeaponHeaders[2]=header;
+ QuickWeaponSlots[6]=slot2;
+ QuickWeaponHeaders[6]=header;
+ break;
+ case ACT_WEAPON4:
+ QuickWeaponSlots[3]=slot;
+ QuickWeaponHeaders[3]=header;
+ QuickWeaponSlots[7]=slot2;
+ QuickWeaponHeaders[7]=header;
+ break;
+ }
+}
+
+//returns both the inventory slot and the header index associated to a quickslot
+void PCStatsStruct::GetSlotAndIndex(unsigned int which, ieWord &slot, ieWord &headerindex)
+{
+ int idx;
+
+ switch(which) {
+ case ACT_QSLOT1: idx = 0; break;
+ case ACT_QSLOT2: idx = 1; break;
+ case ACT_QSLOT3: idx = 2; break;
+ case ACT_QSLOT4: idx = 3; break;
+ case ACT_QSLOT5: idx = 4; break;
+ default: abort();
+ }
+ slot=QuickItemSlots[idx];
+ headerindex=QuickItemHeaders[idx];
+}
+
+//return the item extended header assigned to an inventory slot (the ability to use)
+//only quickslots have this assignment, equipment items got all abilities available
+int PCStatsStruct::GetHeaderForSlot(int slot)
+{
+ int i;
+
+ for(i=0;i<MAX_QUICKITEMSLOT;i++) {
+ if(QuickItemSlots[i]==slot) return (ieWordSigned) QuickItemHeaders[i];
+ }
+
+ for(i=0;i<MAX_QUICKWEAPONSLOT;i++) {
+ if(QuickWeaponSlots[i]==slot) return (ieWordSigned) QuickWeaponHeaders[i];
+ }
+ return -1;
+}
diff --git a/gemrb/core/Scriptable/PCStatStruct.h b/gemrb/core/Scriptable/PCStatStruct.h
new file mode 100644
index 0000000..f081f58
--- /dev/null
+++ b/gemrb/core/Scriptable/PCStatStruct.h
@@ -0,0 +1,117 @@
+/* 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 PCSTATSTRUCT_H
+#define PCSTATSTRUCT_H
+
+#include "exports.h"
+#include "ie_types.h"
+
+#define MAX_QUICKITEMSLOT 5 //pst has 5
+#define MAX_QUICKWEAPONSLOT 8 //iwd2 has 4x2
+#define MAX_QSLOTS 9 //iwd2 has 9
+#define MAX_PORTRAIT_ICONS 12
+#define MAX_FAVOURITES 4
+#define SOUNDFOLDERSIZE 32+1
+#define MAX_INTERACT 24
+
+//action buttons
+//the order of these buttons are based on opcode #144
+#define ACT_NONE 100 //iwd2's maximum is 99
+#define ACT_STEALTH 0
+#define ACT_THIEVING 1
+#define ACT_CAST 2
+#define ACT_QSPELL1 3
+#define ACT_QSPELL2 4
+#define ACT_QSPELL3 5
+#define ACT_TURN 6
+#define ACT_TALK 7
+#define ACT_USE 8
+#define ACT_QSLOT1 9
+#define ACT_QSLOT4 10 //this seems to be intentionally so
+#define ACT_QSLOT2 11
+#define ACT_QSLOT3 12
+#define ACT_QSLOT5 31 //this is intentional too
+#define ACT_INNATE 13
+#define ACT_DEFEND 14 //these are gemrb specific
+#define ACT_ATTACK 15
+#define ACT_WEAPON1 16
+#define ACT_WEAPON2 17
+#define ACT_WEAPON3 18
+#define ACT_WEAPON4 19
+#define ACT_BARDSONG 20
+#define ACT_STOP 21
+#define ACT_SEARCH 22
+#define ACT_SHAPE 23
+#define ACT_TAMING 24
+#define ACT_SKILLS 25
+#define ACT_WILDERNESS 26
+//gui navigation (scrolling button rows left or right)
+#define ACT_LEFT 32
+#define ACT_RIGHT 33
+
+#define MAX_ACT_COUNT 34 //update this
+
+#define GUIBT_COUNT (MAX_QSLOTS + 3)
+
+class GEM_EXPORT PCStatsStruct {
+public:
+ ieStrRef BestKilledName;
+ ieDword BestKilledXP;
+ ieDword AwayTime;
+ ieDword JoinDate;
+ ieDword unknown10;
+ ieDword KillsChapterXP;
+ ieDword KillsChapterCount;
+ ieDword KillsTotalXP;
+ ieDword KillsTotalCount;
+ ieResRef FavouriteSpells[MAX_FAVOURITES];
+ ieWord FavouriteSpellsCount[MAX_FAVOURITES];
+ ieResRef FavouriteWeapons[MAX_FAVOURITES];
+ ieWord FavouriteWeaponsCount[MAX_FAVOURITES];
+ ieResRef SoundSet;
+ char SoundFolder[SOUNDFOLDERSIZE];
+ ieDword ExtraSettings[16]; //iwd2 - expertise, hamstring, arterial strike, etc
+ ieResRef QuickSpells[MAX_QSLOTS]; //iwd2 uses 9, others use only 3
+ ieWord QuickWeaponSlots[MAX_QUICKWEAPONSLOT]; //iwd2 uses 8, others use only 4
+ ieWord QuickWeaponHeaders[MAX_QUICKWEAPONSLOT];
+ ieWord QuickItemSlots[MAX_QUICKITEMSLOT]; //pst has 5, others use only 3
+ ieWord QuickItemHeaders[MAX_QUICKITEMSLOT];
+ ieByte QSlots[GUIBT_COUNT]; //iwd2 specific
+ ieByte QuickSpellClass[MAX_QSLOTS];
+ ieWord PortraitIcons[MAX_PORTRAIT_ICONS];
+ ieWord PreviousPortraitIcons[MAX_PORTRAIT_ICONS];
+ ieByte PortraitIconString[MAX_PORTRAIT_ICONS+2];
+ ieDword LastLeft; //trigger
+ ieDword LastJoined; //trigger
+ ieDword Interact[MAX_INTERACT];
+ ieWord Happiness;
+private:
+ void SetQuickItemSlot(int x, int slot, int headerindex);
+public:
+ PCStatsStruct();
+ void IncrementChapter();
+ void NotifyKill(ieDword xp, ieStrRef name);
+ void InitQuickSlot(unsigned int which, int slot, int headerindex);
+ void SetSlotIndex(unsigned int which, ieWord headerindex);
+ void GetSlotAndIndex(unsigned int which, ieWord &slot, ieWord &headerindex);
+ int GetHeaderForSlot(int slot);
+};
+#endif
diff --git a/gemrb/core/Scriptable/Scriptable.cpp b/gemrb/core/Scriptable/Scriptable.cpp
new file mode 100644
index 0000000..9efced6
--- /dev/null
+++ b/gemrb/core/Scriptable/Scriptable.cpp
@@ -0,0 +1,1991 @@
+/* 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 "Scriptable/Scriptable.h"
+
+#include "strrefs.h"
+#include "win32def.h"
+
+#include "Audio.h"
+#include "DisplayMessage.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "Item.h"
+#include "Map.h"
+#include "Projectile.h"
+#include "Spell.h"
+#include "SpriteCover.h"
+#include "TileMap.h"
+#include "Video.h"
+#include "GameScript/GSUtils.h"
+#include "GUI/GameControl.h"
+#include "Scriptable/InfoPoint.h"
+
+#include <cassert>
+#include <cmath>
+
+#define YESNO(x) ( (x)?"Yes":"No")
+
+// we start this at a non-zero value to make debugging easier
+static ieDword globalActorCounter = 10000;
+
+/***********************
+ * Scriptable Class *
+ ***********************/
+Scriptable::Scriptable(ScriptableType type)
+{
+ Type = type;
+ for (int i = 0; i < MAX_SCRIPTS; i++) {
+ Scripts[i] = NULL;
+ }
+ overHeadText = NULL;
+ overHeadTextPos.empty();
+ textDisplaying = 0;
+ timeStartDisplaying = 0;
+ scriptName[0] = 0;
+ TriggerID = 0; //used by SendTrigger
+ LastTriggerObject = LastTrigger = 0;
+ LastEntered = 0;
+ LastDisarmed = 0;
+ LastDisarmFailed = 0;
+ LastUnlocked = 0;
+ LastOpenFailed = 0;
+ LastPickLockFailed = 0;
+ DialogName = 0;
+ CurrentAction = NULL;
+ CurrentActionState = 0;
+ CurrentActionTarget = 0;
+ CurrentActionInterruptable = true;
+ UnselectableTimer = 0;
+ startTime = 0; //executing scripts
+ lastRunTime = 0; //evaluating scripts
+ lastDelay = 0;
+ Dialog[0] = 0;
+
+ globalID = ++globalActorCounter;
+
+ interval = ( 1000 / AI_UPDATE_TIME );
+ WaitCounter = 0;
+ if (Type == ST_ACTOR) {
+ InternalFlags = IF_VISIBLE | IF_ONCREATION | IF_USEDSAVE;
+ } else {
+ InternalFlags = IF_ACTIVE | IF_VISIBLE | IF_ONCREATION | IF_NOINT;
+ }
+ area = 0;
+ Pos.x = 0;
+ Pos.y = 0;
+
+ LastCasterOnMe = 0;
+ LastSpellOnMe = 0xffffffff;
+ LastCasterSeen = 0;
+ LastSpellSeen = 0xffffffff;
+ SpellHeader = -1;
+ SpellResRef[0] = 0;
+ LastTargetPos.empty();
+ locals = new Variables();
+ locals->SetType( GEM_VARIABLES_INT );
+ locals->ParseKey( 1 );
+ InitTriggers();
+
+ memset( script_timers,0, sizeof(script_timers));
+}
+
+Scriptable::~Scriptable(void)
+{
+ if (CurrentAction) {
+ ReleaseCurrentAction();
+ }
+ ClearActions();
+ for (int i = 0; i < MAX_SCRIPTS; i++) {
+ if (Scripts[i]) {
+ delete( Scripts[i] );
+ }
+ }
+ if (overHeadText) {
+ core->FreeString( overHeadText );
+ }
+ if (locals) {
+ delete( locals );
+ }
+}
+
+void Scriptable::SetScriptName(const char* text)
+{
+ //if (text && text[0]) { //this leaves some uninitialized bytes
+ //lets hope this won't break anything
+ if (text) {
+ strnspccpy( scriptName, text, 32 );
+ }
+}
+
+/** Gets the DeathVariable */
+const char* Scriptable::GetScriptName(void) const
+{
+ return scriptName;
+}
+
+void Scriptable::SetDialog(const char *resref) {
+ if (gamedata->Exists(resref, IE_DLG_CLASS_ID) ) {
+ strnuprcpy(Dialog, resref, 8);
+ }
+ }
+
+Map* Scriptable::GetCurrentArea() const
+{
+ //this could be NULL, always check it
+ return area;
+}
+
+void Scriptable::SetMap(Map *map)
+{
+ if (map && (map->GetCurrentArea()!=map)) {
+ //a map always points to itself (if it is a real map)
+ printMessage("Scriptable","Invalid map set!\n",LIGHT_RED);
+ abort();
+ }
+ area = map;
+}
+
+//ai is nonzero if this is an actor currently in the party
+//if the script level is AI_SCRIPT_LEVEL, then we need to
+//load an AI script (.bs) instead of (.bcs)
+void Scriptable::SetScript(const ieResRef aScript, int idx, bool ai)
+{
+ if (idx >= MAX_SCRIPTS) {
+ printMessage("Scriptable","Invalid script index!\n",LIGHT_RED);
+ abort();
+ }
+ if (Scripts[idx]) {
+ delete Scripts[idx];
+ }
+ Scripts[idx] = NULL;
+ // NONE is an 'invalid' script name, never used seriously
+ // This hack is to prevent flooding of the console
+ if (aScript[0] && stricmp(aScript, "NONE") ) {
+ if (idx!=AI_SCRIPT_LEVEL) ai = false;
+ Scripts[idx] = new GameScript( aScript, this, idx, ai );
+ }
+}
+
+void Scriptable::SetScript(int index, GameScript* script)
+{
+ if (index >= MAX_SCRIPTS) {
+ printMessage("Scriptable","Invalid script index!\n",LIGHT_RED);
+ return;
+ }
+ if (Scripts[index] ) {
+ delete Scripts[index];
+ }
+ Scripts[index] = script;
+}
+
+void Scriptable::SetSpellResRef(ieResRef resref) {
+ strnuprcpy(SpellResRef, resref, 8);
+}
+
+void Scriptable::DisplayHeadText(const char* text)
+{
+ if (overHeadText) {
+ core->FreeString( overHeadText );
+ }
+ overHeadText = (char *) text;
+ overHeadTextPos.empty();
+ if (text) {
+ timeStartDisplaying = core->GetGame()->Ticks;
+ textDisplaying = 1;
+ }
+ else {
+ timeStartDisplaying = 0;
+ textDisplaying = 0;
+ }
+}
+
+/* 'fix' the current overhead text in the current position */
+void Scriptable::FixHeadTextPos()
+{
+ overHeadTextPos = Pos;
+}
+
+#define MAX_DELAY 6000
+static const Color black={0,0,0,0};
+
+void Scriptable::DrawOverheadText(const Region &screen)
+{
+ unsigned long time = core->GetGame()->Ticks;
+ Palette *palette = NULL;
+
+ if (!textDisplaying)
+ return;
+
+ time -= timeStartDisplaying;
+
+ Font* font = core->GetFont( 1 );
+ if (time >= MAX_DELAY) {
+ textDisplaying = 0;
+ return;
+ } else {
+ time = (MAX_DELAY-time)/10;
+ if (time<256) {
+ const Color overHeadColor = {time,time,time,time};
+ palette = core->CreatePalette(overHeadColor, black);
+ }
+ }
+
+ int cs = 100;
+ if (Type == ST_ACTOR) {
+ cs = ((Selectable *) this)->size*50;
+ }
+
+ short x, y;
+ if (overHeadTextPos.isempty()) {
+ x = Pos.x;
+ y = Pos.y;
+ } else {
+ x = overHeadTextPos.x;
+ y = overHeadTextPos.y;
+ }
+
+ Region rgn( x-100+screen.x, y - cs + screen.y, 200, 400 );
+ font->Print( rgn, ( unsigned char * ) overHeadText,
+ palette?palette:core->InfoTextPalette, IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_TOP, false );
+ gamedata->FreePalette(palette);
+}
+
+void Scriptable::DelayedEvent()
+{
+ lastRunTime = core->GetGame()->Ticks;
+}
+
+void Scriptable::ImmediateEvent()
+{
+ lastRunTime = 0;
+}
+
+bool Scriptable::IsPC() const
+{
+ if(Type == ST_ACTOR) {
+ if (((Actor *) this)->GetStat(IE_EA) <= EA_CHARMED) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Scriptable::ExecuteScript(int scriptCount)
+{
+ // area scripts still run for at least the current area, in bg1 (see ar2631, confirmed by testing)
+ // but not in bg2 (kill Abazigal in ar6005)
+ if (core->GetGameControl()->GetScreenFlags()&SF_CUTSCENE) {
+ if (! (core->HasFeature(GF_CUTSCENE_AREASCRIPTS) && Type == ST_AREA)) {
+ return;
+ }
+ }
+
+ if ((InternalFlags & IF_NOINT) && (CurrentAction || GetNextAction())) {
+ return;
+ }
+
+ if (!CurrentActionInterruptable) {
+ if (!CurrentAction && !GetNextAction()) abort();
+ return;
+ }
+
+ // only allow death scripts to run once, hopefully?
+ // this is probably terrible logic which needs moving elsewhere
+ if ((lastRunTime != 0) && (InternalFlags & IF_JUSTDIED)) {
+ return;
+ }
+
+ ieDword thisTime = core->GetGame()->Ticks;
+ if (( thisTime - lastRunTime ) < 1000) {
+ return;
+ }
+
+ lastDelay = lastRunTime;
+ lastRunTime = thisTime;
+
+ bool alive = false;
+
+ bool continuing = false, done = false;
+ for (int i = 0;i<scriptCount;i++) {
+ //disable AI script level for actors in party when the player disabled them
+ if ((i == AI_SCRIPT_LEVEL) && Type == ST_ACTOR && ((Actor *) this)->InParty) {
+ if (core->GetGame()->ControlStatus&CS_PARTY_AI) {
+ continue;
+ }
+ }
+
+ GameScript *Script = Scripts[i];
+ if (Script) {
+ alive |= Script->Update(&continuing, &done);
+ }
+
+ /* scripts are not concurrent, see WAITPC override script for example */
+ if (done) break;
+ }
+ if (alive && UnselectableTimer) {
+ UnselectableTimer--;
+ if (!UnselectableTimer) {
+ if (Type == ST_ACTOR) {
+ ((Actor *) this)->SetCircleSize();
+ }
+ }
+ }
+ InternalFlags &= ~IF_ONCREATION;
+}
+
+void Scriptable::AddAction(Action* aC)
+{
+ if (!aC) {
+ printf( "[GameScript]: NULL action encountered for %s!\n",scriptName );
+ return;
+ }
+
+ InternalFlags|=IF_ACTIVE;
+ aC->IncRef();
+
+ // attempt to handle 'instant' actions, from instant.ids, which run immediately
+ // when added if the action queue is empty, even on actors which are Held/etc
+ if (!CurrentAction && !GetNextAction()) {
+ if (actionflags[aC->actionID] & AF_INSTANT) {
+ CurrentAction = aC;
+ GameScript::ExecuteAction( this, CurrentAction );
+ return;
+ }
+ }
+
+ actionQueue.push_back( aC );
+}
+
+void Scriptable::AddActionInFront(Action* aC)
+{
+ if (!aC) {
+ printf( "[GameScript]: NULL action encountered for %s!\n",scriptName );
+ return;
+ }
+ InternalFlags|=IF_ACTIVE;
+ actionQueue.push_front( aC );
+ aC->IncRef();
+}
+
+Action* Scriptable::GetNextAction() const
+{
+ if (actionQueue.size() == 0) {
+ return NULL;
+ }
+ return actionQueue.front();
+}
+
+Action* Scriptable::PopNextAction()
+{
+ if (actionQueue.size() == 0) {
+ return NULL;
+ }
+ Action* aC = actionQueue.front();
+ actionQueue.pop_front();
+ return aC;
+}
+
+void Scriptable::ClearActions()
+{
+ ReleaseCurrentAction();
+ for (unsigned int i = 0; i < actionQueue.size(); i++) {
+ Action* aC = actionQueue.front();
+ actionQueue.pop_front();
+ aC->Release();
+ }
+ actionQueue.clear();
+ WaitCounter = 0;
+ LastTarget = 0;
+ //clear the triggers as fast as possible when queue ended?
+ ClearTriggers();
+
+ if (Type == ST_ACTOR) {
+ Interrupt();
+ } else {
+ NoInterrupt();
+ }
+}
+
+void Scriptable::ReleaseCurrentAction()
+{
+ if (CurrentAction) {
+ CurrentAction->Release();
+ CurrentAction = NULL;
+ }
+
+ CurrentActionState = 0;
+ CurrentActionTarget = 0;
+ CurrentActionInterruptable = true;
+}
+
+void Scriptable::ProcessActions(bool force)
+{
+ unsigned long thisTime = core->GetGame()->Ticks;
+
+ if (!force && (( thisTime - startTime ) < interval)) {
+ return;
+ }
+ startTime = thisTime;
+ if (WaitCounter) {
+ WaitCounter--;
+ if (WaitCounter) return;
+ }
+
+ while (true) {
+ CurrentActionInterruptable = true;
+ if (!CurrentAction) {
+ CurrentAction = PopNextAction();
+ }
+ if (!CurrentAction) {
+ ClearActions();
+ //removing the triggers at the end of the
+ //block
+ //ClearTriggers();
+ break;
+ }
+ GameScript::ExecuteAction( this, CurrentAction );
+ //break execution in case of a Wait flag
+ if (WaitCounter) {
+ //clear triggers while waiting
+ //ClearTriggers();
+ break;
+ }
+ //break execution in case of blocking action
+ if (CurrentAction) {
+ break;
+ }
+ //break execution in case of movement
+ //we should not actually break here, or else fix waypoints
+ if (InMove()) {
+ break;
+ }
+ }
+ //most likely the best place to clear triggers is here
+ //queue is empty, or there is a looong action subject to break
+ ClearTriggers();
+ if (InternalFlags&IF_IDLE) {
+ Deactivate();
+ }
+}
+
+bool Scriptable::InMove() const
+{
+ if (Type!=ST_ACTOR) {
+ return false;
+ }
+ Movable *me = (Movable *) this;
+ return me->GetNextStep()!=NULL;
+}
+
+void Scriptable::SetWait(unsigned long time)
+{
+ WaitCounter = time;
+}
+
+unsigned long Scriptable::GetWait() const
+{
+ return WaitCounter;
+}
+
+void Scriptable::LeaveDialog()
+{
+ InternalFlags |=IF_WASINDIALOG;
+}
+
+void Scriptable::Hide()
+{
+ InternalFlags &=~(IF_VISIBLE);
+}
+
+void Scriptable::Unhide()
+{
+ InternalFlags |= IF_VISIBLE;
+}
+
+void Scriptable::Interrupt()
+{
+ InternalFlags &= ~IF_NOINT;
+}
+
+void Scriptable::NoInterrupt()
+{
+ InternalFlags |= IF_NOINT;
+}
+
+//also turning off the idle flag so it won't run continuously
+void Scriptable::Deactivate()
+{
+ InternalFlags &=~(IF_ACTIVE|IF_IDLE);
+}
+
+//turning off the not interruptable flag, actions should reenable it themselves
+//also turning off the idle flag
+//heh, no, i wonder why did i touch the interruptable flag here
+void Scriptable::Activate()
+{
+ InternalFlags |= IF_ACTIVE;
+ InternalFlags &= ~IF_IDLE;
+}
+
+void Scriptable::PartyRested()
+{
+ InternalFlags |=IF_PARTYRESTED;
+}
+
+ieDword Scriptable::GetInternalFlag()
+{
+ return InternalFlags;
+}
+
+void Scriptable::InitTriggers()
+{
+ tolist.clear();
+ bittriggers = 0;
+}
+
+void Scriptable::ClearTriggers()
+{
+ for (TriggerObjects::iterator m = tolist.begin(); m != tolist.end (); m++) {
+ *(*m) = 0;
+ }
+ if (!bittriggers) {
+ return;
+ }
+ if (bittriggers & BT_DIE) {
+ InternalFlags &= ~IF_JUSTDIED;
+ }
+ if (bittriggers & BT_ONCREATION) {
+ InternalFlags &= ~IF_ONCREATION;
+ }
+ if (bittriggers & BT_BECAMEVISIBLE) {
+ InternalFlags &= ~IF_BECAMEVISIBLE;
+ }
+ if (bittriggers & BT_PARTYRESTED) {
+ InternalFlags &= ~IF_PARTYRESTED;
+ }
+ if (bittriggers & BT_WASINDIALOG) {
+ InternalFlags &= ~IF_WASINDIALOG;
+ }
+ if (bittriggers & BT_PARTYRESTED) {
+ InternalFlags &= ~IF_PARTYRESTED;
+ }
+ InitTriggers();
+}
+
+void Scriptable::SetBitTrigger(ieDword bittrigger)
+{
+ bittriggers |= bittrigger;
+}
+
+void Scriptable::AddTrigger(ieDword *actorref)
+{
+ tolist.push_back(actorref);
+}
+
+static EffectRef fx_set_invisible_state_ref = { "State:Invisible", -1 };
+
+void Scriptable::CreateProjectile(const ieResRef SpellResRef, ieDword tgt, int level, bool fake)
+{
+ Spell* spl = gamedata->GetSpell( SpellResRef );
+ Spell* spltmp= NULL;
+ Actor *caster = NULL;
+
+ //PST has a weird effect, called Enoll Eva's duplication
+ //it creates every projectile of the affected actor twice
+ int duplicate = 1;
+ if (Type == ST_ACTOR) {
+ caster = (Actor *) this;
+ //FIXME: 1 duplicate is no duplicate, right?
+ duplicate = caster->wildSurgeMods.num_castings;
+ if (!duplicate) {
+ duplicate = 1;
+ }
+ }
+ if (core->HasFeature(GF_PST_STATE_FLAGS) && (Type == ST_ACTOR)) {
+ if (caster->GetStat(IE_STATE_ID)&STATE_EE_DUPL) {
+ //seriously, wild surges and EE in the same game?
+ //anyway, it would be too many duplications
+ duplicate = 2;
+ }
+ }
+
+ // when doing wild magic mods, use a copy of the spell to avoid mod propagation into the cache
+ if (caster && (caster->wildSurgeMods.target_change_type || caster->wildSurgeMods.saving_throw_mod || caster->wildSurgeMods.projectile_id)) {
+ spltmp = gamedata->GetUncachedSpell( SpellResRef );
+ }
+
+ while(duplicate --) {
+ Projectile *pro = NULL;
+ // jump through hoops to skip applying selftargetting spells to the caster
+ // if we'll be changing the target
+ int tct = 0;
+ if (caster) {
+ tct = caster->wildSurgeMods.target_change_type;
+ }
+ if (!caster || !tct || tct == WSTC_ADDTYPE || !caster->wildSurgeMods.projectile_id) {
+ pro = spl->GetProjectile(this, SpellHeader, LastTargetPos);
+ if (!pro) {
+ return;
+ }
+ pro->SetCaster(GetGlobalID(), level);
+ }
+
+ Point origin = Pos;
+ if (Type == ST_TRIGGER || Type == ST_PROXIMITY) {
+ // try and make projectiles start from the right trap position
+ // see the traps in the duergar/assassin battle in bg2 dungeon
+ // see also function below - maybe we should fix Pos instead
+ origin = ((InfoPoint *)this)->TrapLaunch;
+ }
+
+ if (caster) {
+ // check for target (type) change
+ int count, i;
+ Actor *newact = NULL;
+ SPLExtHeader *seh = NULL;
+ Effect *fx = NULL;
+ switch (caster->wildSurgeMods.target_change_type) {
+ case WSTC_SETTYPE:
+ seh = &spltmp->ext_headers[SpellHeader];
+ for (i=0; i < seh->FeatureCount; i++) {
+ seh->features[i].Target = caster->wildSurgeMods.target_type;
+ }
+ // we need to fetch the projectile, so the effect queue is created
+ // (skipped above)
+ pro = spltmp->GetProjectile(this, SpellHeader, LastTargetPos);
+ pro->SetCaster(GetGlobalID(), level);
+ break;
+ case WSTC_ADDTYPE:
+ // TODO: unhardcode to allow for mixing all the target types
+ // caster gets selftargetting fx when the projectile is fetched above
+ seh = &spltmp->ext_headers[SpellHeader];
+ for (i=0; i < seh->FeatureCount; i++) {
+ if (seh->features[i].Target == FX_TARGET_SELF) {
+ seh->features[i].Target = caster->wildSurgeMods.target_type;
+ } else {
+ // also apply to the caster
+ fx = seh->features+i;
+ core->ApplyEffect(fx, caster, caster);
+ }
+ }
+ // we need to refetch the projectile, so the effect queue is created
+ pro = spltmp->GetProjectile(this, SpellHeader, LastTargetPos);
+ pro->SetCaster(GetGlobalID(), level);
+ break;
+ case WSTC_RANDOMIZE:
+ count = area->GetActorCount(false);
+ newact = area->GetActor(core->Roll(1,count,-1), false);
+ if (count > 1 && newact == caster) {
+ while (newact == caster) {
+ newact = area->GetActor(core->Roll(1,count,-1), false);
+ }
+ }
+ if (tgt) {
+ LastTarget = newact->GetGlobalID();
+ LastTargetPos = newact->Pos;
+ } else {
+ // no better idea; I wonder if the original randomized point targets at all
+ LastTargetPos = newact->Pos;
+ }
+
+ // make it also work for self-targetting spells:
+ // change the payload or this was all in vain
+ seh = &spltmp->ext_headers[SpellHeader];
+ for (i=0; i < seh->FeatureCount; i++) {
+ if (seh->features[i].Target == FX_TARGET_SELF) {
+ seh->features[i].Target = FX_TARGET_PRESET;
+ }
+ }
+ // we need to fetch the projectile, so the effect queue is created
+ // (skipped above)
+ pro = spltmp->GetProjectile(this, SpellHeader, LastTargetPos);
+ pro->SetCaster(GetGlobalID(), level);
+ break;
+ default: //0 - do nothing
+ break;
+ }
+
+ // apply the saving throw mod
+ if (caster->wildSurgeMods.saving_throw_mod) {
+ seh = &spltmp->ext_headers[SpellHeader];
+ for (i=0; i < seh->FeatureCount; i++) {
+ seh->features[i].SavingThrowBonus += caster->wildSurgeMods.saving_throw_mod;
+ }
+ }
+
+ // change the projectile
+ if (caster->wildSurgeMods.projectile_id) {
+ spltmp->ext_headers[SpellHeader].ProjectileAnimation = caster->wildSurgeMods.projectile_id;
+ // make it also work for self-targetting spells:
+ // change the payload or this was all in vain
+ seh = &spltmp->ext_headers[SpellHeader];
+ for (i=0; i < seh->FeatureCount; i++) {
+ if (seh->features[i].Target == FX_TARGET_SELF) {
+ seh->features[i].Target = FX_TARGET_PRESET;
+ }
+ }
+ // we need to refetch the projectile, so the new one is used
+ pro = spltmp->GetProjectile(this, SpellHeader, LastTargetPos);
+ pro->SetCaster(GetGlobalID(), level);
+ }
+
+ // check for the speed mod
+ if (caster->wildSurgeMods.projectile_speed_mod) {
+ pro->Speed = (pro->Speed * caster->wildSurgeMods.projectile_speed_mod) / 100;
+ if (!pro->Speed) {
+ pro->Speed = 1;
+ }
+ }
+ }
+
+ if (tgt) {
+ area->AddProjectile(pro, origin, LastTarget, fake);
+ } else {
+ area->AddProjectile(pro, origin, LastTargetPos);
+ }
+ }
+
+ ieDword spellnum=ResolveSpellNumber( SpellResRef );
+ if (spellnum!=0xffffffff) {
+ area->SeeSpellCast(this, spellnum);
+
+ // caster - Casts spellname : target OR
+ // caster - spellname : target (repeating spells)
+ Scriptable *target = NULL;
+ char tmp[100];
+ const char* msg = core->GetString(displaymsg->GetStringReference(STR_ACTION_CAST), 0);
+ const char* spell = core->GetString(spl->SpellName);
+ if (LastTarget) {
+ target = area->GetActorByGlobalID(LastTarget);
+ if (!target) {
+ target=core->GetGame()->GetActorByGlobalID(LastTarget);
+ }
+ }
+ if (stricmp(spell, "")) {
+ if (target) {
+ snprintf(tmp, sizeof(tmp), "%s %s : %s", msg, spell, target->GetName(-1));
+ } else {
+ snprintf(tmp, sizeof(tmp), "%s : %s", spell, GetName(-1));
+ }
+ displaymsg->DisplayStringName(tmp, 0xffffff, this);
+ }
+
+ if(LastTarget) {
+ if (target && (Type==ST_ACTOR) ) {
+ target->LastSpellOnMe = spellnum;
+ target->LastCasterOnMe = caster->GetGlobalID();
+ // don't cure invisibility if this is a self targetting invisibility spell
+ // like shadow door
+ //can't check GetEffectBlock, since it doesn't construct the queue for selftargetting spells
+ bool invis = false;
+ unsigned int opcode = EffectQueue::ResolveEffect(fx_set_invisible_state_ref);
+ for (unsigned int i=0; i < spl->ext_headers[SpellHeader].FeatureCount; i++) {
+ if (spl->GetExtHeader(SpellHeader)->features[i].Opcode == opcode) {
+ invis = true;
+ break;
+ }
+ }
+ if (invis && spl->GetExtHeader(SpellHeader)->Target == TARGET_SELF) {
+ //pass
+ } else {
+ caster->CureInvisibility();
+ }
+ // sanctuary ends with all hostile actions or when the caster targets someone else
+ if (target != this || spl->Flags & SF_HOSTILE) {
+ caster->CureSanctuary();
+ }
+ }
+ }
+ }
+
+ core->Autopause(AP_SPELLCAST);
+
+ gamedata->FreeSpell(spl, SpellResRef, false);
+ if (spltmp) {
+ delete spltmp;
+ }
+
+}
+
+void Scriptable::CastSpellPointEnd(int level)
+{
+ Actor *caster = NULL;
+ if (Type == ST_ACTOR) {
+ caster = ((Actor *) this);
+ caster->SetStance(IE_ANI_CONJURE);
+ if (level == 0) {
+ Spell* spl = gamedata->GetSpell(SpellResRef); // this was checked before we got here
+ Actor *actor = NULL;
+ if (Type == ST_ACTOR) {
+ actor = (Actor *) this;
+ level = actor->GetCasterLevel(spl->SpellType);
+ }
+ gamedata->FreeSpell(spl, SpellResRef, false);
+ }
+ }
+
+ if (SpellHeader == -1) {
+ LastTargetPos.empty();
+ return;
+ }
+
+ if (LastTargetPos.isempty()) {
+ SpellHeader = -1;
+ return;
+ }
+
+ if (!SpellResRef[0]) {
+ return;
+ }
+
+ CreateProjectile(SpellResRef, 0, level, false);
+
+ SpellHeader = -1;
+ SpellResRef[0] = 0;
+ LastTarget = 0;
+ LastTargetPos.empty();
+ if (caster) {
+ memset(&(caster->wildSurgeMods), 0, sizeof(caster->wildSurgeMods));
+ }
+}
+
+void Scriptable::CastSpellEnd(int level)
+{
+ Actor *caster = NULL;
+ if (Type == ST_ACTOR) {
+ caster = ((Actor *) this);
+ caster->SetStance(IE_ANI_CONJURE);
+ if (level == 0) {
+ Spell* spl = gamedata->GetSpell(SpellResRef); // this was checked before we got here
+ Actor *actor = NULL;
+ if (Type == ST_ACTOR) {
+ actor = (Actor *) this;
+ level = actor->GetCasterLevel(spl->SpellType);
+ }
+ gamedata->FreeSpell(spl, SpellResRef, false);
+ }
+ }
+
+ if (SpellHeader == -1) {
+ LastTarget = 0;
+ return;
+ }
+ if (!LastTarget) {
+ SpellHeader = -1;
+ return;
+ }
+ if (!SpellResRef[0]) {
+ return;
+ }
+
+ //if the projectile doesn't need to follow the target, then use the target position
+ CreateProjectile(SpellResRef, LastTarget, level, GetSpellDistance(SpellResRef, this)==0xffffffff);
+ SpellHeader = -1;
+ SpellResRef[0] = 0;
+ LastTarget = 0;
+ LastTargetPos.empty();
+ if (caster) {
+ memset(&(caster->wildSurgeMods), 0, sizeof(caster->wildSurgeMods));
+ }
+}
+
+// check for input sanity and good casting conditions
+int Scriptable::CanCast(const ieResRef SpellResRef) {
+ Spell* spl = gamedata->GetSpell(SpellResRef);
+ if (!spl) {
+ SpellHeader = -1;
+ printMessage("Scriptable", "Spell not found, aborting cast!\n", LIGHT_RED);
+ return 0;
+ }
+
+ // check for area dead magic
+ // tob AR3004 is a dead magic area, but using a script with personal dead magic
+ if (area->GetInternalFlag()&AF_DEADMAGIC) {
+ // TODO: display fizzling animation
+ displaymsg->DisplayConstantStringName(STR_DEADMAGIC_FAIL, 0xffffff, this);
+ return 0;
+ }
+
+ // various individual checks
+ if (Type == ST_ACTOR) {
+ Actor *actor = (Actor *) this;
+
+ // check for silence
+ // only a handful of spells don't have a verbal component -
+ // the original hardcoded vocalize and a few more
+ // we (also) ignore nonmagic spells
+ if (actor->Modified[IE_STATE_ID] & STATE_SILENCED) {
+ if (!(core->GetSpecialSpell(spl->Name)&SP_SILENCE) && !(spl->Flags&SF_HLA)) {
+ printMessage("Scriptable", "Tried to cast while silenced!\n", YELLOW);
+ return 0;
+ }
+ }
+
+ // check for personal dead magic
+ if (actor->Modified[IE_DEADMAGIC]) {
+ // TODO: display fizzling animation
+ displaymsg->DisplayConstantStringName(STR_DEADMAGIC_FAIL, 0xffffff, this);
+ return 0;
+ }
+
+ // check for miscast magic and similar
+ ieDword roll = actor->LuckyRoll(1, 100, 0, 0);
+ bool failed = false;
+ switch(spl->SpellType)
+ {
+ case IE_SPL_PRIEST:
+ if (actor->Modified[IE_SPELLFAILUREPRIEST] >= roll) {
+ failed = true;
+ }
+ break;
+ case IE_SPL_WIZARD:
+ if (actor->Modified[IE_SPELLFAILUREMAGE] >= roll) {
+ failed = true;
+ }
+ break;
+ case IE_SPL_INNATE:
+ if (actor->Modified[IE_SPELLFAILUREINNATE] >= roll) {
+ failed = true;
+ }
+ break;
+ }
+ if (failed) {
+ // TODO: display fizzling animation
+ displaymsg->DisplayConstantStringName(STR_MISCASTMAGIC, 0xffffff, this);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+//set target as point
+//if spell needs to be depleted, do it
+//if spell is illegal stop casting
+int Scriptable::CastSpellPoint( ieResRef &SpellRef, const Point &target, bool deplete, bool instant )
+{
+ LastTarget = 0;
+ LastTargetPos.empty();
+ if (Type == ST_ACTOR) {
+ Actor *actor = (Actor *) this;
+ if (actor->HandleCastingStance(SpellRef,deplete) ) {
+ printMessage("Scriptable", "Spell not known or memorized, aborting cast!\n", LIGHT_RED);
+ return -1;
+ }
+ }
+ if(deplete && !CanCast(SpellRef)) {
+ SpellResRef[0] = 0;
+ if (Type == ST_ACTOR) {
+ Actor *actor = (Actor *) this;
+ actor->SetStance(IE_ANI_READY);
+ }
+ return -1;
+ }
+
+ if (!SpellResRef[0]) {
+ SetSpellResRef(SpellRef);
+ }
+
+ LastTargetPos = target;
+
+ if(!CheckWildSurge()) {
+ return -1;
+ }
+ return SpellCast(instant);
+}
+
+//set target as actor (if target isn't actor, use its position)
+//if spell needs to be depleted, do it
+//if spell is illegal stop casting
+int Scriptable::CastSpell( ieResRef &SpellRef, Scriptable* target, bool deplete, bool instant )
+{
+ LastTarget = 0;
+ LastTargetPos.empty();
+ if (Type == ST_ACTOR) {
+ Actor *actor = (Actor *) this;
+ if (actor->HandleCastingStance(SpellRef,deplete) ) {
+ printMessage("Scriptable", "Spell not known or memorized, aborting cast!\n", LIGHT_RED);
+ return -1;
+ }
+ }
+
+ // FIXME: fishy
+ if (!target) target = this;
+
+ if(deplete && !CanCast(SpellRef)) {
+ SpellResRef[0] = 0;
+ if (Type == ST_ACTOR) {
+ Actor *actor = (Actor *) this;
+ actor->SetStance(IE_ANI_READY);
+ }
+ return -1;
+ }
+
+ if (!SpellResRef[0]) {
+ SetSpellResRef(SpellRef);
+ }
+
+ LastTargetPos = target->Pos;
+ if (target->Type==ST_ACTOR) {
+ LastTarget = target->GetGlobalID();
+ }
+
+ if(!CheckWildSurge()) {
+ return -1;
+ }
+
+ return SpellCast(instant);
+}
+
+static EffectRef fx_force_surge_modifier_ref = { "ForceSurgeModifier", -1 };
+
+//start spellcasting (common part)
+int Scriptable::SpellCast(bool instant)
+{
+ Spell* spl = gamedata->GetSpell(SpellResRef); // this was checked before we got here
+ Actor *actor = NULL;
+ int level = 0;
+ if (Type == ST_ACTOR) {
+ actor = (Actor *) this;
+
+ //The ext. index is here to calculate the casting time
+ level = actor->GetCasterLevel(spl->SpellType);
+ SpellHeader = spl->GetHeaderIndexFromLevel(level);
+ } else {
+ SpellHeader = 0;
+ }
+
+ SPLExtHeader *header = spl->GetExtHeader(SpellHeader);
+ int casting_time = (int)header->CastingTime;
+ // how does this work for non-actors exactly?
+ if (actor) {
+ // The mental speed effect can shorten or lengthen the casting time.
+ casting_time -= (int)actor->Modified[IE_MENTALSPEED];
+ if (casting_time < 0) casting_time = 0;
+ }
+ // this is a guess which seems approximately right so far
+ int duration = (casting_time*core->Time.round_size) / 10;
+ if (instant) {
+ duration = 0;
+ }
+ if (actor) {
+ //cfb
+ EffectQueue *fxqueue = spl->GetEffectBlock(this, this->Pos, -1, level);
+ fxqueue->SetOwner(actor);
+ if (!actor->Modified[IE_AVATARREMOVAL]) {
+ spl->AddCastingGlow(fxqueue, duration, actor->Modified[IE_SEX]);
+ }
+ fxqueue->AddAllEffects(actor, actor->Pos);
+ delete fxqueue;
+ actor->WMLevelMod = 0;
+ if (actor->Modified[IE_FORCESURGE] == 1) {
+ // affects only the next spell cast, but since the timing is permanent,
+ // we have to remove it manually
+ actor->fxqueue.RemoveAllEffectsWithParam(fx_force_surge_modifier_ref, 1);
+ }
+ }
+
+ gamedata->FreeSpell(spl, SpellResRef, false);
+ return duration;
+}
+
+// Anyone with some wildness has 5% chance of getting a wild surge when casting,
+// but most innates are excluded, due to being nonmagic.
+// A d100 roll is made, some stat boni are added, then either:
+// 1. the spell is cast normally (score of 100 or more)
+// 2. one or more wild surges happen and something else is cast
+// 2.1. this can loop, since some surges cause rerolls
+int Scriptable::CheckWildSurge()
+{
+ if (Type != ST_ACTOR || core->HasFeature(GF_3ED_RULES)) {
+ return 1;
+ }
+ Actor *caster = (Actor *) this;
+
+ int roll = core->Roll(1, 100, 0);
+ if ((roll <= 5 && caster->Modified[IE_SURGEMOD]) || caster->Modified[IE_FORCESURGE]) {
+ ieResRef OldSpellResRef;
+ memcpy(OldSpellResRef, SpellResRef, sizeof(OldSpellResRef));
+ Spell *spl = gamedata->GetSpell( OldSpellResRef ); // this was checked before we got here
+ // ignore non-magic "spells"
+ if (!(spl->Flags&SF_HLA)) {
+ int check = roll + caster->GetCasterLevel(spl->SpellType) + caster->Modified[IE_SURGEMOD];
+ // hundred or more means a normal cast
+ if (check < 100) {
+ // display feedback: Wild Surge: bla bla
+ char text[200];
+ snprintf(text, 200, "%s %s", core->GetString(displaymsg->GetStringReference(STR_WILDSURGE), 0), core->GetString(core->SurgeSpells[check-1].message, 0));
+ displaymsg->DisplayStringName(text, 0xffffff, this);
+
+ // lookup the spell in the "check" row of wildmag.2da
+ ieResRef surgeSpellRef;
+ memset(surgeSpellRef, 0, sizeof(surgeSpellRef));
+ strncpy(surgeSpellRef, core->SurgeSpells[check-1].spell, 8);
+
+ if (!gamedata->Exists(surgeSpellRef, IE_SPL_CLASS_ID)) {
+ // handle the hardcoded cases - they'll also fail here
+ if (!HandleHardcodedSurge(surgeSpellRef, spl, caster)) {
+ //free the spell handle because we need to return
+ gamedata->FreeSpell(spl, OldSpellResRef, false);
+ return 0;
+ }
+ } else {
+ // finally change the spell
+ // the hardcoded bunch does it on its own when needed
+ strncpy(SpellResRef, surgeSpellRef, 8);
+ }
+ }
+ }
+
+ //free the spell handle
+ gamedata->FreeSpell(spl, OldSpellResRef, false);
+ }
+
+ return 1;
+}
+
+bool Scriptable::HandleHardcodedSurge(ieResRef surgeSpellRef, Spell *spl, Actor *caster)
+{
+ // format: ID or ID.param1 or +SPELLREF
+ int types = caster->spellbook.GetTypes();
+ int lvl = spl->SpellLevel-1;
+ int count, i, tmp, tmp2, tmp3;
+ Scriptable *target = NULL;
+ Point targetpos(-1, -1);
+ ieResRef newspl;
+
+ int level = caster->GetCasterLevel(spl->SpellType);
+ switch (surgeSpellRef[0]) {
+ case '+': // cast normally, but also cast SPELLREF first
+ core->ApplySpell(surgeSpellRef+1, caster, caster, level);
+ break;
+ case '0': // cast spell param1 times
+ strtok(surgeSpellRef,".");
+ count = strtol(strtok(NULL,"."), NULL, 0);
+ caster->wildSurgeMods.num_castings = count;
+ break;
+ case '1': // change projectile (id) to param1
+ strtok(surgeSpellRef,".");
+ count = strtol(strtok(NULL,"."), NULL, 0);
+ caster->wildSurgeMods.projectile_id = count;
+ break;
+ case '2': // also target target type param1
+ strtok(surgeSpellRef,".");
+ count = strtol(strtok(NULL,"."), NULL, 0);
+ caster->wildSurgeMods.target_type = count;
+ caster->wildSurgeMods.target_change_type = WSTC_ADDTYPE;
+ break;
+ case '3': // (wild surge) roll param1 more times
+ strtok(surgeSpellRef,".");
+ count = strtol(strtok(NULL,"."), NULL, 0);
+ // force surge and then cast
+ // force the surge roll to be < 100, so we cast a spell from the surge table
+ tmp = caster->Modified[IE_FORCESURGE];
+ tmp2 = caster->Modified[IE_SURGEMOD];
+ tmp3 = caster->WMLevelMod; // also save caster level; the original didn't reroll the bonus
+ caster->Modified[IE_FORCESURGE] = 7;
+ caster->Modified[IE_SURGEMOD] = - caster->GetCasterLevel(spl->SpellType); // nulify the bonus
+ if (LastTarget) {
+ target = area->GetActorByGlobalID(LastTarget);
+ if (!target) {
+ target = core->GetGame()->GetActorByGlobalID(LastTarget);
+ }
+ }
+ if (!LastTargetPos.isempty()) {
+ targetpos = LastTargetPos;
+ } else if (target) {
+ targetpos = target->Pos;
+ }
+ for (i=0; i<count; i++) {
+ if (target) {
+ caster->CastSpell(SpellResRef, target, false, true);
+ strncpy(newspl, SpellResRef, 8);
+ caster->WMLevelMod = tmp3;
+ caster->CastSpellEnd(level);
+ } else {
+ caster->CastSpellPoint(SpellResRef, targetpos, false, true);
+ strncpy(newspl, SpellResRef, 8);
+ caster->WMLevelMod = tmp3;
+ caster->CastSpellPointEnd(level);
+ }
+ // reset the ref, since CastSpell*End destroyed it
+ strncpy(SpellResRef, newspl, 8);
+ }
+ caster->Modified[IE_FORCESURGE] = tmp;
+ caster->Modified[IE_SURGEMOD] = tmp2;
+ break;
+ case '4': // change the target type to param1
+ strtok(surgeSpellRef,".");
+ count = strtol(strtok(NULL,"."), NULL, 0);
+ caster->wildSurgeMods.target_type = count;
+ caster->wildSurgeMods.target_change_type = WSTC_SETTYPE;
+ break;
+ case '5': // change the target to a random actor
+ caster->wildSurgeMods.target_change_type = WSTC_RANDOMIZE;
+ break;
+ case '6': // change saving throw (+param1)
+ strtok(surgeSpellRef,".");
+ count = strtol(strtok(NULL,"."), NULL, 0);
+ caster->wildSurgeMods.saving_throw_mod = count;
+ break;
+ case '7': // random spell of the same level (FIXME: make an effect out of this?)
+ // change this if we ever want the surges to respect the original type
+ for (i=0; i<types; i++) {
+ unsigned int spellCount = caster->spellbook.GetKnownSpellsCount(i, lvl);
+ if (!spellCount) continue;
+ int id = core->Roll(1, spellCount, -1);
+ CREKnownSpell *ck = caster->spellbook.GetKnownSpell(i, lvl, id);
+ if (ck) {
+ strncpy(SpellResRef, ck->SpellResRef, 8);
+ break;
+ }
+ }
+ break;
+ case '8': // set projectile speed to param1 %
+ strtok(surgeSpellRef,".");
+ count = strtol(strtok(NULL,"."), NULL, 0);
+ caster->wildSurgeMods.projectile_speed_mod = count;
+ break;
+ default:
+ SpellHeader = -1;
+ SpellResRef[0] = 0;
+ printMessage("Scriptable", "New spell not found, aborting cast mid-surge!\n", LIGHT_RED);
+ caster->SetStance(IE_ANI_READY);
+ return false;
+ }
+ return true;
+}
+
+bool Scriptable::TimerActive(ieDword ID)
+{
+ if (ID>=MAX_TIMER) {
+ return false;
+ }
+ if (script_timers[ID]) {
+ return true;
+ }
+ return false;
+}
+
+bool Scriptable::TimerExpired(ieDword ID)
+{
+ if (ID>=MAX_TIMER) {
+ return false;
+ }
+ if (script_timers[ID] && script_timers[ID] < core->GetGame()->GameTime) {
+ // expired timers become inactive after being checked
+ script_timers[ID] = 0;
+ return true;
+ }
+ return false;
+}
+
+void Scriptable::StartTimer(ieDword ID, ieDword expiration)
+{
+ if (ID>=MAX_TIMER) {
+ printMessage("Scriptable", " ", RED);
+ printf("Timer id %d exceeded MAX_TIMER %d\n", ID, MAX_TIMER);
+ return;
+ }
+ script_timers[ID]= core->GetGame()->GameTime + expiration*AI_UPDATE_TIME;
+}
+
+/********************
+ * Selectable Class *
+ ********************/
+
+Selectable::Selectable(ScriptableType type)
+ : Scriptable( type )
+{
+ Selected = false;
+ Over = false;
+ size = 0;
+ cover = NULL;
+ circleBitmap[0] = NULL;
+ circleBitmap[1] = NULL;
+}
+
+void Selectable::SetSpriteCover(SpriteCover* c)
+{
+ delete cover;
+ cover = c;
+}
+
+Selectable::~Selectable(void)
+{
+ delete cover;
+}
+
+void Selectable::SetBBox(const Region &newBBox)
+{
+ BBox = newBBox;
+}
+
+static const unsigned long tp_steps[8]={3,2,1,0,1,2,3,4};
+
+void Selectable::DrawCircle(const Region &vp)
+{
+ /* BG2 colours ground circles as follows:
+ dark green for unselected party members
+ bright green for selected party members
+ flashing green/white for a party member the mouse is over
+ bright red for enemies
+ yellow for panicked actors
+ flashing red/white for enemies the mouse is over
+ flashing cyan/white for neutrals the mouse is over
+ */
+
+ if (size<=0) {
+ return;
+ }
+ Color mix;
+ Color* col = &selectedColor;
+ Sprite2D* sprite = circleBitmap[0];
+
+ if (Selected) {
+ sprite = circleBitmap[1];
+ } else if (Over) {
+ //doing a time dependent flashing of colors
+ //if it is too fast, increase the 6 to 7
+ unsigned long step;
+ GetTime( step );
+ step = tp_steps [(step >> 6) & 7];
+ mix.a = overColor.a;
+ mix.r = (overColor.r*step+selectedColor.r*(8-step))/8;
+ mix.g = (overColor.g*step+selectedColor.g*(8-step))/8;
+ mix.b = (overColor.b*step+selectedColor.b*(8-step))/8;
+ col = &mix;
+ } else if (IsPC()) {
+ col = &overColor;
+ }
+
+ if (sprite) {
+ core->GetVideoDriver()->BlitSprite( sprite, Pos.x - vp.x, Pos.y - vp.y, true );
+ } else {
+ // for size >= 2, radii are (size-1)*16, (size-1)*12
+ // for size == 1, radii are 12, 9
+ int csize = (size - 1) * 4;
+ if (csize < 4) csize = 3;
+ core->GetVideoDriver()->DrawEllipse( (ieWord) (Pos.x - vp.x), (ieWord) (Pos.y - vp.y),
+ (ieWord) (csize * 4), (ieWord) (csize * 3), *col );
+ }
+}
+
+// Check if P is over our ground circle
+bool Selectable::IsOver(const Point &P) const
+{
+ int csize = size;
+ if (csize < 2) csize = 2;
+
+ int dx = P.x - Pos.x;
+ int dy = P.y - Pos.y;
+
+ // check rectangle first
+ if (dx < -(csize-1)*16 || dx > (csize-1)*16) return false;
+ if (dy < -(csize-1)*12 || dy > (csize-1)*12) return false;
+
+ // then check ellipse
+ int r = 9*dx*dx + 16*dy*dy; // 48^2 * ( (dx/16)^2 + (dy/12)^2 )
+
+ return (r <= 48*48*(csize-1)*(csize-1));
+}
+
+bool Selectable::IsSelected() const
+{
+ return Selected == 1;
+}
+
+void Selectable::SetOver(bool over)
+{
+ Over = over;
+}
+
+//don't call this function after rendering the cover and before the
+//blitting of the sprite or bad things will happen :)
+void Selectable::Select(int Value)
+{
+ if (Selected!=0x80 || Value!=1) {
+ Selected = (ieWord) Value;
+ }
+ //forcing regeneration of the cover
+ SetSpriteCover(NULL);
+}
+
+void Selectable::SetCircle(int circlesize, const Color &color, Sprite2D* normal_circle, Sprite2D* selected_circle)
+{
+ size = circlesize;
+ selectedColor = color;
+ overColor.r = color.r >> 1;
+ overColor.g = color.g >> 1;
+ overColor.b = color.b >> 1;
+ overColor.a = color.a;
+ circleBitmap[0] = normal_circle;
+ circleBitmap[1] = selected_circle;
+}
+
+//used for creatures
+int Selectable::WantDither()
+{
+ //if dithering is disabled globally, don't do it
+ if (core->FogOfWar&FOG_DITHERSPRITES) {
+ return 0;
+ }
+ //if actor is dead, dither it if polygon wants
+ if (Selected&0x80) {
+ return 1;
+ }
+ //if actor is selected dither it
+ if (Selected) {
+ return 2;
+ }
+ return 1;
+}
+
+/***********************
+ * Highlightable Class *
+ ***********************/
+
+Highlightable::Highlightable(ScriptableType type)
+ : Scriptable( type )
+{
+ outline = NULL;
+ Highlight = false;
+ Cursor = IE_CURSOR_NORMAL;
+ KeyResRef[0] = 0;
+}
+
+Highlightable::~Highlightable(void)
+{
+ if (outline) {
+ delete( outline );
+ }
+}
+
+bool Highlightable::IsOver(const Point &Pos) const
+{
+ if (!outline) {
+ return false;
+ }
+ return outline->PointIn( Pos );
+}
+
+void Highlightable::DrawOutline() const
+{
+ if (!outline) {
+ return;
+ }
+ core->GetVideoDriver()->DrawPolyline( outline, outlineColor, true );
+}
+
+void Highlightable::SetCursor(unsigned char CursorIndex)
+{
+ Cursor = CursorIndex;
+}
+
+bool Highlightable::TryUnlock(Actor *actor, bool removekey) {
+ const char *Key = GetKey();
+ Actor *haskey = NULL;
+
+ if (Key && actor->InParty) {
+ Game *game = core->GetGame();
+ //allow unlock when the key is on any partymember
+ for (int idx = 0; idx < game->GetPartySize(false); idx++) {
+ Actor *pc = game->FindPC(idx + 1);
+ if (!pc) continue;
+
+ if (pc->inventory.HasItem(Key,0) ) {
+ haskey = pc;
+ break;
+ }
+ }
+ } else if (Key) {
+ //actor is not in party, check only actor
+ if (actor->inventory.HasItem(Key,0) ) {
+ haskey = actor;
+ }
+ }
+
+ if (!haskey) {
+ return false;
+ }
+
+ if (removekey) {
+ CREItem *item = NULL;
+ haskey->inventory.RemoveItem(Key,0,&item);
+ //the item should always be existing!!!
+ if (item) {
+ delete item;
+ }
+ }
+
+ return true;
+}
+
+
+/*****************
+ * Movable Class *
+ *****************/
+
+Movable::Movable(ScriptableType type)
+ : Selectable( type )
+{
+ Destination = Pos;
+ Orientation = 0;
+ NewOrientation = 0;
+ StanceID = 0;
+ path = NULL;
+ step = NULL;
+ timeStartStep = 0;
+ lastFrame = NULL;
+ Area[0] = 0;
+ AttackMovements[0] = 100;
+ AttackMovements[1] = 0;
+ AttackMovements[2] = 0;
+}
+
+Movable::~Movable(void)
+{
+ if (path) {
+ ClearPath();
+ }
+}
+
+int Movable::GetPathLength()
+{
+ PathNode *node = GetNextStep(0);
+ int i = 0;
+ while (node->Next) {
+ i++;
+ node = node->Next;
+ }
+ return i;
+}
+
+PathNode *Movable::GetNextStep(int x)
+{
+ if (!step) {
+ DoStep((unsigned int) ~0);
+ }
+ PathNode *node = step;
+ while(node && x--) {
+ node = node->Next;
+ }
+ return node;
+}
+
+Point Movable::GetMostLikelyPosition()
+{
+ if (!path) {
+ return Pos;
+ }
+
+//actually, sometimes middle path would be better, if
+//we stand in Destination already
+ int halfway = GetPathLength()/2;
+ PathNode *node = GetNextStep(halfway);
+ if (node) {
+ return Point((ieWord) ((node->x*16)+8), (ieWord) ((node->y*12)+6) );
+ }
+ return Destination;
+}
+
+void Movable::SetStance(unsigned int arg)
+{
+ //don't modify stance from dead back to anything if the actor is dead
+ if ((StanceID==IE_ANI_TWITCH || StanceID==IE_ANI_DIE) && (arg!=IE_ANI_TWITCH) ) {
+ if (GetInternalFlag()&IF_REALLYDIED) {
+ printMessage("Movable","Stance overridden by death\n", YELLOW);
+ return;
+ }
+ }
+
+ if (StanceID == IE_ANI_CONJURE && StanceID != arg && Type ==ST_ACTOR) {
+ Actor *caster = (Actor *) this;
+ if (caster->casting_sound) {
+ caster->casting_sound->Stop();
+ caster->casting_sound.release();
+ }
+ }
+
+ if (arg<MAX_ANIMS) {
+ StanceID=(unsigned char) arg;
+
+ if (StanceID == IE_ANI_ATTACK) {
+ // Set stance to a random attack animation
+
+ int random = rand()%100;
+ if (random < AttackMovements[0]) {
+ StanceID = IE_ANI_ATTACK_BACKSLASH;
+ } else if (random < AttackMovements[0] + AttackMovements[1]) {
+ StanceID = IE_ANI_ATTACK_SLASH;
+ } else {
+ StanceID = IE_ANI_ATTACK_JAB;
+ }
+ }
+
+ } else {
+ StanceID=IE_ANI_AWAKE; //
+ printf("Tried to set invalid stance id (%u)\n", arg);
+ }
+}
+
+void Movable::SetAttackMoveChances(ieWord *amc)
+{
+ AttackMovements[0]=amc[0];
+ AttackMovements[1]=amc[1];
+ AttackMovements[2]=amc[2];
+}
+
+
+
+//this could be used for WingBuffet as well
+void Movable::MoveLine(int steps, int Pass, ieDword orient)
+{
+ //remove previous path
+ ClearPath();
+ if (!steps)
+ return;
+ Point p = Pos;
+ p.x/=16;
+ p.y/=14;
+ path = area->GetLine( p, steps, orient, Pass );
+}
+
+void AdjustPositionTowards(Point &Pos, ieDword time_diff, unsigned int walk_speed, short srcx, short srcy, short destx, short desty) {
+ if (destx > srcx)
+ Pos.x += ( unsigned short )
+ ( ( ( ( ( destx * 16 ) + 8 ) - Pos.x ) * ( time_diff ) ) / walk_speed );
+ else
+ Pos.x -= ( unsigned short )
+ ( ( ( Pos.x - ( ( destx * 16 ) + 8 ) ) * ( time_diff ) ) / walk_speed );
+ if (desty > srcy)
+ Pos.y += ( unsigned short )
+ ( ( ( ( ( desty * 12 ) + 6 ) - Pos.y ) * ( time_diff ) ) / walk_speed );
+ else
+ Pos.y -= ( unsigned short )
+ ( ( ( Pos.y - ( ( desty * 12 ) + 6 ) ) * ( time_diff ) ) / walk_speed );
+}
+
+// returns whether we made all pending steps (so, false if we must be called again this tick)
+// we can't just do them all here because the caller might have to update searchmap etc
+bool Movable::DoStep(unsigned int walk_speed, ieDword time)
+{
+ if (!path) {
+ return true;
+ }
+ if (!time) time = core->GetGame()->Ticks;
+ if (!walk_speed) {
+ // zero speed: no movement
+ timeStartStep = time;
+ StanceID = IE_ANI_READY;
+ return true;
+ }
+ if (!step) {
+ step = path;
+ timeStartStep = time;
+ } else if (step->Next && (( time - timeStartStep ) >= walk_speed)) {
+ //printf("[New Step] : Orientation = %d\n", step->orient);
+ step = step->Next;
+ timeStartStep = timeStartStep + walk_speed;
+ }
+ SetOrientation (step->orient, false);
+ StanceID = IE_ANI_WALK;
+ if ((Type == ST_ACTOR) && (InternalFlags & IF_RUNNING)) {
+ StanceID = IE_ANI_RUN;
+ }
+ Pos.x = ( step->x * 16 ) + 8;
+ Pos.y = ( step->y * 12 ) + 6;
+ if (!step->Next) {
+ // we reached our destination, we are done
+ ClearPath();
+ NewOrientation = Orientation;
+ //since clearpath no longer sets currentaction to NULL
+ //we set it here
+ //no we don't, action is responsible for releasing itself
+ //ReleaseCurrentAction();
+ return true;
+ }
+ if (( time - timeStartStep ) >= walk_speed) {
+ // we didn't finish all pending steps, yet
+ return false;
+ }
+ AdjustPositionTowards(Pos, time - timeStartStep, walk_speed, step->x, step->y, step->Next->x, step->Next->y);
+ return true;
+}
+
+void Movable::AddWayPoint(const Point &Des)
+{
+ if (!path) {
+ WalkTo(Des);
+ return;
+ }
+ Destination = Des;
+ //it is tempting to use 'step' here, as it could
+ //be about half of the current path already
+ PathNode *endNode = path;
+ while(endNode->Next) {
+ endNode = endNode->Next;
+ }
+ Point p(endNode->x, endNode->y);
+ area->ClearSearchMapFor(this);
+ PathNode *path2 = area->FindPath( p, Des, size );
+ endNode->Next = path2;
+ //probably it is wise to connect it both ways?
+ path2->Parent = endNode;
+}
+
+void Movable::FixPosition()
+{
+ if (Type!=ST_ACTOR) {
+ return;
+ }
+ Actor *actor = (Actor *) this;
+ if (actor->GetStat(IE_DONOTJUMP)&DNJ_BIRD ) {
+ return;
+ }
+ //before fixposition, you should remove own shadow
+ area->ClearSearchMapFor(this);
+ Pos.x/=16;
+ Pos.y/=12;
+ GetCurrentArea()->AdjustPosition(Pos);
+ Pos.x=Pos.x*16+8;
+ Pos.y=Pos.y*12+6;
+}
+
+void Movable::WalkTo(const Point &Des, int distance)
+{
+ Point from;
+
+ // maybe caller should be responsible for this
+ if ((Des.x/16 == Pos.x/16) && (Des.y/12 == Pos.y/12)) {
+ ClearPath();
+ return;
+ }
+
+ // the prev_step stuff is a naive attempt to allow re-pathing while moving
+ PathNode *prev_step = NULL;
+ unsigned char old_stance = StanceID;
+ if (step && step->Next) {
+ // don't interrupt in the middle of a step; path from the next one
+ prev_step = new PathNode(*step);
+ from.x = ( step->Next->x * 16 ) + 8;
+ from.y = ( step->Next->y * 12 ) + 6;
+ }
+
+ ClearPath();
+ if (!prev_step) {
+ FixPosition();
+ from = Pos;
+ }
+ area->ClearSearchMapFor(this);
+ if (distance) {
+ path = area->FindPathNear( from, Des, size, distance );
+ } else {
+ path = area->FindPath( from, Des, size, distance );
+ }
+ //ClearPath sets destination, so Destination must be set after it
+ //also we should set Destination only if there is a walkable path
+ if (path) {
+ Destination = Des;
+
+ if (prev_step) {
+ // we want to smoothly continue, please
+ // this all needs more thought! but it seems to work okay
+ StanceID = old_stance;
+
+ if (path->Next) {
+ // this is a terrible hack to make up for the
+ // pathfinder orienting the first node wrong
+ // should be fixed in pathfinder and not here!
+ Point next, follow;
+ next.x = path->x; next.y = path->y;
+ follow.x = path->Next->x;
+ follow.y = path->Next->y;
+ path->orient = GetOrient(follow, next);
+ }
+
+ // then put the prev_step at the beginning of the path
+ prev_step->Next = path;
+ path->Parent = prev_step;
+ path = prev_step;
+
+ step = path;
+ }
+ } else {
+ // pathing failed
+ if (prev_step) {
+ delete( prev_step );
+ FixPosition();
+ }
+ }
+}
+
+void Movable::RunAwayFrom(const Point &Des, int PathLength, int flags)
+{
+ ClearPath();
+ area->ClearSearchMapFor(this);
+ path = area->RunAway( Pos, Des, size, PathLength, flags );
+}
+
+void Movable::RandomWalk(bool can_stop, bool run)
+{
+ if (path) {
+ return;
+ }
+ //if not continous random walk, then stops for a while
+ if (can_stop && (rand()&3) ) {
+ SetWait((rand()&7)+7);
+ return;
+ }
+ if (run) {
+ InternalFlags|=IF_RUNNING;
+ }
+ //the commenting-out of the clear search map call was removed in 0.4.0
+ //if you want to put it back for some reason, check
+ //if the searchmap is not eaten up
+ area->ClearSearchMapFor(this);
+ Point p = Pos;
+
+ //selecting points around a circle's edge around actor (didn't work better)
+ //int x = core->Roll(1,100,-50);
+ //p.x+=x;
+ //p.y+=(int) sqrt(100-x*x);
+
+ //selecting points in a square around actor
+ p.x+=core->Roll(1,50,-25);
+ p.y+=core->Roll(1,50,-25);
+ //the 5th parameter is controlling the orientation of the actor
+ //0 - back away, 1 - face direction
+ path = area->RunAway( Pos, p, size, 50, 1 );
+}
+
+void Movable::MoveTo(const Point &Des)
+{
+ area->ClearSearchMapFor(this);
+ Pos = Des;
+ Destination = Des;
+ if (BlocksSearchMap()) {
+ area->BlockSearchMap( Pos, size, IsPC()?PATH_MAP_PC:PATH_MAP_NPC);
+ }
+}
+
+void Movable::ClearPath()
+{
+ //this is to make sure attackers come to us
+ //make sure ClearPath doesn't screw Destination (in the rare cases Destination
+ //is set before ClearPath
+ Destination = Pos;
+ if (StanceID==IE_ANI_WALK || StanceID==IE_ANI_RUN) {
+ StanceID = IE_ANI_AWAKE;
+ }
+ InternalFlags&=~IF_NORECTICLE;
+ PathNode* thisNode = path;
+ while (thisNode) {
+ PathNode* nextNode = thisNode->Next;
+ delete( thisNode );
+ thisNode = nextNode;
+ }
+ path = NULL;
+ step = NULL;
+ //don't call ReleaseCurrentAction
+}
+
+void Movable::DrawTargetPoint(const Region &vp)
+{
+ if (!path || !Selected || (InternalFlags&IF_NORECTICLE) )
+ return;
+
+ // recticles are never drawn in cutscenes
+ if ((core->GetGameControl()->GetScreenFlags()&SF_CUTSCENE))
+ return;
+
+ // generates "step" from sequence 3 2 1 0 1 2 3 4
+ // updated each 1/15 sec
+ unsigned long step;
+ GetTime( step );
+ step = tp_steps [(step >> 6) & 7];
+
+ step = step + 1;
+ int csize = (size - 1) * 4;
+ if (csize < 4) csize = 3;
+
+ /* segments should not go outside selection radius */
+ unsigned short xradius = (csize * 4) - 5;
+ unsigned short yradius = (csize * 3) - 5;
+ ieWord xcentre = (ieWord) (Destination.x - vp.x);
+ ieWord ycentre = (ieWord) (Destination.y - vp.y);
+
+ // TODO: 0.5 and 0.7 are pretty much random values
+ // right segment
+ core->GetVideoDriver()->DrawEllipseSegment( xcentre + step, ycentre, xradius,
+ yradius, selectedColor, -0.5, 0.5 );
+ // top segment
+ core->GetVideoDriver()->DrawEllipseSegment( xcentre, ycentre - step, xradius,
+ yradius, selectedColor, -0.7 - M_PI_2, 0.7 - M_PI_2 );
+ // left segment
+ core->GetVideoDriver()->DrawEllipseSegment( xcentre - step, ycentre, xradius,
+ yradius, selectedColor, -0.5 - M_PI, 0.5 - M_PI );
+ // bottom segment
+ core->GetVideoDriver()->DrawEllipseSegment( xcentre, ycentre + step, xradius,
+ yradius, selectedColor, -0.7 - M_PI - M_PI_2, 0.7 - M_PI - M_PI_2 );
+}
+
+/**********************
+ * Tiled Object Class *
+ **********************/
+
+TileObject::TileObject()
+{
+ opentiles = NULL;
+ opencount = 0;
+ closedtiles = NULL;
+ closedcount = 0;
+ Flags = 0;
+}
+
+TileObject::~TileObject()
+{
+ if (opentiles) {
+ free( opentiles );
+ }
+ if (closedtiles) {
+ free( closedtiles );
+ }
+}
+
+void TileObject::SetOpenTiles(unsigned short* Tiles, int cnt)
+{
+ if (opentiles) {
+ free( opentiles );
+ }
+ opentiles = Tiles;
+ opencount = cnt;
+}
+
+void TileObject::SetClosedTiles(unsigned short* Tiles, int cnt)
+{
+ if (closedtiles) {
+ free( closedtiles );
+ }
+ closedtiles = Tiles;
+ closedcount = cnt;
+}
+
diff --git a/gemrb/core/Scriptable/Scriptable.h b/gemrb/core/Scriptable/Scriptable.h
new file mode 100644
index 0000000..d35a9bb
--- /dev/null
+++ b/gemrb/core/Scriptable/Scriptable.h
@@ -0,0 +1,438 @@
+/* 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 SCRIPTABLE_H
+#define SCRIPTABLE_H
+
+#include "exports.h"
+
+#include "CharAnimations.h"
+#include "Inventory.h"
+#include "PathFinder.h"
+#include "Sprite2D.h"
+#include "TileOverlay.h"
+#include "Variables.h"
+
+#include <list>
+
+class Action;
+class Actor;
+class Container;
+class Door;
+class GameScript;
+class Gem_Polygon;
+class Highlightable;
+class InfoPoint;
+class Movable;
+class Scriptable;
+class Selectable;
+class Spell;
+class SpriteCover;
+
+#define MAX_SCRIPTS 8
+#define MAX_GROUND_ICON_DRAWN 3
+#define MAX_TIMER 256
+
+/** The distance of operating a trigger, container, etc. */
+#define MAX_OPERATING_DISTANCE 40 //a search square is 16x12
+/** The distance between PC's who are about to enter a new area */
+#define MAX_TRAVELING_DISTANCE 400
+
+#define SCR_OVERRIDE 0
+#define SCR_AREA 1
+#define SCR_SPECIFICS 2
+#define SCR_RESERVED 3
+#define SCR_CLASS 4
+#define SCR_RACE 5
+#define SCR_GENERAL 6
+#define SCR_DEFAULT 7
+
+//pst trap flags (portal)
+#define PORTAL_CURSOR 1
+#define PORTAL_TRAVEL 2
+
+//trigger flags
+#define TRAP_INVISIBLE 1
+#define TRAP_RESET 2
+#define TRAVEL_PARTY 4
+#define TRAP_DETECTABLE 8
+//#define TRAP_16 16
+#define TRAP_LOWMEM 32 //special treatment when low on memory ?
+#define TRAP_NPC 64
+//#define TRAP_128 128
+#define TRAP_DEACTIVATED 256
+#define TRAVEL_NONPC 512
+#define TRAP_USEPOINT 1024 //override usage point of travel regions (used for sound in PST traps)
+#define INFO_DOOR 2048 //info trigger blocked by door
+
+//internal actor flags
+#define IF_GIVEXP 1 //give xp for this death
+#define IF_JUSTDIED 2 //Died() will return true
+#define IF_FROMGAME 4 //this is an NPC or PC
+#define IF_REALLYDIED 8 //real death happened, actor will be set to dead
+#define IF_NORECTICLE 16 //draw recticle (target mark)
+#define IF_NOINT 32 //cannot interrupt the actions of this actor (save is not possible!)
+#define IF_CLEANUP 64 //actor died chunky death, or other total destruction
+#define IF_RUNNING 128 //actor is running
+//these bits could be set by a WalkTo
+#define IF_RUNFLAGS (IF_RUNNING|IF_NORECTICLE|IF_NOINT)
+#define IF_BECAMEVISIBLE 0x100//actor just became visible (trigger event)
+#define IF_INITIALIZED 0x200
+#define IF_USEDSAVE 0x400 //actor needed saving throws
+#define IF_TARGETGONE 0x800 //actor's target is gone (trigger event)
+#define IF_USEEXIT 0x1000 //
+#define IF_INTRAP 0x2000 //actor is currently in a trap (intrap trigger event)
+#define IF_WASINDIALOG 0x4000 //actor just left dialog
+
+//scriptable flags
+#define IF_ACTIVE 0x10000
+#define IF_VISIBLE 0x40000
+#define IF_ONCREATION 0x80000
+#define IF_IDLE 0x100000
+#define IF_PARTYRESTED 0x200000 //party rested trigger event
+
+//the actor should stop attacking
+#define IF_STOPATTACK (IF_JUSTDIED|IF_REALLYDIED|IF_CLEANUP|IF_IDLE)
+
+//CheckTravel return value
+#define CT_CANTMOVE 0 //inactive
+#define CT_ACTIVE 1 //actor can move
+#define CT_GO_CLOSER 2 //entire team would move, but not close enough
+#define CT_WHOLE 3 //team can move
+#define CT_SELECTED 4 //not all selected actors are there
+#define CT_MOVE_SELECTED 5 //all selected can move
+
+//bits for binary trigger bitfield
+#define BT_DIE 1
+#define BT_ONCREATION 2
+#define BT_BECAMEVISIBLE 4
+#define BT_WASINDIALOG 8
+#define BT_PARTYRESTED 16
+#define BT_VACANT 32
+
+//xp bonus types (for xpbonus.2da)
+#define XP_LOCKPICK 0
+#define XP_DISARM 1
+#define XP_LEARNSPELL 2
+
+typedef enum ScriptableType { ST_ACTOR = 0, ST_PROXIMITY = 1, ST_TRIGGER = 2,
+ST_TRAVEL = 3, ST_DOOR = 4, ST_CONTAINER = 5, ST_AREA = 6, ST_GLOBAL = 7 } ScriptableType;
+
+typedef std::list<ieDword *> TriggerObjects;
+
+//#define SEA_RESET 0x00000002
+//#define SEA_PARTY_REQUIRED 0x00000004
+
+class GEM_EXPORT Scriptable {
+public:
+ Scriptable(ScriptableType type);
+ virtual ~Scriptable(void);
+private:
+ TriggerObjects tolist;
+ ieDword bittriggers;
+ unsigned long startTime;
+ unsigned long interval;
+ unsigned long WaitCounter;
+ // script_timers should probably be a std::map to
+ // conserve memory (usually at most 2 ids are used)
+ ieDword script_timers[MAX_TIMER];
+ ieDword globalID;
+protected: //let Actor access this
+ Map *area;
+ ieVariable scriptName;
+ ieDword InternalFlags; //for triggers
+ ieResRef Dialog;
+ std::list< Action*> actionQueue;
+ Action* CurrentAction;
+public:
+ int CurrentActionState;
+ ieDword CurrentActionTarget;
+ bool CurrentActionInterruptable;
+ ieDword lastDelay;
+ ieDword lastRunTime;
+ Variables* locals;
+ ScriptableType Type;
+ Point Pos;
+ ieStrRef DialogName;
+ ieResRef EnterWav; //play this wav file when stepping on the trap
+ GameScript* Scripts[MAX_SCRIPTS];
+ char* overHeadText;
+ Point overHeadTextPos;
+ unsigned char textDisplaying;
+ unsigned long timeStartDisplaying;
+ ieDword UnselectableTimer;
+ ieDword TriggerID; //for sendtrigger
+ ieDword LastTrigger; // also LastClosed
+ ieDword LastTriggerObject; // hack until someone fixes triggers
+ ieDword LastEntered; // also LastOpened
+ ieDword LastDisarmed; // also LastAttacker
+ ieDword LastDisarmFailed; //also LastTarget
+ ieDword LastUnlocked;
+ ieDword LastOpenFailed; // also LastPickpocketFailed
+ ieDword LastPickLockFailed;
+ int LastOrder;
+ ieDword LastOrderer;
+ ieDword LastSpellOnMe; //Last spell cast on this scriptable
+ ieDword LastCasterOnMe; //Last spellcaster on this scriptable
+ ieDword LastSpellSeen; //Last spell seen to be cast
+ ieDword LastCasterSeen; //Last spellcaster seen
+ Point LastTargetPos;
+ int SpellHeader;
+ ieResRef SpellResRef;
+public:
+ /** Gets the Dialog ResRef */
+ const char* GetDialog(void) const
+ {
+ return Dialog;
+ }
+ void SetDialog(const char *resref);
+ void SetScript(const ieResRef aScript, int idx, bool ai=false);
+ void SetSpellResRef(ieResRef resref);
+ void SetWait(unsigned long time);
+ unsigned long GetWait() const;
+ void LeaveDialog();
+ void Interrupt();
+ void NoInterrupt();
+ void Hide();
+ void Unhide();
+ void Activate();
+ void Deactivate();
+ void PartyRested();
+ ieDword GetInternalFlag();
+ const char* GetScriptName() const;
+ Map* GetCurrentArea() const;
+ void SetMap(Map *map);
+ void SetScript(int index, GameScript* script);
+ void DisplayHeadText(const char* text);
+ void FixHeadTextPos();
+ void SetScriptName(const char* text);
+ //call this to deny script running in the next AI cycle
+ void DelayedEvent();
+ //call this to enable script running as soon as possible
+ void ImmediateEvent();
+ bool IsPC() const;
+ void ExecuteScript(int scriptCount);
+ void AddAction(Action* aC);
+ void AddActionInFront(Action* aC);
+ Action* GetCurrentAction() const { return CurrentAction; }
+ Action* GetNextAction() const;
+ Action* PopNextAction();
+ void ClearActions();
+ void ReleaseCurrentAction();
+ bool InMove() const;
+ void ProcessActions(bool force);
+ //these functions handle clearing of triggers that resulted a
+ //true condition (whole triggerblock returned true)
+ void InitTriggers();
+ void ClearTriggers();
+ void SetBitTrigger(ieDword bittrigger);
+ void AddTrigger(ieDword *actorref);
+ /* re/draws overhead text on the map screen */
+ void DrawOverheadText(const Region &screen);
+ /* check if casting is allowed at all */
+ int CanCast(const ieResRef SpellResRef);
+ /* check for and trigger a wild surge */
+ int CheckWildSurge();
+ /* actor/scriptable casts spell */
+ int CastSpellPoint( ieResRef &SpellRef, const Point &Target, bool deplete, bool instant = false );
+ int CastSpell( ieResRef &SpellRef, Scriptable* Target, bool deplete, bool instant = false );
+ /* spellcasting finished */
+ void CastSpellPointEnd(int level);
+ void CastSpellEnd(int level);
+ ieDword GetGlobalID() const { return globalID; }
+ /** timer functions (numeric ID, not saved) */
+ bool TimerActive(ieDword ID);
+ bool TimerExpired(ieDword ID);
+ void StartTimer(ieDword ID, ieDword expiration);
+ virtual char* GetName(int /*which*/) const { return NULL; }
+private:
+ /* used internally to handle start of spellcasting */
+ int SpellCast(bool instant);
+ /* also part of the spellcasting process, creating the projectile */
+ void CreateProjectile(const ieResRef SpellResRef, ieDword tgt, int level, bool fake);
+ /* do some magic for the wierd/awesome wild surges */
+ bool HandleHardcodedSurge(ieResRef surgeSpellRef, Spell *spl, Actor *caster);
+};
+
+class GEM_EXPORT Selectable : public Scriptable {
+public:
+ Selectable(ScriptableType type);
+ virtual ~Selectable(void);
+public:
+ Region BBox;
+ ieWord Selected; //could be 0x80 for unselectable
+ bool Over;
+ Color selectedColor;
+ Color overColor;
+ Sprite2D *circleBitmap[2];
+ int size;
+private:
+ // current SpriteCover for wallgroups
+ SpriteCover* cover;
+public:
+ void SetBBox(const Region &newBBox);
+ void DrawCircle(const Region &vp);
+ bool IsOver(const Point &Pos) const;
+ void SetOver(bool over);
+ bool IsSelected() const;
+ void Select(int Value);
+ void SetCircle(int size, const Color &color, Sprite2D* normal_circle, Sprite2D* selected_circle);
+
+ /* store SpriteCover */
+ void SetSpriteCover(SpriteCover* c);
+ /* get stored SpriteCover */
+ SpriteCover* GetSpriteCover() const { return cover; }
+ /* want dithered SpriteCover */
+ int WantDither();
+};
+
+class GEM_EXPORT Highlightable : public Scriptable {
+public:
+ Highlightable(ScriptableType type);
+ virtual ~Highlightable(void);
+ virtual int TrapResets() const = 0;
+ virtual bool CanDetectTrap() const { return true; }
+ virtual bool PossibleToSeeTrap() const;
+public:
+ Gem_Polygon* outline;
+ Color outlineColor;
+ ieDword Cursor;
+ bool Highlight;
+ Point TrapLaunch;
+ ieWord TrapDetectionDiff;
+ ieWord TrapRemovalDiff;
+ ieWord Trapped;
+ ieWord TrapDetected;
+ ieResRef KeyResRef;
+public:
+ bool IsOver(const Point &Pos) const;
+ void DrawOutline() const;
+ void SetCursor(unsigned char CursorIndex);
+ const char* GetKey(void) const
+ {
+ if (KeyResRef[0]) return KeyResRef;
+ return NULL;
+ }
+ void SetTrapDetected(int x);
+ void TryDisarm(Actor *actor);
+ //detect trap, set skill to 256 if you want sure fire
+ void DetectTrap(int skill);
+ //returns true if trap is visible, only_detected must be true
+ //if you want to see discovered traps, false is for cheats
+ bool VisibleTrap(int only_detected) const;
+ //returns true if trap has been triggered, tumble skill???
+ virtual bool TriggerTrap(int skill, ieDword ID);
+ bool TryUnlock(Actor *actor, bool removekey);
+};
+
+class GEM_EXPORT Movable : public Selectable {
+private: //these seem to be sensitive, so get protection
+ unsigned char StanceID;
+ unsigned char Orientation, NewOrientation;
+ ieWord AttackMovements[3];
+
+ PathNode* path; //whole path
+ PathNode* step; //actual step
+public:
+ Movable(ScriptableType type);
+ virtual ~Movable(void);
+ Point Destination;
+ ieDword timeStartStep;
+ Sprite2D* lastFrame;
+ ieResRef Area;
+public:
+ PathNode *GetNextStep(int x);
+ int GetPathLength();
+//inliners to protect data consistency
+ inline PathNode * GetNextStep() {
+ if (!step) {
+ DoStep((unsigned int) ~0);
+ }
+ return step;
+ }
+
+ inline unsigned char GetOrientation() const {
+ return Orientation;
+ }
+
+ inline unsigned char GetNextFace() {
+ //slow turning
+ if (Orientation != NewOrientation) {
+ if ( ( (NewOrientation-Orientation) & (MAX_ORIENT-1) ) <= MAX_ORIENT/2) {
+ Orientation++;
+ } else {
+ Orientation--;
+ }
+ Orientation = Orientation&(MAX_ORIENT-1);
+ }
+
+ return Orientation;
+ }
+ inline unsigned char GetStance() const {
+ return StanceID;
+ }
+
+ inline void SetOrientation(int value, bool slow) {
+ //MAX_ORIENT == 16, so we can do this
+ NewOrientation = (unsigned char) (value&(MAX_ORIENT-1));
+ if (!slow) {
+ Orientation = NewOrientation;
+ }
+ }
+
+ void SetStance(unsigned int arg);
+ void SetAttackMoveChances(ieWord *amc);
+ bool DoStep(unsigned int walk_speed, ieDword time = 0);
+ void AddWayPoint(const Point &Des);
+ void RunAwayFrom(const Point &Des, int PathLength, int flags);
+ void RandomWalk(bool can_stop, bool run);
+ void MoveLine(int steps, int Pass, ieDword Orient);
+ void FixPosition();
+ void WalkTo(const Point &Des, int MinDistance = 0);
+ void MoveTo(const Point &Des);
+ void ClearPath();
+ void DrawTargetPoint(const Region &vp);
+ /* returns the most likely position of this actor */
+ Point GetMostLikelyPosition();
+ virtual bool BlocksSearchMap() const = 0;
+
+};
+
+//Tiled objects are not used (and maybe not even implemented correctly in IE)
+//they seem to be most closer to a door and probably obsoleted by it
+//are they scriptable?
+class GEM_EXPORT TileObject {
+public:
+ TileObject(void);
+ ~TileObject(void);
+ void SetOpenTiles(unsigned short *indices, int count);
+ void SetClosedTiles(unsigned short *indices, int count);
+
+public:
+ ieVariable Name;
+ ieResRef Tileset; //or wed door ID?
+ ieDword Flags;
+ unsigned short* opentiles;
+ ieDword opencount;
+ unsigned short* closedtiles;
+ ieDword closedcount;
+};
+
+#endif
diff --git a/gemrb/core/ScriptedAnimation.cpp b/gemrb/core/ScriptedAnimation.cpp
new file mode 100644
index 0000000..f1c9d9e
--- /dev/null
+++ b/gemrb/core/ScriptedAnimation.cpp
@@ -0,0 +1,777 @@
+/* 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.
+ *
+ *
+ */
+
+// This class handles VVC files of BG2/ToB and converts BAM files to the
+// common internal animation format on the fly.
+
+#include "ScriptedAnimation.h"
+
+#include "win32def.h"
+
+#include "Audio.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "Video.h"
+
+#define ILLEGAL 0 //
+#define ONE 1 //hold
+#define TWO 2 //onset + hold
+#define THREE 3 //onset + hold + release
+#define DOUBLE 4 //has twin (pst)
+#define FIVE 8 //five faces (orientation)
+#define NINE 16 //nine faces (orientation)
+#define SEVENEYES 32 //special hack for seven eyes
+
+#define DEFAULT_FRAMERATE 15
+#define MAX_CYCLE_TYPE 16
+static const ieByte ctypes[MAX_CYCLE_TYPE]={
+ ILLEGAL, ONE, TWO, THREE, TWO|DOUBLE, ONE|FIVE, THREE|DOUBLE, ILLEGAL,
+ SEVENEYES, ONE|NINE, TWO|FIVE, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,THREE|FIVE,
+};
+
+static const ieByte SixteenToNine[3*MAX_ORIENT]={
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1,
+ 9,10,11,12,13,14,15,16,17,16,15,14,13,12,11,10,
+ 18,19,20,21,22,23,24,25,26,25,24,23,22,21,20,19
+};
+static const ieByte SixteenToFive[3*MAX_ORIENT]={
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 3, 3, 2, 2, 1, 1,
+ 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 8, 8, 7, 7, 6, 6,
+ 10,10,11,11,12,12,13,13,14,14,13,13,12,12,11,11
+};
+
+ScriptedAnimation::ScriptedAnimation()
+{
+ Init();
+}
+
+void ScriptedAnimation::Init()
+{
+ cover = NULL;
+ memset(anims,0,sizeof(anims));
+ palette = NULL;
+ sounds[0][0] = 0;
+ sounds[1][0] = 0;
+ sounds[2][0] = 0;
+ memset(&Tint,0,sizeof(Tint));
+ Transparency = 0;
+ Fade = 0;
+ SequenceFlags = 0;
+ XPos = YPos = ZPos = 0;
+ FrameRate = DEFAULT_FRAMERATE;
+ FaceTarget = 0;
+ Orientation = 0;
+ Dither = 0;
+ Duration = 0xffffffff;
+ justCreated = true;
+ PaletteName[0]=0;
+ twin = NULL;
+ Phase = P_NOTINITED;
+ effect_owned = false;
+ active = true;
+ Delay = 0;
+ light = NULL;
+ LightX = 0;
+ LightY = 0;
+ LightZ = 0;
+ starttime = 0;
+}
+
+void ScriptedAnimation::Override(ScriptedAnimation *templ)
+{
+ Transparency = templ->Transparency;
+ SequenceFlags = templ->SequenceFlags;
+ XPos = templ->XPos;
+ YPos = templ->YPos;
+ ZPos = templ->ZPos;
+ FrameRate = templ->FrameRate;
+ FaceTarget = templ->FaceTarget;
+ for (unsigned int i=0;i<3;i++) {
+ memcpy(sounds[i],templ->sounds[i],sizeof(ieResRef));
+ }
+ if (templ->Duration!=0xffffffff) {
+ SetDefaultDuration(templ->Duration);
+ }
+ if (templ->PaletteName[0]) {
+ SetFullPalette(templ->PaletteName);
+ }
+}
+
+//prepare the animation before doing anything
+void ScriptedAnimation::PrepareAnimation(Animation *anim, ieDword Transparency)
+{
+ if (Transparency&IE_VVC_MIRRORX) {
+ anim->MirrorAnimation();
+ }
+ if (Transparency&IE_VVC_MIRRORY) {
+ anim->MirrorAnimationVert();
+ }
+}
+
+/* Creating animation from BAM */
+void ScriptedAnimation::LoadAnimationFactory(AnimationFactory *af, int gettwin)
+{
+ //getcycle returns NULL if there is no such cycle
+ //special case, PST double animations
+
+ memcpy(ResName, af->ResRef, sizeof(ResName) );
+ unsigned int cCount=af->GetCycleCount();
+ if (cCount>=MAX_CYCLE_TYPE) {
+ cCount=1;
+ }
+
+ int type = ctypes[cCount];
+ switch(gettwin) {
+ case 2:
+ if (type==TWO) {
+ type=ONE|DOUBLE;
+ }
+ gettwin=0;
+ break;
+ case 1:
+ type=ONE|DOUBLE;
+ break;
+ }
+
+ if (type==ILLEGAL) {
+ cCount=1;
+ type=ONE;
+ }
+ else if (type&DOUBLE) {
+ cCount/=2;
+ }
+
+ //these fields mark that the anim cycles should 'follow' the target's orientation
+ if (type&FIVE) {
+ FaceTarget = 5;
+ cCount = MAX_ORIENT*(type&3);
+ } else if (type&NINE) {
+ FaceTarget = 9;
+ cCount = MAX_ORIENT*(type&3);
+ } else {
+ FaceTarget = 0;
+ }
+
+ for(unsigned int i=0;i<cCount;i++) {
+ bool mirror = false;
+ int c = i;
+ int p = i;
+ if (type&DOUBLE) {
+ c<<=1;
+ if (gettwin) c++;
+ } else if (type&FIVE) {
+ c=SixteenToFive[c];
+ if ((i&15)>=5) mirror = true;
+ } else if (type&NINE) {
+ c=SixteenToNine[c];
+ if ((i&15)>=9) mirror = true;
+ } else if (!(type&SEVENEYES)) {
+ p*=MAX_ORIENT;
+ }
+
+ anims[p] = af->GetCycle( (ieByte) c );
+ if (anims[p]) {
+ anims[p]->pos=0;
+ if (mirror) {
+ anims[p]->MirrorAnimation();
+ }
+ anims[p]->gameAnimation=true;
+ }
+ }
+
+ for (unsigned int o = 0; o<MAX_ORIENT; o++) {
+ unsigned int p_hold = P_HOLD*MAX_ORIENT+o;
+ unsigned int p_onset = P_ONSET*MAX_ORIENT+o;
+ unsigned int p_release = P_RELEASE*MAX_ORIENT+o;
+ //if there is no hold anim, move the onset anim there
+ if (!anims[p_hold]) {
+ anims[p_hold]=anims[p_onset];
+ anims[p_onset]=NULL;
+ }
+ //onset and release phases are to be played only once
+ if (anims[p_onset])
+ anims[p_onset]->Flags |= S_ANI_PLAYONCE;
+ if (anims[p_release])
+ anims[p_release]->Flags |= S_ANI_PLAYONCE;
+ }
+ //we are getting a twin, no need of going further,
+ //if there is any more common initialisation, it should
+ //go above this point
+ if (gettwin) {
+ return;
+ }
+ if (type&DOUBLE) {
+ twin = new ScriptedAnimation();
+ twin->LoadAnimationFactory(af, 1);
+ }
+ SetPhase(P_ONSET);
+}
+
+/* Creating animation from VVC */
+ScriptedAnimation::ScriptedAnimation(DataStream* stream, bool autoFree)
+{
+ Init();
+ if (!stream) {
+ return;
+ }
+
+ char Signature[8];
+
+ stream->Read( Signature, 8);
+ if (strncmp( Signature, "VVC V1.0", 8 ) != 0) {
+ printf( "Not a valid VVC File\n" );
+ if (autoFree)
+ delete( stream );
+ return;
+ }
+ ieResRef Anim1ResRef;
+ ieDword seq1, seq2, seq3;
+ stream->ReadResRef( Anim1ResRef );
+ //there is no proof it is a second resref
+ //stream->ReadResRef( Anim2ResRef );
+ stream->Seek( 8, GEM_CURRENT_POS );
+ stream->ReadDword( &Transparency );
+ stream->Seek( 4, GEM_CURRENT_POS );
+ stream->ReadDword( &SequenceFlags );
+ stream->Seek( 4, GEM_CURRENT_POS );
+ ieDword tmp;
+ stream->ReadDword( &tmp );
+ XPos = (signed) tmp;
+ stream->ReadDword( &tmp ); //this affects visibility
+ ZPos = (signed) tmp;
+ stream->Seek( 4, GEM_CURRENT_POS );
+ stream->ReadDword( &FrameRate );
+
+ if (!FrameRate) FrameRate = DEFAULT_FRAMERATE;
+
+ stream->ReadDword( &FaceTarget );
+ stream->Seek( 16, GEM_CURRENT_POS );
+ stream->ReadDword( &tmp ); //this doesn't affect visibility
+ YPos = (signed) tmp;
+ stream->ReadDword( &LightX );
+ stream->ReadDword( &LightY );
+ stream->ReadDword( &LightZ );
+ stream->ReadDword( &Duration );
+ stream->Seek( 8, GEM_CURRENT_POS );
+ stream->ReadDword( &seq1 );
+ if (seq1>0) seq1--; //hack but apparently it works this way
+ stream->ReadDword( &seq2 );
+ stream->Seek( 8, GEM_CURRENT_POS );
+ stream->ReadResRef( sounds[P_ONSET] );
+ stream->ReadResRef( sounds[P_HOLD] );
+ stream->Seek( 8, GEM_CURRENT_POS );
+ stream->ReadDword( &seq3 );
+ stream->ReadResRef( sounds[P_RELEASE] );
+
+ //if there are no separate phases, then fill the p_hold fields
+ bool phases = (seq2 || seq3);
+
+ // hacks for seq2/seq3, same as for seq1 above
+ // (not sure if seq3 is needed)
+ if (seq2>0) seq2--;
+ if (seq3>0) seq3--;
+
+ if (SequenceFlags&IE_VVC_BAM) {
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( Anim1ResRef, IE_BAM_CLASS_ID );
+ //no idea about vvc phases, i think they got no endphase?
+ //they certainly got onset and hold phases
+ //the face target flag should be handled too
+ for (int i=0;i<MAX_ORIENT;i++) {
+ unsigned int p_hold = P_HOLD*MAX_ORIENT+i;
+ unsigned int p_onset = P_ONSET*MAX_ORIENT+i;
+ unsigned int p_release = P_RELEASE*MAX_ORIENT+i;
+
+ int c = seq1;
+ if (phases) {
+ switch (FaceTarget) {
+ case 5:
+ c=SixteenToFive[i];
+ break;
+ case 9:
+ c=SixteenToNine[i];
+ break;
+ case 16:
+ //this is an uglybugly hack, i still have to
+ //figure out what 'FaceTarget' really is
+ if ( (int) af->GetCycleCount()>i) c=i;
+ break;
+ }
+ anims[p_onset] = af->GetCycle( c );
+ if (anims[p_onset]) {
+ PrepareAnimation(anims[p_onset], Transparency);
+ //creature anims may start at random position, vvcs always start on 0
+ anims[p_onset]->pos=0;
+ //vvcs are always paused
+ anims[p_onset]->gameAnimation=true;
+ anims[p_onset]->Flags |= S_ANI_PLAYONCE;
+ }
+ }
+
+ c = phases ? seq2 : seq1;
+ if (c || !phases) {
+ switch (FaceTarget) {
+ case 5:
+ c=SixteenToFive[i];
+ break;
+ case 9:
+ c=SixteenToNine[i];
+ break;
+ case 16:
+ //this is an uglybugly hack, i still have to
+ //figure out what 'FaceTarget' really is
+ if ((int) af->GetCycleCount()>i) c=i;
+ break;
+ }
+ anims[p_hold] = af->GetCycle( c );
+ if (anims[p_hold]) {
+ PrepareAnimation(anims[p_hold], Transparency);
+
+ anims[p_hold]->pos=0;
+ anims[p_hold]->gameAnimation=true;
+ if (!(SequenceFlags&IE_VVC_LOOP) ) {
+ anims[p_hold]->Flags |= S_ANI_PLAYONCE;
+ }
+ }
+ }
+
+ c = seq3;
+ if (c) {
+ switch (FaceTarget) {
+ case 5:
+ c=SixteenToFive[i];
+ break;
+ case 9:
+ c=SixteenToNine[i];
+ break;
+ case 16:
+ //this is an uglybugly hack, i still have to
+ //figure out what 'FaceTarget' really is
+ if ( (int) af->GetCycleCount()>i) c=i;
+ break;
+ }
+ anims[p_release] = af->GetCycle( ( unsigned char ) c );
+ if (anims[p_release]) {
+ PrepareAnimation(anims[p_release], Transparency);
+
+ anims[p_release]->pos=0;
+ anims[p_release]->gameAnimation=true;
+ anims[p_release]->Flags |= S_ANI_PLAYONCE;
+ }
+ }
+ }
+ PreparePalette();
+ }
+
+ SetPhase(P_ONSET);
+
+ if (autoFree) {
+ delete( stream );
+ }
+}
+
+ScriptedAnimation::~ScriptedAnimation(void)
+{
+ for(unsigned int i=0;i<3*MAX_ORIENT;i++) {
+ if (anims[i]) {
+ delete( anims[i] );
+ }
+ }
+ gamedata->FreePalette(palette, PaletteName);
+
+ if (cover) {
+ SetSpriteCover(NULL);
+ }
+ if (twin) {
+ delete twin;
+ }
+ if (sound_handle) {
+ sound_handle->Stop();
+ sound_handle.release();
+ }
+ if(light) {
+ core->GetVideoDriver()->FreeSprite(light);
+ }
+}
+
+void ScriptedAnimation::SetPhase(int arg)
+{
+ if (arg>=P_ONSET && arg<=P_RELEASE) {
+ Phase = arg;
+ }
+ SetSpriteCover(NULL);
+ if (twin) {
+ twin->SetPhase(Phase);
+ }
+}
+
+void ScriptedAnimation::SetSound(int arg, const ieResRef sound)
+{
+ if (arg>=P_ONSET && arg<=P_RELEASE) {
+ memcpy(sounds[arg],sound,sizeof(sound));
+ }
+ //no need to call the twin
+}
+
+void ScriptedAnimation::PlayOnce()
+{
+ SequenceFlags&=~IE_VVC_LOOP;
+ for (unsigned int i=0;i<3*MAX_ORIENT;i++) {
+ if (anims[i]) {
+ anims[i]->Flags |= S_ANI_PLAYONCE;
+ }
+ }
+ if (twin) {
+ twin->PlayOnce();
+ }
+}
+
+void ScriptedAnimation::SetFullPalette(const ieResRef PaletteResRef)
+{
+ gamedata->FreePalette(palette, PaletteName);
+ palette=gamedata->GetPalette(PaletteResRef);
+ memcpy(PaletteName, PaletteResRef, sizeof(PaletteName) );
+ if (twin) {
+ twin->SetFullPalette(PaletteResRef);
+ }
+}
+
+void ScriptedAnimation::SetFullPalette(int idx)
+{
+ ieResRef PaletteResRef;
+
+ //make sure this field is zero terminated, or strlwr will run rampant!!!
+ snprintf(PaletteResRef,sizeof(PaletteResRef),"%.7s%d",ResName, idx);
+ strnlwrcpy(PaletteResRef,PaletteResRef,8);
+ SetFullPalette(PaletteResRef);
+ //no need to call twin
+}
+
+#define PALSIZE 12
+static Color NewPal[PALSIZE];
+
+void ScriptedAnimation::SetPalette(int gradient, int start)
+{
+ //get a palette
+ GetPaletteCopy();
+ if (!palette)
+ return;
+ //default start
+ if (start==-1) {
+ start=4;
+ }
+ core->GetPalette( gradient&255, PALSIZE, NewPal );
+
+ memcpy( &palette->col[start], NewPal, PALSIZE*sizeof( Color ) );
+ if (twin) {
+ twin->SetPalette(gradient, start);
+ }
+}
+
+int ScriptedAnimation::GetCurrentFrame()
+{
+ Animation *anim = anims[P_HOLD*MAX_ORIENT];
+ if (anim) {
+ return anim->GetCurrentFrame();
+ }
+ return 0;
+}
+
+ieDword ScriptedAnimation::GetSequenceDuration(ieDword multiplier)
+{
+ //P_HOLD * MAX_ORIENT == MAX_ORIENT
+ Animation *anim = anims[P_HOLD*MAX_ORIENT];
+ if (anim) {
+ return anim->GetFrameCount()*multiplier/FrameRate;
+ }
+ return 0;
+}
+
+void ScriptedAnimation::SetDelay(ieDword delay)
+{
+ Delay = delay;
+ if (twin) {
+ twin->Delay=delay;
+ }
+}
+
+void ScriptedAnimation::SetDefaultDuration(ieDword duration)
+{
+ if (!(SequenceFlags&(IE_VVC_LOOP|IE_VVC_FREEZE) )) return;
+ if (Duration==0xffffffff) {
+ Duration = duration;
+ }
+ if (twin) {
+ twin->Duration=Duration;
+ }
+}
+
+void ScriptedAnimation::SetOrientation(int orientation)
+{
+ if (orientation==-1) {
+ return;
+ }
+ if(FaceTarget) {
+ Orientation=(ieByte) orientation;
+ } else {
+ Orientation = 0;
+ }
+ if (twin) {
+ twin->Orientation=Orientation;
+ }
+}
+
+bool ScriptedAnimation::HandlePhase(Sprite2D *&frame)
+{
+ unsigned int inc = 0;
+
+ if (justCreated) {
+ if (Phase == P_NOTINITED) {
+ printMessage("ScriptedAnimation", "Not fully initialised VVC!\n", LIGHT_RED);
+ return true;
+ }
+ unsigned long time;
+ time = core->GetGame()->Ticks;
+ if (starttime == 0) {
+ starttime = time;
+ }
+ if (( time - starttime ) >= ( unsigned long ) ( 1000 / FrameRate )) {
+ inc = (time-starttime)*FrameRate/1000;
+ starttime += inc*1000/FrameRate;
+ }
+
+ if (Delay>inc) {
+ Delay-=inc;
+ return false;
+ }
+
+ if (SequenceFlags&IE_VVC_LIGHTSPOT) {
+ light = core->GetVideoDriver()->CreateLight(LightX, LightZ);
+ }
+
+ if (Duration!=0xffffffff) {
+ Duration += core->GetGame()->GameTime;
+ }
+
+retry:
+ if (sounds[Phase][0] != 0) {
+ sound_handle = core->GetAudioDrv()->Play( sounds[Phase] );
+ }
+
+ if (justCreated && !anims[P_ONSET*MAX_ORIENT+Orientation]) {
+ Phase = P_HOLD;
+ }
+ justCreated = false;
+ }
+
+ // if we're looping forever and we didn't get 'bumped' by an effect
+ if (effect_owned && (SequenceFlags&IE_VVC_LOOP) && Duration==0xffffffff && !active) {
+ PlayOnce();
+ }
+
+ if (!anims[Phase*MAX_ORIENT+Orientation]) {
+ if (Phase>=P_RELEASE) {
+ return true;
+ }
+ Phase++;
+ goto retry;
+ }
+ frame = anims[Phase*MAX_ORIENT+Orientation]->NextFrame();
+
+ //explicit duration
+ if (Phase==P_HOLD) {
+ if (core->GetGame()->GameTime>Duration) {
+ Phase++;
+ goto retry;
+ }
+ }
+ if (SequenceFlags&IE_VVC_FREEZE) {
+ return false;
+ }
+
+ //automatically slip from onset to hold to release
+ if (!frame || anims[Phase*MAX_ORIENT+Orientation]->endReached) {
+ if (Phase>=P_RELEASE) {
+ return true;
+ }
+ //this section implements the freeze fading effect (see ice dagger)
+ if (frame && Fade && Tint.a && (Phase==P_HOLD) ) {
+ if (Tint.a<=Fade) {
+ return true;
+ }
+ Tint.a-=Fade;
+ return false;
+ }
+ Phase++;
+ goto retry;
+ }
+ return false;
+}
+
+//it is not sure if we need tint at all
+bool ScriptedAnimation::Draw(const Region &screen, const Point &Pos, const Color &p_tint, Map *area, int dither, int orientation)
+{
+ if (FaceTarget) {
+ SetOrientation(orientation);
+ }
+
+ // not sure
+ if (twin) {
+ twin->Draw(screen, Pos, p_tint, area, dither, -1);
+ }
+
+ Video *video = core->GetVideoDriver();
+
+ Sprite2D* frame;
+
+ if (HandlePhase(frame)) {
+ //expired
+ return true;
+ }
+
+ //delayed
+ if (justCreated) {
+ return false;
+ }
+
+ ieDword flag = BLIT_TRANSSHADOW;
+ //transferring flags to SDLdriver, this will have to be consolidated later
+
+ if (Transparency & IE_VVC_TRANSPARENT) {
+ flag |= BLIT_HALFTRANS;
+ }
+
+ Color tint = Tint;
+
+ //darken, greyscale, red tint are probably not needed if the global tint works
+ //these are used in the original engine to implement weather/daylight effects
+ //on the other hand
+
+ if (Transparency & IE_VVC_GREYSCALE) {
+ flag |= BLIT_GREY;
+ }
+
+ if (Transparency & IE_VVC_SEPIA) {
+ flag |= BLIT_RED;
+ }
+
+ if (Transparency & BLIT_TINTED) {
+ flag |= BLIT_TINTED;
+ }
+
+ if ((Transparency & IE_VVC_TINT)==IE_VVC_TINT) {
+ tint = p_tint;
+ }
+
+ int cx = Pos.x + XPos;
+ int cy = Pos.y - ZPos + YPos;
+
+ if( SequenceFlags&IE_VVC_NOCOVER) {
+ if (cover) SetSpriteCover(NULL);
+ } else {
+ if (!cover || (Dither!=dither) || (!cover->Covers(cx, cy, frame->XPos, frame->YPos, frame->Width, frame->Height)) ) {
+ Dither = dither;
+ Animation *anim = anims[Phase*MAX_ORIENT+Orientation];
+ SetSpriteCover(area->BuildSpriteCover(cx, cy, -anim->animArea.x,
+ -anim->animArea.y, anim->animArea.w, anim->animArea.h, dither) );
+ }
+ assert(cover->Covers(cx, cy, frame->XPos, frame->YPos, frame->Width, frame->Height));
+ }
+
+ video->BlitGameSprite( frame, cx + screen.x, cy + screen.y, flag, tint, cover, palette, &screen);
+ if (light) {
+ video->BlitGameSprite( light, cx + screen.x, cy + screen.y, 0, tint, NULL, NULL, &screen);
+ }
+ return false;
+}
+
+void ScriptedAnimation::PreparePalette()
+{
+ if (Transparency&IE_VVC_BLENDED) {
+ GetPaletteCopy();
+ if (!palette)
+ return;
+ if (!palette->alpha) {
+ palette->CreateShadedAlphaChannel();
+ }
+ }
+}
+
+void ScriptedAnimation::SetBlend()
+{
+ Transparency |= IE_VVC_BLENDED;
+ PreparePalette();
+ if (twin)
+ twin->SetBlend();
+}
+
+void ScriptedAnimation::SetFade(ieByte initial, int speed)
+{
+ Tint.r=255;
+ Tint.g=255;
+ Tint.b=255;
+ Tint.a=initial;
+ Fade=speed;
+ Transparency|=BLIT_TINTED;
+}
+
+void ScriptedAnimation::GetPaletteCopy()
+{
+ if (palette)
+ return;
+ //it is not sure that the first position will have a resource in it
+ //therefore the cycle
+ for (unsigned int i=0;i<3*MAX_ORIENT;i++) {
+ if (anims[i]) {
+ Sprite2D* spr = anims[i]->GetFrame(0);
+ if (spr) {
+ palette = spr->GetPalette()->Copy();
+ //we need only one palette, so break here
+ break;
+ }
+ }
+ }
+}
+
+void ScriptedAnimation::AlterPalette(const RGBModifier& mod)
+{
+ GetPaletteCopy();
+ if (!palette)
+ return;
+ palette->SetupGlobalRGBModification(palette,mod);
+ if (twin) {
+ twin->AlterPalette(mod);
+ }
+}
+
+ScriptedAnimation *ScriptedAnimation::DetachTwin()
+{
+ if (!twin) {
+ return NULL;
+ }
+ ScriptedAnimation * ret = twin;
+ //ret->YPos+=ret->ZPos+1;
+ if (ret->ZPos>=0) {
+ ret->ZPos=-1;
+ }
+ twin=NULL;
+ return ret;
+}
diff --git a/gemrb/core/ScriptedAnimation.h b/gemrb/core/ScriptedAnimation.h
new file mode 100644
index 0000000..088a8bf
--- /dev/null
+++ b/gemrb/core/ScriptedAnimation.h
@@ -0,0 +1,153 @@
+/* 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 SCRIPTEDANIMATION_H
+#define SCRIPTEDANIMATION_H
+
+#include "exports.h"
+
+#include "AnimationFactory.h"
+#include "Audio.h"
+#include "Map.h"
+#include "Palette.h"
+#include "SpriteCover.h"
+#include "System/DataStream.h"
+
+//scripted animation flags
+#define S_ANI_PLAYONCE 8 //(same as area animation)
+
+#define IE_VVC_TRANSPARENT 0x00000002
+#define IE_VVC_BLENDED 0x00000008
+#define IE_VVC_MIRRORX 0x00000010
+#define IE_VVC_MIRRORY 0x00000020
+#define IE_VVC_CLIPPED 0x00000040
+#define IE_VVC_3D_BLEND 0x00000200
+#define IE_VVC_NOCOVER_2 0x00000400
+#define IE_VVC_NO_TIMESTOP 0x00000800 //ignore timestop palette
+#define IE_VVC_NO_SEPIA 0x00001000 //ignore dream palette
+#define IE_VVC_2D_BLEND 0x00002000
+#define IE_VVC_TINT 0x00030000 //2 bits need to be set for tint
+#define IE_VVC_GREYSCALE 0x00080000 //timestopped palette
+#define IE_VVC_DARKEN 0x00100000 //this is unsure
+#define IE_VVC_GLOWING 0x00200000 //internal gamma
+#define IE_VVC_SEPIA 0x02000000 //dream palette
+
+#define IE_VVC_LOOP 0x00000001
+#define IE_VVC_LIGHTSPOT 0x00000002 //draw lightspot
+#define IE_VVC_HEIGHT 0x00000004
+#define IE_VVC_BAM 0x00000008
+#define IE_VVC_OWN_PAL 0x00000010
+#define IE_VVC_NOCOVER 0x00000040
+#define IE_VVC_MID_BRIGHTEN 0x00000080
+#define IE_VVC_HIGH_BRIGHTEN 0x00000100
+
+
+//#define IE_VVC_UNUSED 0xe0000000U
+//gemrb specific sequence flags
+#define IE_VVC_FREEZE 0x80000000
+
+//phases
+#define P_NOTINITED -1
+#define P_ONSET 0
+#define P_HOLD 1
+#define P_RELEASE 2
+
+class GEM_EXPORT ScriptedAnimation {
+public:
+ ScriptedAnimation();
+ ~ScriptedAnimation(void);
+ ScriptedAnimation(DataStream* stream, bool autoFree = true);
+ void Init();
+ void LoadAnimationFactory(AnimationFactory *af, int gettwin = 0);
+ void Override(ScriptedAnimation *templ);
+ //there are 3 phases: start, hold, release
+ //it will usually cycle in the 2. phase
+ //the anims could also be used 'orientation based' if FaceTarget is
+ //set to 5, 9, 16
+ Animation* anims[3*MAX_ORIENT];
+ //there is only one palette
+ Palette *palette;
+ ieResRef sounds[3];
+ ieResRef PaletteName;
+ Color Tint;
+ int Fade;
+ ieDword Transparency;
+ ieDword SequenceFlags;
+ int Dither;
+ //these are signed
+ int XPos, YPos, ZPos;
+ ieDword LightX, LightY, LightZ;
+ Sprite2D* light;//this is just a round/halftrans sprite, has no animation
+ ieDword FrameRate;
+ ieDword FaceTarget;
+ ieByte Orientation;
+ ieDword Duration;
+ ieDword Delay;
+ bool justCreated;
+ ieResRef ResName;
+ int Phase;
+ SpriteCover* cover;
+ ScriptedAnimation *twin;
+ bool active;
+ bool effect_owned;
+ Holder<SoundHandle> sound_handle;
+ unsigned long starttime;
+public:
+ //draws the next frame of the videocell
+ bool Draw(const Region &screen, const Point &Pos, const Color &tint, Map *area, int dither, int orientation);
+ //sets phase (0-2)
+ void SetPhase(int arg);
+ //sets sound for phase (p_onset, p_hold, p_release)
+ void SetSound(int arg, const ieResRef sound);
+ //sets the animation to play only once
+ void PlayOnce();
+ //sets gradient colour slot to gradient
+ void SetPalette(int gradient, int start=-1);
+ //sets complete palette to ResRef
+ void SetFullPalette(const ieResRef PaletteResRef);
+ //sets complete palette to own name+index
+ void SetFullPalette(int idx);
+ //sets spritecover
+ void SetSpriteCover(SpriteCover* c) { delete cover; cover = c; }
+ /* get stored SpriteCover */
+ SpriteCover* GetSpriteCover() const { return cover; }
+ int GetCurrentFrame();
+ ieDword GetSequenceDuration(ieDword multiplier);
+ /* sets up a delay in the beginning of the vvc */
+ void SetDelay(ieDword delay);
+ /* sets default duration if it wasn't set yet */
+ void SetDefaultDuration(unsigned int duration);
+ /* sets up the direction of the vvc */
+ void SetOrientation(int orientation);
+ /* transforms vvc to blended */
+ void SetBlend();
+ /* sets fade effect at end of animation (pst feature) */
+ void SetFade(ieByte initial, int speed);
+ /* alters palette with rgb factor */
+ void AlterPalette(const RGBModifier &rgb);
+ /* returns possible twin after altering it to become underlay */
+ ScriptedAnimation *DetachTwin();
+private:
+ void PrepareAnimation(Animation *anim, ieDword Transparency);
+ void PreparePalette();
+ bool HandlePhase(Sprite2D *&frame);
+ void GetPaletteCopy();
+};
+
+#endif
diff --git a/gemrb/core/SoundMgr.cpp b/gemrb/core/SoundMgr.cpp
new file mode 100644
index 0000000..2172155
--- /dev/null
+++ b/gemrb/core/SoundMgr.cpp
@@ -0,0 +1,32 @@
+/* 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 "SoundMgr.h"
+
+const TypeID SoundMgr::ID = { "SoundMgr" };
+
+SoundMgr::SoundMgr(void)
+ : samples( 0 ), channels( 0 ), samplerate( 0 )
+{
+}
+
+SoundMgr::~SoundMgr(void)
+{
+}
diff --git a/gemrb/core/SoundMgr.h b/gemrb/core/SoundMgr.h
new file mode 100644
index 0000000..3b01a95
--- /dev/null
+++ b/gemrb/core/SoundMgr.h
@@ -0,0 +1,64 @@
+/* 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 SOUNDMGR_H
+#define SOUNDMGR_H
+
+#include "ie_types.h"
+
+#include "Resource.h"
+#include "System/DataStream.h"
+
+/**
+ * Base Class for sound plugins
+ */
+class GEM_EXPORT SoundMgr : public Resource {
+public:
+ static const TypeID ID;
+public:
+ SoundMgr(void);
+ virtual ~SoundMgr(void);
+ /**
+ * Read up to cnt samples into memory
+ *
+ * @param[out] memory Array to hold samples read.
+ * @param[in] cnt number of samples to read.
+ * @returns Number of samples read.
+ */
+ virtual int read_samples( short* memory, int cnt ) = 0 ;
+ int get_channels() const
+ {
+ return channels;
+ }
+ int get_samplerate() const
+ {
+ return samplerate;
+ }
+ int get_length() const
+ {
+ return samples;
+ } // returns the total samples count
+protected:
+ int samples; // total count of sound samples
+ int channels;
+ int samplerate;
+};
+
+#endif
diff --git a/gemrb/core/Spell.cpp b/gemrb/core/Spell.cpp
new file mode 100644
index 0000000..06512eb
--- /dev/null
+++ b/gemrb/core/Spell.cpp
@@ -0,0 +1,237 @@
+/* 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 the .spl (spell) files of the game.
+
+#include "Spell.h"
+
+#include "win32def.h"
+
+#include "Audio.h"
+#include "Game.h"
+#include "Interface.h"
+#include "Projectile.h"
+#include "ProjectileServer.h"
+#include "Scriptable/Actor.h"
+
+SPLExtHeader::SPLExtHeader(void)
+{
+ features = NULL;
+}
+
+SPLExtHeader::~SPLExtHeader(void)
+{
+ delete [] features;
+}
+
+Spell::Spell(void)
+{
+ ext_headers = NULL;
+ casting_features = NULL;
+}
+
+Spell::~Spell(void)
+{
+ //Spell is in the core, so this is not needed, i guess (Avenger)
+ //core->FreeSPLExt(ext_headers, casting_features);
+ delete [] ext_headers;
+ delete [] casting_features;
+}
+
+int Spell::GetHeaderIndexFromLevel(int level) const
+{
+ if (level<0) return -1;
+ if (Flags & SF_SIMPLIFIED_DURATION) {
+ return level;
+ }
+ int block_index;
+ for(block_index=0;block_index<ExtHeaderCount-1;block_index++) {
+ if (ext_headers[block_index+1].RequiredLevel>level) {
+ return block_index;
+ }
+ }
+ return ExtHeaderCount-1;
+}
+
+//-1 will return cfb
+//0 will always return first spell block
+//otherwise set to caster level
+static EffectRef fx_casting_glow_ref = { "CastingGlow", -1 };
+
+void Spell::AddCastingGlow(EffectQueue *fxqueue, ieDword duration, int gender)
+{
+ char g, t;
+ Effect *fx;
+ ieResRef Resource;
+
+ int cgsound = CastingSound;
+ if (cgsound>=0 && duration > 1) {
+ //bg2 style
+ if(cgsound&0x100) {
+ switch(gender) {
+ default: g = 'm'; break;
+ case SEX_FEMALE: g = 'f'; break;
+ case SEX_OTHER: case SEX_NEITHER: g = 's'; break;
+ }
+ } else {
+ //how style
+
+ switch(gender) {
+ default: g = 'm'; break;
+ case SEX_FEMALE: g = 'f'; break;
+ }
+ }
+ if (SpellType==IE_SPL_PRIEST) {
+ t = 'p';
+ } else {
+ t = 'm';
+ }
+ snprintf(Resource, 9,"CHA_%c%c%02d", g, t, cgsound&0xff);
+ // only actors have fxqueue's and also the parent function checks for that
+ Actor *caster = (Actor *) fxqueue->GetOwner();
+ caster->casting_sound = core->GetAudioDrv()->Play(Resource, caster->Pos.x, caster->Pos.y);
+ }
+
+ fx = EffectQueue::CreateEffect(fx_casting_glow_ref, 0, CastingGraphics, FX_DURATION_ABSOLUTE);
+ fx->Duration = core->GetGame()->GameTime + duration;
+ fx->InventorySlot = 0xffff;
+ fx->Projectile = 0;
+ fxqueue->AddEffect(fx);
+ //AddEffect creates a copy, we need to destroy the original
+ delete fx;
+}
+
+EffectQueue *Spell::GetEffectBlock(Scriptable *self, const Point &pos, int block_index, int level, ieDword pro) const
+{
+ Effect *features;
+ int count;
+
+ //iwd2 has this hack
+ if (block_index>=0) {
+ if (Flags & SF_SIMPLIFIED_DURATION) {
+ features = ext_headers[0].features;
+ count = ext_headers[0].FeatureCount;
+ } else {
+ features = ext_headers[block_index].features;
+ count = ext_headers[block_index].FeatureCount;
+ }
+ } else {
+ features = casting_features;
+ count = CastingFeatureCount;
+ }
+ EffectQueue *fxqueue = new EffectQueue();
+ EffectQueue *selfqueue = NULL;
+
+ for (int i=0;i<count;i++) {
+ Effect *fx = features+i;
+
+ if ((Flags & SF_SIMPLIFIED_DURATION) && (block_index>=0)) {
+ //hack the effect according to Level
+ //fxqueue->AddEffect will copy the effect,
+ //so we don't risk any overwriting
+ if (EffectQueue::HasDuration(features+i)) {
+ fx->Duration = (TimePerLevel*block_index+TimeConstant)*core->Time.round_sec;
+ }
+ }
+ //fill these for completeness, inventoryslot is a good way
+ //to discern a spell from an item effect
+
+ fx->InventorySlot = 0xffff;
+ //the hostile flag is used to determine if this was an attack
+ fx->SourceFlags = Flags;
+ fx->CasterLevel = level;
+
+ // apply the stat-based spell duration modifier
+ if (self->Type == ST_ACTOR) {
+ Actor *caster = (Actor *) self;
+ if (caster->Modified[IE_SPELLDURATIONMODMAGE] && SpellType == IE_SPL_WIZARD) {
+ fx->Duration = (fx->Duration * caster->Modified[IE_SPELLDURATIONMODMAGE]) / 100;
+ } else if (caster->Modified[IE_SPELLDURATIONMODPRIEST] && SpellType == IE_SPL_PRIEST) {
+ fx->Duration = (fx->Duration * caster->Modified[IE_SPELLDURATIONMODPRIEST]) / 100;
+ }
+ }
+
+ if (fx->Target != FX_TARGET_SELF) {
+ fx->Projectile = pro;
+ fxqueue->AddEffect( fx );
+ } else {
+ fx->Projectile = 0;
+ fx->PosX=pos.x;
+ fx->PosY=pos.y;
+ if (!selfqueue) {
+ selfqueue = new EffectQueue();
+ }
+ // effects should be able to affect non living targets
+ //This is done by NULL target, the position should be enough
+ //to tell which non-actor object is affected
+ selfqueue->AddEffect( fx );
+ }
+ }
+ if (selfqueue) {
+ Actor *target = (self->Type==ST_ACTOR)?(Actor *) self:NULL;
+ core->ApplyEffectQueue(selfqueue, target, self);
+ delete selfqueue;
+ }
+ return fxqueue;
+}
+
+Projectile *Spell::GetProjectile(Scriptable *self, int header, const Point &target) const
+{
+ SPLExtHeader *seh = GetExtHeader(header);
+ if (!seh) {
+ printMessage("Spell", "Cannot retrieve spell header!!! ",RED);
+ printf("required header: %d, maximum: %d\n", header, (int) ExtHeaderCount);
+ return NULL;
+ }
+ Projectile *pro = core->GetProjectileServer()->GetProjectileByIndex(seh->ProjectileAnimation);
+ if (seh->FeatureCount) {
+ pro->SetEffects(GetEffectBlock(self, target, header, seh->ProjectileAnimation));
+ }
+ return pro;
+}
+
+//get the casting distance of the spell
+//it depends on the casting level of the actor
+//if actor isn't given, then the first header is used
+unsigned int Spell::GetCastingDistance(Scriptable *Sender) const
+{
+ int level = 0;
+ Actor *actor = NULL;
+ if (Sender && Sender->Type==ST_ACTOR) {
+ actor = (Actor *) Sender;
+ level = actor->GetCasterLevel(SpellType);
+ }
+
+ if (level<1) {
+ level = 1;
+ }
+ int idx = GetHeaderIndexFromLevel(level);
+ SPLExtHeader *seh = GetExtHeader(idx);
+ if (!seh) {
+ printMessage("Spell", "Cannot retrieve spell header!!! ",RED);
+ printf("required header: %d, maximum: %d\n", idx, (int) ExtHeaderCount);
+ return 0;
+ }
+
+ if (seh->Target==TARGET_DEAD) {
+ return 0xffffffff;
+ }
+ return (unsigned int) seh->Range;
+}
diff --git a/gemrb/core/Spell.h b/gemrb/core/Spell.h
new file mode 100644
index 0000000..810cb41
--- /dev/null
+++ b/gemrb/core/Spell.h
@@ -0,0 +1,174 @@
+/* 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 Spell.h
+ * Declares Spell, class for magic incantations, cleric prayers,
+ * bardic songs and innate abilities
+ * @author The GemRB Project
+ */
+
+#ifndef SPELL_H
+#define SPELL_H
+
+#include "exports.h"
+#include "ie_types.h"
+
+#include "EffectQueue.h"
+
+class Projectile;
+
+//values for Spell usability Flags
+
+#define SF_HOSTILE 0x400
+#define SF_NO_LOS 0x800
+#define SF_NOT_INDOORS 0x2000
+#define SF_HLA 0x4000 // probably this means a nonmagical ability
+#define SF_TRIGGER 0x8000
+#define SF_NOT_IN_COMBAT 0x10000
+//this is a relocated bit (used in iwd2 as 0x4000)
+#define SF_SIMPLIFIED_DURATION 0x40
+
+//spelltypes in spells
+#define IE_SPL_ITEM 0
+#define IE_SPL_WIZARD 1
+#define IE_SPL_PRIEST 2
+#define IE_SPL_PSION 3
+#define IE_SPL_INNATE 4
+#define IE_SPL_SONG 5
+
+//this is not the same as the book types which is 3 or 11)
+#define NUM_SPELL_TYPES 6
+
+#define SPEC_IDENTIFY 1 //spells that don't appear in the casting bar
+#define SPEC_SILENCE 2 //spells that can be cast when silenced
+#define SPEC_DEAD 4 //spells that can target dead actors despite their target type is 1 (pst hack)
+/**
+ * @class SPLExtHeader
+ * Header for Spell special effects
+ */
+
+class GEM_EXPORT SPLExtHeader {
+public:
+ SPLExtHeader();
+ ~SPLExtHeader();
+
+ ieByte SpellForm;
+ ieByte unknown1;
+ ieByte Location;
+ ieByte unknown2;
+ ieResRef MemorisedIcon;
+ ieByte Target;
+ ieByte TargetNumber;
+ ieWord Range;
+ ieWord RequiredLevel;
+ ieDword CastingTime;
+ ieWord DiceSides;
+ ieWord DiceThrown;
+ ieWord DamageBonus;
+ ieWord DamageType;
+ ieWord FeatureCount;
+ ieWord FeatureOffset;
+ ieWord Charges;
+ ieWord ChargeDepletion;
+ ieWord ProjectileAnimation;
+ Effect* features;
+};
+
+/**
+ * @class Spell
+ * Class for magic incantations, cleric prayers,
+ * bardic songs and innate abilities.
+ */
+
+class GEM_EXPORT Spell {
+public:
+ Spell();
+ ~Spell();
+
+ SPLExtHeader *ext_headers;
+ Effect* casting_features;
+
+ /** Resref of the spell itself */
+ ieResRef Name;
+ ieStrRef SpellName;
+ ieStrRef SpellNameIdentified;
+ ieResRef CompletionSound;
+ ieDword Flags;
+ ieWord SpellType;
+ ieWord ExclusionSchool;
+ ieWord PriestType;
+ ieWord CastingGraphics;
+ ieByte unknown1;
+ ieWord PrimaryType;
+ ieByte SecondaryType;
+ ieDword unknown2;
+ ieDword unknown3;
+ ieDword unknown4;
+ ieDword SpellLevel;
+ ieWord unknown5;
+ ieResRef SpellbookIcon;
+ ieWord unknown6;
+ ieDword unknown7;
+ ieDword unknown8;
+ ieDword unknown9;
+ ieStrRef SpellDesc;
+ ieStrRef SpellDescIdentified;
+ ieDword unknown10;
+ ieDword unknown11;
+ ieDword unknown12;
+ ieDword ExtHeaderOffset;
+ ieWord ExtHeaderCount;
+ ieDword FeatureBlockOffset;
+ ieWord CastingFeatureOffset;
+ ieWord CastingFeatureCount;
+
+ // IWD2 only
+ ieDword TimePerLevel;
+ ieDword TimeConstant;
+ char unknown13[8];
+ //derived values
+ int CastingSound;
+
+public:
+ //returns the requested extended header
+ inline SPLExtHeader *GetExtHeader(unsigned int which) const
+ {
+ if (Flags & SF_SIMPLIFIED_DURATION) {
+ which = 0;
+ }
+
+ if(ExtHeaderCount<=which) {
+ return NULL;
+ }
+ return ext_headers+which;
+ }
+ //converts a wanted level to block index count
+ int GetHeaderIndexFromLevel(int level) const;
+ //-1 will return the cfb
+ EffectQueue *GetEffectBlock(Scriptable *self, const Point &pos, int block_index, int level, ieDword pro=0) const;
+ // add appropriate casting glow effect
+ void AddCastingGlow(EffectQueue *fxqueue, ieDword duration, int gender);
+ //returns a projectile created from an extended header
+ Projectile *GetProjectile(Scriptable *self, int headerindex, const Point &pos) const;
+ unsigned int GetCastingDistance(Scriptable *Sender) const;
+};
+
+#endif // ! SPELL_H
diff --git a/gemrb/core/SpellMgr.cpp b/gemrb/core/SpellMgr.cpp
new file mode 100644
index 0000000..661588f
--- /dev/null
+++ b/gemrb/core/SpellMgr.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 "SpellMgr.h"
+
+SpellMgr::SpellMgr(void)
+{
+}
+
+SpellMgr::~SpellMgr(void)
+{
+}
diff --git a/gemrb/core/SpellMgr.h b/gemrb/core/SpellMgr.h
new file mode 100644
index 0000000..44c1032
--- /dev/null
+++ b/gemrb/core/SpellMgr.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.
+ *
+ *
+ */
+
+/**
+ * @file SpellMgr.h
+ * Declares SpellMgr class, abstract loader for Spell objects
+ * @author The GemRB Project
+ */
+
+#ifndef SPELLMGR_H
+#define SPELLMGR_H
+
+#include "Plugin.h"
+#include "Spell.h"
+#include "System/DataStream.h"
+
+/**
+ * @class SpellMgr
+ * Abstract loader for Spell objects
+ */
+
+class GEM_EXPORT SpellMgr : public Plugin {
+public:
+ SpellMgr(void);
+ virtual ~SpellMgr(void);
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual Spell* GetSpell(Spell *spl, bool silent=false) = 0;
+};
+
+#endif
diff --git a/gemrb/core/Spellbook.cpp b/gemrb/core/Spellbook.cpp
new file mode 100644
index 0000000..b910faf
--- /dev/null
+++ b/gemrb/core/Spellbook.cpp
@@ -0,0 +1,1016 @@
+/* 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 "Spellbook.h"
+
+#include "GameData.h"
+#include "Interface.h"
+#include "Projectile.h"
+#include "Spell.h"
+#include "TableMgr.h"
+#include "Scriptable/Actor.h"
+
+#include <cstdio>
+
+static bool SBInitialized = false;
+static int NUM_BOOK_TYPES = 3;
+static bool IWD2Style = false;
+
+//spell header-->spell book type conversion (iwd2 is different)
+static const int spelltypes[NUM_SPELL_TYPES]={
+ IE_SPELL_TYPE_INNATE, IE_SPELL_TYPE_WIZARD, IE_SPELL_TYPE_PRIEST,
+ IE_SPELL_TYPE_WIZARD, IE_SPELL_TYPE_INNATE, IE_SPELL_TYPE_SONG
+};
+
+Spellbook::Spellbook()
+{
+ if (!SBInitialized) {
+ InitializeSpellbook();
+ }
+ spells = new std::vector<CRESpellMemorization*> [NUM_BOOK_TYPES];
+}
+
+void Spellbook::InitializeSpellbook()
+{
+ if (!SBInitialized) {
+ SBInitialized=true;
+ if (core->HasFeature(GF_HAS_SPELLLIST)) {
+ NUM_BOOK_TYPES=NUM_IWD2_SPELLTYPES; //iwd2 spell types
+ } else {
+ NUM_BOOK_TYPES=NUM_SPELLTYPES; //bg/pst/iwd1 spell types
+ }
+ }
+ return;
+}
+
+void Spellbook::ReleaseMemory()
+{
+ SBInitialized=false;
+}
+
+Spellbook::~Spellbook()
+{
+ for (int i = 0; i < NUM_BOOK_TYPES; i++) {
+ for (unsigned int j = 0; j < spells[i].size(); j++) {
+ if (spells[i][j]) {
+ FreeSpellPage( spells[i][j] );
+ spells[i][j] = NULL;
+ }
+ }
+ }
+ ClearSpellInfo();
+ delete [] spells;
+}
+
+void Spellbook::FreeSpellPage(CRESpellMemorization *sm)
+{
+ size_t i = sm->known_spells.size();
+ while(i--) {
+ delete sm->known_spells[i];
+ }
+ i = sm->memorized_spells.size();
+ while(i--) {
+ delete sm->memorized_spells[i];
+ }
+ delete sm;
+}
+
+// FIXME: exclude slayer, all bhaal innates?
+void Spellbook::CopyFrom(const Actor *source)
+{
+ if (!source) {
+ return;
+ }
+
+ // clear it first
+ for (int i = 0; i < NUM_BOOK_TYPES; i++) {
+ for (unsigned int j = 0; j < spells[i].size(); j++) {
+ if (spells[i][j]) {
+ FreeSpellPage( spells[i][j] );
+ spells[i][j] = NULL;
+ }
+ }
+ spells[i].clear();
+ }
+ ClearSpellInfo();
+
+ const Spellbook &wikipedia = source->spellbook;
+
+ for (int t = 0; t < NUM_BOOK_TYPES; t++) {
+ for (size_t i = 0; i < wikipedia.spells[t].size(); i++) {
+ unsigned int k;
+ CRESpellMemorization *wm = wikipedia.spells[t][i];
+ CRESpellMemorization *sm = new CRESpellMemorization();
+ spells[t].push_back(sm);
+ sm->Level = wm->Level;
+ sm->Number = wm->Number;
+ sm->Number2 = wm->Number2;
+ sm->Type = wm->Type;
+ for (k = 0; k < wm->known_spells.size(); k++) {
+ CREKnownSpell *tmp_known = new CREKnownSpell();
+ sm->known_spells.push_back(tmp_known);
+ memcpy(tmp_known, wm->known_spells[k], sizeof(CREKnownSpell));
+ }
+ for (k = 0; k < wm->memorized_spells.size(); k++) {
+ CREMemorizedSpell *tmp_mem = new CREMemorizedSpell();
+ sm->memorized_spells.push_back(tmp_mem);
+ memcpy(tmp_mem, wm->memorized_spells[k], sizeof(CREMemorizedSpell));
+ }
+ }
+ }
+
+ sorcerer = wikipedia.sorcerer;
+}
+
+//ITEM, SPPR, SPWI, SPIN, SPCL
+int sections[]={3,0,1,2,2};
+
+//flags bits
+// 1 - unmemorize it
+bool Spellbook::HaveSpell(int spellid, ieDword flags)
+{
+ int type = spellid/1000;
+ if (type>4) {
+ return false;
+ }
+ type = sections[type];
+ if (type >= NUM_BOOK_TYPES) {
+ return false;
+ }
+ spellid = spellid % 1000;
+
+ for (unsigned int j = 0; j < GetSpellLevelCount(type); j++) {
+ CRESpellMemorization* sm = spells[type][j];
+ for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
+ CREMemorizedSpell* ms = sm->memorized_spells[k];
+ if (ms->Flags) {
+ if (atoi(ms->SpellResRef+4)==spellid) {
+ if (flags&HS_DEPLETE) {
+ if (DepleteSpell(ms) && (sorcerer & (1<<type) ) ) {
+ DepleteLevel (sm);
+ }
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool Spellbook::KnowSpell(int spellid)
+{
+ int type = spellid/1000;
+ if (type>4) {
+ return false;
+ }
+ type = sections[type];
+ if (type >= NUM_BOOK_TYPES) {
+ return false;
+ }
+ spellid = spellid % 1000;
+
+ for (unsigned int j = 0; j < GetSpellLevelCount(type); j++) {
+ CRESpellMemorization* sm = spells[type][j];
+ for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
+ CREKnownSpell* ks = sm->known_spells[k];
+ if (atoi(ks->SpellResRef+4)==spellid) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//if resref=="" then it is a knownanyspell
+bool Spellbook::KnowSpell(const char *resref)
+{
+ for (int i = 0; i < NUM_BOOK_TYPES; i++) {
+ for (unsigned int j = 0; j < spells[i].size(); j++) {
+ CRESpellMemorization* sm = spells[i][j];
+ for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
+ CREKnownSpell* ks = sm->known_spells[k];
+ if (resref[0] && stricmp(ks->SpellResRef, resref) ) {
+ continue;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//if resref=="" then it is a haveanyspell
+bool Spellbook::HaveSpell(const char *resref, ieDword flags)
+{
+ for (int i = 0; i < NUM_BOOK_TYPES; i++) {
+ for (unsigned int j = 0; j < spells[i].size(); j++) {
+ CRESpellMemorization* sm = spells[i][j];
+ for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
+ CREMemorizedSpell* ms = sm->memorized_spells[k];
+ if (ms->Flags) {
+ if (resref[0] && stricmp(ms->SpellResRef, resref) ) {
+ continue;
+ }
+ if (flags&HS_DEPLETE) {
+ if (DepleteSpell(ms) && (sorcerer & (1<<i) ) ) {
+ DepleteLevel (sm);
+ }
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+int Spellbook::GetTypes() const
+{
+ return NUM_BOOK_TYPES;
+}
+
+bool Spellbook::IsIWDSpellBook() const
+{
+ return IWD2Style;
+}
+
+unsigned int Spellbook::GetSpellLevelCount(int type) const
+{
+ assert(type < NUM_BOOK_TYPES);
+ return (unsigned int) spells[type].size();
+}
+
+unsigned int Spellbook::GetTotalPageCount() const
+{
+ unsigned int total = 0;
+ for (int type = 0; type < NUM_BOOK_TYPES; type++) {
+ total += GetSpellLevelCount(type);
+ }
+ return total;
+}
+
+unsigned int Spellbook::GetTotalKnownSpellsCount() const
+{
+ unsigned int total = 0;
+ for (int type = 0; type < NUM_BOOK_TYPES; type++) {
+ unsigned int level = GetSpellLevelCount(type);
+ while(level--) {
+ total += GetKnownSpellsCount(type, level);
+ }
+ }
+ return total;
+}
+
+unsigned int Spellbook::GetTotalMemorizedSpellsCount() const
+{
+ unsigned int total = 0;
+ for (int type = 0; type < NUM_BOOK_TYPES; type++) {
+ unsigned int level = GetSpellLevelCount(type);
+ while(level--) {
+ total += GetMemorizedSpellsCount(type, level);
+ }
+ }
+ return total;
+}
+
+// returns the number of known spells of level (level+1)
+unsigned int Spellbook::GetKnownSpellsCount(int type, unsigned int level) const
+{
+ if (type >= NUM_BOOK_TYPES || level >= GetSpellLevelCount(type))
+ return 0;
+ return (unsigned int) spells[type][level]->known_spells.size();
+}
+
+//called when a spell was removed from spellbook
+//this one purges all instances of known spells of the same name from memory
+void Spellbook::RemoveMemorization(CRESpellMemorization* sm, const ieResRef ResRef)
+{
+ std::vector< CREMemorizedSpell* >::iterator ms;
+
+ for (ms = sm->memorized_spells.begin(); ms != sm->memorized_spells.end(); ms++) {
+ if (strnicmp(ResRef, (*ms)->SpellResRef, sizeof(ieResRef) ) ) {
+ continue;
+ }
+ delete *ms;
+ sm->memorized_spells.erase(ms);
+ ms--;
+ }
+}
+
+//removes one instance of spell (from creknownspell)
+bool Spellbook::RemoveSpell(CREKnownSpell* spell)
+{
+ for (int i = 0; i < NUM_BOOK_TYPES; i++) {
+ std::vector< CRESpellMemorization* >::iterator sm;
+ for (sm = spells[i].begin(); sm != spells[i].end(); sm++) {
+ std::vector< CREKnownSpell* >::iterator ks;
+ for (ks = (*sm)->known_spells.begin(); ks != (*sm)->known_spells.end(); ks++) {
+ if (*ks == spell) {
+ ieResRef ResRef;
+
+ memcpy(ResRef, (*ks)->SpellResRef, sizeof(ieResRef) );
+ delete *ks;
+ (*sm)->known_spells.erase(ks);
+ RemoveMemorization(*sm, ResRef);
+ ClearSpellInfo();
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+//removes all instances of spellid (probably not needed)
+//IWD2 clab files use it
+void Spellbook::RemoveSpell(int spellid)
+{
+ int type = spellid/1000;
+ if (type>4) {
+ return;
+ }
+ type = sections[type];
+ if (type >= NUM_BOOK_TYPES) {
+ return;
+ }
+ spellid = spellid % 1000;
+ std::vector< CRESpellMemorization* >::iterator sm;
+ for (sm = spells[type].begin(); sm != spells[type].end(); sm++) {
+ std::vector< CREKnownSpell* >::iterator ks;
+
+ for (ks = (*sm)->known_spells.begin(); ks != (*sm)->known_spells.end(); ks++) {
+ if (atoi((*ks)->SpellResRef+4)==spellid) {
+ ieResRef ResRef;
+
+ memcpy(ResRef, (*ks)->SpellResRef, sizeof(ieResRef) );
+ delete *ks;
+ (*sm)->known_spells.erase(ks);
+ RemoveMemorization(*sm, ResRef);
+ ks--;
+ ClearSpellInfo();
+ }
+ }
+ }
+}
+
+//removes spell from both memorized/book
+void Spellbook::RemoveSpell(const ieResRef ResRef)
+{
+ for (int type = 0; type<NUM_BOOK_TYPES; type++) {
+ std::vector< CRESpellMemorization* >::iterator sm;
+ for (sm = spells[type].begin(); sm != spells[type].end(); sm++) {
+ std::vector< CREKnownSpell* >::iterator ks;
+
+ for (ks = (*sm)->known_spells.begin(); ks != (*sm)->known_spells.end(); ks++) {
+ if (strnicmp(ResRef, (*ks)->SpellResRef, sizeof(ieResRef) ) ) {
+ continue;
+ }
+ delete *ks;
+ (*sm)->known_spells.erase(ks);
+ RemoveMemorization(*sm, ResRef);
+ ks--;
+ ClearSpellInfo();
+ }
+ }
+ }
+}
+
+void Spellbook::SetBookType(int bt)
+{
+ sorcerer = bt;
+}
+
+//returns the page group of the spellbook this spelltype belongs to
+//psionics are stored in the mage spell list
+//wizard/priest are trivial
+//songs are stored elsewhere
+//wildshapes are marked as innate, they need some hack to get stored
+//in the right group
+//the rest are stored as innate
+int Spellbook::GetSpellType(int spelltype)
+{
+ if (IWD2Style) return spelltype;
+
+ if (spelltype<6) {
+ return spelltypes[spelltype];
+ }
+ return IE_SPELL_TYPE_INNATE;
+}
+
+int Spellbook::LearnSpell(Spell *spell, int memo)
+{
+ CREKnownSpell *spl = new CREKnownSpell();
+ strncpy(spl->SpellResRef, spell->Name, 8);
+ spl->Type = (ieWord) GetSpellType(spell->SpellType);
+ if ( spl->Type == IE_SPELL_TYPE_INNATE) {
+ spl->Level = 0;
+ }
+ else {
+ spl->Level = (ieWord) (spell->SpellLevel-1);
+ }
+ bool ret=AddKnownSpell(spl, memo);
+ if (!ret) {
+ delete spl;
+ }
+ return spell->SpellLevel; // return only the spell level (xp is based on xpbonus)
+}
+
+//if flg is set, it will be also memorized
+bool Spellbook::AddKnownSpell(CREKnownSpell *spl, int flg)
+{
+ int type = spl->Type;
+ if (type >= NUM_BOOK_TYPES) {
+ return false;
+ }
+ unsigned int level = spl->Level;
+ if ( level >= GetSpellLevelCount(type) ) {
+ CRESpellMemorization *sm = new CRESpellMemorization();
+ sm->Type = (ieWord) type;
+ sm->Level = (ieWord) level;
+ sm->Number = sm->Number2 = 0;
+ if ( !AddSpellMemorization(sm) ) {
+ delete sm;
+ return false;
+ }
+ }
+
+ spells[type][level]->known_spells.push_back(spl);
+ if (type==IE_SPELL_TYPE_INNATE) {
+ spells[type][level]->Number++;
+ spells[type][level]->Number2++;
+ }
+ if (flg) {
+ MemorizeSpell(spl, true);
+ }
+ return true;
+}
+
+CREKnownSpell* Spellbook::GetKnownSpell(int type, unsigned int level, unsigned int index) const
+{
+ if (type >= NUM_BOOK_TYPES || level >= GetSpellLevelCount(type) || index >= spells[type][level]->known_spells.size())
+ return NULL;
+ return spells[type][level]->known_spells[index];
+}
+
+unsigned int Spellbook::GetMemorizedSpellsCount(int type) const
+{
+ unsigned int count = 0;
+ size_t i=GetSpellLevelCount(type);
+ while(i--) {
+ count += (unsigned int) spells[type][i]->memorized_spells.size();
+ }
+ return count;
+}
+
+unsigned int Spellbook::GetMemorizedSpellsCount(int type, unsigned int level) const
+{
+ if (type >= NUM_BOOK_TYPES)
+ return 0;
+ if (level >= GetSpellLevelCount(type))
+ return 0;
+ return (unsigned int) spells[type][level]->memorized_spells.size();
+}
+
+CREMemorizedSpell* Spellbook::GetMemorizedSpell(int type, unsigned int level, unsigned int index) const
+{
+ if (type >= NUM_BOOK_TYPES || level >= GetSpellLevelCount(type) || index >= spells[type][level]->memorized_spells.size())
+ return NULL;
+ return spells[type][level]->memorized_spells[index];
+}
+
+//creates a spellbook level
+bool Spellbook::AddSpellMemorization(CRESpellMemorization* sm)
+{
+ if (sm->Type>=NUM_BOOK_TYPES) {
+ return false;
+ }
+ std::vector<CRESpellMemorization*>* s = &spells[sm->Type];
+ //when loading, level starts on 0
+ unsigned int level = sm->Level;
+ if (level > MAX_SPELL_LEVEL ) {
+ return false;
+ }
+
+ while (s->size() < level ) {
+ // this code previously added NULLs, leading to crashes,
+ // so this is an attempt to make it not broken
+ CRESpellMemorization *newsm = new CRESpellMemorization();
+ newsm->Type = sm->Type;
+ newsm->Level = (ieWord) s->size();
+ newsm->Number = newsm->Number2 = 0;
+ s->push_back( newsm );
+ }
+
+ // only add this one if necessary
+ assert (s->size() == level);
+ s->push_back(sm);
+ return true;
+}
+
+//apply the wisdom bonus on all spell levels for type
+//count is optimally the count of spell levels
+void Spellbook::BonusSpells(int type, int count, int *bonuses)
+{
+ int level = GetSpellLevelCount(type);
+ if (level>count) level=count;
+ for (int i = 0; i < level; i++) {
+ CRESpellMemorization* sm = GetSpellMemorization(type, i);
+ sm->Number2+=bonuses[i];
+ }
+}
+
+//call this in every ai cycle when recalculating spell bonus
+//TODO:add in wisdom bonus here
+void Spellbook::ClearBonus()
+{
+ int type;
+
+ for (type = 0; type < NUM_BOOK_TYPES; type++) {
+ int level = GetSpellLevelCount(type);
+ for (int i = 0; i < level; i++) {
+ CRESpellMemorization* sm = GetSpellMemorization(type, i);
+ sm->Number2=sm->Number;
+ }
+ }
+}
+
+CRESpellMemorization *Spellbook::GetSpellMemorization(unsigned int type, unsigned int level)
+{
+ if (type >= (unsigned int)NUM_BOOK_TYPES)
+ return NULL;
+
+ CRESpellMemorization *sm;
+ if (level >= GetSpellLevelCount(type)) {
+ sm = new CRESpellMemorization();
+ sm->Type = (ieWord) type;
+ sm->Level = (ieWord) level;
+ sm->Number = sm->Number2 = 0;
+ if ( !AddSpellMemorization(sm) ) {
+ delete sm;
+ return NULL;
+ }
+ assert(sm == spells[type][level]);
+ } else {
+ sm = spells[type][level];
+ }
+ return sm;
+}
+//if bonus is not set, then sets the base value (adjusts bonus too)
+//if bonus is set, then sets only the bonus
+//if the bonus value is 0, then the bonus is double base value
+//bonus is cummulative, but not saved
+void Spellbook::SetMemorizableSpellsCount(int Value, int type, unsigned int level, bool bonus)
+{
+ int diff;
+
+ if (type >= NUM_BOOK_TYPES) {
+ return;
+ }
+
+ CRESpellMemorization* sm = GetSpellMemorization(type, level);
+ if (bonus) {
+ if (!Value) {
+ Value=sm->Number;
+ }
+ sm->Number2=(ieWord) (sm->Number2+Value);
+ }
+ else {
+ diff=sm->Number2-sm->Number;
+ sm->Number=(ieWord) Value;
+ sm->Number2=(ieWord) (Value+diff);
+ }
+}
+
+int Spellbook::GetMemorizableSpellsCount(int type, unsigned int level, bool bonus) const
+{
+ if (type >= NUM_BOOK_TYPES || level >= GetSpellLevelCount(type))
+ return 0;
+ CRESpellMemorization* sm = spells[type][level];
+ if (bonus)
+ return sm->Number2;
+ return sm->Number;
+}
+
+bool Spellbook::MemorizeSpell(CREKnownSpell* spell, bool usable)
+{
+ CRESpellMemorization* sm = spells[spell->Type][spell->Level];
+ if (sm->Number2 <= sm->memorized_spells.size()) {
+ //it is possible to have sorcerer type spellbooks for any spellbook type
+ if (! (sorcerer & (1<<spell->Type) ) )
+ return false;
+ }
+
+ CREMemorizedSpell* mem_spl = new CREMemorizedSpell();
+ strncpy( mem_spl->SpellResRef, spell->SpellResRef, 8 );
+ mem_spl->Flags = usable ? 1 : 0; // FIXME: is it all it's used for?
+
+ sm->memorized_spells.push_back( mem_spl );
+ ClearSpellInfo();
+ return true;
+}
+
+bool Spellbook::UnmemorizeSpell(CREMemorizedSpell* spell)
+{
+ for (int i = 0; i < NUM_BOOK_TYPES; i++) {
+ std::vector< CRESpellMemorization* >::iterator sm;
+ for (sm = spells[i].begin(); sm != spells[i].end(); sm++) {
+ std::vector< CREMemorizedSpell* >::iterator s;
+ for (s = (*sm)->memorized_spells.begin(); s != (*sm)->memorized_spells.end(); s++) {
+ if (*s == spell) {
+ delete *s;
+ (*sm)->memorized_spells.erase( s );
+ ClearSpellInfo();
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Spellbook::UnmemorizeSpell(const ieResRef ResRef, bool deplete)
+{
+ for (int type = 0; type<NUM_BOOK_TYPES; type++) {
+ std::vector< CRESpellMemorization* >::iterator sm;
+ for (sm = spells[type].begin(); sm != spells[type].end(); sm++) {
+ std::vector< CREMemorizedSpell* >::iterator s;
+ for (s = (*sm)->memorized_spells.begin(); s != (*sm)->memorized_spells.end(); s++) {
+ if (strnicmp(ResRef, (*s)->SpellResRef, sizeof(ieResRef) ) ) {
+ continue;
+ }
+ if (deplete) {
+ (*s)->Flags = 0;
+ } else {
+ delete *s;
+ (*sm)->memorized_spells.erase( s );
+ }
+ ClearSpellInfo();
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+//bitfield disabling type: 1 - mage, 2 - cleric etc
+//level: if set, then finds that level only
+CREMemorizedSpell* Spellbook::FindUnchargedSpell(int type, int level)
+{
+ int mask=1;
+
+ for (int i = 0; i < NUM_BOOK_TYPES; i++) {
+ if (type&mask) {
+ mask<<=1;
+ continue;
+ }
+ mask<<=1;
+ for (unsigned int j = 0; j<spells[i].size(); j++) {
+ CRESpellMemorization* sm = spells[i][j];
+ if (level && (sm->Level!=level-1)) {
+ continue;
+ }
+
+ for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
+ CREMemorizedSpell *ret = sm->memorized_spells[k];
+ if (ret->Flags == 0) {
+ return ret;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+//creates sorcerer style memory for the given spell type
+void Spellbook::CreateSorcererMemory(int type)
+{
+ for (size_t j = 0; j < spells[type].size(); j++) {
+ CRESpellMemorization* sm = spells[type][j];
+
+ size_t cnt = sm->memorized_spells.size();
+ while(cnt--) {
+ delete sm->memorized_spells[cnt];
+ }
+ sm->memorized_spells.clear();
+ for (unsigned int k = 0; k < sm->known_spells.size(); k++) {
+ CREKnownSpell *ck = sm->known_spells[k];
+ cnt = sm->Number2;
+ while(cnt--) {
+ MemorizeSpell(ck, true);
+ }
+ }
+ }
+}
+
+void Spellbook::ChargeAllSpells()
+{
+ int j = 1;
+ for (int i = 0; i < NUM_BOOK_TYPES; j+=j,i++) {
+ //this spellbook page type is sorcerer-like
+ if (sorcerer&j ) {
+ CreateSorcererMemory(i);
+ continue;
+ }
+
+ for (unsigned int j = 0; j < spells[i].size(); j++) {
+ CRESpellMemorization* sm = spells[i][j];
+
+ for (unsigned int k = 0; k < sm->memorized_spells.size(); k++)
+ ChargeSpell( sm->memorized_spells[k] );
+ }
+ }
+}
+
+//unmemorizes the highest level spell possible
+//returns true if successful
+bool Spellbook::DepleteSpell(int type)
+{
+ if (type>=NUM_BOOK_TYPES) {
+ return false;
+ }
+ size_t j = GetSpellLevelCount(type);
+ while(j--) {
+ CRESpellMemorization* sm = spells[type][j];
+
+ for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
+ if (DepleteSpell( sm->memorized_spells[k] )) {
+ if (sorcerer & (1<<type) ) {
+ DepleteLevel (sm);
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void Spellbook::DepleteLevel(CRESpellMemorization* sm)
+{
+ size_t cnt = sm->memorized_spells.size();
+ ieResRef last={""};
+ for (size_t i = 0; i < cnt && cnt>0; i++) {
+ CREMemorizedSpell *cms = sm->memorized_spells[i];
+ //sorcerer spells are created in orderly manner
+ if (strncmp(last,cms->SpellResRef,8) ) {
+ memcpy(last, cms->SpellResRef, sizeof(ieResRef) );
+ delete cms;
+ sm->memorized_spells.erase(sm->memorized_spells.begin()+i);
+ i--;
+ cnt--;
+ }
+ }
+}
+
+bool Spellbook::DepleteSpell(int type, unsigned int page, unsigned int slot)
+{
+ bool ret;
+
+ if (NUM_BOOK_TYPES<=type) {
+ return false;
+ }
+ if (spells[type].size()<=page) {
+ return false;
+ }
+ CRESpellMemorization* sm = spells[page][type];
+ if (sm->memorized_spells.size()<=slot) {
+ return false;
+ }
+
+ CREMemorizedSpell* cms = sm->memorized_spells[slot];
+ ret = DepleteSpell(cms);
+ if (ret && (sorcerer & (1<<type) ) ) {
+ DepleteLevel (sm);
+ }
+
+ return ret;
+}
+
+bool Spellbook::ChargeSpell(CREMemorizedSpell* spl)
+{
+ spl->Flags = 1;
+ ClearSpellInfo();
+ return true;
+}
+
+bool Spellbook::DepleteSpell(CREMemorizedSpell* spl)
+{
+ if (spl->Flags) {
+ spl->Flags = 0;
+ ClearSpellInfo();
+ return true;
+ }
+ return false;
+}
+
+void Spellbook::ClearSpellInfo()
+{
+ size_t i = spellinfo.size();
+ while(i--) {
+ delete spellinfo[i];
+ }
+ spellinfo.clear();
+}
+
+bool Spellbook::GetSpellInfo(SpellExtHeader *array, int type, int startindex, int count)
+{
+ memset(array, 0, count * sizeof(SpellExtHeader) );
+ if (spellinfo.size() == 0) {
+ GenerateSpellInfo();
+ }
+ int actual = 0;
+ bool ret = false;
+ for (unsigned int i = 0; i<spellinfo.size(); i++) {
+ if ( !(type & (1<<spellinfo[i]->type)) ) {
+ continue;
+ }
+ if (startindex>0) {
+ startindex--;
+ continue;
+ }
+ if (actual>=count) {
+ ret = true;
+ break;
+ }
+ memcpy(array+actual, spellinfo[i], sizeof(SpellExtHeader));
+ actual++;
+ }
+ return ret;
+}
+
+// returns the size of spellinfo vector, if type is nonzero it is used as filter
+// for example type==1 lists the number of different mage spells
+unsigned int Spellbook::GetSpellInfoSize(int type)
+{
+ size_t i = spellinfo.size();
+ if (!i) {
+ GenerateSpellInfo();
+ i = spellinfo.size();
+ }
+ if (!type) {
+ return (unsigned int) i;
+ }
+ unsigned int count = 0;
+ while(i--) {
+ if (1<<(spellinfo[i]->type)&type) {
+ count++;
+ }
+ }
+ return count;
+}
+
+SpellExtHeader *Spellbook::FindSpellInfo(unsigned int level, unsigned int type, const ieResRef spellname)
+{
+ size_t i = spellinfo.size();
+ while(i--) {
+ if ( (spellinfo[i]->level==level) &&
+ (spellinfo[i]->type==type) &&
+ !strnicmp(spellinfo[i]->spellname, spellname, 8)) {
+ return spellinfo[i];
+ }
+ }
+ return NULL;
+}
+
+void Spellbook::AddSpellInfo(unsigned int sm_level, unsigned int sm_type, const ieResRef spellname, unsigned int idx)
+{
+ Spell *spl = gamedata->GetSpell(spellname);
+ if (!spl)
+ return;
+ if (spl->ExtHeaderCount<1)
+ return;
+
+ ieDword level = 0;
+ SpellExtHeader *seh = FindSpellInfo(sm_level, sm_type, spellname);
+ if (seh) {
+ seh->count++;
+ return;
+ }
+
+ seh = new SpellExtHeader;
+ spellinfo.push_back( seh );
+
+ memcpy(seh->spellname, spellname, sizeof(ieResRef) );
+ int ehc;
+
+ for (ehc = 0; ehc < spl->ExtHeaderCount-1; ehc++) {
+ if (level<spl->ext_headers[ehc+1].RequiredLevel) {
+ break;
+ }
+ }
+
+ SPLExtHeader *ext_header = spl->ext_headers+ehc;
+ seh->headerindex = ehc;
+ seh->level = sm_level;
+ seh->type = sm_type;
+ seh->slot = idx;
+ seh->count = 1;
+ seh->SpellForm = ext_header->SpellForm;
+ memcpy(seh->MemorisedIcon, ext_header->MemorisedIcon,sizeof(ieResRef) );
+ seh->Target = ext_header->Target;
+ seh->TargetNumber = ext_header->TargetNumber;
+ seh->Range = ext_header->Range;
+ seh->Projectile = ext_header->ProjectileAnimation;
+ seh->CastingTime = (ieWord) ext_header->CastingTime;
+ seh->strref = spl->SpellName;
+ gamedata->FreeSpell(spl, spellname, false);
+}
+
+void Spellbook::SetCustomSpellInfo(ieResRef *data, ieResRef spell, int type)
+{
+ ClearSpellInfo();
+ if (data) {
+ for(int i = 0; i<type;i++) {
+ AddSpellInfo(0,0,data[i],-1);
+ }
+ return;
+ }
+
+ //if data is not set, use the known spells list to set up the spellinfo list
+ for(int i = 0; i<NUM_BOOK_TYPES; i++) {
+ if ((1<<i)&type) {
+ for(unsigned int j = 0; j<spells[i].size(); j++) {
+ CRESpellMemorization* sm = spells[i][j];
+
+ for(unsigned int k=0;k<sm->known_spells.size(); k++) {
+ CREKnownSpell* slot = sm->known_spells[k];
+ if (!slot)
+ continue;
+ //skip the spell itself
+ if (!strnicmp(slot->SpellResRef, spell, sizeof(ieResRef)))
+ continue;
+ AddSpellInfo(sm->Level, sm->Type, slot->SpellResRef, -1);
+ }
+ }
+ }
+ }
+}
+
+// grouping the castable spells
+void Spellbook::GenerateSpellInfo()
+{
+ ClearSpellInfo(); //just in case
+ for (int i = 0; i < NUM_BOOK_TYPES; i++) {
+ for (unsigned int j = 0; j < spells[i].size(); j++) {
+ CRESpellMemorization* sm = spells[i][j];
+
+ for (unsigned int k = 0; k < sm->memorized_spells.size(); k++) {
+ CREMemorizedSpell* slot = sm->memorized_spells[k];
+ if (!slot)
+ continue;
+ if (!slot->Flags)
+ continue;
+ AddSpellInfo(sm->Level, sm->Type, slot->SpellResRef, k);
+ }
+ }
+ }
+}
+
+void Spellbook::dump()
+{
+ unsigned int k;
+
+ printf( "SPELLBOOK:\n" );
+ for (int i = 0; i < NUM_BOOK_TYPES; i++) {
+ for (unsigned int j = 0; j < spells[i].size(); j++) {
+ CRESpellMemorization* sm = spells[i][j];
+
+ if (sm->known_spells.size())
+ printf( " Known spells:\n" );
+ for (k = 0; k < sm->known_spells.size(); k++) {
+ CREKnownSpell* spl = sm->known_spells[k];
+ if (!spl) continue;
+
+ printf ( " %2d: %8s L: %d T: %d\n", k, spl->SpellResRef, spl->Level, spl->Type );
+ }
+
+ if (sm->memorized_spells.size())
+ printf( " Memorized spells:\n" );
+ for (k = 0; k < sm->memorized_spells.size (); k++) {
+ CREMemorizedSpell* spl = sm->memorized_spells[k];
+ if (!spl) continue;
+
+ printf ( " %2u: %8s %x\n", k, spl->SpellResRef, spl->Flags );
+ }
+ }
+ }
+}
diff --git a/gemrb/core/Spellbook.h b/gemrb/core/Spellbook.h
new file mode 100644
index 0000000..97c962e
--- /dev/null
+++ b/gemrb/core/Spellbook.h
@@ -0,0 +1,248 @@
+/* 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 Spellbook.h
+ * Declares Spellbook, class implementing creature's spellbook
+ * and (maybe) spell management
+ * @author The GemRB Project
+ */
+
+#ifndef SPELLBOOK_H
+#define SPELLBOOK_H
+
+#include "exports.h"
+#include "ie_types.h"
+#include "win32def.h"
+
+#include <vector>
+
+class Actor;
+class Spell;
+
+#define MAX_SPELL_LEVEL 16
+
+//HaveSpell flags
+#define HS_DEPLETE 1
+
+//LearnSpell flags
+#define LS_ADDXP 1 //give xp for learning it
+#define LS_LEARN 2 //give message when learned it
+#define LS_STATS 4 //check stats (alignment, etc)
+#define LS_MEMO 8 //memorize it instantly (add innate)
+#define LS_NOXP 16 //disable giving of xp (LS_ADDXP)
+
+//LearnSpell return values
+#define LSR_OK 0
+#define LSR_KNOWN 1 //already knows
+#define LSR_INVALID 2 //invalid resref
+#define LSR_FAILED 3 //failed stat roll
+#define LSR_STAT 4 //insufficient stat (can't learn the spell due to low stat)
+#define LSR_LEVEL 5 //insufficient level (low mage, etc level)
+#define LSR_FULL 6 //can't learn more spells of this level (due to level)
+
+// !!! Keep these synchronized with GUIDefines.py !!!
+typedef enum ieSpellType {
+ IE_SPELL_TYPE_PRIEST = 0,
+ IE_SPELL_TYPE_WIZARD = 1,
+ IE_SPELL_TYPE_INNATE = 2,
+ IE_SPELL_TYPE_SONG = 3 //not in spellbook
+} ieSpellType;
+
+#define NUM_SPELLTYPES 3
+
+typedef enum ieIWD2SpellType {
+ IE_IWD2_SPELL_BARD = 0,
+ IE_IWD2_SPELL_CLERIC = 1,
+ IE_IWD2_SPELL_DRUID = 2,
+ IE_IWD2_SPELL_PALADIN = 3,
+ IE_IWD2_SPELL_RANGER = 4,
+ IE_IWD2_SPELL_SORCEROR = 5,
+ IE_IWD2_SPELL_WIZARD = 6,
+ IE_IWD2_SPELL_DOMAIN = 7,
+ IE_IWD2_SPELL_INNATE = 8,
+ IE_IWD2_SPELL_SONG = 9,
+ IE_IWD2_SPELL_SHAPE = 10
+} ieIWD2SpellType;
+
+#define NUM_IWD2_SPELLTYPES 11
+
+struct CREKnownSpell {
+ ieResRef SpellResRef;
+ ieWord Level;
+ ieWord Type;
+};
+
+struct CREMemorizedSpell {
+ ieResRef SpellResRef;
+ ieDword Flags;
+};
+
+struct CRESpellMemorization {
+ ieWord Level;
+ ieWord Number;
+ ieWord Number2;
+ ieWord Type;
+
+ std::vector<CREKnownSpell*> known_spells;
+ std::vector<CREMemorizedSpell*> memorized_spells;
+};
+
+struct SpellExtHeader {
+ ieDword level;
+ ieDword count;
+ ieDword type; //spelltype
+ ieDword headerindex;
+ ieDword slot;
+ //these come from the header
+ ieByte SpellForm;
+ ieResRef MemorisedIcon;
+ ieByte Target;
+ ieByte TargetNumber;
+ ieWord Range;
+ ieWord Projectile;
+ ieWord CastingTime;
+ //other data
+ ieResRef spellname;
+ ieDword strref; //the spell's name
+};
+
+/**
+ * @class Spellbook
+ * Class implementing creature's spellbook and (maybe) spell management
+ */
+
+class GEM_EXPORT Spellbook {
+private:
+ std::vector<CRESpellMemorization*> *spells;
+ std::vector<SpellExtHeader*> spellinfo;
+ int sorcerer;
+
+ /** Sets spell from memorized as 'already-cast' */
+ bool DepleteSpell(CREMemorizedSpell* spl);
+ /** Depletes a sorcerer type spellpage by one */
+ void DepleteLevel(CRESpellMemorization* sm);
+ /** Adds a single spell to the spell info list */
+ void AddSpellInfo(unsigned int level, unsigned int type, const ieResRef name, unsigned int idx);
+ /** regenerates the spellinfo list */
+ void GenerateSpellInfo();
+ /** looks up the spellinfo list for an element */
+ SpellExtHeader *FindSpellInfo(unsigned int level, unsigned int type, const ieResRef name);
+ /** removes all instances of a spell from a given page */
+ void RemoveMemorization(CRESpellMemorization* sm, const ieResRef ResRef);
+ /** adds a spell to the book, internal */
+ bool AddKnownSpell(CREKnownSpell *spl, int memo);
+ /** Adds a new CRESpellMemorization, to the *end* only */
+ bool AddSpellMemorization(CRESpellMemorization* sm);
+
+public:
+ Spellbook();
+ ~Spellbook();
+ static void InitializeSpellbook();
+ static void ReleaseMemory();
+
+ void FreeSpellPage(CRESpellMemorization* sm);
+ /** duplicates the source spellbook into the current one */
+ void CopyFrom(const Actor *source);
+ /** Check if the spell is memorised, optionally deplete it (casting) */
+ bool HaveSpell(const char *resref, ieDword flags);
+ bool HaveSpell(int spellid, ieDword flags);
+ /** Check if the spell is in the book */
+ bool KnowSpell(const char *resref);
+ bool KnowSpell(int spellid);
+
+ /** returns a CRESpellMemorization pointer */
+ CRESpellMemorization *GetSpellMemorization(unsigned int type, unsigned int level);
+ int GetTypes() const;
+ bool IsIWDSpellBook() const;
+ unsigned int GetSpellLevelCount(int type) const;
+ unsigned int GetTotalPageCount() const;
+ unsigned int GetTotalKnownSpellsCount() const;
+ unsigned int GetTotalMemorizedSpellsCount() const;
+ unsigned int GetKnownSpellsCount(int type, unsigned int level) const;
+ /** adds the priest slot bonuses from mxsplwis */
+ void BonusSpells(int type, int count, int *bonuses);
+ /** clears up the spell bonuses before recalculation */
+ void ClearBonus();
+ /** removes a spell from memory/book */
+ bool RemoveSpell(CREKnownSpell* spell);
+ /** this removes ALL spells of name ResRef */
+ void RemoveSpell(const ieResRef ResRef);
+ /** this removes ALL spells matching spellid */
+ void RemoveSpell(int spellid);
+
+ /** sets the book type */
+ void SetBookType(int clss);
+ /** returns the page number for the spelltype */
+ static int GetSpellType(int spelltype);
+ /** adds a spell to the book, returns experience if learned */
+ int LearnSpell(Spell *spell, int memo);
+ CREKnownSpell* GetKnownSpell(int type, unsigned int level, unsigned int index) const;
+ unsigned int GetMemorizedSpellsCount(int type) const;
+ unsigned int GetMemorizedSpellsCount(int type, unsigned int level) const;
+ CREMemorizedSpell* GetMemorizedSpell(int type, unsigned int level, unsigned int index) const;
+
+ int GetMemorizableSpellsCount(int type, unsigned int level, bool bonus) const;
+ void SetMemorizableSpellsCount(int Value, int type, unsigned int level, bool bonus);
+
+ /** Adds spell from known to memorized */
+ bool MemorizeSpell(CREKnownSpell* spl, bool usable);
+
+ /** Removes memorized spell */
+ bool UnmemorizeSpell(CREMemorizedSpell* spl);
+
+ /** Removes (or just depletes) memorized spell by ResRef */
+ bool UnmemorizeSpell(const char *resref, bool deplete);
+
+ /** finds the first spell needing to rememorize */
+ CREMemorizedSpell* FindUnchargedSpell(int type, int level=0);
+
+ /** Sets spell from memorized as 'not-yet-cast' */
+ bool ChargeSpell(CREMemorizedSpell* spl);
+
+ /** Sets spell from memorized as 'already-cast' */
+ bool DepleteSpell(int type, unsigned int page, unsigned int slot);
+
+ /** picks the highest spell of type and makes it 'already cast' */
+ bool DepleteSpell(int type);
+
+ /** recharges all spells */
+ void ChargeAllSpells();
+
+ /** creates sorcerer's selection of spells to memorise:
+ selects all spells as many times as the spell page allows */
+ void CreateSorcererMemory(int type);
+
+ /** returns the number of distinct spells (generates spellinfo) */
+ unsigned int GetSpellInfoSize(int type);
+
+ /** generates a custom spellinfo list for fx_select_spell */
+ void SetCustomSpellInfo(ieResRef *data, ieResRef spell, int type);
+
+ /** invalidates the spellinfo list */
+ void ClearSpellInfo();
+
+ /** lists spells of a type */
+ bool GetSpellInfo(SpellExtHeader *array, int type, int startindex, int count);
+ /** Dumps spellbook to stdout for debugging */
+ void dump();
+};
+
+#endif
diff --git a/gemrb/core/Sprite2D.cpp b/gemrb/core/Sprite2D.cpp
new file mode 100644
index 0000000..0e1ac3a
--- /dev/null
+++ b/gemrb/core/Sprite2D.cpp
@@ -0,0 +1,151 @@
+/* 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 "Sprite2D.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+#include "Video.h"
+
+const TypeID Sprite2D::ID = { "Sprite2D" };
+
+Sprite2D::Sprite2D()
+{
+ BAM = false;
+ vptr = NULL;
+ pixels = NULL;
+ XPos = 0;
+ YPos = 0;
+ RefCount = 1;
+}
+
+Sprite2D::~Sprite2D(void)
+{
+}
+
+bool Sprite2D::IsPixelTransparent(unsigned short x, unsigned short y) const
+{
+ if (x >= Width || y >= Height) return true;
+
+ if (!BAM) {
+ return core->GetVideoDriver()->GetPixel(vptr, x, y)==0;
+ }
+
+ Sprite2D_BAM_Internal* data = (Sprite2D_BAM_Internal*)vptr;
+
+ if (data->flip_ver)
+ y = Height - y - 1;
+ if (data->flip_hor)
+ x = Width - x - 1;
+
+ int skipcount = y * Width + x;
+
+ const ieByte* rle = (const ieByte*)pixels;
+ if (data->RLE) {
+ while (skipcount > 0) {
+ if (*rle++ == data->transindex)
+ skipcount -= (*rle++)+1;
+ else
+ skipcount--;
+ }
+ } else {
+ // uncompressed
+ rle += skipcount;
+ skipcount = 0;
+ }
+ if (skipcount < 0 || *rle == data->transindex)
+ return true;
+
+ return false;
+}
+
+/** Get the Palette of a Sprite */
+Palette* Sprite2D::GetPalette() const
+{
+ if (!vptr) return NULL;
+ if (!BAM) {
+ return core->GetVideoDriver()->GetPalette(vptr);
+ }
+
+ Sprite2D_BAM_Internal* data = (Sprite2D_BAM_Internal*)vptr;
+ data->pal->IncRef();
+ return data->pal;
+}
+
+void Sprite2D::SetPalette(Palette* pal)
+{
+ if (!vptr) return;
+ if (!BAM) {
+ core->GetVideoDriver()->SetPalette(vptr, pal);
+ } else {
+ Sprite2D_BAM_Internal* data = (Sprite2D_BAM_Internal*)vptr;
+ data->pal->Release();
+ pal->IncRef();
+ data->pal = pal;
+ }
+}
+
+Color Sprite2D::GetPixel(unsigned short x, unsigned short y) const
+{
+ Color c = { 0, 0, 0, 0 };
+
+ if (x >= Width || y >= Height) return c;
+
+ if (!BAM) {
+ core->GetVideoDriver()->GetPixel(vptr, x, y, c);
+ return c;
+ }
+
+ Sprite2D_BAM_Internal* data = (Sprite2D_BAM_Internal*)vptr;
+
+ if (data->flip_ver)
+ y = Height - y - 1;
+ if (data->flip_hor)
+ x = Width - x - 1;
+
+ int skipcount = y * Width + x;
+
+ const ieByte *rle = (const ieByte*)pixels;
+ if (data->RLE) {
+ while (skipcount > 0) {
+ if (*rle++ == data->transindex)
+ skipcount -= (*rle++)+1;
+ else
+ skipcount--;
+ }
+ } else {
+ // uncompressed
+ rle += skipcount;
+ skipcount = 0;
+ }
+
+ if (skipcount >= 0 && *rle != data->transindex) {
+ c = data->pal->col[*rle];
+ c.a = 0xff;
+ }
+ return c;
+}
+
+void Sprite2D::release()
+{
+ Sprite2D *that = this;
+ core->GetVideoDriver()->FreeSprite(that);
+}
diff --git a/gemrb/core/Sprite2D.h b/gemrb/core/Sprite2D.h
new file mode 100644
index 0000000..2e1ceaf
--- /dev/null
+++ b/gemrb/core/Sprite2D.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.
+ *
+ *
+ */
+
+/**
+ * @file Sprite2D.h
+ * Declares Sprite2D, class representing bitmap data
+ * @author The GemRB Project
+ */
+
+#ifndef SPRITE2D_H
+#define SPRITE2D_H
+
+#include "RGBAColor.h"
+#include "exports.h"
+
+#include "Palette.h"
+#include "TypeID.h"
+
+class AnimationFactory;
+
+/**
+ * @class Sprite2D
+ * Class representing bitmap data.
+ * Objects of this class are usually created by Video driver.
+ */
+
+class Sprite2D_BAM_Internal {
+public:
+ Sprite2D_BAM_Internal() { pal = 0; }
+ ~Sprite2D_BAM_Internal() { if (pal) { pal->Release(); pal = 0; } }
+
+ Palette* pal;
+ bool RLE;
+ int transindex;
+ bool flip_hor;
+ bool flip_ver;
+
+ // The AnimationFactory in which the data for this sprite is stored.
+ // (Used for refcounting of the data.)
+ AnimationFactory* source;
+};
+
+class GEM_EXPORT Sprite2D {
+public:
+ static const TypeID ID;
+public:
+ /** Pointer to the Driver Video Structure */
+ void* vptr;
+ bool BAM;
+ const void* pixels;
+ int XPos, YPos, Width, Height, Bpp;
+ Sprite2D(void);
+ ~Sprite2D(void);
+ bool IsPixelTransparent(unsigned short x, unsigned short y) const;
+ Palette *GetPalette() const;
+ void SetPalette(Palette *pal);
+ Color GetPixel(unsigned short x, unsigned short y) const;
+public: // public only for SDLVideo
+ int RefCount;
+public:
+ void acquire() { ++RefCount; }
+ void release();
+};
+
+#endif // ! SPRITE2D_H
diff --git a/gemrb/core/SpriteCover.cpp b/gemrb/core/SpriteCover.cpp
new file mode 100644
index 0000000..943de09
--- /dev/null
+++ b/gemrb/core/SpriteCover.cpp
@@ -0,0 +1,49 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 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 "SpriteCover.h"
+
+#include "Interface.h"
+#include "Video.h"
+
+SpriteCover::SpriteCover()
+{
+ pixels = 0;
+}
+
+SpriteCover::~SpriteCover()
+{
+ core->GetVideoDriver()->DestroySpriteCover(this);
+}
+
+bool SpriteCover::Covers(int x, int y, int xpos, int ypos,
+ int width, int height) const
+{
+ // if basepoint changed, no longer valid
+ if (x != worldx || y != worldy) return false;
+
+ // top-left not covered
+ if (xpos > XPos || ypos > YPos) return false;
+
+ // bottom-right not covered
+ if (width-xpos > Width-XPos || height-ypos > Height-YPos) return false;
+
+ return true;
+}
diff --git a/gemrb/core/SpriteCover.h b/gemrb/core/SpriteCover.h
new file mode 100644
index 0000000..9e14847
--- /dev/null
+++ b/gemrb/core/SpriteCover.h
@@ -0,0 +1,39 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 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.
+ *
+ *
+ */
+
+#ifndef SPRITECOVER_H
+#define SPRITECOVER_H
+
+#include "exports.h"
+
+class GEM_EXPORT SpriteCover {
+public:
+ unsigned char* pixels;
+ int worldx, worldy; // world coords for which the cover has been computed
+ int XPos, YPos, Width, Height;
+ int flags;
+ SpriteCover(void);
+ ~SpriteCover(void);
+
+ bool Covers(int x, int y, int xpos, int ypos, int width, int height) const;
+};
+
+
+#endif
diff --git a/gemrb/core/Store.cpp b/gemrb/core/Store.cpp
new file mode 100644
index 0000000..5d60994
--- /dev/null
+++ b/gemrb/core/Store.cpp
@@ -0,0 +1,283 @@
+/* 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 .sto (store) files of the game.
+//Inns, pubs, temples, backpacks are also implemented by stores.
+
+#include "Store.h"
+
+#include "win32def.h"
+
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "Item.h"
+#include "GameScript/GameScript.h"
+
+Store::Store(void)
+{
+ HasTriggers = false;
+ purchased_categories = NULL;
+ drinks = NULL;
+ cures = NULL;
+ version = 0;
+ StoreOwnerID = 0;
+}
+
+Store::~Store(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < items.size(); i++) {
+ if (items[i]->trigger)
+ items[i]->trigger->Release();
+ delete( items[i] );
+ }
+ if(drinks)
+ free(drinks);
+ if(cures)
+ free(cures);
+ if (purchased_categories)
+ free( purchased_categories );
+}
+
+bool Store::IsItemAvailable(unsigned int slot) const
+{
+ Game * game = core->GetGame();
+ //0 - not infinite, not conditional
+ //-1 - infinite
+ //other - pst trigger ref
+
+ Trigger *trigger = items[slot]->trigger;
+ if (trigger) {
+ return trigger->Evaluate(game->GetPC(game->GetSelectedPCSingle(), false))!=0;
+ }
+ return true;
+}
+
+int Store::GetRealStockSize()
+{
+ int count=ItemsCount;
+ if (!HasTriggers) {
+ return count;
+ }
+ for (unsigned int i=0;i<ItemsCount;i++) {
+ if (!IsItemAvailable(i) ) {
+ count--;
+ }
+ }
+ return count;
+}
+
+int Store::AcceptableItemType(ieDword type, ieDword invflags, bool pc) const
+{
+ int ret;
+
+ //don't allow any movement of undroppable items
+ if (invflags&IE_INV_ITEM_UNDROPPABLE ) {
+ ret = 0;
+ } else {
+ ret = IE_STORE_BUY|IE_STORE_SELL|IE_STORE_STEAL;
+ }
+ if (invflags&IE_INV_ITEM_UNSTEALABLE) {
+ ret &= ~IE_STORE_STEAL;
+ }
+ if (!(invflags&IE_INV_ITEM_IDENTIFIED) ) {
+ ret |= IE_STORE_ID;
+ }
+ if (pc && (Type<STT_BG2CONT) ) {
+ //can't sell critical items
+ if (!(invflags&IE_INV_ITEM_DESTRUCTIBLE )) {
+ ret &= ~IE_STORE_SELL;
+ }
+ //don't allow selling of non destructible items
+ //don't allow selling of critical items (they could still be put in bags)
+ if ((invflags&(IE_INV_ITEM_DESTRUCTIBLE|IE_INV_ITEM_CRITICAL))!=IE_INV_ITEM_DESTRUCTIBLE) {
+ ret &= ~IE_STORE_SELL;
+ }
+
+ //check if store buys stolen items
+ if ((invflags&IE_INV_ITEM_STOLEN) && !(Type&IE_STORE_FENCE) ) {
+ ret &= ~IE_STORE_SELL;
+ }
+ }
+
+ if (!pc) {
+ return ret;
+ }
+
+ for (ieDword i=0;i<PurchasedCategoriesCount;i++) {
+ if (type==purchased_categories[i]) {
+ return ret;
+ }
+ }
+
+ //Even if the store doesn't purchase the item, it can still ID it
+ return ret & ~IE_STORE_SELL;
+}
+
+STOCure *Store::GetCure(unsigned int idx) const
+{
+ if (idx>=CuresCount) {
+ return NULL;
+ }
+ return cures+idx;
+}
+
+STODrink *Store::GetDrink(unsigned int idx) const
+{
+ if (idx>=DrinksCount) {
+ return NULL;
+ }
+ return drinks+idx;
+}
+
+//We need this weirdness for PST item lookup
+STOItem *Store::GetItem(unsigned int idx)
+{
+ if (!HasTriggers) {
+ if (idx>=items.size()) {
+ return NULL;
+ }
+ return items[idx];
+ }
+
+ for (unsigned int i=0;i<ItemsCount;i++) {
+ if (IsItemAvailable(i)) {
+ if (!idx) {
+ return items[i];
+ }
+ idx--;
+ }
+ }
+ return NULL;
+}
+
+unsigned int Store::FindItem(const ieResRef itemname, bool usetrigger) const
+{
+ for (unsigned int i=0;i<ItemsCount;i++) {
+ if (usetrigger) {
+ if (!IsItemAvailable(i) ) {
+ continue;
+ }
+ }
+ STOItem *temp = items[i];
+ if (!strnicmp(itemname, temp->ItemResRef, 8) ) {
+ return i;
+ }
+ }
+ return (unsigned int) -1;
+}
+
+STOItem *Store::FindItem(CREItem *item, bool exact)
+{
+ for (unsigned int i=0;i<ItemsCount;i++) {
+ if (!IsItemAvailable(i) ) {
+ continue;
+ }
+ STOItem *temp = items[i];
+
+ if (strnicmp(item->ItemResRef, temp->ItemResRef, 8) ) {
+ continue;
+ }
+ if(exact) {
+ if (temp->InfiniteSupply==-1) {
+ return temp;
+ }
+ //check if we could simply merge the item into the stock or need a new entry
+ if ((temp->StackAmount>=99) || memcmp(temp->Usages, item->Usages, sizeof(item->Usages))) {
+ continue;
+ }
+ }
+ return temp;
+ }
+ return NULL;
+}
+
+//some stores can recharge items
+void Store::RechargeItem(CREItem *item)
+{
+//is there any flag which store can recharge?
+ Item *itm = gamedata->GetItem(item->ItemResRef);
+ if (!itm) {
+ return;
+ }
+ if (!itm->LoreToID) {
+ item->Flags |= IE_INV_ITEM_IDENTIFIED;
+ }
+ //gemrb extension, some shops won't recharge items
+ if (!(Flags&IE_STORE_RECHARGE)) {
+ for (int i=0;i<CHARGE_COUNTERS;i++) {
+ ITMExtHeader *h = itm->GetExtHeader(i);
+ if (!h) {
+ item->Usages[i]=0;
+ continue;
+ }
+ if (h->RechargeFlags&IE_ITEM_RECHARGE) {
+ item->Usages[i] = h->Charges;
+ }
+ }
+ }
+ gamedata->FreeItem(itm, item->ItemResRef, 0);
+}
+
+void Store::AddItem(CREItem *item)
+{
+ RechargeItem(item);
+ STOItem *temp = FindItem(item, true);
+
+ if (temp) {
+ if (temp->InfiniteSupply!=-1) {
+ temp->StackAmount++;
+ }
+ return;
+ }
+
+ temp = new STOItem();
+ //It is important to initialize these fields, if STOItem ever changes to
+ //a real class from struct, make sure the fields are cleared
+ memset( temp, 0, sizeof (STOItem ) );
+ memcpy( temp, item, sizeof( CREItem ) );
+ items.push_back (temp );
+ ItemsCount++;
+}
+
+void Store::RemoveItem( unsigned int idx )
+{
+ if (items.size()!=ItemsCount) {
+ printMessage("Store","Inconsistent store", LIGHT_RED);
+ abort();
+ }
+ if (ItemsCount<=idx) {
+ return;
+ }
+ items.erase(items.begin()+idx);
+ ItemsCount--;
+}
+
+ieDword Store::GetOwnerID() const
+{
+ return StoreOwnerID;
+}
+
+void Store::SetOwnerID(ieDword owner)
+{
+ StoreOwnerID = owner;
+}
diff --git a/gemrb/core/Store.h b/gemrb/core/Store.h
new file mode 100644
index 0000000..aaabd35
--- /dev/null
+++ b/gemrb/core/Store.h
@@ -0,0 +1,178 @@
+/* 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 Store.h
+ * Declares Store, class describing shops, temples and pubs, etc.
+ * @author The GemRB Project
+ */
+
+
+#ifndef STORE_H
+#define STORE_H
+
+#include "exports.h"
+#include "globals.h"
+#include "ie_types.h"
+
+#include <vector>
+
+//bah!
+class CREItem;
+class Trigger;
+
+typedef enum StoreType { STT_STORE=0, STT_TAVERN=1, STT_INN=2, STT_TEMPLE=3,
+STT_BG2CONT=4, STT_IWD2CONT=5 } StoreType;
+
+typedef enum StoreActionType { STA_BUYSELL=0, STA_IDENTIFY=1, STA_STEAL=2,
+STA_CURE=3, STA_DONATE=4, STA_DRINK=5, STA_ROOMRENT=6, STA_OPTIONAL=0x80} StoreActionType;
+
+#define IE_STORE_BUY 1
+#define IE_STORE_SELL 2
+#define IE_STORE_ID 4
+#define IE_STORE_STEAL 8
+#define IE_STORE_DONATE 16 //gemrb extension
+#define IE_STORE_CURE 32
+#define IE_STORE_DRINK 64
+#define IE_STORE_SELECT 0x40 //valid when these flags used as store action
+#define IE_STORE_RENT 128 //gemrb extension
+#define IE_STORE_QUALITY 0x600 //2 bits
+#define IE_STORE_FENCE 0x2000 //
+#define IE_STORE_RECHARGE 0x4000 //gemrb extension, if set, store won't recharge
+
+
+/**
+ * @struct STOItem
+ * Item in a store, together with available amount etc.
+ */
+struct STOItem {
+ ieResRef ItemResRef;
+ ieWord PurchasedAmount;
+ ieWord Usages[CHARGE_COUNTERS];
+ ieDword Flags;
+ // 2 cached values from associated item. LEAVE IT SIGNED!
+ int Weight;
+ int StackAmount;
+ ieDword AmountInStock;
+ ieDwordSigned InfiniteSupply;
+ // V1.1
+ Trigger *trigger;
+ //ieDword TriggerRef; use infinitesupply
+ char unknown2[56];
+};
+
+
+/**
+ * @struct STODrink
+ * Kind of drink in a pub, with its associated rumour and price
+ */
+
+struct STODrink {
+ ieResRef RumourResRef;
+ ieStrRef DrinkName;
+ ieDword Price;
+ ieDword Strength;
+};
+
+
+/**
+ * @struct STOCure
+ * Kind of cure available in a temple, with its associated price
+ */
+
+struct STOCure {
+ ieResRef CureResRef;
+ ieDword Price;
+};
+
+/**
+ * @class Store
+ * Class describing shops, temples, pubs, etc.
+ */
+
+class GEM_EXPORT Store {
+public:
+ Store();
+ ~Store();
+
+ std::vector< STOItem*> items;
+ STODrink* drinks;
+ STOCure* cures;
+ ieDword* purchased_categories;
+
+ ieResRef Name;
+ ieDword Type;
+ ieStrRef StoreName;
+ ieDword Flags;
+ ieDword SellMarkup;
+ ieDword BuyMarkup;
+ ieDword DepreciationRate;
+ ieWord StealFailureChance;
+ ieWord Capacity;
+ char unknown[8];
+ ieDword PurchasedCategoriesOffset;
+ ieDword PurchasedCategoriesCount;
+ ieDword ItemsOffset;
+ //don't use this value directly, use GetRealStockSize
+ ieDword ItemsCount;
+ ieDword Lore;
+ ieDword IDPrice;
+ ieResRef RumoursTavern;
+ ieDword DrinksOffset;
+ ieDword DrinksCount;
+ ieResRef RumoursTemple;
+ ieDword AvailableRooms;
+ ieDword RoomPrices[4];
+ ieDword CuresOffset;
+ ieDword CuresCount;
+ bool HasTriggers;
+ char unknown2[36];
+
+ // IWD2 only
+ char unknown3[80];
+
+ int version;
+ // the scripting name of the owner
+ ieDword StoreOwnerID;
+
+public: //queries
+ int AcceptableItemType(ieDword type, ieDword invflags, bool pc) const;
+ STOCure *GetCure(unsigned int idx) const;
+ STODrink *GetDrink(unsigned int idx) const;
+ STOItem *GetItem(unsigned int idx);
+ /** Evaluates item availability triggers */
+ int GetRealStockSize();
+ /** Recharges item */
+ void RechargeItem(CREItem *item);
+ /** Adds a new item to the store (selling) */
+ void AddItem(CREItem* item);
+ void RemoveItem(unsigned int idx);
+ /** Returns index of item */
+ unsigned int FindItem(const ieResRef item, bool usetrigger) const;
+ const char *GetOwner() const;
+ ieDword GetOwnerID() const;
+ void SetOwnerID(ieDword owner);
+private:
+ /** Finds a mergeable item in the stock, if exact is set, it checks for usage counts too */
+ STOItem *FindItem(CREItem *item, bool exact);
+ bool IsItemAvailable(unsigned int slot) const;
+};
+
+#endif // ! STORE_H
diff --git a/gemrb/core/StoreMgr.cpp b/gemrb/core/StoreMgr.cpp
new file mode 100644
index 0000000..b2d6bd0
--- /dev/null
+++ b/gemrb/core/StoreMgr.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 "StoreMgr.h"
+
+StoreMgr::StoreMgr(void)
+{
+}
+
+StoreMgr::~StoreMgr(void)
+{
+}
diff --git a/gemrb/core/StoreMgr.h b/gemrb/core/StoreMgr.h
new file mode 100644
index 0000000..16a7070
--- /dev/null
+++ b/gemrb/core/StoreMgr.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.
+ *
+ *
+ */
+
+/**
+ * @file StoreMgr.h
+ * Declares StoreMgr class, loader for Store objects
+ * @author The GemRB Project
+ */
+
+
+#ifndef STOREMGR_H
+#define STOREMGR_H
+
+#include "Plugin.h"
+#include "Store.h"
+#include "System/DataStream.h"
+
+/**
+ * @class StoreMgr
+ * Abstract loader for Store objects
+ */
+
+class GEM_EXPORT StoreMgr : public Plugin {
+public:
+ StoreMgr(void);
+ virtual ~StoreMgr(void);
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual Store* GetStore(Store *s) = 0;
+
+ virtual int GetStoredFileSize(Store *s) = 0;
+ virtual int PutStore(DataStream* stream, Store *s) = 0;
+};
+
+#endif
diff --git a/gemrb/core/StringMgr.cpp b/gemrb/core/StringMgr.cpp
new file mode 100644
index 0000000..202f4f3
--- /dev/null
+++ b/gemrb/core/StringMgr.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 "StringMgr.h"
+
+StringMgr::StringMgr(void)
+{
+}
+
+StringMgr::~StringMgr(void)
+{
+}
diff --git a/gemrb/core/StringMgr.h b/gemrb/core/StringMgr.h
new file mode 100644
index 0000000..be18bd1
--- /dev/null
+++ b/gemrb/core/StringMgr.h
@@ -0,0 +1,62 @@
+/* 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 StringMgr.h
+ * Declares StringMgr class, loader for StringBlock objects (.TLK files)
+ * @author The GemRB Project
+ */
+
+
+#ifndef STRINGMGR_H
+#define STRINGMGR_H
+
+#include "Plugin.h"
+#include "System/DataStream.h"
+
+/**
+ * @struct StringBlock
+ * Text and its associated sound.
+ */
+
+struct StringBlock {
+ char* text;
+ ieResRef Sound;
+};
+
+/**
+ * @class StringMgr
+ * Abstract loader for StringBlock objects (strings in .TLK files)
+ */
+
+class GEM_EXPORT StringMgr : public Plugin {
+public:
+ StringMgr(void);
+ virtual ~StringMgr(void);
+ virtual void OpenAux() = 0;
+ virtual void CloseAux() = 0;
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual char* GetString(ieStrRef strref, unsigned int flags = 0) = 0;
+ virtual StringBlock GetStringBlock(ieStrRef strref, unsigned int flags = 0) = 0;
+ virtual void FreeString(char *str) = 0;
+ virtual ieStrRef UpdateString(ieStrRef strref, const char *text) = 0;
+};
+
+#endif // ! STRINGMGR_H
diff --git a/gemrb/core/SymbolMgr.cpp b/gemrb/core/SymbolMgr.cpp
new file mode 100644
index 0000000..dcd030a
--- /dev/null
+++ b/gemrb/core/SymbolMgr.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 "SymbolMgr.h"
+
+SymbolMgr::SymbolMgr(void)
+{
+}
+
+SymbolMgr::~SymbolMgr(void)
+{
+}
diff --git a/gemrb/core/SymbolMgr.h b/gemrb/core/SymbolMgr.h
new file mode 100644
index 0000000..ebf3399
--- /dev/null
+++ b/gemrb/core/SymbolMgr.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 SymbolMgr.h
+ * Declares SymbolMgr class, abstract loader for symbol tables (.IDS files)
+ * @author The GemRB Project
+ */
+
+
+#ifndef SYMBOLMGR_H
+#define SYMBOLMGR_H
+
+/** GetValue returns this if text is not found in arrays */
+#define SYMBOL_VALUE_NOT_LOCATED -65535
+
+#include "Plugin.h"
+#include "System/DataStream.h"
+
+/**
+ * @class SymbolMgr
+ * Abstract loader for symbol tables (.IDS files)
+ */
+
+class GEM_EXPORT SymbolMgr : public Plugin {
+public:
+ SymbolMgr(void);
+ virtual ~SymbolMgr(void);
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual int GetValue(const char* text) const = 0;
+ virtual char* GetValue(int val) const = 0;
+ virtual char* GetStringIndex(unsigned int Index) const = 0;
+ virtual int GetValueIndex(unsigned int Index) const = 0;
+ virtual int FindValue(int val) const = 0;
+ virtual int FindString(char *str, int len) const = 0;
+ virtual int GetSize() const = 0;
+};
+
+#endif // ! SYMBOLMGR_H
diff --git a/gemrb/core/System/CachedFileStream.cpp b/gemrb/core/System/CachedFileStream.cpp
new file mode 100644
index 0000000..f9ae248
--- /dev/null
+++ b/gemrb/core/System/CachedFileStream.cpp
@@ -0,0 +1,213 @@
+/* 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 "System/CachedFileStream.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+
+CachedFileStream::CachedFileStream(const char* stream, bool autoFree)
+{
+ ExtractFileFromPath( filename, stream );
+ PathJoin( originalfile, core->CachePath, filename, NULL );
+
+ str = _fopen( originalfile, "rb" );
+ if (str == NULL) { // File was not found in cache
+ if (core->GameOnCD) {
+ _FILE* src = _fopen( stream, "rb" );
+#ifdef _DEBUG
+ core->CachedFileStreamPtrCount++;
+#endif
+ _FILE* dest = _fopen( originalfile, "wb" );
+#ifdef _DEBUG
+ core->CachedFileStreamPtrCount++;
+#endif
+ void* buff = malloc( 1024 * 1000 );
+ do {
+ size_t len = _fread( buff, 1, 1024 * 1000, src );
+ size_t c = _fwrite( buff, 1, len, dest );
+ if (c != len) {
+ printf("CachedFileStream failed to write to cached file '%s' (from '%s')\n", originalfile, stream);
+ abort();
+ }
+ } while (!_feof( src ));
+ free( buff );
+ _fclose( src );
+#ifdef _DEBUG
+ core->CachedFileStreamPtrCount--;
+#endif
+ _fclose( dest );
+#ifdef _DEBUG
+ core->CachedFileStreamPtrCount--;
+#endif
+ } else { // Don't cache files already on hdd
+ strncpy(originalfile, stream, _MAX_PATH);
+ }
+ str = _fopen( originalfile, "rb" );
+ }
+#ifdef _DEBUG
+ core->CachedFileStreamPtrCount++;
+#endif
+ startpos = 0;
+ _fseek( str, 0, SEEK_END );
+ size = _ftell( str );
+ _fseek( str, 0, SEEK_SET );
+ Pos = 0;
+ this->autoFree = autoFree;
+}
+
+CachedFileStream::CachedFileStream(CachedFileStream* cfs, int startpos,
+ int size, bool autoFree)
+{
+ this->size = size;
+ this->startpos = startpos;
+ this->autoFree = autoFree;
+ char cpath[_MAX_PATH];
+ PathJoin( cpath, core->CachePath, cfs->filename, NULL );
+ str = _fopen( cpath, "rb" );
+ if (str == NULL) {
+ str = _fopen( cfs->originalfile, "rb" );
+ if (str == NULL) {
+ printf( "Can't open stream (maybe leaking?)\n" );
+ return;
+ }
+ strncpy( originalfile, cfs->originalfile, sizeof(originalfile) );
+ strncpy( filename, cfs->filename, sizeof(filename) );
+ } else {
+ strncpy( originalfile, cpath, sizeof(originalfile) );
+ strncpy( filename, cfs->filename, sizeof(filename) );
+ }
+#ifdef _DEBUG
+ core->CachedFileStreamPtrCount++;
+#endif
+ _fseek( str, startpos, SEEK_SET );
+
+
+ Pos = 0;
+}
+
+CachedFileStream::~CachedFileStream(void)
+{
+ if (autoFree && str) {
+#ifdef _DEBUG
+ core->CachedFileStreamPtrCount--;
+#endif
+ _fclose( str );
+ }
+ str = NULL;
+ //autoFree = false; //File stream destructor hack
+}
+
+int CachedFileStream::Read(void* dest, unsigned int length)
+{
+ //we don't allow partial reads anyway, so it isn't a problem that
+ //i don't adjust length here (partial reads are evil)
+ if (Pos+length>size ) {
+ return GEM_ERROR;
+ }
+
+ unsigned int c = (unsigned int) _fread( dest, 1, length, str );
+ if (c != length) {
+ return GEM_ERROR;
+ }
+ if (Encrypted) {
+ ReadDecrypted( dest, c );
+ }
+ Pos += c;
+ return c;
+}
+
+int CachedFileStream::Write(const void* src, unsigned int length)
+{
+ // do encryption here if needed
+
+ unsigned int c = (unsigned int) _fwrite( src, 1, length, str );
+ if (c != length) {
+ return GEM_ERROR;
+ }
+ Pos += c;
+ //this is needed only if you want to Seek in a written file
+ if (Pos>size) {
+ size = Pos;
+ }
+ return c;
+}
+
+int CachedFileStream::Seek(int newpos, int type)
+{
+ switch (type) {
+ case GEM_CURRENT_POS:
+ _fseek( str, newpos, SEEK_CUR );
+ Pos += newpos;
+ break;
+
+ case GEM_STREAM_START:
+ _fseek( str, startpos + newpos, SEEK_SET );
+ Pos = newpos;
+ break;
+
+ default:
+ return GEM_ERROR;
+ }
+ //we went past the buffer
+ if (Pos>size) {
+ printf("[Streams]: Invalid seek position: %ld (limit: %ld)\n",Pos, size);
+ return GEM_ERROR;
+ }
+ return GEM_OK;
+}
+
+/** No descriptions */
+int CachedFileStream::ReadLine(void* buf, unsigned int maxlen)
+{
+ if (!maxlen) {
+ return 0;
+ }
+ unsigned char * p = ( unsigned char * ) buf;
+ if (_feof( str )) {
+ p[0]=0;
+ return -1;
+ }
+ if (Pos >= size) {
+ p[0]=0;
+ return -1;
+ }
+ unsigned int i = 0;
+ while (i < ( maxlen - 1 )) {
+ int ch = _fgetc( str );
+ if (Pos == size)
+ break;
+ if (Encrypted)
+ ch ^= GEM_ENCRYPTION_KEY[Pos & 63];
+ Pos++;
+ if (( ( char ) ch ) == '\n')
+ break;
+ if (( ( char ) ch ) == '\t')
+ ch = ' ';
+ if (( ( char ) ch ) != '\r')
+ p[i++] = ch;
+ //Warning:this feof implementation reads forward one byte
+ if (_feof( str ))
+ break;
+ }
+ p[i] = 0;
+ return i;
+}
diff --git a/gemrb/core/System/CachedFileStream.h b/gemrb/core/System/CachedFileStream.h
new file mode 100644
index 0000000..40042eb
--- /dev/null
+++ b/gemrb/core/System/CachedFileStream.h
@@ -0,0 +1,45 @@
+/* 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 CACHEDFILESTREAM_H
+#define CACHEDFILESTREAM_H
+
+#include "System/DataStream.h"
+
+#include "exports.h"
+
+class GEM_EXPORT CachedFileStream : public DataStream// : public FileStream
+{
+private:
+ bool autoFree;
+ unsigned long startpos;
+ _FILE* str;
+public:
+ CachedFileStream(const char* stream, bool autoFree = true);
+ CachedFileStream(CachedFileStream* cfs, int startpos, int size,
+ bool autoFree = true);
+ ~CachedFileStream(void);
+ int Read(void* dest, unsigned int length);
+ int Write(const void* src, unsigned int length);
+ int Seek(int pos, int startpos);
+ /** No descriptions */
+ int ReadLine(void* buf, unsigned int maxlen);
+};
+#endif
diff --git a/gemrb/core/System/DataStream.cpp b/gemrb/core/System/DataStream.cpp
new file mode 100644
index 0000000..71c0f19
--- /dev/null
+++ b/gemrb/core/System/DataStream.cpp
@@ -0,0 +1,185 @@
+/* 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 "System/DataStream.h"
+
+#include "win32def.h"
+
+#include <ctype.h>
+
+static bool EndianSwitch = false;
+
+DataStream::DataStream(void)
+{
+ Pos = 0;
+ Encrypted = false;
+}
+
+DataStream::~DataStream(void)
+{
+}
+
+void DataStream::SetEndianSwitch(int tmp)
+{
+ EndianSwitch = !! tmp;
+}
+
+bool DataStream::IsEndianSwitch()
+{
+ return EndianSwitch;
+}
+
+/** Returns true if the stream is encrypted */
+bool DataStream::CheckEncrypted()
+{
+ ieWord two;
+ Seek( 0, GEM_STREAM_START );
+ Read( &two, 2 );
+ if (two == 0xFFFF) {
+ Pos = 0;
+ Encrypted = true;
+ size -= 2;
+ return true;
+ }
+ Seek( 0, GEM_STREAM_START );
+ Encrypted = false;
+ return false;
+}
+/** No descriptions */
+void DataStream::ReadDecrypted(void* buf, unsigned int size)
+{
+ for (unsigned int i = 0; i < size; i++)
+ ( ( unsigned char * ) buf )[i] ^= GEM_ENCRYPTION_KEY[( Pos + i ) & 63];
+}
+
+void DataStream::Rewind()
+{
+ Seek( Encrypted ? 2 : 0, GEM_STREAM_START );
+ Pos = 0;
+}
+
+unsigned long DataStream::GetPos() const
+{
+ return Pos;
+}
+
+unsigned long DataStream::Size() const
+{
+ return size;
+}
+
+unsigned long DataStream::Remains() const
+{
+ return size-Pos;
+}
+
+int DataStream::ReadWord(ieWord *dest)
+{
+ int len = Read(dest, 2);
+ if (EndianSwitch) {
+ unsigned char tmp;
+ tmp=((unsigned char *) dest)[0];
+ ((unsigned char *) dest)[0]=((unsigned char *) dest)[1];
+ ((unsigned char *) dest)[1]=tmp;
+ }
+ return len;
+}
+
+int DataStream::ReadWordSigned(ieWordSigned *dest)
+{
+ int len = Read(dest, 2);
+ if (EndianSwitch) {
+ unsigned char tmp;
+ tmp=((unsigned char *) dest)[0];
+ ((unsigned char *) dest)[0]=((unsigned char *) dest)[1];
+ ((unsigned char *) dest)[1]=tmp;
+ }
+ return len;
+}
+
+int DataStream::WriteWord(const ieWord *src)
+{
+ int len;
+ if (EndianSwitch) {
+ char tmp[2];
+ tmp[0]=((unsigned char *) src)[1];
+ tmp[1]=((unsigned char *) src)[0];
+ len = Write( tmp, 2 );
+ }
+ else {
+ len = Write( src, 2 );
+ }
+ return len;
+}
+
+int DataStream::ReadDword(ieDword *dest)
+{
+ int len = Read(dest, 4);
+ if (EndianSwitch) {
+ unsigned char tmp;
+ tmp=((unsigned char *) dest)[0];
+ ((unsigned char *) dest)[0]=((unsigned char *) dest)[3];
+ ((unsigned char *) dest)[3]=tmp;
+ tmp=((unsigned char *) dest)[1];
+ ((unsigned char *) dest)[1]=((unsigned char *) dest)[2];
+ ((unsigned char *) dest)[2]=tmp;
+ }
+ return len;
+}
+
+int DataStream::WriteDword(const ieDword *src)
+{
+ int len;
+ if (EndianSwitch) {
+ char tmp[4];
+ tmp[0]=((unsigned char *) src)[3];
+ tmp[1]=((unsigned char *) src)[2];
+ tmp[2]=((unsigned char *) src)[1];
+ tmp[3]=((unsigned char *) src)[0];
+ len = Write( tmp, 4 );
+ }
+ else {
+ len = Write( src, 4 );
+ }
+ return len;
+}
+
+int DataStream::ReadResRef(ieResRef dest)
+{
+ int len = Read(dest, 8);
+ int i;
+ // lowercase the resref
+ for(i = 0; i < 8; i++) {
+ dest[i] = (char) tolower(dest[i]);
+ }
+ // remove trailing spaces
+ for (i = 7; i >= 0; i--) {
+ if (dest[i] == ' ') dest[i] = 0;
+ else break;
+ }
+ // null-terminate
+ dest[8] = 0;
+ return len;
+}
+
+int DataStream::WriteResRef(const ieResRef src)
+{
+ return Write( src, 8);
+}
diff --git a/gemrb/core/System/DataStream.h b/gemrb/core/System/DataStream.h
new file mode 100644
index 0000000..e20c118
--- /dev/null
+++ b/gemrb/core/System/DataStream.h
@@ -0,0 +1,77 @@
+/* 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 DataStream.h
+ * Declares DataStream, abstract class for reading and writing data.
+ * @author The GemRB Project
+ */
+
+
+#ifndef DATASTREAM_H
+#define DATASTREAM_H
+
+#include "exports.h"
+#include "globals.h"
+
+#define GEM_CURRENT_POS 0
+#define GEM_STREAM_START 1
+#define GEM_STREAM_END 2
+
+/**
+ * @class DataStream
+ * Abstract base for streams, classes for reading and writing data.
+ */
+
+class GEM_EXPORT DataStream {
+protected:
+ unsigned long Pos;
+ unsigned long size;
+ bool Encrypted;
+public:
+ char filename[16]; //8+1+3+1 padded to dword
+ char originalfile[_MAX_PATH];
+public:
+ DataStream(void);
+ virtual ~DataStream(void);
+ virtual int Read(void* dest, unsigned int len) = 0;
+ int ReadWord(ieWord* dest);
+ int ReadWordSigned (ieWordSigned* dest);
+ int ReadDword(ieDword* dest);
+ int ReadResRef(ieResRef dest);
+ virtual int Write(const void* src, unsigned int len) = 0;
+ int WriteWord(const ieWord* src);
+ int WriteDword(const ieDword* src);
+ int WriteResRef(const ieResRef src);
+ virtual int Seek(int pos, int startpos) = 0;
+ unsigned long Remains() const;
+ unsigned long Size() const;
+ unsigned long GetPos() const;
+ void Rewind();
+ /** Returns true if the stream is encrypted */
+ bool CheckEncrypted();
+ void ReadDecrypted(void* buf, unsigned int size);
+ virtual int ReadLine(void* buf, unsigned int maxlen) = 0;
+ /** Endian Switch setup */
+ static void SetEndianSwitch(int par);
+ static bool IsEndianSwitch();
+};
+
+#endif // ! DATASTREAM_H
diff --git a/gemrb/core/System/FileStream.cpp b/gemrb/core/System/FileStream.cpp
new file mode 100644
index 0000000..eeeabc1
--- /dev/null
+++ b/gemrb/core/System/FileStream.cpp
@@ -0,0 +1,281 @@
+/* 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 "System/FileStream.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+
+FileStream::FileStream(void)
+{
+ opened = false;
+ created = false;
+ str = NULL;
+ autoFree = false;
+}
+
+FileStream::~FileStream(void)
+{
+ if (autoFree && str) {
+#ifdef _DEBUG
+ core->FileStreamPtrCount--;
+#endif
+ _fclose( str );
+ }
+ str = NULL;
+}
+
+bool FileStream::Open(const char* fname, bool aF)
+{
+ if (str && autoFree) {
+#ifdef _DEBUG
+ core->FileStreamPtrCount--;
+#endif
+ _fclose( str );
+ str = NULL;
+ }
+
+ if (!file_exists(fname)) {
+ return false;
+ }
+
+ autoFree = aF;
+ str = _fopen( fname, "rb" );
+ if (str == NULL) {
+ return false;
+ }
+#ifdef _DEBUG
+ core->FileStreamPtrCount++;
+#endif
+ startpos = 0;
+ opened = true;
+ created = false;
+ //FIXME: this is a very lame way to tell the file length
+ _fseek( str, 0, SEEK_END );
+ size = _ftell( str );
+ _fseek( str, 0, SEEK_SET );
+ ExtractFileFromPath( filename, fname );
+ strncpy( originalfile, fname, _MAX_PATH);
+ Pos = 0;
+ return true;
+}
+
+bool FileStream::Modify(const char* fname, bool aF)
+{
+ if (str && autoFree) {
+#ifdef _DEBUG
+ core->FileStreamPtrCount--;
+#endif
+ _fclose( str );
+ }
+ autoFree = aF;
+ str = _fopen( fname, "r+b" );
+ if (str == NULL) {
+ return false;
+ }
+#ifdef _DEBUG
+ core->FileStreamPtrCount++;
+#endif
+ startpos = 0;
+ opened = true;
+ created = true;
+ //FIXME: this is a very lame way to tell the file length
+ _fseek( str, 0, SEEK_END );
+ size = _ftell( str );
+ _fseek( str, 0, SEEK_SET );
+ ExtractFileFromPath( filename, fname );
+ strncpy( originalfile, fname, _MAX_PATH);
+ Pos = 0;
+ return true;
+}
+
+bool FileStream::Open(_FILE* stream, int spos, int maxsize, bool aF)
+{
+ if (str && autoFree) {
+#ifdef _DEBUG
+ core->FileStreamPtrCount--;
+#endif
+ _fclose( str );
+ }
+ autoFree = aF;
+ str = stream;
+ if (str == NULL) {
+ return false;
+ }
+#ifdef _DEBUG
+ core->FileStreamPtrCount++;
+#endif
+ startpos = spos;
+ opened = true;
+ created = false;
+ size = maxsize;
+ filename[0]=0;
+ originalfile[0]=0;
+ _fseek( str, spos, SEEK_SET );
+ Pos = 0;
+ return true;
+}
+
+//Creating file in the cache
+//Create is ALWAYS autofree
+bool FileStream::Create(const char* fname, SClass_ID ClassID)
+{
+ return Create(core->CachePath, fname, ClassID);
+}
+
+//Creating file outside of the cache
+bool FileStream::Create(const char *folder, const char* fname, SClass_ID ClassID)
+{
+ if (str && autoFree) {
+#ifdef _DEBUG
+ core->FileStreamPtrCount--;
+#endif
+ _fclose( str );
+ }
+ autoFree = true;
+ ExtractFileFromPath( filename, fname );
+ strcpy( originalfile, folder );
+ strcat( originalfile, SPathDelimiter);
+ strcat( originalfile, filename );
+ strcat( originalfile, core->TypeExt( ClassID ) );
+ str = _fopen( originalfile, "wb" );
+ if (str == NULL) {
+ return false;
+ }
+ opened = true;
+ created = true;
+ Pos = 0;
+ size = 0;
+ startpos = 0;
+ return true;
+}
+
+int FileStream::Read(void* dest, unsigned int length)
+{
+ if (!opened) {
+ return GEM_ERROR;
+ }
+ //we don't allow partial reads anyway, so it isn't a problem that
+ //i don't adjust length here (partial reads are evil)
+ if (Pos+length>size ) {
+ return GEM_ERROR;
+ }
+ size_t c = _fread( dest, 1, length, str );
+ if (c != length) {
+ return GEM_ERROR;
+ }
+ if (Encrypted) {
+ ReadDecrypted( dest, c );
+ }
+ Pos += c;
+ return c;
+}
+
+int FileStream::Write(const void* src, unsigned int length)
+{
+ if (!created) {
+ return GEM_ERROR;
+ }
+ // do encryption here if needed
+
+ size_t c = _fwrite( src, 1, length, str );
+ if (c != length) {
+ return GEM_ERROR;
+ }
+ Pos += c;
+ if (Pos>size) {
+ size = Pos;
+ }
+ return c;
+}
+
+int FileStream::Seek(int newpos, int type)
+{
+ if (!opened && !created) {
+ return GEM_ERROR;
+ }
+ switch (type) {
+ case GEM_STREAM_END:
+ _fseek( str, startpos + size - newpos, SEEK_SET);
+ Pos = size - newpos;
+ break;
+ case GEM_CURRENT_POS:
+ _fseek( str, newpos, SEEK_CUR );
+ Pos += newpos;
+ break;
+
+ case GEM_STREAM_START:
+ _fseek( str, startpos + newpos, SEEK_SET );
+ Pos = newpos;
+ break;
+
+ default:
+ return GEM_ERROR;
+ }
+ if (Pos>size) {
+ printf("[Streams]: Invalid seek position %ld in file %s (limit: %ld)\n",Pos, filename, size);
+ return GEM_ERROR;
+ }
+ return GEM_OK;
+}
+
+/** No descriptions */
+int FileStream::ReadLine(void* buf, unsigned int maxlen)
+{
+ if(!maxlen) {
+ return 0;
+ }
+ unsigned char * p = ( unsigned char * ) buf;
+ if (_feof( str )) {
+ p[0]=0;
+ return -1;
+ }
+ if (Pos >= size) {
+ p[0]=0;
+ return -1;
+ }
+ unsigned int i = 0;
+ while (i < ( maxlen - 1 )) {
+ int ch = _fgetc( str );
+ if (Pos == size)
+ break;
+ if (Encrypted) {
+ ch ^= GEM_ENCRYPTION_KEY[Pos & 63];
+ }
+ Pos++;
+ if (( ( char ) ch ) == '\n')
+ break;
+ if (( ( char ) ch ) == '\t')
+ ch = ' ';
+ if (( ( char ) ch ) != '\r')
+ p[i++] = ch;
+ //Warning:this feof implementation reads forward one byte
+ if (_feof( str ))
+ break;
+ }
+ p[i] = 0;
+ return i;
+}
+
+unsigned long FileStream::GetStartPos() const
+{
+ return startpos;
+}
diff --git a/gemrb/core/System/FileStream.h b/gemrb/core/System/FileStream.h
new file mode 100644
index 0000000..8c8a28a
--- /dev/null
+++ b/gemrb/core/System/FileStream.h
@@ -0,0 +1,63 @@
+/* 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 FileStream.h
+ * Declares FileStream class, stream reading/writing data from/to a file in a filesystem.
+ * @author The GemRB Project
+ */
+
+
+#ifndef FILESTREAM_H
+#define FILESTREAM_H
+
+#include "System/DataStream.h"
+
+#include "exports.h"
+#include "globals.h"
+
+/**
+ * @class FileStream
+ * Reads and writes data from/to files on a filesystem
+ */
+
+class GEM_EXPORT FileStream : public DataStream {
+private:
+ bool autoFree;
+ unsigned long startpos;
+ _FILE* str;
+ bool opened, created;
+public:
+ FileStream(void);
+ ~FileStream(void);
+
+ bool Open(const char* filename, bool autoFree = true);
+ bool Open(_FILE* stream, int startpos, int size, bool autoFree = false);
+ bool Modify(const char* filename, bool autoFree = true);
+ bool Create(const char* folder, const char* filename, SClass_ID ClassID);
+ bool Create(const char* filename, SClass_ID ClassID);
+ int Read(void* dest, unsigned int length);
+ int Write(const void* src, unsigned int length);
+ int Seek(int pos, int startpos);
+ unsigned long GetStartPos() const;
+ int ReadLine(void* buf, unsigned int maxlen);
+};
+
+#endif // ! FILESTREAM_H
diff --git a/gemrb/core/System/MemoryStream.cpp b/gemrb/core/System/MemoryStream.cpp
new file mode 100644
index 0000000..1ce9656
--- /dev/null
+++ b/gemrb/core/System/MemoryStream.cpp
@@ -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.
+ *
+ *
+ */
+
+#include "System/MemoryStream.h"
+
+#include "win32def.h"
+
+#include <cstring>
+
+MemoryStream::MemoryStream(void* buffer, int length, bool autoFree)
+{
+ ptr = buffer;
+ size = length;
+ Pos = 0;
+ strcpy( filename, "" );
+ this->autoFree = autoFree;
+}
+
+MemoryStream::~MemoryStream(void)
+{
+ if (autoFree) {
+ free( ptr );
+ }
+}
+
+int MemoryStream::Read(void* dest, unsigned int length)
+{
+ if (length + Pos > size) {
+ return GEM_ERROR;
+ }
+ ieByte* p = ( ieByte* ) ptr + Pos;
+ memcpy( dest, p, length );
+ if (Encrypted) {
+ ReadDecrypted( dest, length );
+ }
+ Pos += length;
+ return GEM_OK;
+}
+
+int MemoryStream::Seek(int newpos, int type)
+{
+ switch (type) {
+ case GEM_CURRENT_POS:
+ if (( Pos + newpos ) > size) {
+ printf("[Streams]: Invalid seek\n");
+ return GEM_ERROR;
+ }
+ Pos += newpos;
+ break;
+
+ case GEM_STREAM_START:
+ if ((unsigned long) newpos > size) {
+ printf("[Streams]: Invalid seek\n");
+ return GEM_ERROR;
+ }
+ Pos = newpos;
+ break;
+
+ default:
+ return GEM_ERROR;
+ }
+ return GEM_OK;
+}
+
+/** No descriptions */
+int MemoryStream::ReadLine(void* buf, unsigned int maxlen)
+{
+ if(!maxlen) {
+ return 0;
+ }
+ unsigned char * p = ( unsigned char * ) buf;
+ if (Pos >= size) {
+ p[0]=0;
+ return -1;
+ }
+ unsigned int i = 0;
+ while (i < ( maxlen - 1 )) {
+ ieByte ch = *( ( ieByte* ) ptr + Pos );
+ if (Pos == size)
+ break;
+ if (Encrypted)
+ p[i] ^= GEM_ENCRYPTION_KEY[Pos & 63];
+ Pos++;
+ if (( ( char ) ch ) == '\n')
+ break;
+ if (( ( char ) ch ) == '\t')
+ ch = ' ';
+ if (( ( char ) ch ) != '\r')
+ p[i++] = ch;
+ }
+ p[i] = 0;
+ return i;
+}
diff --git a/gemrb/core/System/MemoryStream.h b/gemrb/core/System/MemoryStream.h
new file mode 100644
index 0000000..adda37f
--- /dev/null
+++ b/gemrb/core/System/MemoryStream.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 MemoryStream.h
+ * Declares MemoryStream class, stream reading/writing data from/to a buffer in memory.
+ * @author The GemRB Project
+ */
+
+
+#ifndef MEMORYSTREAM_H
+#define MEMORYSTREAM_H
+
+#include "System/DataStream.h"
+
+#include "exports.h"
+#include "globals.h"
+
+/**
+ * @class MemoryStream
+ * Reads and writes data from/to a buffer in memory.
+ */
+
+class GEM_EXPORT MemoryStream : public DataStream {
+private:
+ void* ptr;
+ //unsigned long length;
+ bool autoFree;
+public:
+ MemoryStream(void* buffer, int length, bool autoFree = true);
+ ~MemoryStream(void);
+ int Read(void* dest, unsigned int length);
+ int Write(const void * /*src*/, unsigned int /*length*/)
+ {
+ return GEM_ERROR;
+ }
+ int Seek(int pos, int startpos);
+ int ReadLine(void* buf, unsigned int maxlen);
+};
+
+#endif // ! MEMORYSTREAM_H
diff --git a/gemrb/core/System/VFS.cpp b/gemrb/core/System/VFS.cpp
new file mode 100644
index 0000000..4e6d348
--- /dev/null
+++ b/gemrb/core/System/VFS.cpp
@@ -0,0 +1,538 @@
+/* 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.
+ *
+ *
+ */
+
+// VFS.cpp : functions to access filesystem in os-independent way
+// and POSIX-like compatibility layer for win
+
+#include "System/VFS.h"
+
+#include "globals.h"
+
+#include "Interface.h"
+
+#if defined(__HAIKU__)
+#include <unistd.h>
+#endif
+
+#include <cstdarg>
+#include <cstring>
+
+#ifndef WIN32
+#include <dirent.h>
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
+
+#ifdef WIN32
+
+struct DIR {
+ char path[_MAX_PATH];
+ bool is_first;
+ struct _finddata_t c_file;
+ long hFile;
+};
+
+struct dirent {
+ char d_name[_MAX_PATH];
+};
+
+// buffer which readdir returns
+static dirent de;
+
+DIR* opendir(const char* filename)
+{
+ DIR* dirp = ( DIR* ) malloc( sizeof( DIR ) );
+ dirp->is_first = 1;
+
+ sprintf( dirp->path, "%s%s*.*", filename, SPathDelimiter );
+ //if((hFile = (long)_findfirst(Path, &c_file)) == -1L) //If there is no file matching our search
+
+ return dirp;
+}
+
+dirent* readdir(DIR* dirp)
+{
+ struct _finddata_t c_file;
+
+ if (dirp->is_first) {
+ dirp->is_first = 0;
+ dirp->hFile = ( long ) _findfirst( dirp->path, &c_file );
+ if (dirp->hFile == -1L)
+ return NULL;
+ } else {
+ if (( long ) _findnext( dirp->hFile, &c_file ) != 0) {
+ return NULL;
+ }
+ }
+
+ strcpy( de.d_name, c_file.name );
+
+ return &de;
+}
+
+void closedir(DIR* dirp)
+{
+ _findclose( dirp->hFile );
+ free( dirp );
+}
+
+
+_FILE* _fopen(const char* filename, const char* mode)
+{
+ DWORD OpenFlags = 0;
+ DWORD AccessFlags = 0;
+ DWORD ShareFlags = 0;
+
+ while (*mode) {
+ if (( *mode == 'w' ) || ( *mode == 'W' )) {
+ OpenFlags |= OPEN_ALWAYS;
+ AccessFlags |= GENERIC_WRITE;
+ ShareFlags |= FILE_SHARE_READ;
+ } else if (( *mode == 'r' ) || ( *mode == 'R' )) {
+ OpenFlags |= OPEN_EXISTING;
+ AccessFlags |= GENERIC_READ;
+ ShareFlags |= FILE_SHARE_READ|FILE_SHARE_WRITE;
+ } else if (( *mode == 'a' ) || ( *mode == 'A' )) {
+ OpenFlags |= OPEN_ALWAYS;
+ AccessFlags |= GENERIC_READ|GENERIC_WRITE;
+ ShareFlags |= FILE_SHARE_READ|FILE_SHARE_WRITE;
+ } else if (*mode == '+') {
+ AccessFlags |= GENERIC_READ|GENERIC_WRITE;
+ ShareFlags |= FILE_SHARE_READ|FILE_SHARE_WRITE;
+ }
+ mode++;
+ }
+ HANDLE hFile = CreateFile( filename, AccessFlags, ShareFlags, NULL,
+ OpenFlags, FILE_ATTRIBUTE_NORMAL, NULL );
+ if (hFile == INVALID_HANDLE_VALUE) {
+ return NULL;
+ }
+ _FILE* ret = ( _FILE* ) malloc( sizeof( _FILE ) );
+ ret->hFile = hFile;
+ return ret;
+}
+
+size_t _fread(void* ptr, size_t size, size_t n, _FILE* stream)
+{
+ if (!stream) {
+ return ( size_t ) 0;
+ }
+ unsigned long read;
+ if (!ReadFile( stream->hFile, ptr, ( unsigned long ) ( size * n ), &read,
+ NULL )) {
+ return ( size_t ) 0;
+ }
+ return ( size_t ) read;
+}
+
+size_t _fwrite(const void* ptr, size_t size, size_t n, _FILE* stream)
+{
+ if (!stream) {
+ return ( size_t ) 0;
+ }
+ unsigned long wrote;
+ if (!WriteFile( stream->hFile, ptr, ( unsigned long ) ( size * n ),
+ &wrote, NULL )) {
+ return ( size_t ) 0;
+ }
+ return ( size_t ) wrote;
+}
+
+size_t _fseek(_FILE* stream, long offset, int whence)
+{
+ if (!stream) {
+ return ( size_t ) 1;
+ }
+ unsigned long method;
+ switch (whence) {
+ case SEEK_SET:
+ method = FILE_BEGIN;
+ break;
+ case SEEK_CUR:
+ method = FILE_CURRENT;
+ break;
+ case SEEK_END:
+ method = FILE_END;
+ break;
+ default:
+ return ( size_t ) 1;
+ }
+ if (SetFilePointer( stream->hFile, offset, NULL, method ) == 0xffffffff) {
+ return ( size_t ) 1;
+ }
+ return ( size_t ) 0;
+}
+
+int _fgetc(_FILE* stream)
+{
+ if (!stream) {
+ return 0;
+ }
+ unsigned char tmp;
+ unsigned long read;
+ BOOL bResult = ReadFile( stream->hFile, &tmp, ( unsigned long ) 1, &read,
+ NULL );
+ if (bResult && read) {
+ return ( int ) tmp;
+ }
+ return EOF;
+}
+
+long int _ftell(_FILE* stream)
+{
+ if (!stream) {
+ return EOF;
+ }
+ unsigned long pos = SetFilePointer( stream->hFile, 0, NULL, FILE_CURRENT );
+ if (pos == 0xffffffff) {
+ return -1L;
+ }
+ return ( long int ) pos;
+}
+
+int _feof(_FILE* stream)
+{
+ if (!stream) {
+ return 0;
+ }
+ unsigned char tmp;
+ unsigned long read;
+ BOOL bResult = ReadFile( stream->hFile, &tmp, ( unsigned long ) 1, &read,
+ NULL );
+ if (bResult && ( read == 0 )) {
+ return 1;
+ } //EOF
+ bResult = SetFilePointer( stream->hFile, -1, NULL, FILE_CURRENT );
+ return 0;
+}
+
+int _fclose(_FILE* stream)
+{
+ if (!stream) {
+ return EOF;
+ }
+ if (!CloseHandle( stream->hFile )) {
+ return EOF;
+ }
+ free( stream );
+ return 0;
+}
+
+#endif // WIN32
+
+
+/** Returns true if path is an existing directory */
+bool dir_exists(const char* path)
+{
+ struct stat buf;
+ buf.st_mode = 0;
+
+ if (stat(path, &buf) < 0) {
+ return false;
+ }
+ if (!S_ISDIR(buf.st_mode)) {
+ return false;
+ }
+
+ return true;
+}
+
+/** Returns true if path is an existing directory */
+bool file_exists(const char* path)
+{
+ struct stat buf;
+ buf.st_mode = 0;
+
+ if (stat(path, &buf) < 0) {
+ return false;
+ }
+ if (!S_ISREG(buf.st_mode)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Appends 'name' to path 'target' and returns 'target'.
+ * It takes care of inserting PathDelimiter ('/' or '\\') if needed
+ */
+char* PathAppend (char* target, const char* name)
+{
+ size_t len = strlen(target);
+
+ if (target[0] != 0 && target[len-1] != PathDelimiter && len+1 < _MAX_PATH) {
+ target[len++] = PathDelimiter;
+ target[len] = 0;
+ }
+ strncat( target+len, name, _MAX_PATH - len - 1 );
+
+ return target;
+}
+
+
+bool FindInDir(const char* Dir, char *Filename)
+{
+ // First test if there's a Filename with exactly same name
+ // and if yes, return it and do not search in the Dir
+ char TempFilePath[_MAX_PATH];
+ strcpy(TempFilePath, Dir);
+ PathAppend( TempFilePath, Filename );
+
+ if (!access( TempFilePath, R_OK )) {
+ return true;
+ }
+
+ if (!core->CaseSensitive) {
+ return false;
+ }
+
+ DirectoryIterator dir(Dir);
+ if (!dir) {
+ return false;
+ }
+
+ // Exact match not found, so try to search for Filename
+ // with different case
+ do {
+ const char *name = dir.GetName();
+ if (stricmp( name, Filename ) == 0) {
+ strcpy( Filename, name );
+ return true;
+ }
+ } while (++dir);
+ return false;
+}
+
+bool PathJoin (char *target, const char *base, ...)
+{
+ va_list ap;
+ va_start(ap, base);
+
+ if (base == NULL) {
+ target[0] = '\0';
+ return false;
+ }
+
+ strcpy(target, base);
+
+ while (char *source = va_arg(ap, char*)) {
+ char *slash;
+ do {
+ char filename[_MAX_PATH] = { '\0' };
+ slash = strchr(source, PathDelimiter);
+ if (slash == source) {
+ ++source;
+ continue;
+ } else if (slash) {
+ strncat(filename, source, slash-source);
+ } else {
+ strcpy(filename, source);
+ }
+ if (!FindInDir(target, filename)) {
+ PathAppend(target, source);
+ goto finish;
+ }
+ PathAppend(target, filename);
+ source = slash + 1;
+ } while (slash);
+ }
+ va_end( ap );
+ return true;
+finish:
+ while (char *source = va_arg(ap, char*)) {
+ PathAppend(target, source);
+ }
+ va_end( ap );
+ return false;
+}
+
+bool PathJoinExt (char* target, const char* dir, const char* base, const char* ext)
+{
+ char file[_MAX_PATH];
+ strcpy(file, base);
+ strcat(file, ".");
+ strcat(file, ext);
+ return PathJoin(target, dir, file, NULL);
+}
+
+/** Fixes path delimiter character (slash).
+ * needslash = true : we add a slash
+ * needslash = false: we remove the slash
+ */
+void FixPath (char *path, bool needslash)
+{
+ size_t i = strlen( path ) - 1;
+
+ if (needslash) {
+ if (path[i] == PathDelimiter) return;
+
+ // if path is already too long, don't do anything
+ if (i >= _MAX_PATH - 2) return;
+ i++;
+ path[i++] = PathDelimiter;
+ }
+ else {
+ if (path[i] != PathDelimiter) return;
+ }
+ path[i] = 0;
+}
+
+int strmatch(const char *string, const char *mask)
+{
+ while(*mask) {
+ if (*mask!='?') {
+ if (tolower(*mask)!=tolower(*string)) {
+ return 1;
+ }
+ }
+ mask++;
+ string++;
+ }
+ return 0;
+}
+
+bool FileGlob(char* target, const char* Dir, const char *glob)
+{
+ DirectoryIterator dir(Dir);
+ if (!dir) {
+ return false;
+ }
+
+ do {
+ const char *name = dir.GetName();
+ if (strmatch( name, glob ) == 0) {
+ strcpy( target, name );
+ return true;
+ }
+ } while (++dir);
+ return false;
+}
+
+
+#ifndef WIN32
+
+void ResolveFilePath(char* FilePath)
+{
+ char TempFilePath[_MAX_PATH];
+
+ if (FilePath[0]=='~') {
+ const char *home = getenv("HOME");
+ if (home) {
+ strcpy(TempFilePath, FilePath+1);
+ PathJoin(FilePath, home, TempFilePath, NULL);
+ return;
+ }
+ }
+
+ if (core && !core->CaseSensitive) {
+ return;
+ }
+ strcpy(TempFilePath, FilePath);
+ PathJoin(FilePath, TempFilePath[0]==PathDelimiter?SPathDelimiter:"", TempFilePath, NULL);
+}
+
+void ResolveFilePath(std::string& FilePath)
+{
+ char TempFilePath[_MAX_PATH];
+
+ if (FilePath[0]=='~') {
+ const char *home = getenv("HOME");
+ if (home) {
+ PathJoin(TempFilePath, home, FilePath.c_str()+1, NULL);
+ FilePath = TempFilePath;
+ return;
+ }
+ }
+
+ if (core && !core->CaseSensitive) {
+ return;
+ }
+ PathJoin(TempFilePath, FilePath[0]==PathDelimiter?SPathDelimiter:"", FilePath.c_str(), NULL);
+ FilePath = TempFilePath;
+}
+
+#endif
+
+void ExtractFileFromPath(char *file, const char *full_path)
+{
+ const char *p;
+ if ((p = strrchr (full_path, PathDelimiter)))
+ strcpy(file, p+1);
+ else if ((p = strchr (full_path, ':')))
+ strcpy(file, p+1);
+ else
+ strcpy(file, full_path);
+}
+
+DirectoryIterator::DirectoryIterator(const char *path)
+ : Directory(NULL), Entry(NULL), Path(path)
+{
+ Rewind();
+}
+
+DirectoryIterator::~DirectoryIterator()
+{
+ if (Directory)
+ closedir(static_cast<DIR*>(Directory));
+}
+
+bool DirectoryIterator::IsDirectory()
+{
+ char dtmp[_MAX_PATH];
+ struct stat fst;
+ GetFullPath(dtmp);
+ stat( dtmp, &fst );
+ return S_ISDIR( fst.st_mode );
+}
+
+char* DirectoryIterator::GetName()
+{
+ return static_cast<dirent*>(Entry)->d_name;
+}
+
+void DirectoryIterator::GetFullPath(char *name)
+{
+ snprintf(name, _MAX_PATH, "%s%s%s", Path, SPathDelimiter, static_cast<dirent*>(Entry)->d_name);
+}
+
+DirectoryIterator& DirectoryIterator::operator++()
+{
+ Entry = readdir(static_cast<DIR*>(Directory));
+ return *this;
+}
+
+void DirectoryIterator::Rewind()
+{
+ if (Directory)
+ closedir(static_cast<DIR*>(Directory));
+ Directory = opendir(Path);
+ if (Directory == NULL)
+ Entry = NULL;
+ else
+ Entry = readdir(static_cast<DIR*>(Directory));
+}
diff --git a/gemrb/core/System/VFS.h b/gemrb/core/System/VFS.h
new file mode 100644
index 0000000..d78cb28
--- /dev/null
+++ b/gemrb/core/System/VFS.h
@@ -0,0 +1,164 @@
+/* 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 VFS.h
+ * Compatibility layer for file and dir access functions on Un*x and MS Win
+ * @author The GemRB Project
+ */
+
+#ifndef VFS_H
+#define VFS_H
+
+#ifndef _MAX_PATH
+#ifdef WIN32
+#define _MAX_PATH 260
+#else
+#define _MAX_PATH FILENAME_MAX
+#endif
+#endif
+
+#include "exports.h"
+#include "globals.h"
+
+#include <string>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <direct.h>
+#include <io.h>
+#include <windows.h>
+#endif
+
+#ifndef R_OK
+#define R_OK 04
+#endif
+
+#ifdef WIN32
+
+typedef struct _FILE {
+ HANDLE hFile;
+} _FILE;
+
+GEM_EXPORT _FILE* _fopen(const char* filename, const char* mode);
+GEM_EXPORT size_t _fread(void* ptr, size_t size, size_t n, _FILE* stream);
+GEM_EXPORT size_t _fwrite(const void* ptr, size_t size, size_t n,
+ _FILE* stream);
+GEM_EXPORT size_t _fseek(_FILE* stream, long offset, int whence);
+GEM_EXPORT int _fgetc(_FILE* stream);
+GEM_EXPORT long int _ftell(_FILE* stream);
+GEM_EXPORT int _feof(_FILE* stream);
+GEM_EXPORT int _fclose(_FILE* stream);
+#define mkdir(path, rights) _mkdir(path)
+#define ResolveFilePath(p)
+
+#else // ! WIN32
+
+#define _FILE FILE
+#define _fopen fopen
+#define _fread fread
+#define _fwrite fwrite
+#define _fseek fseek
+#define _fgetc fgetc
+#define _ftell ftell
+#define _feof feof
+#define _fclose fclose
+
+/** Handle ~ -> $HOME mapping and do initial case-sensitity check */
+GEM_EXPORT void ResolveFilePath(char* FilePath);
+GEM_EXPORT void ResolveFilePath(std::string& FilePath);
+
+#endif // ! WIN32
+
+#ifdef WIN32
+const char PathDelimiter = '\\';
+const char PathListSeparator = ';';
+#else
+const char PathDelimiter = '/';
+const char PathListSeparator = ':';
+#endif
+const char SPathDelimiter[] = { PathDelimiter, '\0' };
+const char SPathListSeparator[] = { PathListSeparator, '\0' };
+
+/**
+ * Finds a file matching a glob.
+ *
+ * @param[out] target name of matching file
+ * @param[in] Dir directory to look in
+ * @param[in] glob pattern to match
+ * @return true if match is found
+ */
+GEM_EXPORT bool FileGlob(char *target, const char* Dir, const char* glob);
+GEM_EXPORT bool dir_exists(const char* path);
+GEM_EXPORT bool file_exists(const char* path);
+
+/**
+ * Joins NULL-terminated list of directories and copies it to 'target'.
+ *
+ * @param[out] target Joined path.
+ * @param[in] base Properly cased path to join to.
+ * @param[in] ... NULL terminated list of paths to join.
+ *
+ * This does a case-sensitive look up for all path components after the first and
+ * properly handles the case when paramater contain slashes.
+ *
+ * NOTE: This no longer handles target==base.
+ *
+ * Example:
+ * char filepath[_MAX_PATH];
+ * PathJoin( filepath, core->GUIScriptsPath, core->GameType, 'GUIDefines.py', NULL );
+ */
+GEM_EXPORT bool PathJoin (char* target, const char* base, ...);
+GEM_EXPORT bool PathJoinExt (char* target, const char* dir, const char* file, const char* ext = NULL);
+GEM_EXPORT void FixPath (char *path, bool needslash);
+
+GEM_EXPORT void ExtractFileFromPath(char *file, const char *full_path);
+
+class GEM_EXPORT DirectoryIterator {
+public:
+ /**
+ * @param[in] path Path to directory to search.
+ *
+ * WARNING: the lifetime of path must be longer than the lifetime
+ * of DirectoryIterator.
+ */
+ DirectoryIterator(const char *path);
+ ~DirectoryIterator();
+ bool IsDirectory();
+ /**
+ * Returns name of current entry.
+ *
+ * The returned string is only valid until the iterator is advanced.
+ * FIXME: This should return a const char*
+ */
+ char *GetName();
+ void GetFullPath(char *);
+ DirectoryIterator& operator++();
+#include "operatorbool.h"
+ OPERATOR_BOOL(DirectoryIterator,void,Entry)
+ bool operator !() { return !Entry; }
+ void Rewind();
+private:
+ void* Directory;
+ void* Entry;
+ const char *Path;
+};
+
+#endif // !VFS_H
diff --git a/gemrb/core/System/android_log_printf.cpp b/gemrb/core/System/android_log_printf.cpp
new file mode 100644
index 0000000..73bf15c
--- /dev/null
+++ b/gemrb/core/System/android_log_printf.cpp
@@ -0,0 +1,35 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2011 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 <stdio.h>
+#include <stdarg.h>
+#include <android/log.h>
+
+int android_log_printf(const char * fmt, ...) {
+ int return_value;
+ va_list ap;
+ va_start(ap, fmt);
+ int characters = vfprintf(stdout, fmt, ap); // determine buffer size
+ if(characters<0) return characters;
+ char* buff = new char[characters+1];
+ return_value = vsprintf(buff, fmt, ap);
+ va_end(ap);
+ __android_log_print(ANDROID_LOG_INFO, "printf:", buff);
+ delete buff;
+ return return_value;
+}
diff --git a/gemrb/core/System/android_log_printf.h b/gemrb/core/System/android_log_printf.h
new file mode 100644
index 0000000..1cc17be
--- /dev/null
+++ b/gemrb/core/System/android_log_printf.h
@@ -0,0 +1,24 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2011 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 IE_ANDROID_LOG_PRINTF_H
+#define IE_ANDROID_LOG_PRINTF_H
+
+int android_log_printf(const char * fmt, ...);
+
+#endif
diff --git a/gemrb/core/System/snprintf.cpp b/gemrb/core/System/snprintf.cpp
new file mode 100644
index 0000000..acd785d
--- /dev/null
+++ b/gemrb/core/System/snprintf.cpp
@@ -0,0 +1,980 @@
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell at astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ * Brandon Long <blong at fiction.net> 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long <blong at fiction.net> 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formated the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler <roessler at guug.de> 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins <me at cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * Andrew Tridgell (tridge at samba.org) Oct 1998
+ * fixed handling of %.0f
+ * added test for HAVE_LONG_DOUBLE
+ *
+ * tridge at samba.org, idra at samba.org, April 2001
+ * got rid of fcvt code (twas buggy and made testing harder)
+ * added C99 semantics
+ *
+ * HT authors
+ * * ht_snprintf/ht_vsnprintf return number of characters actually
+ * written instead of the number of characters that would
+ * have been written.
+ * * added '%y' to allow object output using Object's toString() method.
+ * * added '%q[dioux]' for formatting qwords.
+ * * added '%b' for formatting in binary notation.
+ **************************************************************/
+#include "win32def.h"
+#ifndef HAVE_SNPRINTF
+
+#include <cctype>
+#include <cstring>
+#include <cstdlib>
+#include <cstdio>
+#include <cstdarg>
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "system/types.h"
+#include "data.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifdef HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif
+
+#ifdef HAVE_LONG_LONG
+#define LLONG long long
+#else
+#define LLONG long
+#endif
+
+static size_t dopr(char *buffer, size_t maxlen, const char *format,
+ va_list args);
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max);
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags);
+static void fmtqword(char *buffer, size_t *currlen, size_t maxlen,
+ sint64 value, int base, int min, int max, int flags);
+static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags);
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_CONV 6
+#define DP_S_DONE 7
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT 1
+#define DP_C_LONG 2
+#define DP_C_LDOUBLE 3
+#define DP_C_LLONG 4
+#define DP_C_QWORD 5
+
+#define char_to_int(p) ((p)- '0')
+#ifndef MAX
+#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
+#endif
+
+static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args)
+{
+ char ch;
+ LLONG value;
+ LDOUBLE fvalue;
+ char *strvalue;
+ int min;
+ int max;
+ int state;
+ int flags;
+ int cflags;
+ size_t currlen;
+
+ state = DP_S_DEFAULT;
+ currlen = flags = cflags = min = 0;
+ max = -1;
+ ch = *format++;
+
+ while (state != DP_S_DONE) {
+ if (ch == '\0') state = DP_S_DONE;
+
+ switch(state) {
+ case DP_S_DEFAULT:
+ if (ch == '%') {
+ state = DP_S_FLAGS;
+ } else {
+ dopr_outch(buffer, &currlen, maxlen, ch);
+ }
+ ch = *format++;
+ break;
+ case DP_S_FLAGS:
+ switch (ch) {
+ case '-':
+ flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if (isdigit((unsigned char)ch)) {
+ min = 10*min + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '*') {
+ min = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_DOT;
+ } else {
+ state = DP_S_DOT;
+ }
+ break;
+ case DP_S_DOT:
+ if (ch == '.') {
+ state = DP_S_MAX;
+ ch = *format++;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MAX:
+ if (isdigit((unsigned char)ch)) {
+ if (max < 0) max = 0;
+ max = 10*max + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '*') {
+ max = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_MOD;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MOD:
+ switch (ch) {
+ case 'h':
+ cflags = DP_C_SHORT;
+ ch = *format++;
+ break;
+ case 'l':
+ cflags = DP_C_LONG;
+ ch = *format++;
+ if (ch == 'l') { /* It's a long long */
+ cflags = DP_C_LLONG;
+ ch = *format++;
+ }
+ break;
+ case 'L':
+ cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ case 'q':
+ cflags = DP_C_QWORD;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ switch (ch) {
+ case 'b':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT) {
+ value = va_arg (args, unsigned int);
+ } else if (cflags == DP_C_LONG) {
+ value = (long)va_arg (args, unsigned long int);
+ } else if (cflags == DP_C_LLONG) {
+ value = (LLONG)va_arg (args, unsigned LLONG);
+ } else if (cflags == DP_C_QWORD) {
+ sint64 *q = va_arg (args, sint64 *);
+ fmtqword(buffer, &currlen, maxlen, *q, 2, min, max, flags);
+ break;
+ } else {
+ value = (long)va_arg (args, unsigned int);
+ }
+ fmtint (buffer, &currlen, maxlen, value, 2, min, max, flags);
+ break;
+ case 'd':
+ case 'i':
+ if (cflags == DP_C_SHORT) {
+ value = va_arg (args, int);
+ } else if (cflags == DP_C_LONG) {
+ value = va_arg (args, long int);
+ } else if (cflags == DP_C_LLONG) {
+ value = va_arg (args, LLONG);
+ } else if (cflags == DP_C_QWORD) {
+ sint64 *q = va_arg (args, sint64 *);
+ fmtqword(buffer, &currlen, maxlen, *q, 10, min, max, flags);
+ break;
+ } else {
+ value = va_arg (args, int);
+ }
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'o':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT) {
+ value = va_arg (args, unsigned int);
+ } else if (cflags == DP_C_LONG) {
+ value = (long)va_arg (args, unsigned long int);
+ } else if (cflags == DP_C_LLONG) {
+ value = (long)va_arg (args, unsigned LLONG);
+ } else if (cflags == DP_C_QWORD) {
+ sint64 *q = va_arg (args, sint64 *);
+ fmtqword(buffer, &currlen, maxlen, *q, 8, min, max, flags);
+ break;
+ } else {
+ value = (long)va_arg (args, unsigned int);
+ }
+ fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+ break;
+ case 'u':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT) {
+ value = va_arg (args, unsigned int);
+ } else if (cflags == DP_C_LONG) {
+ value = (long)va_arg (args, unsigned long int);
+ } else if (cflags == DP_C_LLONG) {
+ value = (LLONG)va_arg (args, unsigned LLONG);
+ } else if (cflags == DP_C_QWORD) {
+ sint64 *q = va_arg (args, sint64 *);
+ fmtqword(buffer, &currlen, maxlen, *q, 10, min, max, flags);
+ break;
+ } else {
+ value = (long)va_arg (args, unsigned int);
+ }
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'X':
+ flags |= DP_F_UP;
+ case 'x':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT) {
+ value = va_arg (args, unsigned int);
+ } else if (cflags == DP_C_LONG) {
+ value = (long)va_arg (args, unsigned long int);
+ } else if (cflags == DP_C_LLONG) {
+ value = (LLONG)va_arg (args, unsigned LLONG);
+ } else if (cflags == DP_C_QWORD) {
+ sint64 *q = va_arg (args, sint64 *);
+ fmtqword(buffer, &currlen, maxlen, *q, 16, min, max, flags);
+ break;
+ } else {
+ value = (long)va_arg (args, unsigned int);
+ }
+ fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+ break;
+ case 'f':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ /* um, floating point? */
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'E':
+ flags |= DP_F_UP;
+ case 'e':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ break;
+ case 'G':
+ flags |= DP_F_UP;
+ case 'g':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ break;
+ case 'c':
+ dopr_outch(buffer, &currlen, maxlen, va_arg (args, int));
+ break;
+ case 's':
+ strvalue = va_arg (args, char *);
+ if (!strvalue) strvalue = "(null)";
+ if (max == -1) {
+ max = strlen(strvalue);
+ }
+ if (min > 0 && max >= 0 && min > max) max = min;
+ fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+ break;
+ case 'p':
+ strvalue = va_arg (args, char *);
+ fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
+ break;
+ case 'n':
+ if (cflags == DP_C_SHORT) {
+ short int *num;
+ num = va_arg (args, short int *);
+ *num = currlen;
+ } else if (cflags == DP_C_LONG) {
+ long int *num;
+ num = va_arg (args, long int *);
+ *num = (long int)currlen;
+ } else if (cflags == DP_C_LLONG) {
+ LLONG *num;
+ num = va_arg (args, LLONG *);
+ *num = (LLONG)currlen;
+ } else {
+ int *num;
+ num = va_arg (args, int *);
+ *num = currlen;
+ }
+ break;
+ case '%':
+ dopr_outch(buffer, &currlen, maxlen, ch);
+ break;
+ case 'w':
+ /* not supported yet, treat as next char */
+ ch = *format++;
+ break;
+ case 'y': {
+ /* object */
+ Object *obj = va_arg (args, Object *);
+ if (obj) {
+ currlen += obj->toString(buffer+currlen, maxlen - currlen);
+ } else {
+ strvalue = "(null)";
+ if (max == -1) {
+ max = strlen(strvalue);
+ }
+ if (min > 0 && max >= 0 && min > max) max = min;
+ fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+ }
+ break;
+ }
+ default:
+ /* Unknown, skip */
+ break;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ flags = cflags = min = 0;
+ max = -1;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+ if (maxlen != 0) {
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else if (maxlen > 0)
+ buffer[maxlen - 1] = '\0';
+ }
+
+ return currlen;
+}
+
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
+#endif
+
+ for (strln = 0; value[strln]; ++strln); /* strlen */
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while ((padlen > 0) && (cnt < max)) {
+ dopr_outch(buffer, currlen, maxlen, ' ');
+ --padlen;
+ ++cnt;
+ }
+ while (*value && (cnt < max)) {
+ dopr_outch(buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while ((padlen < 0) && (cnt < max)) {
+ dopr_outch(buffer, currlen, maxlen, ' ');
+ ++padlen;
+ ++cnt;
+ }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags)
+{
+#define MAX_CONVERT_PLACES 40
+ int signvalue = 0;
+ unsigned long uvalue;
+ char convert[MAX_CONVERT_PLACES];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if (!(flags & DP_F_UNSIGNED)) {
+ if (value < 0) {
+ signvalue = '-';
+ uvalue = -value;
+ } else {
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[uvalue % (unsigned)base];
+ uvalue = (uvalue / (unsigned)base );
+ } while (uvalue && (place < MAX_CONVERT_PLACES));
+ if (place == MAX_CONVERT_PLACES) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO) {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place);
+#endif
+
+ /* Spaces */
+ while (spadlen > 0) {
+ dopr_outch(buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue) dopr_outch(buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0) {
+ while (zpadlen > 0) {
+ dopr_outch(buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0) dopr_outch(buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch(buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static void fmtqword(char *buffer, size_t *currlen, size_t maxlen,
+ sint64 value, int base, int min, int max, int flags)
+{
+#undef MAX_CONVERT_PLACES
+#define MAX_CONVERT_PLACES 80
+ int signvalue = 0;
+ uint64 uvalue;
+ char convert[MAX_CONVERT_PLACES];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0) max = 0;
+
+ uvalue = value;
+
+ if (!(flags & DP_F_UNSIGNED)) {
+ if (value < 0) {
+ signvalue = '-';
+ uvalue = -uvalue;
+ } else {
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ uint64 uv = uvalue % (uint64)base;
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[uv];
+ uvalue = (uvalue / (uint64)base);
+ } while ((uvalue != 0) && (place < MAX_CONVERT_PLACES));
+ if (place == MAX_CONVERT_PLACES) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO) {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS) spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place);
+#endif
+
+ /* Spaces */
+ while (spadlen > 0) {
+ dopr_outch(buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue) dopr_outch(buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0) {
+ while (zpadlen > 0) {
+ dopr_outch(buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0) dopr_outch(buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch(buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static LDOUBLE abs_val(LDOUBLE value)
+{
+ return (value < 0) ? -value : value;
+}
+
+static LDOUBLE POW10(int exp)
+{
+ LDOUBLE result = 1;
+
+ while (exp) {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static LLONG ROUND(LDOUBLE value)
+{
+ LLONG intpart;
+
+ intpart = (LLONG)value;
+ value = value - intpart;
+ if (value >= 0.5) intpart++;
+
+ return intpart;
+}
+
+/* a replacement for modf that doesn't need the math library. Should
+ be portable, but slow */
+static double my_modf(double x0, double *iptr)
+{
+ int i;
+ long l;
+ double x = x0;
+ double f = 1.0;
+
+ for (i=0;i<100;i++) {
+ l = (long)x;
+ if (l <= (x+1) && l >= (x-1)) break;
+ x *= 0.1;
+ f *= 10.0;
+ }
+
+ if (i == 100) {
+ /* yikes! the number is beyond what we can handle. What do we do? */
+ (*iptr) = 0;
+ return 0;
+ }
+
+ if (i != 0) {
+ double i2;
+ double ret;
+
+ ret = my_modf(x0-l*f, &i2);
+ (*iptr) = l*f + i2;
+ return ret;
+ }
+
+ (*iptr) = l;
+ return x - (*iptr);
+}
+
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ double ufvalue;
+ char iconvert[311];
+ char fconvert[311];
+ int iplace = 0;
+ int fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int caps = 0;
+ int index;
+ double intpart;
+ double fracpart;
+ double temp;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0) {
+ signvalue = '-';
+ } else {
+ if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
+ signvalue = '+';
+ } else {
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+#if 0
+ if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
+#endif
+
+ /*
+ * Sorry, we only support 16 digits past the decimal because of our
+ * conversion method
+ */
+ if (max > 16)
+ max = 16;
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+
+ temp = ufvalue;
+ my_modf(temp, &intpart);
+
+ fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
+
+ if (fracpart >= POW10(max)) {
+ intpart++;
+ fracpart -= POW10(max);
+ }
+
+
+ /* Convert integer part */
+ do {
+ temp = intpart;
+ my_modf(intpart*0.1, &intpart);
+ temp = temp*0.1;
+ index = (int) ((temp -intpart +0.05)* 10.0);
+ /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
+ /* printf ("%llf, %f, %x\n", temp, intpart, index); */
+ iconvert[iplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
+ } while (intpart && (iplace < 311));
+ if (iplace == 311) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ if (fracpart)
+ {
+ do {
+ temp = fracpart;
+ my_modf(fracpart*0.1, &fracpart);
+ temp = temp*0.1;
+ index = (int) ((temp -fracpart +0.05)* 10.0);
+ /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
+ /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
+ fconvert[fplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
+ } while(fracpart && (fplace < 311));
+ if (fplace == 311) fplace--;
+ }
+ fconvert[fplace] = 0;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
+ zpadlen = max - fplace;
+ if (zpadlen < 0) zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0)) {
+ if (signvalue) {
+ dopr_outch(buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0) {
+ dopr_outch(buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0) {
+ dopr_outch(buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ dopr_outch(buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
+#endif
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ if (max > 0) {
+ dopr_outch(buffer, currlen, maxlen, '.');
+
+ while (fplace > 0)
+ dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
+ }
+
+ while (zpadlen > 0) {
+ dopr_outch(buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (padlen < 0) {
+ dopr_outch(buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen < maxlen) {
+ buffer[(*currlen)] = c;
+ }
+ (*currlen)++;
+}
+
+int ht_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ if ((int)count < 0) count = 0;
+ int res = dopr(str, count, fmt, args);
+ if (count) count--;
+ return str ? MIN(res, (int)count) : count;
+}
+
+int ht_snprintf(char *str, size_t count, const char *fmt,...)
+{
+ size_t ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = ht_vsnprintf(str, count, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+int ht_vasprintf(char **ptr, const char *format, va_list ap)
+{
+ int ret;
+
+ ret = dopr(NULL, 0, format, ap);
+ if (ret <= 0) {
+ *ptr = NULL;
+ return 0;
+ }
+
+ (*ptr) = (char *)malloc(ret+1);
+ if (!*ptr) return 0;
+ ret = ht_vsnprintf(*ptr, ret+1, format, ap);
+
+ return ret;
+}
+
+
+int ht_asprintf(char **ptr, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = ht_vasprintf(ptr, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+int ht_vfprintf(FILE *file, const char *fmt, va_list args)
+{
+#if 0
+ char *buf;
+ int ret = ht_vasprintf(&buf, fmt, args);
+ fputs(buf, file);
+ free(buf);
+#else
+ char buf[1024];
+ int ret = ht_vsnprintf(buf, sizeof buf, fmt, args);
+ fputs(buf, file);
+#endif
+ return ret;
+}
+
+int ht_fprintf(FILE *file, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = ht_vfprintf(file, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+
+int ht_vprintf(const char *fmt, va_list args)
+{
+ return ht_vfprintf(stdout, fmt, args);
+}
+
+int ht_printf(const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = ht_vprintf(fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+
+#endif //HAVE_SNPRINTF
diff --git a/gemrb/core/System/snprintf.h b/gemrb/core/System/snprintf.h
new file mode 100644
index 0000000..262a4e2
--- /dev/null
+++ b/gemrb/core/System/snprintf.h
@@ -0,0 +1,41 @@
+/*
+ * HT Editor
+ * snprintf.h
+ *
+ * Copyright (C) 1999-2003 Sebastian Biallas (sb at biallas.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SNPRINTF_H__
+#define __SNPRINTF_H__
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int ht_asprintf(char **ptr, const char *format, ...);
+int ht_vasprintf(char **ptr, const char *format, va_list ap);
+
+int ht_snprintf(char *str, size_t count, const char *fmt, ...);
+int ht_vsnprintf(char *str, size_t count, const char *fmt, va_list args);
+
+int ht_fprintf(FILE *file, const char *fmt, ...);
+int ht_vfprintf(FILE *file, const char *fmt, va_list args);
+
+int ht_printf(const char *fmt, ...);
+int ht_vprintf(const char *fmt, va_list args);
+
+#define snprintf ht_snprintf
+#endif
diff --git a/gemrb/core/System/swab.c b/gemrb/core/System/swab.c
new file mode 100644
index 0000000..6cd4f9f
--- /dev/null
+++ b/gemrb/core/System/swab.c
@@ -0,0 +1,33 @@
+/* Copyright (C) 1992, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include "swab.h"
+
+void swab (const void *bfrom, void *bto, ssize_t n)
+{
+ const char *from = (const char *) bfrom;
+ char *to = (char *) bto;
+
+ n &= ~((ssize_t) 1);
+ while (n > 1)
+ {
+ const char b0 = from[--n], b1 = from[--n];
+ to[n] = b0;
+ to[n + 1] = b1;
+ }
+}
diff --git a/gemrb/core/System/swab.h b/gemrb/core/System/swab.h
new file mode 100644
index 0000000..8788ad7
--- /dev/null
+++ b/gemrb/core/System/swab.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 1992, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+#ifndef IE_SWAB_H
+#define IE_SWAB_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef _SSIZE_T_DEFINED_
+# define _SSIZE_T_DEFINED_
+ typedef long int ssize_t;
+#endif
+
+void swab(const void *bfrom, void *bto, ssize_t n);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+#endif
diff --git a/gemrb/core/TableMgr.cpp b/gemrb/core/TableMgr.cpp
new file mode 100644
index 0000000..fb47c68
--- /dev/null
+++ b/gemrb/core/TableMgr.cpp
@@ -0,0 +1,84 @@
+/* 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 "TableMgr.h"
+
+#include "GameData.h"
+#include "Interface.h"
+
+TableMgr::TableMgr()
+{
+}
+TableMgr::~TableMgr()
+{
+}
+
+
+AutoTable::AutoTable()
+{
+}
+
+AutoTable::AutoTable(const char* ResRef)
+{
+ load(ResRef);
+}
+
+AutoTable::AutoTable(const AutoTable& other)
+{
+ *this = other;
+}
+
+AutoTable& AutoTable::operator=(const AutoTable& other)
+{
+ if (other.table) {
+ tableref = other.tableref;
+ table = gamedata->GetTable(tableref);
+ } else {
+ table.release();
+ }
+ return *this;
+}
+
+bool AutoTable::load(const char* ResRef)
+{
+ release();
+
+ int ref = gamedata->LoadTable(ResRef);
+ if (ref == -1)
+ return false;
+
+ tableref = (unsigned int)ref;
+ table = gamedata->GetTable(tableref);
+ return true;
+}
+
+AutoTable::~AutoTable()
+{
+ release();
+}
+
+void AutoTable::release()
+{
+ if (table) {
+ gamedata->DelTable(tableref);
+ table.release();
+ }
+}
+
diff --git a/gemrb/core/TableMgr.h b/gemrb/core/TableMgr.h
new file mode 100644
index 0000000..5e38557
--- /dev/null
+++ b/gemrb/core/TableMgr.h
@@ -0,0 +1,99 @@
+/* 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 TableMgr.h
+ * Declares TableMgr class, abstract loader for Table objects (.2DA files)
+ * @author The GemRB Project
+ */
+
+
+#ifndef TABLEMGR_H
+#define TABLEMGR_H
+
+#include "globals.h"
+
+#include "Plugin.h"
+
+/**
+ * @class TableMgr
+ * Abstract loader for Table objects (.2DA files)
+ */
+
+class GEM_EXPORT TableMgr : public Plugin {
+public:
+ TableMgr();
+ virtual ~TableMgr();
+ /** Returns the actual number of Rows in the Table */
+ virtual ieDword GetRowCount() const = 0;
+ /** Returns the number of Columns in the Table */
+ virtual ieDword GetColNamesCount() const = 0;
+ /** Returns the actual number of Columns in a row */
+ virtual ieDword GetColumnCount(unsigned int row = 0) const = 0;
+ /** Returns a pointer to a zero terminated 2da element,
+ * 0,0 returns the default value, it may return NULL */
+ virtual const char* QueryField(unsigned int row = 0, unsigned int column = 0) const = 0;
+ /** Returns a pointer to a zero terminated 2da element,
+ * uses column name and row name to search the field,
+ * may return NULL */
+ virtual const char* QueryField(const char* row, const char* column) const = 0;
+ /** Returns default value of table. */
+ virtual const char* QueryDefault() const = 0;
+ virtual int GetColumnIndex(const char* colname) const = 0;
+ virtual int GetRowIndex(const char* rowname) const = 0;
+ virtual const char* GetColumnName(unsigned int index) const = 0;
+ /** Returns a Row Name, returns NULL on error */
+ virtual const char* GetRowName(unsigned int index) const = 0;
+ virtual int FindTableValue(unsigned int column, long value, int start = 0) const = 0;
+ virtual int FindTableValue(unsigned int column, const char* value, int start = 0) const = 0;
+
+ /** Opens a Table File */
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+};
+
+/**
+ * Utility class to automatically handle loading a table,
+ * and obtain and free a reference to it.
+ */
+class GEM_EXPORT AutoTable
+{
+public:
+ AutoTable();
+ AutoTable(const char* ResRef);
+ ~AutoTable();
+ AutoTable(const AutoTable &);
+ AutoTable& operator=(const AutoTable&);
+
+ bool load(const char* ResRef);
+ void release();
+ bool ok() const { return table; }
+ operator bool() const { return table; }
+
+ const TableMgr& operator*() const { return *table; }
+ const TableMgr* operator->() const { return &*table; }
+ const TableMgr* ptr() const { return &*table; }
+
+private:
+ Holder<TableMgr> table;
+ unsigned int tableref;
+};
+
+
+#endif // ! TABLEMGR_H
diff --git a/gemrb/core/Tile.cpp b/gemrb/core/Tile.cpp
new file mode 100644
index 0000000..405a297
--- /dev/null
+++ b/gemrb/core/Tile.cpp
@@ -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.
+ *
+ *
+ */
+
+#include "Tile.h"
+
+Tile::Tile(Animation* anim, Animation* sec)
+{
+ tileIndex = 0;
+ this->anim[0] = anim;
+ if (sec) {
+ this->anim[1] = sec;
+ } else {
+ this->anim[1] = NULL;
+ }
+}
+
+Tile::~Tile(void)
+{
+ if (anim[0]) {
+ delete( anim[0] );
+ }
+ if (anim[1]) {
+ delete( anim[1] );
+ }
+}
diff --git a/gemrb/core/Tile.h b/gemrb/core/Tile.h
new file mode 100644
index 0000000..88c1ffb
--- /dev/null
+++ b/gemrb/core/Tile.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 TILE_H
+#define TILE_H
+
+#include "RGBAColor.h"
+#include "exports.h"
+
+#include "Animation.h"
+
+class GEM_EXPORT Tile {
+public:
+ Tile(Animation* anim, Animation* sec = NULL);
+ ~Tile(void);
+ unsigned char tileIndex;
+ unsigned char om;
+ Color SearchMap[16];
+ Color HeightMap[16];
+ Color LightMap[16];
+ Color NLightMap[16];
+ Animation* anim[2];
+};
+
+#endif
diff --git a/gemrb/core/TileMap.cpp b/gemrb/core/TileMap.cpp
new file mode 100644
index 0000000..ae2fc7a
--- /dev/null
+++ b/gemrb/core/TileMap.cpp
@@ -0,0 +1,649 @@
+/* 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 "TileMap.h"
+
+#include "Interface.h"
+#include "Video.h"
+
+#include "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.h"
+
+TileMap::TileMap(void)
+{
+ XCellCount = 0;
+ YCellCount = 0;
+ LargeMap = !core->HasFeature(GF_SMALL_FOG);
+}
+
+TileMap::~TileMap(void)
+{
+ size_t i;
+
+ for (i = 0; i < overlays.size(); i++) {
+ delete( overlays[i] );
+ }
+ for (i = 0; i < overlays.size(); i++) {
+ delete( rain_overlays[i]);
+ }
+ for (i = 0; i < infoPoints.size(); i++) {
+ delete( infoPoints[i] );
+ }
+ for (i = 0; i < containers.size(); i++) {
+ delete( containers[i] );
+ }
+ for (i = 0; i < doors.size(); i++) {
+ delete( doors[i] );
+ }
+}
+
+//this needs in case of a tileset switch (for extended night)
+void TileMap::ClearOverlays()
+{
+ size_t i;
+
+ for (i = 0; i < overlays.size(); i++) {
+ delete( overlays[i] );
+ }
+ overlays.clear();
+ for (i = 0; i < overlays.size(); i++) {
+ delete( rain_overlays[i]);
+ }
+ rain_overlays.clear();
+}
+
+//tiled objects
+TileObject* TileMap::AddTile(const char *ID, const char* Name, unsigned int Flags,
+ unsigned short* openindices, int opencount, unsigned short* closeindices, int closecount)
+{
+ TileObject* tile = new TileObject();
+ tile->Flags=Flags;
+ strnspccpy(tile->Name, Name, 32);
+ strnlwrcpy(tile->Tileset, ID, 8);
+ tile->SetOpenTiles( openindices, opencount );
+ tile->SetClosedTiles( closeindices, closecount );
+ tiles.push_back(tile);
+ return tile;
+}
+
+TileObject* TileMap::GetTile(unsigned int idx)
+{
+ if (idx >= tiles.size()) {
+ return NULL;
+ }
+ return tiles[idx];
+}
+
+//doors
+Door* TileMap::AddDoor(const char *ID, const char* Name, unsigned int Flags,
+ int ClosedIndex, unsigned short* indices, int count,
+ Gem_Polygon* open, Gem_Polygon* closed)
+{
+ Door* door = new Door( overlays[0] );
+ door->Flags = Flags;
+ door->closedIndex = ClosedIndex;
+ door->SetTiles( indices, count );
+ door->SetPolygon( false, closed );
+ door->SetPolygon( true, open );
+ door->SetName( ID );
+ door->SetScriptName( Name );
+ doors.push_back( door );
+ return door;
+}
+
+Door* TileMap::GetDoor(unsigned int idx) const
+{
+ if (idx >= doors.size()) {
+ return NULL;
+ }
+ return doors[idx];
+}
+
+Door* TileMap::GetDoor(const Point &p) const
+{
+ for (size_t i = 0; i < doors.size(); i++) {
+ Gem_Polygon *doorpoly;
+
+ Door* door = doors[i];
+ if (door->Flags&DOOR_OPEN)
+ doorpoly = door->open;
+ else
+ doorpoly = door->closed;
+
+ if (doorpoly->BBox.x > p.x)
+ continue;
+ if (doorpoly->BBox.y > p.y)
+ continue;
+ if (doorpoly->BBox.x + doorpoly->BBox.w < p.x)
+ continue;
+ if (doorpoly->BBox.y + doorpoly->BBox.h < p.y)
+ continue;
+ if (doorpoly->PointIn( p ))
+ return door;
+ }
+ return NULL;
+}
+
+Door* TileMap::GetDoorByPosition(const Point &p) const
+{
+ for (size_t i = 0; i < doors.size(); i++) {
+ Door* door = doors[i];
+
+ if (door->toOpen[0].x==p.x && door->toOpen[0].y==p.y) {
+ return door;
+ }
+ if (door->toOpen[1].x==p.x && door->toOpen[1].y==p.y) {
+ return door;
+ }
+ }
+ return NULL;
+}
+
+Door* TileMap::GetDoor(const char* Name) const
+{
+ if (!Name) {
+ return NULL;
+ }
+ for (size_t i = 0; i < doors.size(); i++) {
+ Door* door = doors[i];
+ if (stricmp( door->GetScriptName(), Name ) == 0)
+ return door;
+ }
+ return NULL;
+}
+
+void TileMap::UpdateDoors()
+{
+ for (size_t i = 0; i < doors.size(); i++) {
+ Door* door = doors[i];
+ door->SetNewOverlay(overlays[0]);
+ }
+}
+
+//overlays, allow pushing of NULL
+void TileMap::AddOverlay(TileOverlay* overlay)
+{
+ if (overlay) {
+ if (overlay->w > XCellCount) {
+ XCellCount = overlay->w;
+ }
+ if (overlay->h > YCellCount) {
+ YCellCount = overlay->h;
+ }
+ }
+ overlays.push_back( overlay );
+}
+
+void TileMap::AddRainOverlay(TileOverlay* overlay)
+{
+ if (overlay) {
+ if (overlay->w > XCellCount) {
+ XCellCount = overlay->w;
+ }
+ if (overlay->h > YCellCount) {
+ YCellCount = overlay->h;
+ }
+ }
+ rain_overlays.push_back( overlay );
+}
+
+void TileMap::DrawOverlays(Region screen, int rain)
+{
+ if (rain) {
+ overlays[0]->Draw( screen, rain_overlays );
+ } else {
+ overlays[0]->Draw( screen, overlays );
+ }
+}
+
+// Size of Fog-Of-War shadow tile (and bitmap)
+#define CELL_SIZE 32
+
+// Ratio of bg tile size and fog tile size
+#define CELL_RATIO 2
+
+// Returns 1 if map at (x;y) was explored, else 0. Points outside map are
+// always considered as explored
+#define IS_EXPLORED( x, y ) (((x) < 0 || (x) >= w || (y) < 0 || (y) >= h) ? 1 : (explored_mask[(w * (y) + (x)) / 8] & (1 << ((w * (y) + (x)) % 8))))
+
+#define IS_VISIBLE( x, y ) (((x) < 0 || (x) >= w || (y) < 0 || (y) >= h) ? 1 : (visible_mask[(w * (y) + (x)) / 8] & (1 << ((w * (y) + (x)) % 8))))
+
+#define FOG(i) vid->BlitSprite( core->FogSprites[i], r.x, r.y, true, &r )
+
+
+void TileMap::DrawFogOfWar(ieByte* explored_mask, ieByte* visible_mask, Region viewport)
+{
+ // viewport - pos & size of the control
+ int w = XCellCount * CELL_RATIO;
+ int h = YCellCount * CELL_RATIO;
+ if (LargeMap) {
+ w++;
+ h++;
+ }
+ Color black = { 0, 0, 0, 255 };
+
+ Video* vid = core->GetVideoDriver();
+ Region vp = vid->GetViewport();
+
+ vp.w = viewport.w;
+ vp.h = viewport.h;
+ if (( vp.x + vp.w ) > w * CELL_SIZE) {
+ vp.x = ( w * CELL_SIZE - vp.w );
+ }
+ if (vp.x < 0) {
+ vp.x = 0;
+ }
+ if (( vp.y + vp.h ) > h * CELL_SIZE) {
+ vp.y = ( h * CELL_SIZE - vp.h );
+ }
+ if (vp.y < 0) {
+ vp.y = 0;
+ }
+ int sx = ( vp.x ) / CELL_SIZE;
+ int sy = ( vp.y ) / CELL_SIZE;
+ int dx = sx + vp.w / CELL_SIZE + 2;
+ int dy = sy + vp.h / CELL_SIZE + 2;
+ int x0 = sx * CELL_SIZE - vp.x;
+ int y0 = sy * CELL_SIZE - vp.y;
+ if (LargeMap) {
+ x0 -= CELL_SIZE / 2;
+ y0 -= CELL_SIZE / 2;
+ dx++;
+ dy++;
+ }
+ for (int y = sy; y < dy && y < h; y++) {
+ for (int x = sx; x < dx && x < w; x++) {
+ Region r = Region(x0 + viewport.x + ( (x - sx) * CELL_SIZE ), y0 + viewport.y + ( (y - sy) * CELL_SIZE ), CELL_SIZE, CELL_SIZE);
+ if (! IS_EXPLORED( x, y )) {
+ // Unexplored tiles are all black
+ vid->DrawRect(r, black, true, true);
+ continue; // Don't draw 'invisible' fog
+ }
+ else {
+ // If an explored tile is adjacent to an
+ // unexplored one, we draw border sprite
+ // (gradient black <-> transparent)
+ // Tiles in four cardinal directions have these
+ // values.
+ //
+ // 1
+ // 2 8
+ // 4
+ //
+ // Values of those unexplored are
+ // added together, the resulting number being
+ // an index of shadow sprite to use. For now,
+ // some tiles are made 'on the fly' by
+ // drawing two or more tiles
+
+ int e = ! IS_EXPLORED( x, y - 1);
+ if (! IS_EXPLORED( x - 1, y )) e |= 2;
+ if (! IS_EXPLORED( x, y + 1 )) e |= 4;
+ if (! IS_EXPLORED( x + 1, y )) e |= 8;
+
+ switch (e) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 6:
+ case 8:
+ case 9:
+ case 12:
+ FOG( e );
+ break;
+ case 5:
+ FOG( 1 );
+ FOG( 4 );
+ break;
+ case 7:
+ FOG( 3 );
+ FOG( 6 );
+ break;
+ case 10:
+ FOG( 2 );
+ FOG( 8 );
+ break;
+ case 11:
+ FOG( 3 );
+ FOG( 9 );
+ break;
+ case 13:
+ FOG( 9 );
+ FOG( 12 );
+ break;
+ case 14:
+ FOG( 6 );
+ FOG( 12 );
+ break;
+ case 15: //this is black too
+ vid->DrawRect(r, black, true, true);
+ break;
+ }
+ }
+
+ if (! IS_VISIBLE( x, y )) {
+ // Invisible tiles are all gray
+ FOG( 16 );
+ continue; // Don't draw 'invisible' fog
+ }
+ else {
+ // If a visible tile is adjacent to an
+ // invisible one, we draw border sprite
+ // (gradient gray <-> transparent)
+ // Tiles in four cardinal directions have these
+ // values.
+ //
+ // 1
+ // 2 8
+ // 4
+ //
+ // Values of those invisible are
+ // added together, the resulting number being
+ // an index of shadow sprite to use. For now,
+ // some tiles are made 'on the fly' by
+ // drawing two or more tiles
+
+ int e = ! IS_VISIBLE( x, y - 1);
+ if (! IS_VISIBLE( x - 1, y )) e |= 2;
+ if (! IS_VISIBLE( x, y + 1 )) e |= 4;
+ if (! IS_VISIBLE( x + 1, y )) e |= 8;
+
+ switch (e) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 6:
+ case 8:
+ case 9:
+ case 12:
+ FOG( 16 + e );
+ break;
+ case 5:
+ FOG( 16 + 1 );
+ FOG( 16 + 4 );
+ break;
+ case 7:
+ FOG( 16 + 3 );
+ FOG( 16 + 6 );
+ break;
+ case 10:
+ FOG( 16 + 2 );
+ FOG( 16 + 8 );
+ break;
+ case 11:
+ FOG( 16 + 3 );
+ FOG( 16 + 9 );
+ break;
+ case 13:
+ FOG( 16 + 9 );
+ FOG( 16 + 12 );
+ break;
+ case 14:
+ FOG( 16 + 6 );
+ FOG( 16 + 12 );
+ break;
+ case 15: //this is unseen too
+ FOG( 16 );
+ break;
+ }
+ }
+ }
+ }
+}
+
+//containers
+void TileMap::AddContainer(Container *c)
+{
+ containers.push_back(c);
+}
+
+Container* TileMap::GetContainer(unsigned int idx) const
+{
+ if (idx >= containers.size()) {
+ return NULL;
+ }
+ return containers[idx];
+}
+
+Container* TileMap::GetContainer(const char* Name) const
+{
+ for (size_t i = 0; i < containers.size(); i++) {
+ Container* cn = containers[i];
+ if (stricmp( cn->GetScriptName(), Name ) == 0)
+ return cn;
+ }
+ return NULL;
+}
+
+//look for a container at position
+//use type = IE_CONTAINER_PILE if you want to find ground piles only
+//in this case, empty piles won't be found!
+Container* TileMap::GetContainer(const Point &position, int type) const
+{
+ for (size_t i = 0; i < containers.size(); i++) {
+ Container* c = containers[i];
+ if (type!=-1) {
+ if (c->Type!=type) {
+ continue;
+ }
+ }
+ if (c->outline->BBox.x > position.x)
+ continue;
+ if (c->outline->BBox.y > position.y)
+ continue;
+ if (c->outline->BBox.x + c->outline->BBox.w < position.x)
+ continue;
+ if (c->outline->BBox.y + c->outline->BBox.h < position.y)
+ continue;
+
+ //IE piles don't have polygons, the bounding box is enough for them
+ if (c->Type == IE_CONTAINER_PILE) {
+ //don't find empty piles if we look for any container
+ //if we looked only for piles, then we still return them
+ if ((type==-1) && !c->inventory.GetSlotCount()) {
+ continue;
+ }
+ return c;
+ }
+ if (c->outline->PointIn( position ))
+ return c;
+ }
+ return NULL;
+}
+
+Container* TileMap::GetContainerByPosition(const Point &position, int type) const
+{
+ for (size_t i = 0; i < containers.size(); i++) {
+ Container* c = containers[i];
+ if (type!=-1) {
+ if (c->Type!=type) {
+ continue;
+ }
+ }
+
+ if (c->Pos.x!=position.x || c->Pos.y!=position.y) {
+ continue;
+ }
+
+ //IE piles don't have polygons, the bounding box is enough for them
+ if (c->Type == IE_CONTAINER_PILE) {
+ //don't find empty piles if we look for any container
+ //if we looked only for piles, then we still return them
+ if ((type==-1) && !c->inventory.GetSlotCount()) {
+ continue;
+ }
+ return c;
+ }
+ return c;
+ }
+ return NULL;
+}
+
+int TileMap::CleanupContainer(Container *container)
+{
+ if (container->Type!=IE_CONTAINER_PILE)
+ return 0;
+ if (container->inventory.GetSlotCount())
+ return 0;
+
+ for (size_t i = 0; i < containers.size(); i++) {
+ if (containers[i]==container) {
+ containers.erase(containers.begin()+i);
+ delete container;
+ return 1;
+ }
+ }
+ printMessage("TileMap", " ", LIGHT_RED);
+ printf("Invalid container cleanup: %s\n", container->GetScriptName());
+ return 1;
+}
+
+//infopoints
+InfoPoint* TileMap::AddInfoPoint(const char* Name, unsigned short Type,
+ Gem_Polygon* outline)
+{
+ InfoPoint* ip = new InfoPoint();
+ ip->SetScriptName( Name );
+ switch (Type) {
+ case 0:
+ ip->Type = ST_PROXIMITY;
+ break;
+
+ case 1:
+ ip->Type = ST_TRIGGER;
+ break;
+
+ case 2:
+ ip->Type = ST_TRAVEL;
+ break;
+ //this is just to satisfy whiny compilers
+ default:
+ ip->Type = ST_PROXIMITY;
+ break;
+ }
+ ip->outline = outline;
+ //ip->Active = true; //set active on creation
+ infoPoints.push_back( ip );
+ return ip;
+}
+
+//if detectable is set, then only detectable infopoints will be returned
+InfoPoint* TileMap::GetInfoPoint(const Point &p, bool detectable) const
+{
+ for (size_t i = 0; i < infoPoints.size(); i++) {
+ InfoPoint* ip = infoPoints[i];
+ //these flags disable any kind of user interaction
+ //scripts can still access an infopoint by name
+ if (ip->Flags&(INFO_DOOR|TRAP_DEACTIVATED) )
+ continue;
+
+ if (detectable) {
+ if ((ip->Type==ST_PROXIMITY) && !ip->VisibleTrap(0) ) {
+ continue;
+ }
+ if (ip->IsPortal()) {
+ // skip portals without PORTAL_CURSOR set
+ if (!(ip->Trapped & PORTAL_CURSOR)) {
+ continue;
+ }
+ }
+ }
+
+ if (!(ip->GetInternalFlag()&IF_ACTIVE))
+ continue;
+ if (ip->outline->BBox.x > p.x)
+ continue;
+ if (ip->outline->BBox.y > p.y)
+ continue;
+ if (ip->outline->BBox.x + ip->outline->BBox.w < p.x)
+ continue;
+ if (ip->outline->BBox.y + ip->outline->BBox.h < p.y)
+ continue;
+ if (ip->outline->PointIn( p ))
+ return ip;
+ }
+ return NULL;
+}
+
+InfoPoint* TileMap::GetInfoPoint(const char* Name) const
+{
+ for (size_t i = 0; i < infoPoints.size(); i++) {
+ InfoPoint* ip = infoPoints[i];
+ if (stricmp( ip->GetScriptName(), Name ) == 0)
+ return ip;
+ }
+ return NULL;
+}
+
+InfoPoint* TileMap::GetInfoPoint(unsigned int idx) const
+{
+ if (idx >= infoPoints.size()) {
+ return NULL;
+ }
+ return infoPoints[idx];
+}
+
+InfoPoint* TileMap::GetTravelTo(const char* Destination) const
+{
+ size_t i=infoPoints.size();
+ while (i--) {
+ InfoPoint *ip = infoPoints[i];
+
+ if (ip->Type!=ST_TRAVEL)
+ continue;
+
+ if (strnicmp( ip->Destination, Destination, 8 ) == 0) {
+ return ip;
+ }
+ }
+ return NULL;
+}
+
+InfoPoint *TileMap::AdjustNearestTravel(Point &p)
+{
+ int min = -1;
+ InfoPoint *best = NULL;
+
+ size_t i=infoPoints.size();
+ while (i--) {
+ InfoPoint *ip = infoPoints[i];
+
+ if (ip->Type!=ST_TRAVEL)
+ continue;
+
+ unsigned int dist = Distance(p, ip);
+ if (dist<(unsigned int) min) {
+ min = dist;
+ best = ip;
+ }
+ }
+ if (best) {
+ p = best->Pos;
+ }
+ return best;
+}
+
+Point TileMap::GetMapSize()
+{
+ return Point((short) (XCellCount*64), (short) (YCellCount*64));
+}
diff --git a/gemrb/core/TileMap.h b/gemrb/core/TileMap.h
new file mode 100644
index 0000000..ad97d10
--- /dev/null
+++ b/gemrb/core/TileMap.h
@@ -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.
+ *
+ *
+ */
+
+#ifndef TILEMAP_H
+#define TILEMAP_H
+
+#include "exports.h"
+
+#include "Polygon.h"
+#include "TileOverlay.h"
+
+//special container types
+#define IE_CONTAINER_PILE 4
+
+class Container;
+class Door;
+class InfoPoint;
+class TileObject;
+
+class GEM_EXPORT TileMap {
+private:
+ std::vector< TileOverlay*> overlays;
+ std::vector< TileOverlay*> rain_overlays;
+ std::vector< Door*> doors;
+ std::vector< Container*> containers;
+ std::vector< InfoPoint*> infoPoints;
+ std::vector< TileObject*> tiles;
+ bool LargeMap;
+public:
+ TileMap(void);
+ ~TileMap(void);
+
+ Door* AddDoor(const char* ID, const char* Name, unsigned int Flags,
+ int ClosedIndex, unsigned short* indices, int count,
+ Gem_Polygon* open, Gem_Polygon* closed);
+ //gets door by active region (click target)
+ Door* GetDoor(const Point &position) const;
+ //gets door by activation position (spell target)
+ Door* GetDoorByPosition(const Point &position) const;
+ Door* GetDoor(unsigned int idx) const;
+ Door* GetDoor(const char* Name) const;
+ size_t GetDoorCount() { return doors.size(); }
+ //update doors for a new overlay
+ void UpdateDoors();
+
+ /* type is an optional filter for container type*/
+ void AddContainer(Container *c);
+ //gets container by active region (click target)
+ Container* GetContainer(const Point &position, int type=-1) const;
+ //gets container by activation position (spell target)
+ Container* GetContainerByPosition(const Point &position, int type=-1) const;
+ Container* GetContainer(const char* Name) const;
+ Container* GetContainer(unsigned int idx) const;
+ /* cleans up empty heaps, returns 1 if container removed*/
+ int CleanupContainer(Container *container);
+ size_t GetContainerCount() const { return containers.size(); }
+
+ InfoPoint* AddInfoPoint(const char* Name, unsigned short Type,
+ Gem_Polygon* outline);
+ InfoPoint* GetInfoPoint(const Point &position, bool detectable) const;
+ InfoPoint* GetInfoPoint(const char* Name) const;
+ InfoPoint* GetInfoPoint(unsigned int idx) const;
+ InfoPoint* GetTravelTo(const char* Destination) const;
+ InfoPoint* AdjustNearestTravel(Point &p);
+ size_t GetInfoPointCount() const { return infoPoints.size(); }
+
+ TileObject* AddTile(const char* ID, const char* Name, unsigned int Flags,
+ unsigned short* openindices, int opencount,unsigned short* closeindices, int closecount);
+ TileObject* GetTile(unsigned int idx);
+ TileObject* GetTile(const char* Name);
+ size_t GetTileCount() { return tiles.size(); }
+
+ void ClearOverlays();
+ void AddOverlay(TileOverlay* overlay);
+ void AddRainOverlay(TileOverlay* overlay);
+ void DrawOverlays(Region screen, int rain);
+ void DrawFogOfWar(ieByte* explored_mask, ieByte* visible_mask, Region viewport);
+ Point GetMapSize();
+public:
+ int XCellCount, YCellCount;
+};
+
+#endif
diff --git a/gemrb/core/TileMapMgr.cpp b/gemrb/core/TileMapMgr.cpp
new file mode 100644
index 0000000..634aa1f
--- /dev/null
+++ b/gemrb/core/TileMapMgr.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 "TileMapMgr.h"
+
+TileMapMgr::TileMapMgr(void)
+{
+}
+
+TileMapMgr::~TileMapMgr(void)
+{
+}
diff --git a/gemrb/core/TileMapMgr.h b/gemrb/core/TileMapMgr.h
new file mode 100644
index 0000000..4333775
--- /dev/null
+++ b/gemrb/core/TileMapMgr.h
@@ -0,0 +1,46 @@
+/* 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 TILEMAPMGR_H
+#define TILEMAPMGR_H
+
+#include "Plugin.h"
+#include "TileMap.h"
+#include "System/DataStream.h"
+
+class GEM_EXPORT TileMapMgr : public Plugin {
+public:
+ TileMapMgr(void);
+ virtual ~TileMapMgr(void);
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual TileMap* GetTileMap(TileMap *tm) = 0;
+ virtual ieWord* GetDoorIndices(char* ResRef, int* count,
+ bool& BaseClosed) = 0;
+ virtual void SetupOpenDoor(unsigned int &index, unsigned int &count) = 0;
+ virtual void SetupClosedDoor(unsigned int &index, unsigned int &count) = 0;
+
+ virtual Wall_Polygon** GetWallGroups() = 0;
+ //returns only the wall polygon counts
+ virtual ieDword GetWallPolygonsCount() = 0;
+ //returns Wall + Door polygon counts
+ virtual ieDword GetPolygonsCount() = 0;
+};
+
+#endif
diff --git a/gemrb/core/TileOverlay.cpp b/gemrb/core/TileOverlay.cpp
new file mode 100644
index 0000000..475192e
--- /dev/null
+++ b/gemrb/core/TileOverlay.cpp
@@ -0,0 +1,132 @@
+/* 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 "TileOverlay.h"
+
+#include "Interface.h"
+#include "Video.h"
+
+bool RedrawTile = false;
+
+TileOverlay::TileOverlay(int Width, int Height)
+{
+ w = Width;
+ h = Height;
+ count = 0;
+ tiles = ( Tile * * ) malloc( w * h * sizeof( Tile * ) );
+}
+
+TileOverlay::~TileOverlay(void)
+{
+ for (int i = 0; i < count; i++) {
+ delete( tiles[i] );
+ }
+ free( tiles );
+}
+
+void TileOverlay::AddTile(Tile* tile)
+{
+ tiles[count++] = tile;
+}
+
+void TileOverlay::BumpViewport(const Region &viewport, Region &vp)
+{
+ bool bump = false;
+ vp.w = viewport.w;
+ vp.h = viewport.h;
+ if (( vp.x + vp.w ) > w * 64) {
+ vp.x = ( w * 64 - vp.w );
+ bump = true;
+ }
+ if (vp.x < 0) {
+ vp.x = 0;
+ bump = true;
+ }
+ if (( vp.y + vp.h ) > h * 64) {
+ vp.y = ( h * 64 - vp.h );
+ bump = true;
+ }
+ if (vp.y < 0) {
+ vp.y = 0;
+ bump = true;
+ }
+ if(bump && !(core->timer->ViewportIsMoving())) {
+ core->timer->SetMoveViewPort( vp.x, vp.y, 0, false );
+ }
+}
+
+void TileOverlay::Draw(Region viewport, std::vector< TileOverlay*> &overlays)
+{
+ Video* vid = core->GetVideoDriver();
+ Region vp = vid->GetViewport();
+
+ // if the video's viewport is partially outside of the map, bump it back
+ BumpViewport(viewport, vp);
+ // determine which tiles are visible
+ int sx = vp.x / 64;
+ int sy = vp.y / 64;
+ int dx = ( vp.x + vp.w + 63 ) / 64;
+ int dy = ( vp.y + vp.h + 63 ) / 64;
+
+ for (int y = sy; y < dy && y < h; y++) {
+ for (int x = sx; x < dx && x < w; x++) {
+ Tile* tile = tiles[( y* w ) + x];
+
+ //draw door tiles if there are any
+ Animation* anim = tile->anim[tile->tileIndex];
+ if (!anim && tile->tileIndex) {
+ anim = tile->anim[0];
+ }
+ vid->BlitTile( anim->NextFrame(), 0, viewport.x + ( x * 64 ),
+ viewport.y + ( y * 64 ), &viewport, false );
+ if (!tile->om || tile->tileIndex) {
+ continue;
+ }
+
+ //draw overlay tiles, they should be half transparent
+ int mask = 2;
+ for (size_t z = 1;z<overlays.size();z++) {
+ TileOverlay * ov = overlays[z];
+ if (ov && ov->count > 0) {
+ Tile *ovtile = ov->tiles[0]; //allow only 1x1 tiles now
+ if (tile->om & mask) {
+ if (RedrawTile) {
+ vid->BlitTile( ovtile->anim[0]->NextFrame(),
+ tile->anim[0]->NextFrame(),
+ viewport.x + ( x * 64 ),
+ viewport.y + ( y * 64 ),
+ &viewport, false );
+ } else {
+ Sprite2D* mask = 0;
+ if (tile->anim[1])
+ mask = tile->anim[1]->NextFrame();
+ vid->BlitTile( ovtile->anim[0]->NextFrame(),
+ mask,
+ viewport.x + ( x * 64 ),
+ viewport.y + ( y * 64 ),
+ &viewport, true );
+ }
+ }
+ }
+ mask<<=1;
+ }
+ }
+ }
+}
diff --git a/gemrb/core/TileOverlay.h b/gemrb/core/TileOverlay.h
new file mode 100644
index 0000000..642f1d3
--- /dev/null
+++ b/gemrb/core/TileOverlay.h
@@ -0,0 +1,46 @@
+/* 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 TILEOVERLAY_H
+#define TILEOVERLAY_H
+
+#include "exports.h"
+
+#include "Tile.h"
+
+#include <vector>
+
+extern bool RedrawTile;
+
+class GEM_EXPORT TileOverlay {
+public:
+ int w, h;
+ //std::vector<Tile*> tiles;
+ Tile** tiles;
+ int count;
+public:
+ TileOverlay(int Width, int Height);
+ ~TileOverlay(void);
+ void AddTile(Tile* tile);
+ void Draw(Region viewport, std::vector< TileOverlay*> &overlays);
+ void BumpViewport(const Region &viewport, Region &vp);
+};
+
+#endif
diff --git a/gemrb/core/TileSetMgr.cpp b/gemrb/core/TileSetMgr.cpp
new file mode 100644
index 0000000..87a0dc7
--- /dev/null
+++ b/gemrb/core/TileSetMgr.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 "TileSetMgr.h"
+
+TileSetMgr::TileSetMgr(void)
+{
+}
+
+TileSetMgr::~TileSetMgr(void)
+{
+}
diff --git a/gemrb/core/TileSetMgr.h b/gemrb/core/TileSetMgr.h
new file mode 100644
index 0000000..0cc21dc
--- /dev/null
+++ b/gemrb/core/TileSetMgr.h
@@ -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.
+ *
+ *
+ */
+
+#ifndef TILESETMGR_H
+#define TILESETMGR_H
+
+#include "Plugin.h"
+#include "Tile.h"
+#include "System/DataStream.h"
+
+class GEM_EXPORT TileSetMgr : public Plugin {
+public:
+ TileSetMgr(void);
+ virtual ~TileSetMgr(void);
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ virtual Tile* GetTile(unsigned short* indexes, int count,
+ unsigned short* secondary = NULL) = 0;
+};
+
+#endif
diff --git a/gemrb/core/TypeID.h b/gemrb/core/TypeID.h
new file mode 100644
index 0000000..65acc4c
--- /dev/null
+++ b/gemrb/core/TypeID.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 TYPE_ID_H
+#define TYPE_ID_H
+
+class TypeID {
+public:
+ const char *description;
+};
+
+#endif
diff --git a/gemrb/core/Variables.cpp b/gemrb/core/Variables.cpp
new file mode 100644
index 0000000..9be058f
--- /dev/null
+++ b/gemrb/core/Variables.cpp
@@ -0,0 +1,506 @@
+/* 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 "Variables.h"
+
+#include "Interface.h" // for LoadInitialValues
+#include "System/FileStream.h" // for LoadInitialValues
+
+/////////////////////////////////////////////////////////////////////////////
+// private inlines
+inline bool Variables::MyCopyKey(char*& dest, const char* key) const
+{
+ int i, j;
+
+ //use j
+ for (i = 0,j = 0; key[i] && j < MAX_VARIABLE_LENGTH - 1; i++)
+ if (key[i] != ' ') {
+ j++;
+ }
+ dest = (char *) malloc(j + 1);
+ if (!dest) {
+ return false;
+ }
+ for (i = 0,j = 0; key[i] && j < MAX_VARIABLE_LENGTH - 1; i++) {
+ if (key[i] != ' ') {
+ dest[j++] = (char) tolower( key[i] );
+ }
+ }
+ dest[j] = 0;
+ return true;
+}
+
+inline unsigned int Variables::MyCompareKey(const char* key, const char *str) const
+{
+ int i,j;
+
+ for (i = 0, j = 0; str[j] && key[i] && i < MAX_VARIABLE_LENGTH - 1 && j < MAX_VARIABLE_LENGTH - 1;) {
+ char c1 = tolower(key[i]);
+ if (c1 == ' ') { i++; continue; }
+ char c2 = tolower(str[j]);
+ if (c2 ==' ') { j++; continue; }
+ if (c1!=c2) return 1;
+ i++;
+ j++;
+ }
+ if (str[j] || key[i]) return 1;
+ return 0;
+}
+
+inline unsigned int Variables::MyHashKey(const char* key) const
+{
+ unsigned int nHash = 0;
+ for (int i = 0; key[i] && i < MAX_VARIABLE_LENGTH; i++) {
+ //the original engine ignores spaces in variable names
+ if (key[i] != ' ')
+ nHash = ( nHash << 5 ) + nHash + tolower( key[i] );
+ }
+ return nHash;
+}
+/////////////////////////////////////////////////////////////////////////////
+// functions
+Variables::iterator Variables::GetNextAssoc(iterator rNextPosition, const char*& rKey,
+ ieDword& rValue) const
+{
+ assert( m_pHashTable != NULL ); // never call on empty map
+
+ Variables::MyAssoc* pAssocRet = ( Variables::MyAssoc* ) rNextPosition;
+
+ if (pAssocRet == NULL) {
+ // find the first association
+ for (unsigned int nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
+ if (( pAssocRet = m_pHashTable[nBucket] ) != NULL)
+ break;
+ assert( pAssocRet != NULL ); // must find something
+ }
+ Variables::MyAssoc* pAssocNext;
+ if (( pAssocNext = pAssocRet->pNext ) == NULL) {
+ // go to next bucket
+ for (unsigned int nBucket = pAssocRet->nHashValue + 1;
+ nBucket < m_nHashTableSize;
+ nBucket++)
+ if (( pAssocNext = m_pHashTable[nBucket] ) != NULL)
+ break;
+ }
+
+ // fill in return data
+ rKey = pAssocRet->key;
+ rValue = pAssocRet->Value.nValue;
+ return ( iterator ) pAssocNext;
+}
+
+Variables::Variables(int nBlockSize, int nHashTableSize)
+{
+ assert( nBlockSize > 0 );
+ assert( nHashTableSize > 16 );
+
+ m_pHashTable = NULL;
+ m_nHashTableSize = nHashTableSize; // default size
+ m_nCount = 0;
+ m_lParseKey = false;
+ m_pFreeList = NULL;
+ m_pBlocks = NULL;
+ m_nBlockSize = nBlockSize;
+ m_type = GEM_VARIABLES_INT;
+}
+
+void Variables::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 =(Variables::MyAssoc **) malloc(sizeof( Variables::MyAssoc *) * nHashSize);
+ memset( m_pHashTable, 0, sizeof( Variables::MyAssoc * ) * nHashSize );
+ }
+ m_nHashTableSize = nHashSize;
+}
+
+void Variables::RemoveAll(ReleaseFun fun)
+{
+ if (m_pHashTable != NULL) {
+ // destroy elements (values and keys)
+ for (unsigned int nHash = 0; nHash < m_nHashTableSize; nHash++) {
+ Variables::MyAssoc* pAssoc;
+ for (pAssoc = m_pHashTable[nHash];
+ pAssoc != NULL;
+ pAssoc = pAssoc->pNext) {
+ if (fun) {
+ fun((void *) pAssoc->Value.sValue);
+ }
+ else if (m_type == GEM_VARIABLES_STRING) {
+ if (pAssoc->Value.sValue) {
+ free( pAssoc->Value.sValue );
+ pAssoc->Value.sValue = NULL;
+ }
+ }
+ if (pAssoc->key) {
+ free(pAssoc->key);
+ pAssoc->key = NULL;
+ }
+ }
+ }
+ }
+
+ // free hash table
+ free(m_pHashTable);
+ m_pHashTable = NULL;
+
+ m_nCount = 0;
+ m_pFreeList = NULL;
+ MemBlock* p = m_pBlocks;
+ while (p != NULL) {
+ MemBlock* pNext = p->pNext;
+ free(p);
+ p = pNext;
+ }
+ m_pBlocks = NULL;
+}
+
+Variables::~Variables()
+{
+ RemoveAll(NULL);
+}
+
+Variables::MyAssoc* Variables::NewAssoc(const char* key)
+{
+ if (m_pFreeList == NULL) {
+ // add another block
+ Variables::MemBlock* newBlock = ( Variables::MemBlock* ) malloc( m_nBlockSize*sizeof( Variables::MyAssoc ) + sizeof( Variables::MemBlock ));
+ assert( newBlock != NULL ); // we must have something
+ newBlock->pNext = m_pBlocks;
+ m_pBlocks = newBlock;
+
+ // chain them into free list
+ Variables::MyAssoc* pAssoc = ( Variables::MyAssoc* ) ( newBlock + 1 );
+ for (int i = 0; i < m_nBlockSize; i++) {
+ pAssoc->pNext = m_pFreeList;
+ m_pFreeList = pAssoc++;
+ }
+ }
+
+ Variables::MyAssoc* pAssoc = m_pFreeList;
+ m_pFreeList = m_pFreeList->pNext;
+ m_nCount++;
+ assert( m_nCount > 0 ); // make sure we don't overflow
+ if (m_lParseKey) {
+ MyCopyKey( pAssoc->key, key );
+ } else {
+ int len;
+ len = strnlen( key, MAX_VARIABLE_LENGTH - 1 );
+ pAssoc->key = (char *) malloc(len + 1);
+ if (pAssoc->key) {
+ memcpy( pAssoc->key, key, len );
+ pAssoc->key[len] = 0;
+ }
+ }
+#ifdef _DEBUG
+ pAssoc->Value.nValue = 0xcccccccc; //invalid value
+ pAssoc->nHashValue = 0xcccccccc; //invalid value
+#endif
+ return pAssoc;
+}
+
+void Variables::FreeAssoc(Variables::MyAssoc* pAssoc)
+{
+ if (pAssoc->key) {
+ free(pAssoc->key);
+ pAssoc->key = NULL;
+ }
+ 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);
+ }
+}
+
+Variables::MyAssoc* Variables::GetAssocAt(const char* key, unsigned int& nHash) const
+ // find association (or return NULL)
+{
+ nHash = MyHashKey( key ) % m_nHashTableSize;
+
+ if (m_pHashTable == NULL) {
+ return NULL;
+ }
+
+ // see if it exists
+ Variables::MyAssoc* pAssoc;
+ for (pAssoc = m_pHashTable[nHash];
+ pAssoc != NULL;
+ pAssoc = pAssoc->pNext) {
+ if (m_lParseKey) {
+ if (!MyCompareKey( pAssoc->key, key) ) {
+ return pAssoc;
+ }
+ } else {
+ if (!strnicmp( pAssoc->key, key, MAX_VARIABLE_LENGTH )) {
+ return pAssoc;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int Variables::GetValueLength(const char* key) const
+{
+ unsigned int nHash;
+ Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
+ if (pAssoc == NULL) {
+ return 0; // not in map
+ }
+
+ return ( int ) strlen( pAssoc->Value.sValue );
+}
+
+bool Variables::Lookup(const char* key, char* dest, int MaxLength) const
+{
+ unsigned int nHash;
+ assert( m_type == GEM_VARIABLES_STRING );
+ Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
+ if (pAssoc == NULL) {
+ dest[0] = 0;
+ return false; // not in map
+ }
+
+ strncpy( dest, pAssoc->Value.sValue, MaxLength );
+ return true;
+}
+
+bool Variables::Lookup(const char* key, char *&dest) const
+{
+ unsigned int nHash;
+ assert(m_type==GEM_VARIABLES_STRING);
+ Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
+ if (pAssoc == NULL) {
+ return false;
+ } // not in map
+
+ dest = pAssoc->Value.sValue;
+ return true;
+}
+
+bool Variables::Lookup(const char* key, void *&dest) const
+{
+ unsigned int nHash;
+ assert(m_type==GEM_VARIABLES_POINTER);
+ Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
+ if (pAssoc == NULL) {
+ return false;
+ } // not in map
+
+ dest = pAssoc->Value.pValue;
+ return true;
+}
+
+bool Variables::Lookup(const char* key, ieDword& rValue) const
+{
+ unsigned int nHash;
+ assert(m_type==GEM_VARIABLES_INT);
+ Variables::MyAssoc* pAssoc = GetAssocAt( key, nHash );
+ if (pAssoc == NULL) {
+ return false;
+ } // not in map
+
+ rValue = pAssoc->Value.nValue;
+ return true;
+}
+
+void Variables::SetAtCopy(const char* key, const char* value)
+{
+ size_t len = strlen(value)+1;
+ char *str=(char *) malloc(len);
+ memcpy(str,value,len);
+ SetAt(key, str);
+}
+
+void Variables::SetAtCopy(const char* key, int newValue)
+{
+ char tmpstr[10]; // should be enough
+ sprintf(tmpstr, "%d", newValue);
+ SetAtCopy(key, tmpstr);
+}
+
+void Variables::SetAt(const char* key, char* value)
+{
+ unsigned int nHash;
+ Variables::MyAssoc* pAssoc;
+
+ assert(strlen(key)<256);
+
+#ifdef _DEBUG
+ // for Avenger, debugging memory issues
+ assert((unsigned char)key[0]!=0xcd);
+#endif
+
+ assert( m_type == GEM_VARIABLES_STRING );
+ if (( pAssoc = GetAssocAt( key, nHash ) ) == NULL) {
+ if (m_pHashTable == NULL)
+ InitHashTable( m_nHashTableSize );
+
+ // it doesn't exist, add a new Association
+ pAssoc = NewAssoc( key );
+ // put into hash table
+ pAssoc->pNext = m_pHashTable[nHash];
+ m_pHashTable[nHash] = pAssoc;
+ } else {
+ if (pAssoc->Value.sValue) {
+ free( pAssoc->Value.sValue );
+ pAssoc->Value.sValue = 0;
+ }
+ }
+
+ //set value only if we have a key
+ if (pAssoc->key) {
+ pAssoc->Value.sValue = value;
+ pAssoc->nHashValue = nHash;
+ }
+}
+
+void Variables::SetAt(const char* key, void* value)
+{
+ unsigned int nHash;
+ Variables::MyAssoc* pAssoc;
+
+ assert( m_type == GEM_VARIABLES_POINTER );
+ if (( pAssoc = GetAssocAt( key, nHash ) ) == NULL) {
+ if (m_pHashTable == NULL)
+ InitHashTable( m_nHashTableSize );
+
+ // it doesn't exist, add a new Association
+ pAssoc = NewAssoc( key );
+ // put into hash table
+ pAssoc->pNext = m_pHashTable[nHash];
+ m_pHashTable[nHash] = pAssoc;
+ } else {
+ if (pAssoc->Value.sValue) {
+ free( pAssoc->Value.sValue );
+ pAssoc->Value.sValue = 0;
+ }
+ }
+
+ //set value only if we have a key
+ if (pAssoc->key) {
+ pAssoc->Value.pValue = value;
+ pAssoc->nHashValue = nHash;
+ }
+
+}
+
+
+void Variables::SetAt(const char* key, ieDword value, bool nocreate)
+{
+ unsigned int nHash;
+ Variables::MyAssoc* pAssoc;
+
+ assert( m_type == GEM_VARIABLES_INT );
+ if (( pAssoc = GetAssocAt( key, nHash ) ) == NULL) {
+ if (nocreate) {
+ printMessage("Variables", " ", YELLOW);
+ printf("Cannot create new variable: %s\n", key);
+ return;
+ }
+
+ if (m_pHashTable == NULL)
+ InitHashTable( m_nHashTableSize );
+
+ // it doesn't exist, add a new Association
+ pAssoc = NewAssoc( key );
+ // put into hash table
+ pAssoc->pNext = m_pHashTable[nHash];
+ m_pHashTable[nHash] = pAssoc;
+ }
+ //set value only if we have a key
+ if (pAssoc->key) {
+ pAssoc->Value.nValue = value;
+ pAssoc->nHashValue = nHash;
+ }
+}
+
+void Variables::Remove(const char* key)
+{
+ unsigned int nHash;
+ Variables::MyAssoc* pAssoc;
+
+ pAssoc = GetAssocAt( key, nHash );
+ if (!pAssoc) return; // not in there
+
+ if (pAssoc == m_pHashTable[nHash]) {
+ // head
+ m_pHashTable[nHash] = pAssoc->pNext;
+ } else {
+ Variables::MyAssoc* prev = m_pHashTable[nHash];
+ // Room for optimization: make each bucket a doubly linked
+ // list to make removes from a bucket O(1).
+ // (This will have limited use in gemrb's case, because we
+ // use relatively large tables and small buckets.)
+ while (prev->pNext != pAssoc) {
+ prev = prev->pNext;
+ assert( prev != NULL );
+ }
+ prev->pNext = pAssoc->pNext;
+ }
+ pAssoc->pNext = 0;
+ FreeAssoc(pAssoc);
+}
+
+void Variables::LoadInitialValues(const char* name)
+{
+ char nPath[_MAX_PATH];
+ // we only support PST's var.var for now
+ PathJoin( nPath, core->GamePath, "var.var", NULL );
+ FileStream fs;
+ if (!fs.Open( nPath, true )) {
+ return;
+ }
+
+ char buffer[41];
+ ieDword value;
+ buffer[40] = 0;
+ ieVariable varname;
+
+ // first value is useless
+ if (!fs.Read(buffer, 40)) return;
+ if (fs.ReadDword(&value) != 4) return;
+
+ while (fs.Remains()) {
+ // read data
+ if (!fs.Read(buffer, 40)) return;
+ if (fs.ReadDword(&value) != 4) return;
+ // is it the type we want? if not, skip
+ if (strnicmp(buffer, name, 6)) continue;
+ // copy variable (types got 2 extra spaces, and the name is padded too)
+ strnspccpy(varname,buffer+8,32);
+ SetAt(varname, value);
+ }
+}
diff --git a/gemrb/core/Variables.h b/gemrb/core/Variables.h
new file mode 100644
index 0000000..db392a8
--- /dev/null
+++ b/gemrb/core/Variables.h
@@ -0,0 +1,127 @@
+/* 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 VARIABLES_H
+#define VARIABLES_H
+
+
+#include "SClassID.h"
+#include "exports.h"
+#include "globals.h"
+#include "win32def.h"
+
+#ifndef ReleaseFun
+typedef void (*ReleaseFun)(void *);
+#endif
+
+#define GEM_VARIABLES_INT 0
+#define GEM_VARIABLES_STRING 1
+#define GEM_VARIABLES_POINTER 2
+
+class GEM_EXPORT Variables {
+protected:
+ // Association
+ class MyAssoc {
+ MyAssoc* pNext;
+ char* key;
+ union {
+ ieDword nValue;
+ char* sValue;
+ void* pValue;
+ } Value;
+ unsigned long nHashValue;
+ friend class Variables;
+ };
+ struct MemBlock {
+ MemBlock* pNext;
+ };
+public:
+ // abstract iteration position
+ typedef MyAssoc *iterator;
+public:
+ // Construction
+ Variables(int nBlockSize = 10, int nHashTableSize = 2049);
+ void LoadInitialValues(const char* name);
+
+ // Attributes
+ //sets the way we handle keys, no parsing for .ini file entries, parsing for game variables
+ //you should set this only on an empty mapping
+ inline int ParseKey(int arg)
+ {
+ assert( m_nCount == 0 );
+ m_lParseKey = ( arg > 0 );
+ return 0;
+ }
+ //sets the way we handle values
+ inline void SetType(int type)
+ {
+ m_type = type;
+ }
+ inline int GetCount() const
+ {
+ return m_nCount;
+ }
+ inline bool IsEmpty() const
+ {
+ return m_nCount == 0;
+ }
+
+ // Lookup
+ int GetValueLength(const char* key) const;
+ bool Lookup(const char* key, char* dest, int MaxLength) const;
+ bool Lookup(const char* key, ieDword& rValue) const;
+ bool Lookup(const char* key, char*& dest) const;
+ bool Lookup(const char* key, void*& dest) const;
+
+ // Operations
+ void SetAtCopy(const char* key, const char* newValue);
+ void SetAtCopy(const char* key, int newValue);
+ void SetAt(const char* key, char* newValue);
+ void SetAt(const char* key, void* newValue);
+ void SetAt(const char* key, ieDword newValue, bool nocreate=false);
+ void Remove(const char* key);
+ void RemoveAll(ReleaseFun fun);
+ void InitHashTable(unsigned int hashSize, bool bAllocNow = true);
+
+ iterator GetNextAssoc(iterator rNextPosition, const char*& rKey,
+ ieDword& rValue) const;
+ // Implementation
+protected:
+ Variables::MyAssoc** m_pHashTable;
+ unsigned int m_nHashTableSize;
+ bool m_lParseKey;
+ int m_nCount;
+ Variables::MyAssoc* m_pFreeList;
+ MemBlock* m_pBlocks;
+ int m_nBlockSize;
+ int m_type; //could be string or ieDword
+
+ Variables::MyAssoc* NewAssoc(const char* key);
+ void FreeAssoc(Variables::MyAssoc*);
+ Variables::MyAssoc* GetAssocAt(const char*, unsigned int&) const;
+ inline bool MyCopyKey(char*& dest, const char* key) const;
+ inline unsigned int MyCompareKey(const char* key, const char *str) const;
+ inline unsigned int MyHashKey(const char*) const;
+
+public:
+ ~Variables();
+};
+
+#endif
diff --git a/gemrb/core/Video.cpp b/gemrb/core/Video.cpp
new file mode 100644
index 0000000..22eeb17
--- /dev/null
+++ b/gemrb/core/Video.cpp
@@ -0,0 +1,230 @@
+/* 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 "Video.h"
+
+#include "win32def.h"
+
+#include "Audio.h"
+#include "Interface.h"
+#include "Palette.h"
+
+#include <cmath>
+
+const TypeID Video::ID = { "Video" };
+
+Video::Video(void)
+{
+ Evnt = NULL;
+
+ // Initialize gamma correction tables
+ for (int i = 0; i < 256; i++) {
+ Gamma22toGamma10[i] = (unsigned char)(0.5 + (pow (i/255.0, 2.2/1.0) * 255.0));
+ Gamma10toGamma22[i] = (unsigned char)(0.5 + (pow (i/255.0, 1.0/2.2) * 255.0));
+ }
+}
+
+Video::~Video(void)
+{
+}
+
+/** Set Event Manager */
+void Video::SetEventMgr(EventMgr* evnt)
+{
+ //if 'evnt' is NULL then no Event Manager will be used
+ Evnt = evnt;
+}
+
+/** Mouse is invisible and cannot interact */
+void Video::SetMouseEnabled(int enabled)
+{
+ DisableMouse = enabled^MOUSE_DISABLED;
+}
+
+/** Mouse cursor is grayed and doesn't click (but visible and movable) */
+void Video::SetMouseGrayed(bool grayed)
+{
+ if (grayed) {
+ DisableMouse |= MOUSE_GRAYED;
+ } else {
+ DisableMouse &= ~MOUSE_GRAYED;
+ }
+}
+
+/** Get the fullscreen mode */
+bool Video::GetFullscreenMode() const
+{
+ return fullscreen;
+}
+
+void Video::BlitTiled(Region rgn, const Sprite2D* img, bool anchor)
+{
+ int xrep = ( rgn.w + img->Width - 1 ) / img->Width;
+ int yrep = ( rgn.h + img->Height - 1 ) / img->Height;
+ for (int y = 0; y < yrep; y++) {
+ for (int x = 0; x < xrep; x++) {
+ BlitSprite(img, rgn.x + (x*img->Width),
+ rgn.y + (y*img->Height), anchor, &rgn);
+ }
+ }
+}
+
+//Sprite conversion, creation
+Sprite2D* Video::CreateAlpha( const Sprite2D *sprite)
+{
+ if (!sprite)
+ return 0;
+
+ unsigned int *pixels = (unsigned int *) malloc (sprite->Width * sprite->Height * 4);
+ int i=0;
+ for (int y = 0; y < sprite->Height; y++) {
+ for (int x = 0; x < sprite->Width; x++) {
+ int sum = 0;
+ int cnt = 0;
+ for (int xx=x-3;xx<=x+3;xx++) {
+ for(int yy=y-3;yy<=y+3;yy++) {
+ if (((xx==x-3) || (xx==x+3)) &&
+ ((yy==y-3) || (yy==y+3))) continue;
+ if (xx < 0 || xx >= sprite->Width) continue;
+ if (yy < 0 || yy >= sprite->Height) continue;
+ cnt++;
+ if (sprite->IsPixelTransparent(xx, yy))
+ sum++;
+ }
+ }
+ int tmp=255 - (sum * 255 / cnt);
+ tmp = tmp * tmp / 255;
+ pixels[i++]=tmp;
+ }
+ }
+ return CreateSprite( sprite->Width, sprite->Height, 32, 0xFF000000,
+ 0x00FF0000, 0x0000FF00, 0x000000FF, pixels );
+}
+
+Sprite2D* Video::SpriteScaleDown( const Sprite2D* sprite, unsigned int ratio )
+{
+ unsigned int Width = sprite->Width / ratio;
+ unsigned int Height = sprite->Height / ratio;
+
+ unsigned int* pixels = (unsigned int *) malloc( Width * Height * 4 );
+ int i = 0;
+
+ for (unsigned int y = 0; y < Height; y++) {
+ for (unsigned int x = 0; x < Width; x++) {
+ Color c = SpriteGetPixelSum( sprite, x, y, ratio );
+
+ *(pixels + i++) = c.r + (c.g << 8) + (c.b << 16) + (c.a << 24);
+ }
+ }
+
+ Sprite2D* small = CreateSprite( Width, Height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000,
+0xff000000, pixels, false, 0 );
+
+ small->XPos = sprite->XPos / ratio;
+ small->YPos = sprite->YPos / ratio;
+
+ return small;
+}
+
+//TODO light could be elliptical in the original engine
+//is it difficult?
+Sprite2D* Video::CreateLight(int radius, int intensity)
+{
+ if(!radius) return NULL;
+ Point p, q;
+ int a;
+ void* pixels = malloc( radius * radius * 4 * 4);
+ int i = 0;
+
+ for (p.y = -radius; p.y < radius; p.y++) {
+ for (p.x = -radius; p.x < radius; p.x++) {
+ a = intensity*(radius-(signed) Distance(p,q))/radius;
+
+ if(a<0) a=0;
+ else if(a>255) a = 255;
+
+ *((unsigned int*)pixels + i++) = 0xffffff + ((a/2) << 24);
+ }
+ }
+
+ Sprite2D* light = CreateSprite( radius*2, radius*2, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000, pixels);
+
+ light->XPos = radius;
+ light->YPos = radius;
+
+ return light;
+}
+
+Color Video::SpriteGetPixelSum(const Sprite2D* sprite, unsigned short xbase, unsigned short ybase, unsigned int ratio)
+{
+ Color sum;
+ unsigned int count = ratio*ratio;
+ unsigned int r=0, g=0, b=0, a=0;
+
+ for (unsigned int x = 0; x < ratio; x++) {
+ for (unsigned int y = 0; y < ratio; y++) {
+ Color c = sprite->GetPixel( xbase*ratio+x, ybase*ratio+y );
+ r += Gamma22toGamma10[c.r];
+ g += Gamma22toGamma10[c.g];
+ b += Gamma22toGamma10[c.b];
+ a += Gamma22toGamma10[c.a];
+ }
+ }
+
+ sum.r = Gamma10toGamma22[r / count];
+ sum.g = Gamma10toGamma22[g / count];
+ sum.b = Gamma10toGamma22[b / count];
+ sum.a = Gamma10toGamma22[a / count];
+
+ return sum;
+}
+
+//Viewport specific
+Region Video::GetViewport() const
+{
+ return Viewport;
+}
+
+void Video::SetViewport(int x, int y, unsigned int w, unsigned int h)
+{
+ if (x>width)
+ x=width;
+ xCorr = x;
+ if (y>height)
+ y=height;
+ yCorr = y;
+ if (w>(unsigned int) width)
+ w=0;
+ Viewport.w = w;
+ if (h>(unsigned int) height)
+ h=0;
+ Viewport.h = h;
+}
+
+void Video::MoveViewportTo(int x, int y)
+{
+ if (x != Viewport.x || y != Viewport.y) {
+ core->GetAudioDrv()->UpdateListenerPos( (x - xCorr) + width / 2, (y - yCorr)
++ height / 2 );
+ Viewport.x = x;
+ Viewport.y = y;
+ }
+}
+
diff --git a/gemrb/core/Video.h b/gemrb/core/Video.h
new file mode 100644
index 0000000..1560e5e
--- /dev/null
+++ b/gemrb/core/Video.h
@@ -0,0 +1,237 @@
+/* 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 Video.h
+ * Declares Video, base class for video output plugins.
+ * @author The GemRB Project
+ */
+
+#ifndef VIDEO_H
+#define VIDEO_H
+
+#include "globals.h"
+
+#include "Animation.h"
+#include "Plugin.h"
+#include "Polygon.h"
+#include "ScriptedAnimation.h"
+#include "GUI/EventMgr.h"
+
+class AnimationFactory;
+class Palette;
+class SpriteCover;
+
+// Note: not all these flags make sense together. Specifically:
+// NOSHADOW overrides TRANSSHADOW
+enum SpriteBlitFlags {
+ BLIT_HALFTRANS = IE_VVC_TRANSPARENT, // 2
+ BLIT_BLENDED = IE_VVC_BLENDED, // 8; not implemented in SDLVideo yet
+ BLIT_MIRRORX = IE_VVC_MIRRORX, // 0x10
+ BLIT_MIRRORY = IE_VVC_MIRRORY, // 0x20
+ BLIT_NOSHADOW = 0x1000,
+ BLIT_TRANSSHADOW = 0x2000,
+ BLIT_TINTED = 0x00010000, // IE_VVC_TINT = 0x00030000
+ BLIT_GREY = IE_VVC_GREYSCALE, // 0x80000; timestop palette
+ BLIT_RED = IE_VVC_SEPIA, // 0x02000000; dream scene palette
+ BLIT_DARK = IE_VVC_DARKEN, // 0x00100000; not implemented in SDLVideo yet
+ BLIT_GLOW = IE_VVC_GLOWING // 0x00200000; not implemented in SDLVideo yet
+ // Note: bits 29,30,31 are used by SDLVideo internally
+};
+
+//disable mouse flags
+#define MOUSE_DISABLED 1
+#define MOUSE_GRAYED 2
+
+// !!! Keep this synchronized with GUIDefines.py !!!
+// used for calculating the tooltip delay limit and the real tooltip delay
+#define TOOLTIP_DELAY_FACTOR 250
+
+/**
+ * @class Video
+ * Base class for video output plugins.
+ */
+
+class GEM_EXPORT Video : public Plugin {
+public:
+ static const TypeID ID;
+public:
+ Video(void);
+ virtual ~Video(void);
+ virtual int Init(void) = 0;
+ virtual int CreateDisplay(int width, int height, int bpp, bool fullscreen) = 0;
+ /** Sets window title of GemRB window */
+ virtual void SetDisplayTitle(char* title, char* icon) = 0;
+ /** Toggles GemRB between fullscreen and windowed mode.
+ * 0 = windowed, 1 = fullscreen, -1 (default) = toggle */
+ virtual bool ToggleFullscreenMode(int set_reset=-1) = 0;
+ /** Swaps displayed and back buffers */
+ virtual int SwapBuffers(void) = 0;
+ /** Grabs and releases mouse cursor within GemRB window */
+ virtual bool ToggleGrabInput() = 0;
+ virtual short GetWidth() = 0;
+ virtual short GetHeight() = 0;
+
+ virtual void InitSpriteCover(SpriteCover* sc, int flags) = 0;
+ virtual void AddPolygonToSpriteCover(SpriteCover* sc, Wall_Polygon* poly) = 0;
+ virtual void DestroySpriteCover(SpriteCover* sc) = 0;
+
+ virtual Sprite2D* CreateSprite(int w, int h, int bpp, ieDword rMask,
+ ieDword gMask, ieDword bMask, ieDword aMask, void* pixels,
+ bool cK = false, int index = 0) = 0;
+ virtual Sprite2D* CreateSprite8(int w, int h, int bpp, void* pixels,
+ void* palette, bool cK = false, int index = 0) = 0;
+ virtual Sprite2D* CreateSpriteBAM8(int /*w*/, int /*h*/, bool /* RLE */,
+ const unsigned char* /*pixeldata*/,
+ AnimationFactory* /*datasrc*/,
+ Palette* /*palette*/,
+ int /*transindex*/) { return 0; }
+ virtual bool SupportsBAMSprites() { return false; }
+ virtual void FreeSprite(Sprite2D* &spr) = 0;
+ virtual Sprite2D* DuplicateSprite(const Sprite2D* spr) = 0;
+ virtual void BlitTile(const Sprite2D* spr, const Sprite2D* mask, int x, int y, const Region* clip, bool trans) = 0;
+ virtual void BlitSprite(const Sprite2D* spr, int x, int y, bool anchor = false,
+ const Region* clip = NULL) = 0;
+
+ // Note: BlitSpriteRegion's clip region is shifted by Viewport.x/y if
+ // anchor is false. This is different from the other BlitSprite functions.
+ virtual void BlitSpriteRegion(const Sprite2D* spr, const Region& size, int x, int y,
+ bool anchor = true, const Region* clip = NULL) = 0;
+ // Note: Tint cannot be constified, because it is modified locally
+ // not a pretty interface :)
+ virtual void BlitGameSprite(const Sprite2D* spr, int x, int y,
+ unsigned int flags, Color tint,
+ SpriteCover* cover, Palette *palette = NULL,
+ const Region* clip = NULL, bool anchor = false) = 0;
+ virtual void SetCursor(Sprite2D* up, Sprite2D* down) = 0;
+ /** Sets a temporary cursor when dragging an Item from Inventory.
+ * VideoDriver will call FreeSprite on it.
+ */
+ virtual void SetDragCursor(Sprite2D* drag) = 0;
+ /** Return GemRB window screenshot.
+ * It's generated from the momentary back buffer */
+ virtual Sprite2D* GetScreenshot( Region r ) = 0;
+ virtual void ConvertToVideoFormat(Sprite2D* sprite) = 0;
+ /** Sets the palette of a plugin specific sprite */
+ virtual void SetPalette(void* data, Palette* pal) = 0;
+ /** This function Draws the Border of a Rectangle as described by the Region parameter. The Color used to draw the rectangle is passes via the Color parameter. */
+ virtual void DrawRect(const Region& rgn, const Color& color, bool fill = true, bool clipped = false) = 0;
+ /** this function draws a clipped sprite */
+ virtual void DrawRectSprite(const Region& rgn, const Color& color, const Sprite2D* sprite) = 0;
+ virtual void SetPixel(short x, short y, const Color& color, bool clipped = false) = 0;
+ virtual void GetPixel(short x, short y, Color& color) = 0;
+ virtual long GetPixel(void *, unsigned short x, unsigned short y) = 0;
+ virtual void GetPixel(void *, unsigned short x, unsigned short y, Color &color) = 0;
+ /** Draws a circle */
+ virtual void DrawCircle(short cx, short cy, unsigned short r, const Color& color, bool clipped = true) = 0;
+ /** Draws an Ellipse Segment */
+ virtual void DrawEllipseSegment(short cx, short cy, unsigned short xr, unsigned short yr, const Color& color,
+ double anglefrom, double angleto, bool drawlines = true, bool clipped = true) = 0;
+ /** Draws an ellipse */
+ virtual void DrawEllipse(short cx, short cy, unsigned short xr,
+ unsigned short yr, const Color& color, bool clipped = true) = 0;
+ /** Draws a polygon on the screen */
+ virtual void DrawPolyline(Gem_Polygon* poly, const Color& color,
+ bool fill = false) = 0;
+ /** Draws a line segment */
+ virtual void DrawLine(short x1, short y1, short x2, short y2,
+ const Color& color, bool clipped = false) = 0;
+ /** Blits a Sprite filling the Region */
+ void BlitTiled(Region rgn, const Sprite2D* img, bool anchor = false);
+ /** Sets Event Manager */
+ void SetEventMgr(EventMgr* evnt);
+ /** Sends a Quit Signal to the Event Queue */
+ virtual bool Quit(void) = 0;
+ /** Gets the Palette of a surface */
+ virtual Palette* GetPalette(void* surface) = 0;
+ /** Flips sprite vertically, returns new sprite */
+ virtual Sprite2D *MirrorSpriteVertical(const Sprite2D *sprite, bool MirrorAnchor) = 0;
+ /** Flips sprite horizontally, returns new sprite */
+ virtual Sprite2D *MirrorSpriteHorizontal(const Sprite2D *sprite, bool MirrorAnchor) = 0;
+ /** Duplicates and transforms sprite to have an alpha channel */
+ Sprite2D* CreateAlpha(const Sprite2D *sprite);
+
+ /** Converts a Screen Coordinate to a Game Coordinate */
+ virtual void ConvertToGame(short& x, short& y) = 0;
+ /** Converts a Game Coordinate to a Screen Coordinate */
+ virtual void ConvertToScreen(short& x, short& y) = 0;
+ /** Sets the Fading Color */
+ virtual void SetFadeColor(int r, int g, int b) = 0;
+ /** Sets the Fading to Color Percentage */
+ virtual void SetFadePercent(int percent) = 0;
+ /** Sets Clip Rectangle */
+ virtual void SetClipRect(const Region* clip) = 0;
+ /** Gets Clip Rectangle */
+ virtual void GetClipRect(Region& clip) = 0;
+ /** returns the current mouse coordinates */
+ virtual void GetMousePos(int &x, int &y) = 0;
+ /** clicks the mouse forcibly */
+ virtual void ClickMouse(unsigned int button) = 0;
+ /** moves the mouse forcibly */
+ virtual void MoveMouse(unsigned int x, unsigned int y) = 0;
+ /** initializes the screen for movie */
+ virtual void InitMovieScreen(int &w, int &h, bool yuv=false) = 0;
+ /** sets the font and color of the movie subtitles */
+ virtual void SetMovieFont(Font *stfont, Palette *pal) = 0;
+ /** draws a movie frame */
+ virtual void showFrame(unsigned char* buf, unsigned int bufw,
+ unsigned int bufh, unsigned int sx, unsigned int sy,
+ unsigned int w, unsigned int h, unsigned int dstx,
+ unsigned int dsty, int truecolor, unsigned char *palette,
+ ieDword titleref) = 0;
+ virtual void showYUVFrame(unsigned char** buf, unsigned int *strides,
+ unsigned int bufw, unsigned int bufh,
+ unsigned int w, unsigned int h,
+ unsigned int dstx, unsigned int dsty,
+ ieDword titleref) = 0;
+ virtual void DrawMovieSubtitle(ieStrRef text) = 0;
+ /** handles events during movie */
+ virtual int PollMovieEvents() = 0;
+ virtual void SetGamma(int brightness, int contrast) = 0;
+public:
+ /** Event Manager Pointer */
+
+ void SetMouseEnabled(int enabled);
+ void SetMouseGrayed(bool grayed);
+ bool GetFullscreenMode() const;
+
+ /** Scales down a sprite by a ratio */
+ Sprite2D* SpriteScaleDown( const Sprite2D* sprite, unsigned int ratio );
+ /** Creates an ellipse or circle shaped sprite with various intensity
+ * for projectile light spots */
+ Sprite2D* CreateLight(int radius, int intensity);
+
+ Color SpriteGetPixelSum (const Sprite2D* sprite, unsigned short xbase, unsigned short ybase, unsigned int ratio);
+ Region GetViewport(void) const;
+ void SetViewport(int x, int y, unsigned int w, unsigned int h);
+ void MoveViewportTo(int x, int y);
+protected:
+ int DisableMouse;
+ short xCorr, yCorr;
+ EventMgr* Evnt;
+ Region Viewport;
+ int width,height,bpp;
+ bool fullscreen;
+
+ unsigned char Gamma10toGamma22[256];
+ unsigned char Gamma22toGamma10[256];
+};
+
+#endif
diff --git a/gemrb/core/VideoMode.h b/gemrb/core/VideoMode.h
new file mode 100644
index 0000000..ee71f74
--- /dev/null
+++ b/gemrb/core/VideoMode.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 VIDEOMODE_H
+#define VIDEOMODE_H
+
+#include "exports.h"
+
+class GEM_EXPORT VideoMode {
+public:
+ VideoMode(void);
+ ~VideoMode(void);
+ VideoMode(const VideoMode& vm);
+ VideoMode(int w, int h, int bpp, bool fs);
+
+private:
+ int Width;
+ int Height;
+ int bpp;
+ bool fullscreen;
+public:
+ void SetWidth(int w);
+ int GetWidth(void) const;
+ void SetHeight(int h);
+ int GetHeight(void) const;
+ void SetBPP(int b);
+ int GetBPP(void) const ;
+ void SetFullScreen(bool fs);
+ bool GetFullScreen(void) const;
+ bool operator==(const VideoMode& cpt) const;
+ VideoMode& operator=(const VideoMode& vm);
+};
+
+#endif
diff --git a/gemrb/core/WindowMgr.cpp b/gemrb/core/WindowMgr.cpp
new file mode 100644
index 0000000..bdde5a5
--- /dev/null
+++ b/gemrb/core/WindowMgr.cpp
@@ -0,0 +1,28 @@
+/* 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 "WindowMgr.h"
+
+WindowMgr::WindowMgr()
+{
+}
+WindowMgr::~WindowMgr()
+{
+}
diff --git a/gemrb/core/WindowMgr.h b/gemrb/core/WindowMgr.h
new file mode 100644
index 0000000..61a3a37
--- /dev/null
+++ b/gemrb/core/WindowMgr.h
@@ -0,0 +1,52 @@
+/* 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 WindowMgr.h
+ * Declares WindowMgr class, abstract loader for GUI windows and controls
+ * @author The GemRB Project
+ */
+
+#ifndef WINDOWMGR_H
+#define WINDOWMGR_H
+
+#include "Plugin.h"
+#include "GUI/Window.h"
+#include "System/DataStream.h"
+
+/**
+ * @class WindowMgr
+ * Abstract loader for GUI windows (and controls with them).
+ * Contrary to its name, it does not work as a window manager
+ */
+
+class GEM_EXPORT WindowMgr : public Plugin {
+public:
+ WindowMgr();
+ virtual ~WindowMgr();
+ /** This function loads all available windows from the 'stream' parameter. */
+ virtual bool Open(DataStream* stream, bool autoFree = true) = 0;
+ /** Returns the i-th window in the Previously Loaded Stream */
+ virtual Window* GetWindow(unsigned int i) = 0;
+ /** Returns the number of available windows */
+ virtual unsigned int GetWindowsCount() = 0;
+};
+
+#endif
diff --git a/gemrb/core/WorldMap.cpp b/gemrb/core/WorldMap.cpp
new file mode 100644
index 0000000..c2face0
--- /dev/null
+++ b/gemrb/core/WorldMap.cpp
@@ -0,0 +1,593 @@
+/* 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 "WorldMap.h"
+
+#include "win32def.h"
+
+#include "Game.h"
+#include "Interface.h"
+#include "Video.h"
+
+#include <list>
+
+WMPAreaEntry::WMPAreaEntry()
+{
+ MapIcon = NULL;
+ StrCaption = NULL;
+ StrTooltip = NULL;
+}
+
+WMPAreaEntry::~WMPAreaEntry()
+{
+ if (StrCaption) {
+ core->FreeString(StrCaption);
+ }
+ if (StrTooltip) {
+ core->FreeString(StrTooltip);
+ }
+ core->GetVideoDriver()->FreeSprite(MapIcon);
+}
+
+void WMPAreaEntry::SetAreaStatus(ieDword arg, int op)
+{
+ switch (op) {
+ case BM_SET: AreaStatus = arg; break;
+ case BM_OR: AreaStatus |= arg; break;
+ case BM_NAND: AreaStatus &= ~arg; break;
+ case BM_XOR: AreaStatus ^= arg; break;
+ case BM_AND: AreaStatus &= arg; break;
+ }
+ core->GetVideoDriver()->FreeSprite(MapIcon);
+}
+
+const char* WMPAreaEntry::GetCaption()
+{
+ if (!StrCaption) {
+ StrCaption = core->GetString(LocCaptionName);
+ }
+ return StrCaption;
+}
+
+const char* WMPAreaEntry::GetTooltip()
+{
+ if (!StrTooltip) {
+ StrTooltip = core->GetString(LocTooltipName);
+ }
+ return StrTooltip;
+}
+
+static int gradients[5]={18,22,19,3,4};
+
+void WMPAreaEntry::SetPalette(int gradient, Sprite2D* MapIcon)
+{
+ if (!MapIcon) return;
+ Palette *palette = new Palette;
+ core->GetPalette( gradient&255, 256, palette->col );
+ MapIcon->SetPalette(palette);
+}
+
+Sprite2D *WMPAreaEntry::GetMapIcon(AnimationFactory *bam)
+{
+ if (!bam) {
+ return NULL;
+ }
+ if (!MapIcon) {
+ int color = -1;
+ int frame = 0;
+ switch (AreaStatus&(WMP_ENTRY_ACCESSIBLE|WMP_ENTRY_VISITED))
+ {
+ case WMP_ENTRY_ACCESSIBLE: frame = 0; break;
+ case WMP_ENTRY_VISITED: frame = 4; break;
+ case WMP_ENTRY_ACCESSIBLE|WMP_ENTRY_VISITED: frame = 1; break;
+ case 0: frame = 2; break;
+ }
+ if (bam->GetCycleSize(IconSeq)<5) {
+ color = gradients[frame];
+ frame = 0;
+ }
+ MapIcon = bam->GetFrame((ieWord) frame, (ieByte) IconSeq);
+ if (!MapIcon) {
+ printf("WMPAreaEntry::GetMapIcon failed for frame %d, seq %d\n", frame, IconSeq);
+ return NULL;
+ }
+ if (color>=0) {
+ // Note: should a game use the same map icon for two different
+ // map locations, we have to duplicate the MapIcon sprite here.
+ // This doesn't occur in BG1, so no need to do that for the moment.
+ SetPalette(color, MapIcon);
+ }
+ }
+ MapIcon->acquire();
+ return MapIcon;
+}
+
+ieDword WMPAreaEntry::GetAreaStatus()
+{
+ ieDword tmp = AreaStatus;
+ if (core->HasFeature(GF_KNOW_WORLD) ) {
+ tmp |=WMP_ENTRY_VISITED;
+ }
+ return tmp;
+}
+
+WorldMap::WorldMap(void)
+{
+ MapMOS = NULL;
+ Distances = NULL;
+ GotHereFrom = NULL;
+ bam = NULL;
+}
+
+//Allocate AE and AL only in Core, otherwise Win32 will
+//be buggy
+void WorldMap::AddAreaEntry(WMPAreaEntry *ae)
+{
+ area_entries.push_back(ae);
+}
+
+void WorldMap::AddAreaLink(WMPAreaLink *al)
+{
+ area_links.push_back(al);
+}
+
+WMPAreaEntry *WorldMap::GetNewAreaEntry() const
+{
+ return new WMPAreaEntry();
+}
+
+void WorldMap::SetAreaEntry(unsigned int x, WMPAreaEntry *ae)
+{
+ //if index is too large, we break
+ if (x>area_entries.size()) {
+ abort();
+ }
+ //altering an existing entry
+ if (x<area_entries.size()) {
+ if (area_entries[x]) {
+ delete area_entries[x];
+ }
+ area_entries[x]=ae;
+ return;
+ }
+ //adding a new entry
+ area_entries.push_back(ae);
+}
+
+void WorldMap::InsertAreaLink(unsigned int areaidx, unsigned int dir, WMPAreaLink *arealink)
+{
+ unsigned int pos;
+ WMPAreaEntry *ae;
+
+ WMPAreaLink *al = new WMPAreaLink();
+ memcpy(al, arealink, sizeof(WMPAreaLink) );
+ unsigned int idx = area_entries[areaidx]->AreaLinksIndex[dir];
+ area_links.insert(area_links.begin()+idx,al);
+
+ unsigned int max = area_entries.size();
+ for(pos = 0; pos<max; pos++) {
+ ae = area_entries[pos];
+ for (unsigned int k=0;k<4;k++) {
+ if ((pos==areaidx) && (k==dir)) {
+ ae->AreaLinksCount[k]++;
+ continue;
+ }
+ if(ae->AreaLinksIndex[k]>=idx) {
+ ae->AreaLinksIndex[k]++;
+ }
+ }
+ }
+ //update the link count, just in case
+ AreaLinksCount++;
+}
+
+void WorldMap::SetAreaLink(unsigned int x, WMPAreaLink *arealink)
+{
+ WMPAreaLink *al =new WMPAreaLink();
+
+ //change this to similar code as above if WMPAreaLink gets non-struct members
+ memcpy( al,arealink,sizeof(WMPAreaLink) );
+
+ //if index is too large, we break
+ if (x>area_links.size()) {
+ abort();
+ }
+ //altering an existing link
+ if (x<area_links.size()) {
+ if (area_links[x]) {
+ delete area_links[x];
+ }
+ area_links[x]=al;
+ return;
+ }
+ //adding a new link
+ area_links.push_back(al);
+}
+
+WorldMap::~WorldMap(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < area_entries.size(); i++) {
+ delete( area_entries[i] );
+ }
+ for (i = 0; i < area_links.size(); i++) {
+ delete( area_links[i] );
+ }
+ if (MapMOS) {
+ core->GetVideoDriver()->FreeSprite(MapMOS);
+ }
+ if (Distances) {
+ free(Distances);
+ }
+ if (GotHereFrom) {
+ free(GotHereFrom);
+ }
+ if (bam) bam = NULL;
+}
+
+void WorldMap::SetMapIcons(AnimationFactory *newicons)
+{
+ bam = newicons;
+}
+void WorldMap::SetMapMOS(Sprite2D *newmos)
+{
+ if (MapMOS) {
+ core->GetVideoDriver()->FreeSprite(MapMOS);
+ }
+ MapMOS = newmos;
+}
+
+WMPAreaEntry* WorldMap::GetArea(const ieResRef AreaName, unsigned int &i) const
+{
+ i=(unsigned int) area_entries.size();
+ while (i--) {
+ if (!strnicmp(AreaName, area_entries[i]->AreaName,8)) {
+ return area_entries[i];
+ }
+ }
+ return NULL;
+}
+
+//Find Worldmap location by nearest area with a smaller number
+//Counting backwards, stop at 1000 boundaries.
+//It is not possible to simply round to 1000, because there are
+//WMP entries like AR8001, and we need to find the best match
+WMPAreaEntry* WorldMap::FindNearestEntry(const ieResRef AreaName, unsigned int &i) const
+{
+ int value = 0;
+ ieResRef tmp;
+
+ sscanf(&AreaName[2],"%4d", &value);
+ do {
+ snprintf(tmp, 9, "%.2s%04d", AreaName, value);
+ WMPAreaEntry* ret = GetArea(tmp, i);
+ if (ret) {
+ return ret;
+ }
+ if (value%1000 == 0) break;
+ value--;
+ }
+ while(1); //value%1000 should protect us from infinite loops
+ i = -1;
+ return NULL;
+}
+
+//this is a pathfinding algorithm
+//you have to find the optimal path
+int WorldMap::CalculateDistances(const ieResRef AreaName, int direction)
+{
+ //first, update reachable/visible areas by worlde.2da if exists
+ UpdateReachableAreas();
+ UpdateAreaVisibility(AreaName, direction);
+ if (direction==-1) {
+ return 0;
+ }
+
+ if (direction<0 || direction>3) {
+ printMessage("WorldMap","", LIGHT_RED);
+ printf("CalculateDistances for invalid direction: %s\n", AreaName);
+ return -1;
+ }
+
+ unsigned int i;
+ if (!GetArea(AreaName, i)) {
+ printMessage("WorldMap","", LIGHT_RED);
+ printf("CalculateDistances for invalid Area: %s\n", AreaName);
+ return -1;
+ }
+ if (Distances) {
+ free(Distances);
+ }
+ if (GotHereFrom) {
+ free(GotHereFrom);
+ }
+
+ printMessage("WorldMap","", GREEN);
+ printf("CalculateDistances for Area: %s\n", AreaName);
+
+ size_t memsize =sizeof(int) * area_entries.size();
+ Distances = (int *) malloc( memsize );
+ GotHereFrom = (int *) malloc( memsize );
+ memset( Distances, -1, memsize );
+ memset( GotHereFrom, -1, memsize );
+ Distances[i] = 0; //setting our own distance
+ GotHereFrom[i] = -1; //we didn't move
+
+ int *seen_entry = (int *) malloc( memsize );
+
+ std::list<int> pending;
+ pending.push_back(i);
+ while(pending.size()) {
+ i=pending.front();
+ pending.pop_front();
+ WMPAreaEntry* ae=area_entries[i];
+ memset( seen_entry, -1, memsize );
+ //all directions should be used
+ for(int d=0;d<4;d++) {
+ int j=ae->AreaLinksIndex[d];
+ int k=j+ae->AreaLinksCount[d];
+ if ((size_t) k>area_links.size()) {
+ printMessage("WorldMap","The worldmap file is corrupted... and it would crash right now!\n",RED);
+ printf("Entry #: %d Direction: %d\n",i,d);
+ break;
+ }
+ for(;j<k;j++) {
+ WMPAreaLink* al = area_links[j];
+ WMPAreaEntry* ae2 = area_entries[al->AreaIndex];
+ unsigned int mydistance = (unsigned int) Distances[i];
+
+ // we must only process the FIRST seen link to each area from this one
+ if (seen_entry[al->AreaIndex] != -1) continue;
+ seen_entry[al->AreaIndex] = 0;
+/*
+ if ( ( (ae->GetAreaStatus() & WMP_ENTRY_PASSABLE) == WMP_ENTRY_PASSABLE) &&
+ ( (ae2->GetAreaStatus() & WMP_ENTRY_WALKABLE) == WMP_ENTRY_WALKABLE)
+*/
+ if ( (ae2->GetAreaStatus() & WMP_ENTRY_WALKABLE) == WMP_ENTRY_WALKABLE) {
+ // al->Flags is the entry direction
+ mydistance += al->DistanceScale * 4;
+ //nonexisting distance is the biggest!
+ if ((unsigned) Distances[al->AreaIndex] > mydistance) {
+ Distances[al->AreaIndex] = mydistance;
+ GotHereFrom[al->AreaIndex] = j;
+ pending.push_back(al->AreaIndex);
+ }
+ }
+ }
+ }
+ }
+
+ free(seen_entry);
+ return 0;
+}
+
+//returns the index of the area owning this link
+unsigned int WorldMap::WhoseLinkAmI(int link_index) const
+{
+ for (unsigned int i=0;i<AreaEntriesCount;i++) {
+ WMPAreaEntry *ae=area_entries[i];
+ for (int direction=0;direction<4;direction++)
+ {
+ int j=ae->AreaLinksIndex[direction];
+ if (link_index>=j) {
+ j+=ae->AreaLinksCount[direction];
+ if(link_index<j) {
+ return i;
+ }
+ }
+ }
+ }
+ return (ieDword) -1;
+}
+
+WMPAreaLink *WorldMap::GetLink(const ieResRef A, const ieResRef B) const
+{
+ unsigned int i,j,k;
+
+ WMPAreaEntry *ae=GetArea( A, i );
+ if (!ae) {
+ return NULL;
+ }
+ //looking for destination area, returning the first link found
+ for (i=0;i<4;i++) {
+ j=ae->AreaLinksCount[i];
+ k=ae->AreaLinksIndex[i];
+ while(j--) {
+ WMPAreaLink *al = area_links[k++];
+ WMPAreaEntry *ae2 = area_entries[al->AreaIndex];
+ //or arearesref?
+ if (strnicmp(ae2->AreaName, B, 8)==0) {
+ return al;
+ }
+ }
+ }
+ return NULL;
+}
+
+//call this function to find out which area we fall into
+//not necessarily the target area
+//if it isn't the same, then a random encounter happened!
+WMPAreaLink *WorldMap::GetEncounterLink(const ieResRef AreaName, bool &encounter) const
+{
+ if (!GotHereFrom) {
+ return NULL;
+ }
+ unsigned int i;
+ WMPAreaEntry *ae=GetArea( AreaName, i ); //target area
+ if (!ae) {
+ printMessage("WorldMap","",LIGHT_RED);
+ printf("No such area: %s\n", AreaName);
+ return NULL;
+ }
+ std::list<WMPAreaLink*> walkpath;
+ printf("Gathering path information for: %s\n", AreaName);
+ while (GotHereFrom[i]!=-1) {
+ printf("Adding path to %d\n", i);
+ walkpath.push_back(area_links[GotHereFrom[i]]);
+ i = WhoseLinkAmI(GotHereFrom[i]);
+ if (i==(ieDword) -1) {
+ printf("Something has been screwed up here (incorrect path)!\n");
+ abort();
+ }
+ }
+
+ printf("Walkpath size is: %d\n",(int) walkpath.size());
+ if (!walkpath.size()) {
+ return NULL;
+ }
+ std::list<WMPAreaLink*>::iterator p=walkpath.begin();
+ WMPAreaLink *lastpath;
+ encounter=false;
+ do {
+ lastpath = *p;
+ if (lastpath->EncounterChance > (unsigned int) (rand()%100)) {
+ encounter=true;
+ break;
+ }
+ p++;
+ }
+ while(p!=walkpath.end() );
+ return lastpath;
+}
+
+int WorldMap::GetDistance(const ieResRef AreaName) const
+{
+ if (!Distances) {
+ return -1;
+ }
+ unsigned int i;
+ if (GetArea( AreaName, i )) {
+ return Distances[i];
+ }
+ return -1;
+}
+
+void WorldMap::UpdateAreaVisibility(const ieResRef AreaName, int direction)
+{
+ unsigned int i;
+
+ WMPAreaEntry* ae=GetArea(AreaName,i);
+ if (!ae)
+ return;
+ //we are here, so we visited and it is visible too (i guess)
+ printf("Updated Area visibility: %s (visited, and visible)\n", AreaName);
+
+ ae->SetAreaStatus(WMP_ENTRY_VISITED|WMP_ENTRY_VISIBLE, BM_OR);
+ if (direction<0 || direction>3)
+ return;
+ i=ae->AreaLinksCount[direction];
+ while (i--) {
+ WMPAreaLink* al = area_links[ae->AreaLinksIndex[direction]+i];
+ WMPAreaEntry* ae2 = area_entries[al->AreaIndex];
+ if (ae2->GetAreaStatus()&WMP_ENTRY_ADJACENT) {
+ printf("Updated Area visibility: %s (accessible, and visible)\n", ae2->AreaName);
+ ae2->SetAreaStatus(WMP_ENTRY_VISIBLE|WMP_ENTRY_ACCESSIBLE, BM_OR);
+ }
+ }
+}
+
+void WorldMap::SetAreaStatus(const ieResRef AreaName, int Bits, int Op)
+{
+ unsigned int i;
+ WMPAreaEntry* ae=GetArea(AreaName,i);
+ if (!ae)
+ return;
+ ae->SetAreaStatus(Bits, Op);
+}
+
+void WorldMap::UpdateReachableAreas()
+{
+ AutoTable tab("worlde");
+ if (!tab) {
+ return;
+ }
+ Game *game = core->GetGame();
+ if (!game) {
+ return;
+ }
+ int idx = tab->GetRowCount();
+ while (idx--) {
+ // 2da rows in format <name> <variable name> <area>
+ // we set the first three flags for <area> if <variable name> is set
+ ieDword varval = 0;
+ const char *varname = tab->QueryField(idx, 0);
+ if (game->locals->Lookup(varname, varval) && varval) {
+ const char *areaname = tab->QueryField(idx, 1);
+ SetAreaStatus(areaname, WMP_ENTRY_VISIBLE | WMP_ENTRY_ADJACENT | WMP_ENTRY_ACCESSIBLE, BM_OR);
+ }
+ }
+}
+
+/****************** WorldMapArray *******************/
+WorldMapArray::WorldMapArray(unsigned int count)
+{
+ CurrentMap = 0;
+ MapCount = count;
+ all_maps = (WorldMap **) calloc(count, sizeof(WorldMap *) );
+ single = true;
+}
+
+WorldMapArray::~WorldMapArray()
+{
+ unsigned int i;
+
+ for (i = 0; i<MapCount; i++) {
+ if (all_maps[i]) {
+ delete all_maps[i];
+ }
+ }
+ free( all_maps );
+}
+
+unsigned int WorldMapArray::FindAndSetCurrentMap(const ieResRef area)
+{
+ unsigned int i, idx;
+
+ for (i = CurrentMap; i<MapCount; i++) {
+ if (all_maps[i]->GetArea (area, idx) ) {
+ CurrentMap = i;
+ return i;
+ }
+ }
+ for (i = 0; i<CurrentMap; i++) {
+ if (all_maps[i]->GetArea (area, idx) ) {
+ CurrentMap = i;
+ return i;
+ }
+ }
+ return CurrentMap;
+}
+
+void WorldMapArray::SetWorldMap(WorldMap *m, unsigned int index)
+{
+ if (index<MapCount) {
+ all_maps[index]=m;
+ }
+}
+
+WorldMap *WorldMapArray::NewWorldMap(unsigned int index)
+{
+ if (all_maps[index]) {
+ delete all_maps[index];
+ }
+ all_maps[index] = new WorldMap();
+ return all_maps[index];
+}
diff --git a/gemrb/core/WorldMap.h b/gemrb/core/WorldMap.h
new file mode 100644
index 0000000..f8cce25
--- /dev/null
+++ b/gemrb/core/WorldMap.h
@@ -0,0 +1,205 @@
+/* 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 WorldMap.h
+ * Declares WorldMap, class describing a top level map of the world.
+ * @author The GemRB Project
+ */
+
+
+#ifndef WORLDMAP_H
+#define WORLDMAP_H
+
+#include "exports.h"
+#include "ie_types.h"
+
+#include "AnimationFactory.h"
+#include "Sprite2D.h"
+
+#include <vector>
+
+/** Area is visible on WorldMap */
+#define WMP_ENTRY_VISIBLE 0x1
+/** Area is visible on WorldMap only when party is in adjacent area */
+#define WMP_ENTRY_ADJACENT 0x2
+/** Area can be travelled into from WorldMap */
+#define WMP_ENTRY_ACCESSIBLE 0x4
+/** Area has already been visited by party */
+#define WMP_ENTRY_VISITED 0x8
+/** Area can be travelled into from WorldMap */
+#define WMP_ENTRY_WALKABLE (WMP_ENTRY_VISIBLE|WMP_ENTRY_ACCESSIBLE)
+/** Area can be passed through when travelling directly to some more distant area on WorldMap */
+#define WMP_ENTRY_PASSABLE (WMP_ENTRY_VISIBLE|WMP_ENTRY_ACCESSIBLE|WMP_ENTRY_VISITED)
+
+/** this is the physical order the links appear in WMPAreaEntry */
+typedef enum ieDirectionType {
+ WMP_NORTH=0,
+ WMP_WEST=1,
+ WMP_SOUTH=2,
+ WMP_EAST=3
+} ieDirectionType;
+
+/**
+ * @class WMPAreaEntry
+ * Holds information about an Area on a WorldMap.
+ */
+
+class GEM_EXPORT WMPAreaEntry {
+public:
+ WMPAreaEntry();
+ ~WMPAreaEntry();
+ ieDword GetAreaStatus();
+ void SetAreaStatus(ieDword status, int op);
+
+ //! return the map icon of this location. Free the sprite afterwards.
+ Sprite2D *GetMapIcon(AnimationFactory *bam);
+ const char* GetCaption();
+ const char* GetTooltip();
+private:
+ ieDword AreaStatus;
+ Sprite2D *MapIcon;
+ char *StrCaption;
+ char *StrTooltip;
+
+ void SetPalette(int gradient, Sprite2D *MapIcon);
+public:
+ ieResRef AreaName;
+ ieResRef AreaResRef;
+ char AreaLongName[32];
+ ieDword IconSeq;
+ ieDword X;
+ ieDword Y;
+ ieStrRef LocCaptionName;
+ ieStrRef LocTooltipName;
+ ieResRef LoadScreenResRef;
+ ieDword AreaLinksIndex[4];
+ ieDword AreaLinksCount[4];
+};
+
+/**
+ * @struct WMPAreaLink
+ * Defines connection and travelling between WorldMap areas
+ */
+
+struct WMPAreaLink {
+ ieDword AreaIndex;
+ char DestEntryPoint[32];
+ ieDword DistanceScale;
+ ieDword DirectionFlags; //where will the player appear on dest. area
+ ieResRef EncounterAreaResRef[5];
+ ieDword EncounterChance;
+};
+
+/**
+ * @class WorldMap
+ * Top level map of the world.
+ * Also defines links between areas, although they are used only when travelling from this map.
+ */
+
+class GEM_EXPORT WorldMap {
+public:
+ WorldMap();
+ ~WorldMap();
+public: //struct members
+ ieResRef MapResRef;
+ ieDword Width;
+ ieDword Height;
+ ieDword MapNumber;
+ ieStrRef AreaName;
+ ieDword unknown1;
+ ieDword unknown2;
+ ieDword AreaEntriesCount;
+ ieDword AreaEntriesOffset;
+ ieDword AreaLinksOffset;
+ ieDword AreaLinksCount;
+ ieResRef MapIconResRef;
+
+ AnimationFactory *bam;
+private: //non-struct members
+ Sprite2D* MapMOS;
+ std::vector< WMPAreaEntry*> area_entries;
+ std::vector< WMPAreaLink*> area_links;
+ int *Distances;
+ int *GotHereFrom;
+public:
+ void SetMapIcons(AnimationFactory *bam);
+ Sprite2D* GetMapMOS() const { return MapMOS; }
+ void SetMapMOS(Sprite2D *newmos);
+ int GetEntryCount() const { return (int) area_entries.size(); }
+ WMPAreaEntry *GetEntry(unsigned int index) { return area_entries[index]; }
+ int GetLinkCount() const { return (int) area_links.size(); }
+ WMPAreaLink *GetLink(unsigned int index) const { return area_links[index]; }
+ WMPAreaEntry *GetNewAreaEntry() const;
+ void SetAreaEntry(unsigned int index, WMPAreaEntry *areaentry);
+ void InsertAreaLink(unsigned int idx, unsigned int dir, WMPAreaLink *arealink);
+ void SetAreaLink(unsigned int index, WMPAreaLink *arealink);
+ void AddAreaEntry(WMPAreaEntry *ae);
+ void AddAreaLink(WMPAreaLink *al);
+ /** Calculates the distances from A, call this when first on an area */
+ int CalculateDistances(const ieResRef A, int direction);
+ /** Returns the precalculated distance to area B */
+ int GetDistance(const ieResRef A) const;
+ /** Returns the link between area A and area B */
+ WMPAreaLink *GetLink(const ieResRef A, const ieResRef B) const;
+ /** Returns the area link we will fall into if we head in B direction */
+ /** If the area name differs it means we are in a random encounter */
+ WMPAreaLink *GetEncounterLink(const ieResRef B, bool &encounter) const;
+ /** Sets area status */
+ void SetAreaStatus(const ieResRef, int Bits, int Op);
+ /** Gets area pointer and index from area name.
+ * also called from WorldMapArray to find the right map */
+ WMPAreaEntry* GetArea(const ieResRef AreaName, unsigned int &i) const;
+ /** Finds an area name closest to the given area */
+ WMPAreaEntry* FindNearestEntry(const ieResRef AreaName, unsigned int &i) const;
+private:
+ /** updates visibility of adjacent areas, called from CalculateDistances */
+ void UpdateAreaVisibility(const ieResRef AreaName, int direction);
+ /** internal function to calculate the distances from areaindex */
+ void CalculateDistance(int areaindex, int direction);
+ unsigned int WhoseLinkAmI(int link_index) const;
+ /** update reachable areas from worlde.2da */
+ void UpdateReachableAreas();
+};
+
+class GEM_EXPORT WorldMapArray {
+public:
+ WorldMapArray(unsigned int count);
+ ~WorldMapArray();
+ void SetWorldMap(WorldMap *m, unsigned int index);
+private:
+ WorldMap **all_maps;
+ unsigned int MapCount;
+ unsigned int CurrentMap;
+ bool single;
+public:
+ bool IsSingle() const { return single; }
+ void SetSingle(bool arg) { single = arg; }
+ unsigned int GetMapCount() const { return MapCount; }
+ unsigned int GetCurrentMapIndex() const { return CurrentMap; }
+ WorldMap *NewWorldMap(unsigned int index);
+ WorldMap *GetWorldMap(unsigned int index) const { return all_maps[index]; }
+ WorldMap *GetCurrentMap() const { return all_maps[CurrentMap]; }
+ void SetWorldMap(unsigned int index);
+ void SetCurrentMap(unsigned int index) { CurrentMap = index; }
+ unsigned int FindAndSetCurrentMap(const ieResRef area);
+};
+
+#endif // ! WORLDMAP_H
diff --git a/gemrb/core/WorldMapMgr.cpp b/gemrb/core/WorldMapMgr.cpp
new file mode 100644
index 0000000..425c2b0
--- /dev/null
+++ b/gemrb/core/WorldMapMgr.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 "WorldMapMgr.h"
+
+WorldMapMgr::WorldMapMgr(void)
+{
+}
+
+WorldMapMgr::~WorldMapMgr(void)
+{
+}
diff --git a/gemrb/core/WorldMapMgr.h b/gemrb/core/WorldMapMgr.h
new file mode 100644
index 0000000..80eed4c
--- /dev/null
+++ b/gemrb/core/WorldMapMgr.h
@@ -0,0 +1,50 @@
+/* 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 WorldMapMgr.h
+ * Declares WorldMapMgr class, loader for WorldMap objects
+ * @author The GemRB Project
+ */
+
+#ifndef WORLDMAPMGR_H
+#define WORLDMAPMGR_H
+
+#include "Plugin.h"
+#include "WorldMap.h"
+#include "System/DataStream.h"
+
+/**
+ * @class WorldMapMgr
+ * Abstract loader for WorldMap objects
+ */
+
+class GEM_EXPORT WorldMapMgr : public Plugin {
+public:
+ WorldMapMgr(void);
+ virtual ~WorldMapMgr(void);
+ virtual bool Open(DataStream* stream1, DataStream* stream2, bool autoFree = true) = 0;
+ virtual WorldMapArray* GetWorldMapArray() = 0;
+
+ virtual int GetStoredFileSize(WorldMapArray *wmap, unsigned int index) = 0;
+ virtual int PutWorldMap(DataStream* stream1, DataStream* stream2, WorldMapArray *wmap) = 0;
+};
+
+#endif
diff --git a/gemrb/core/damages.h b/gemrb/core/damages.h
new file mode 100644
index 0000000..adb20cc
--- /dev/null
+++ b/gemrb/core/damages.h
@@ -0,0 +1,53 @@
+/* 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 DAMAGE_H
+#define DAMAGE_H
+
+//damage types
+#define DAMAGE_CRUSHING 0
+#define DAMAGE_ACID 1
+#define DAMAGE_COLD 2
+#define DAMAGE_ELECTRICITY 4
+#define DAMAGE_FIRE 8
+#define DAMAGE_PIERCING 0x10
+#define DAMAGE_POISON 0x20
+#define DAMAGE_MAGIC 0x40
+#define DAMAGE_MISSILE 0x80
+#define DAMAGE_SLASHING 0x100
+#define DAMAGE_MAGICFIRE 0x200
+#define DAMAGE_PIERCINGMISSILE 0x400 //iwd2
+#define DAMAGE_MAGICCOLD 0x400
+#define DAMAGE_CRUSHINGMISSILE 0x400 //iwd2
+#define DAMAGE_STUNNING 0x800
+#define DAMAGE_SOULEATER 0x1000 //iwd2
+#define DAMAGE_DISEASE 0x4000 //iwd2
+
+#define DAMAGE_CHUNKING 0x8000
+//damage levels
+#define DL_CRITICAL 0
+#define DL_BLOOD 1
+#define DL_FIRE 4
+#define DL_ELECTRICITY 7
+#define DL_COLD 11
+#define DL_ACID 14
+#define DL_DISINTEGRATE 17
+
+#endif
diff --git a/gemrb/docs/CMakeLists.txt b/gemrb/docs/CMakeLists.txt
new file mode 100644
index 0000000..37f622c
--- /dev/null
+++ b/gemrb/docs/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_SUBDIRECTORY( en )
diff --git a/gemrb/docs/Makefile.am b/gemrb/docs/Makefile.am
new file mode 100644
index 0000000..81fee21
--- /dev/null
+++ b/gemrb/docs/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = en
diff --git a/gemrb/docs/en/CMakeLists.txt b/gemrb/docs/en/CMakeLists.txt
new file mode 100644
index 0000000..f087418
--- /dev/null
+++ b/gemrb/docs/en/CMakeLists.txt
@@ -0,0 +1,7 @@
+INSTALL(
+ DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ DESTINATION ${DOC_DIR}
+ PATTERN CMakeLists.txt EXCLUDE
+ PATTERN Makefile.am EXCLUDE
+ PATTERN doxygen/* EXCLUDE
+)
diff --git a/gemrb/docs/en/CheatKeys.txt b/gemrb/docs/en/CheatKeys.txt
new file mode 100644
index 0000000..0706a55
--- /dev/null
+++ b/gemrb/docs/en/CheatKeys.txt
@@ -0,0 +1,88 @@
+CHEAT AND DEBUG KEYS
+--------------------
+
+
+Following is a list of cheats you can use during development of GemRB.
+If you implement a new cheatkey, try to assign the same key as in the
+original engine (if you implement a similar function).
+This list can be incomplete and obsolete - for current status, look into
+gemrb/plugins/Core/GameControl.cpp, functions GameControl::OnKeyRelease()
+and GameControl::Draw().
+
+Cheat keys are disabled by default. To activate them, either type command
+GemRB.EnableCheatKeys(1) on console (pops-up with CTRL+SPACE key combination)
+or put it into some GUIScript - e.g. for PS:T in Start.py.
+
+GameControl control must be focused for the keys to be recognized, so it
+might be needed to 'click' the GameControl (i.e. map) first.
+
+
+Ctrl-A - Alters the animation ID of the actor. You have to hover your
+ mouse over it.
+
+Ctrl-B - Draws path from start point marked by Ctrl-O to current mouse
+ position.
+
+Ctrl-C - Force casts a hardcoded spell. The last selected actor is the
+ caster and the target is the door/actor currently under the
+ pointer. This currently casts knock (SPWI207).
+
+Ctrl-D - Trap or trapped container pointed w/ mouse is disarmed.
+
+Ctrl-F - Toggles fullscreen mode
+
+Ctrl-G - Dumps the global (game) object. Currently shows only loaded areas.
+
+Ctrl-I - Triggers an interaction between the last pointed npc and a random
+ party member.
+
+Ctrl-J - Teleports (jumps) selected actors to current mouse position.
+
+Ctrl-K - Kicks the actor out of the party.
+
+Ctrl-L - Plays the S056ICBL animation over the actor. (This exists in PST only)
+ TODO: iterate through animations, like the IE does.
+
+Ctrl-M - Prints (on terminal or DOS window) useful info on pointed actor, door
+ container or infopoint and current map
+
+Ctrl-O - Marks current mouse position as start point (origin) for path drawn
+ with Ctrl-B
+
+Ctrl-P - Centers the viewport on the selected actor.
+
+Ctrl-Q - The pointed actor will join the party.
+
+Ctrl-R - Resurrects pointed actor. If she's already alive, just heals.
+
+Ctrl-S - Alters the stance (animation state) of the actor. You have to
+ hover your mouse over it.
+
+Ctrl-T - Advances time by one hour.
+
+Ctrl-V - Explores a small, random part of the pointed area.
+
+Ctrl-X - Prints (on terminal or DOS window) name of current area script
+ and current mouse position converted to game coordinates.
+
+Ctrl-Y - Kills pointed actor or unlocks the pointed door/container, even
+ if it requires a key.
+
+Ctrl-Z - Same as Ctrl-A but backward
+
+Ctrl-1 - Changes the armour level
+
+Ctrl-4 - Toggles debug flag DEBUG_SHOW_INFOPOINTS (show all
+ traps, infopoints and wallgroups)
+
+Ctrl-6 - Toggles debug flag DEBUG_SHOW_LIGHTMAP (show the lightmap)
+
+Ctrl-7 - Toggle drawing of Fog-Of-War (actually explored bitmap atm.)
+ in GameControl.
+
+Ctrl-8 - Toggle drawing of searchmap over the area in GameControl.
+
+ALT - Toggles debug flag DEBUG_SHOW_CONTAINERS (show all containers
+ and doors)
+
+TAB - Sets debug flag DEBUG_XXX while pressed (unused)
diff --git a/gemrb/docs/en/CodingStyle.txt b/gemrb/docs/en/CodingStyle.txt
new file mode 100644
index 0000000..a0aeaa8
--- /dev/null
+++ b/gemrb/docs/en/CodingStyle.txt
@@ -0,0 +1,9 @@
+Header file include order.
+
+- Header file associated to cpp file, or plugin base class header.
+- Additional plugin headers.
+- Headers in includes.
+- Headers in core followed by subdirectories.
+- System and library headers.
+
+Each item should be sorted in ascii order, and separated by blank lines.
diff --git a/gemrb/docs/en/Engine/Charcolors.txt b/gemrb/docs/en/Engine/Charcolors.txt
new file mode 100644
index 0000000..1d8de00
--- /dev/null
+++ b/gemrb/docs/en/Engine/Charcolors.txt
@@ -0,0 +1,36 @@
+There are 4 distinct palettes.
+
+Known values of palette (p) are:
+0 Body
+1 Weapon
+2 Shield (or Off-hand weapon)
+3 Helmet
+
+Each has 7 colour slots at most.
+Known values of slots (s) are:
+Body:
+ 0 - Metal
+ 1 - Minor
+ 2 - Major
+ 3 - Skin
+ 4 - Leather
+ 5 - Armor
+ 6 - Hair
+
+Weapon:
+ 0 - Crossguard
+ ..
+ 5 - Grip
+ 6 - Blade
+
+etc.
+
+The combined palette location is given in <p><s> format, where both numbers are 4 bits.
+
+
+
+In GemRB we store these values in the 7 stats starting at IE_COLORS.
+Each stat stores four colour gradients (each as a byte).
+The n-th stat contains the n-th colour for body, weapon, shield, helmet, from
+least significant to most significant byte.
+
diff --git a/gemrb/docs/en/Engine/Containers.txt b/gemrb/docs/en/Engine/Containers.txt
new file mode 100644
index 0000000..86f8e0c
--- /dev/null
+++ b/gemrb/docs/en/Engine/Containers.txt
@@ -0,0 +1,57 @@
+
+Container specification (based on IESDP)
+
+Relevant structures:
+--------------------
+
+typedef struct {
+ char containername[32];
+ short posx, posy;
+ short type;
+ short lockdiff;
+ long flags;
+ short trapdetect;
+ short trapremove;
+ short trapped;
+ short trapvisible;
+ short launchx, launchy;
+ short p1x, p1y, p2x, p2y; //minimum bounding box
+ long firstitem;
+ long itemcount;
+ char trapscript[8];
+ long firstvertex;
+ short vertexcount;
+ short unknown56;
+ char scriptname[32];
+ char keyitem[8];
+ short unknown80;
+ short unknown82;
+ long strref; //STRREF!!!
+ char unused[56];
+} area_container;
+
+
+Description:
+------------
+The container's name is not really relevant, it is used only in editors. Scripts reference a container by
+the scriptname field. The container's position determines the opening location.
+All container types are similar, except that only a few got a special graphics (BAM), see containr.2da in GemRB.
+The ground piles are special containers of type 4. Ground pile containers show the item's ground icon instead of
+the container's trigger polygon. Containers could be locked, the lockdiff field determines the difficulty to open
+them by force. The trapdetect and trapremove fields determine the trap detection and removal difficulties.
+The trapped field shows if the trap is still trapped, and the trapvisible field (GemRB implements it as a countdown)
+shows if the trap is still visible. The launch point determines the origin of spells cast by the trap script.
+The bounding box has effect on the polygon only. The trapscript is not necessarily a trap, this script is always running
+not like the door's script. There are several triggers that might affect this script. (OpenFail, Open, Lockpick fail).
+The firstitem/itemcount fields point to the inventory of the container (or ground pile).
+The keyitem determines if there is a key. (There should be a keyremoval field like with doors, but it isn't yet found).
+The strref is the openfail message (when it is 100% impossible to open).
+
+Container flags:
+----------------
+0x00000001 - The container is locked, opening by brute force, key or lockpick is needed.
+0x00000002 - ?
+0x00000004 - ?
+0x00000008 - The script isn't removed when it is triggered.
+0x00000010 - ?
+0x00000020 - The container is disabled, the polygon is still active, but the container is not accessible to PC's
diff --git a/gemrb/docs/en/Engine/Doors.txt b/gemrb/docs/en/Engine/Doors.txt
new file mode 100644
index 0000000..dbed88f
--- /dev/null
+++ b/gemrb/docs/en/Engine/Doors.txt
@@ -0,0 +1,101 @@
+
+Door specification (based on IESDP)
+
+Relevant structures:
+--------------------
+
+typedef struct {
+ char doorid[8]; //matched with area_door.doorid
+ short closed; //this is largely unknown, probably it shows which tile represents the closed state
+ short firstdoortileidx; //door tile cell indices are 16 bit values
+ short countdoortileidx;
+ short countpolygonopen; //wallpolygon count for the open door state
+ short countpolygonclose; //wallpolygon count for the closed door state
+ long offsetpolygonopen; //wallpolygon offset for the open door state
+ long offsetpolygonclose; //wallpolygon offset for the closed door state
+} wed_door;
+
+typedef struct {
+ char doorname[32]; //scripting name
+ char doorid[8]; //wed reference
+ long flags; //door flags, see below
+ long firstvertexopen; //first open vertex index
+ short countvertexopen; //count of open vertices
+ short countvertexclose; //count of closed vertices
+ long firstvertexclose; //first closed vertex index
+ short op1x,op1y,op2x,op2y; //minimum bounding box for open
+ short cp1x,cp1y,cp2x,cp2y; //minimum bounding box for close
+ long firstblockopen; //first open impeded blocks (for searchmap)
+ short countblockopen; //count of open impeded blocks
+ short countblockclose; //count of closed impeded blocks
+ long firstblockclose; //first closed impeded blocks (for searchmap)
+ long unknown54; //unused in tob
+ char openres[8]; //opening sound resource reference
+ char closeres[8]; //closing sound resource reference
+ long cursortype; //mouse cursor type (when mouse is over the door polygon)
+ short trapdetect; //difficulty of detecting a trap
+ short trapremoval; //difficulty of removing a trap
+ short trapflags; //
+ short trapdetflags; //
+ short launchx, launchy; //traps are launched from this point
+ char key[8]; //key item resource reference
+ char openscript[8]; //(trap) script, activated at first triggering (opening door)
+ long locked; //
+ long lockremoval; //difficulty of opening the door
+ short locp1x, locp1y; //open location 1
+ short locp2x, locp2y; //open location 2
+ long strref; //check this for missing strings STRREF!!!
+ char regionlink[24];
+ long nameref; //check this for missing strings STRREF!!!
+ char dlgref[8]; //?
+ char unknownc0[8]; //?
+} area_door;
+
+Description:
+------------
+
+Scripts reference the door using the doorname field (Scripting Name).
+The relation between the two structures (wed/area) is established by the doorid field.
+Each door is a 'wall', the wall polygons are stored in the wed part of the door.
+The doors also reference two sets of tiles (also in the wed).
+Each door is a special info point with a separate trigger area (polygon) for the open/closed state.
+The bounding boxes are used when rendering the trigger area polygons (actually, could be auto-generated).
+The impeded blocks are superpositioned on the searchmap, they block passage, vision (unless door is transparent) or
+changing the door's state (unless door is sliding).
+The opening/closing sounds of a door are modifiable, though there is a default one (PST has no default sounds).
+If a door is hidden, then the default sound is different.
+The cursor type applies when the mouse is over the trigger area of the door (both states have the same cursor).
+If a door has a script assigned and the trap is detectable the trap detection difficulty determines the chance of
+success. If the difficulty is exactly 100, then it is impossible to detect (a flaw in the original engine lets >100
+values to be detectable). In the impossible case, a message (strref) will be displayed.
+The trap removal difficulty is similarly handled. If trapflags is nonzero, then the trap is still active.
+If Trapdetflags is nonzero, then the trap is still visible. (GemRB will implement the latter as a counter which
+counts down until 0). The trap (if the script starts a spell) is fired from the launch point.
+The key resource has meaning only if the door is locked. If it is empty, then the door cannot be unlocked by key.
+The door is also openable by brute force, the difficulty for this is stored in lockremoval.
+The actor who operates the door will walk up to the closest of the open locations.
+Regionlink is a reference to a travel region which is blocked by the door, if the door is closed.
+//not tested
+Nameref is the name of the door in case of a dialog. DlgRef is the door's dialog.
+
+The door flags are these:
+
+Value Meaning if set
+----- --------------
+0x00000001 - The door is closed (PST), the door is open (other games). When a door's state is changed,
+ (OpenDoor/CloseDoor actions), its (trap) script is triggered.
+0x00000002 - The door is locked, the lock difficulty must be set to nonzero to have this any effect.
+0x00000004 - The script isn't removed when it is triggered.
+0x00000008 - The trap (script) is detectable (fair to set it for real traps).
+0x00000010 - Broken?
+0x00000020 - Can't close?
+0x00000040 - An info trigger is linked to this door.
+0x00000080 - Secret door
+0x00000100 - Secret door already found (purple outline)
+0x00000200 - The impeded doors are ignored concerning vision (door is transparent)
+0x00000400 - The key object is removed when unlocking the door
+0x00000800 - The impeded blocks are ignored when opening the door (sliding door)
+0x00001000 - ?
+0x00002000 - ?
+0x00004000 - ?
+0x00008000 - ?
diff --git a/gemrb/docs/en/Engine/Effects.txt b/gemrb/docs/en/Engine/Effects.txt
new file mode 100644
index 0000000..c6e139e
--- /dev/null
+++ b/gemrb/docs/en/Engine/Effects.txt
@@ -0,0 +1,116 @@
+A kind of 'specification' for the IE game effects.
+This description uses the IESDP effect structure definition with updates.
+
+V1.0 effect struct (you need to convert it to V2 on the fly)
+typedef struct {
+ short feature; //opcode
+ unsigned char target; //target type
+ unsigned char power; //level
+ parameter par1;
+ parameter par2;
+ unsigned char timing; //timing method
+ unsigned char resist; //resistance type
+ long duration;
+ unsigned char prob2; //usually 100
+ unsigned char prob1; //usually 0
+ char resource[8]; //1. resource
+ long count;
+ long sides;
+ long stype;
+ long sbonus;
+ long unknown2c; //unused in V1.0, but copied over to 2.0
+} feat_block;
+
+//effect body (V2.0 effects)
+//please note that in an .eff file there is an additional header
+//before these (the first 8 bytes are doubled)
+Offset Size (data type) Description
+0x0000 4 (char array) For on disk effects, this is a copy of the Signature field from the header. For embedded EFF V2.0 structures, this is zeroed out.
+0x0004 4 (char array) For on disk effects, this is a copy of the Version field from the header. For embedded EFF V2.0 structures, this is zeroed out.
+0x0008 4 (dword) Effect type
+0x000c 4 (dword) Target type
+0x0010 4 (dword) Power (level)
+0x0014 4 (dword) NP1
+0x0018 4 (dword) NP2
+0x001c 4 (dword) Flags (timing method)
+ 0 - duration
+ 1 - permanent
+ 2 - while equipped (source of effect)
+ 3 - delayed duration (after delay duration)
+ 4 - delayed (after delay it is permanent)
+ 5 - special, delayed, unsaved
+ 6 - special, duration
+ 7 - special, ?
+ 8 - permanent, unsaved
+ 9 - permanent after death
+ 10 - trigger (just expired)
+
+0x0020 4 (dword) Time (duration)
+0x0024 2 (word) Probability 1
+0x0026 2 (word) Probability 2
+0x0028 8 (resref) resource
+0x0030 4 (dword) die sides/max level
+0x0034 4 (dword) dice count/min level
+0x0038 4 (dword) save type (stype)
+0x003c 4 (dword) save bonus (sbonus)
+0x0040 4 (dword) Is Variable? (same as 0x2c in EFF V1.0)
+0x0044 4 (dword) Spell School (used for dispelling)
+0x0048 4*3 (dword) Unknown
+0x0054 4 (dword) Resistance Type (resist)
+0x0058 4 (dword) NP3
+0x005c 4 (dword) NP4
+0x0060 4*2 (dword) unknown
+0x0068 8 (resref) VVC
+0x0070 8 (resref) 3. resource
+0x0078 4*2 (point) Source point
+0x0080 4*2 (point) Target point
+0x0088 4 (dword) Source type of effect (0 - none, 1 - item, 2 - spell)
+0x008c 8 (resref) Source of Effect (used for equipping and dispelling)
+0x0094 4 (dword) resource flags
+0x0098 4 (dword) projectile
+0x009c 4 (dword) item slot
+0x00a0 32 (bytes) variable
+0x00c0 4*2 (dword) unknown
+0x00c8 4 (dword) Secondary Type (used for dispelling)
+0x00cc 4*15 (dword) unknown
+
+
+An effect's lifecycle:
+(apply time)
+1. Check if it affects the target
+- if percentages don't match, drop it
+- if level limits don't match, drop it (some effects don't have this)
+- if resistable, check for resistance (once for an applied block)
+- if saving throw applies, check for it (once for an applied block)
+2. If it is a delayed effect
+- precalculate the time of onset (in game time) (store it in the duration field).
+- put it on the fx list
+3. If it is an instant effect (some opcodes ignore delays, so they are always instant)
+
+(in each update cycle in creatures)
+1. copy the original stats to the modified stats
+2. apply all effects in their original order
+- if the effect isn't in time (delayed or delayed duration, 3 or 4), skip it
+- if a delayed effect reached time: change it to permanent (i think this is a different permanent)
+- if a delayed duration effect reached time: change it to duration
+- if the effect is permanent (1 or 9), apply it (some effects couldn't be permanent, these effects just apply, then go away)
+- if the effect reached end (duration), set a special timing method (10)
+- if an effect reached expiration (10) remove it from the queue
+
+(dispelling/removal)
+There should be a way of:
+1. Dispelling all by power level ( remove all effects <= power level)
+2. Dispelling all by source of effect ( remove all effects == soe)
+3. Dispelling all or first that matches a spell school or secondary type
+- there is a 'dispellable' flag in the effects which normally disables dispelling. But there should be a 'forced dispel' flag.
+4 On death: remove all effects except (9)
+
+(saving)
+Effects that are not saved: 2 -'while equipped'
+
+
+(probability, gemrb specific)
+If the low probability field was set to 100, then the high probability field contains the
+stat which determines the chance of the effect. For example:
+100/136 would determine the chance of the effect based on the caster's detect illusions skill.
+
diff --git a/gemrb/docs/en/Engine/Makefile.am b/gemrb/docs/en/Engine/Makefile.am
new file mode 100644
index 0000000..340c98d
--- /dev/null
+++ b/gemrb/docs/en/Engine/Makefile.am
@@ -0,0 +1,3 @@
+enginedocs_DATA = *.txt
+enginedocsdir = $(docdir)/Engine/
+EXTRA_DIST = *.txt
diff --git a/gemrb/docs/en/Engine/Projectile.txt b/gemrb/docs/en/Engine/Projectile.txt
new file mode 100644
index 0000000..f69c898
--- /dev/null
+++ b/gemrb/docs/en/Engine/Projectile.txt
@@ -0,0 +1,38 @@
+Projectile extension flags for GemRB
+
+These flags reside in the .pro format on offset 0x2c
+
+1 Bounce from walls
+2 Continue on original path as travel projectile after explosion
+4 Freeze as travel projectile after explosion
+8 No travel path (move to destination immediately, thus skipping the travel phase)
+16 Trail bams got the same orientation behaviour as the travel bam
+32 Curved path
+64 Random starting frame for travel projectile
+128 Pillar type projectiles (instead of orientations, the cycles are drawn vertically on top of each other)
+256 Half transparent travel projectile (not blend, but could be combined with it)
+512 Static tinted travel projectile
+1024 Create another projectile with an ID one less than the current one (missile iteration)
+2048 Tile the whole area of effect with the travel bam
+4096 Freefalling trajectory (appears horizontally over the target)
+8192 Incoming trajectory (appears diagonally over the target)
+16384 The area of effect is a line from source to target
+32768 The area of effect is a line (wall) crossing target, ahead of source
+65536 Draw behind (under) the target
+0x20000 Draw pop in/hold/pop out animation sequence
+0x40000 Internal flag for 0x20000 (after pop in phase was done) - could be used to play 2 phases, first shadow, then travel bam
+0x80000 Slowly fade out a freezed projectile (used with flag 0x4)
+0x100000 Display string in setup (string reference is stored on offset 0x30)
+0x200000 Random movement instead of path
+
+These fields are in the .pro format in the Extension structure:
+0x228 - ResRef - spread animation (RESOURCE2 in areapro.2da)
+0x230 - ResRef - secondary animation (RESOURCE3 in areapro.2da)
+0x238 - ResRef - area sound (SOUND2 in areapro.2da)
+0x240 - Dword - flags in areapro.2da
+
+Other areapro.2da fields
+0x21c - ResRef - RESOURCE1
+0x208 - ResRef - SOUND1
+
+See areapro.2da for more information.
diff --git a/gemrb/docs/en/Engine/Triggers.txt b/gemrb/docs/en/Engine/Triggers.txt
new file mode 100644
index 0000000..4722e74
--- /dev/null
+++ b/gemrb/docs/en/Engine/Triggers.txt
@@ -0,0 +1,8 @@
+ Door Container Travel Trap Info Actor
+LastEntered Opened - - Entered/IsOverMe - LastHitter
+LastTrigger Closed - - - Clicked LastSummoner
+LastUnlocked Unlocked - - - - LastTalkedTo
+LastDisarmFailed DisarmFailed DisarmFailed - DisarmFailed - LastTarget
+LastDisarmed Disarmed Disarmed - Disarmed - LastAttacker
+LastOpenFailed OpenFailed OpenFailed -
+ PickPocketFailed
diff --git a/gemrb/docs/en/Engine/Usability.txt b/gemrb/docs/en/Engine/Usability.txt
new file mode 100644
index 0000000..41028c3
--- /dev/null
+++ b/gemrb/docs/en/Engine/Usability.txt
@@ -0,0 +1,5 @@
+Usability hacks in the original IE engine
+
+(0x00040000) Assasin - 15 skill points (skills.2da)
+(0x00080000) Bountyhunter - 20 skill points (skills.2da)
+(0x00100000) Swashbuckler - no backstab multiplier (TODO)
diff --git a/gemrb/docs/en/EngineChanges.txt b/gemrb/docs/en/EngineChanges.txt
new file mode 100644
index 0000000..2a45d94
--- /dev/null
+++ b/gemrb/docs/en/EngineChanges.txt
@@ -0,0 +1,29 @@
+Changes to the original engines
+*******************************
+
+
+Generally the changes were designed to be compatible with the original game files. No additional fields added (so far), only previously unused parts are used.
+
+1. Items entries:
+Item entries in stores, containers, creatures will have additional flags. These flags will extend the usage of HasItem, DestroyItem, TakeItem scripting functions. The first four flags should be familiar.
+
+IE_ITEM_IDENTIFIED = 1
+IE_ITEM_UNSTEALABLE = 2
+IE_ITEM_STOLEN = 4
+IE_ITEM_UNDROPPABLE = 8
+//these are GemRB extensions
+IE_ITEM_ACQUIRED = 0x10
+IE_ITEM_DESTRUCTIBLE = 0x20
+IE_ITEM_EQUIPPED = 0x40
+IE_ITEM_STACKED = 0x80
+
+This will make possible scripts like these:
+HasAnyItem(STOLEN)
+TakeAllStolenItem()
+DropAllEquippedItem()
+HasItem("SWORD",EQUIPPED|IDENTIFIED)
+
+2. Journal entries:
+Journal entries will have an additional Group ID. This group ID will make it possible to remove a group of journal entries along with adding a new one of the same group using solely the dialog structure. This eliminates the problem of residue entries. Also, you don't have to remember the journal entry strref, just assign a unique group (quest ID).
+The quest ID byte will be stored on offset 0x0002 in the transition table entry.
+The former flags field (it was a dword) will be a word. Its highest bit will be the group flag. Unless this flag is set, the behaviour will be the same as before. If you set this flag, then all journal entries set with the same id will be removed before adding the new one. The scripting actions will also accept an additional quest ID parameter.
diff --git a/gemrb/docs/en/GUIScript/ActOnPC.txt b/gemrb/docs/en/GUIScript/ActOnPC.txt
new file mode 100644
index 0000000..5cdefa6
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ActOnPC.txt
@@ -0,0 +1,13 @@
+
+Prototype: ActOnPC(player)
+
+Metaclass Prototype: /
+
+Description: Targets the selected PC for an action (cast spell, attack, ...)
+
+Parameters: player - the pc's party position (1-10)
+
+Return value: /
+
+See also: ClearActions, SetModalState, SpellCast
+
diff --git a/gemrb/docs/en/GUIScript/AddGameTypeHint.txt b/gemrb/docs/en/GUIScript/AddGameTypeHint.txt
new file mode 100644
index 0000000..5c48c72
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/AddGameTypeHint.txt
@@ -0,0 +1,17 @@
+Prototype: GemRB.AddGameTypeHint(type, weight, flags=0)
+
+Description: Asserts that GameType should be TYPE, with confidence WEIGHT.
+ This is used by Autodetect.py scripts when GameType was
+ set to 'auto' in config file.
+
+Parameters: type - GameType (e.g. bg1, bg2, iwd, how, iwd2, pst and possibly others)
+ weight - numeric, confidence that TYPE is correct. Standard games should use
+ values <= 100, (eventual) new games based on the standard ones
+ should use values above 100.
+ flags - numeric, not used now
+
+
+Return value: N/A
+
+MD5: 1c130785e329448b743a7f160b07a312
+
diff --git a/gemrb/docs/en/GUIScript/AdjustScrolling.txt b/gemrb/docs/en/GUIScript/AdjustScrolling.txt
new file mode 100644
index 0000000..76bd4ce
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/AdjustScrolling.txt
@@ -0,0 +1,26 @@
+
+Prototype: AdjustScrolling(WindowIndex, ControlIndex, x, y)
+
+Metaclass Prototype: AdjustScrolling(x, y)
+
+Description: Sets the scrolling offset of a WorldMapControl.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+x,y - scrolling offset values
+
+Return value: N/A
+
+Example:
+ #northeast
+ Button = GemRB.GetControl (Window, 9)
+ GemRB.SetEvent (Window, Button, IE_GUI_BUTTON_ON_PRESS, "MapNE")
+...
+def MapNE():
+ GemRB.AdjustScrolling (Window, WorldMapControl, 10, -10)
+ return
+The above lines set up a button event. When the button is pressed the worldmap will be shifted in the northeastern direction.
+
+See also: CreateWorldMapControl
+
+
+MD5: 500775c70886362ce1ebc9353505027f
diff --git a/gemrb/docs/en/GUIScript/ApplyEffect.txt b/gemrb/docs/en/GUIScript/ApplyEffect.txt
new file mode 100644
index 0000000..6d9dc66
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ApplyEffect.txt
@@ -0,0 +1,31 @@
+
+Prototype: GemRB.ApplyEffect(PartyID, opcode, param1, param2[, resref, resref2, resref3, source])
+
+Description: Creates a basic effect and applies it on the player character
+marked by PartyID.
+This function could be used to add stats that are stored in effect blocks.
+
+Parameters:
+PartyID - the player character's index in the party
+opcode - the effect opcode (for values see effects.ids)
+param1 - parameter 1 for the opcode
+param2 - parameter 2 for the opcode
+resref - optional resource reference to set in effect
+resref2 - (optional) resource reference to set in the effect
+resref3 - (optional) resource reference to set in the effect
+resref4 - (optional) resource reference to set in the effect
+source - (optional) source to set in the effect
+
+Return value: N/A
+
+Example:
+ for i in range(ProfCount-8):
+ StatID = GemRB.GetTableValue(TmpTable, i+8, 0)
+ Value = GemRB.GetVar ("Prof "+str(i) )
+ if Value:
+ GemRB.ApplyEffect (MyChar, "Proficiency", Value, StatID )
+
+The above example sets the weapon proficiencies in a bg2's CharGen9.py script.
+
+See also: SpellCast, SetPlayerStat, GetPlayerStat, CountEffects
+
diff --git a/gemrb/docs/en/GUIScript/ApplySpell.txt b/gemrb/docs/en/GUIScript/ApplySpell.txt
new file mode 100644
index 0000000..7bfc008
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ApplySpell.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.ApplySpell(PartyID, resref)
+
+Description: Applies a spell on the actor marked by PartyID.
+This function can be used to add abilities that are stored as spells (eg. innates)
+
+Parameters:
+PartyID - the player character's index in the party
+resref - spell resource reference
+
+Return value: N/A
+
+See also: SpellCast, ApplyEffect, CountEffects
+
diff --git a/gemrb/docs/en/GUIScript/AttachScrollBar.txt b/gemrb/docs/en/GUIScript/AttachScrollBar.txt
new file mode 100644
index 0000000..ed93c50
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/AttachScrollBar.txt
@@ -0,0 +1,12 @@
+
+Prototype: AttachScrollBar(WindowIndex, ControlIndex, ScrollBarControlIndex)
+
+Description: Attaches a scrollbar to a control. If the control receives mousewheel events, it will be relayed to the ScrollBar. TextArea controls will also be synchronised with the scrollbar. If there is a single ScrollBar on the window, or the ScrollBar was set with SetDefaultScrollBar, this command is not needed.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+ScrollBarControlIndex - the scrollbar's index on the same window
+
+Return value: N/A
+
+See also: ConvertEdit, SetDefaultScrollBar
+
diff --git a/gemrb/docs/en/GUIScript/CanUseItemType.txt b/gemrb/docs/en/GUIScript/CanUseItemType.txt
new file mode 100644
index 0000000..4576a82
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CanUseItemType.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.CanUseItemType(slottype, itemname[, actor, equipped])
+
+Description: Checks the itemtype vs. slottype, and also checks the usability flags vs. Actor's stats (alignment, class, race, kit etc.)
+
+Parameters:
+slottype - the slot to check (See ie_slots.py)
+itemname - the resource reference of the item
+actor - the actor's number in the team (if 0, then actor is not unimportant)
+equipped - whether the item is equipped (if so, don't consider disabled items to be unusable)
+
+Return value: boolean
+
+See also: DropDraggedItem, UseItem
+
diff --git a/gemrb/docs/en/GUIScript/ChangeContainerItem.txt b/gemrb/docs/en/GUIScript/ChangeContainerItem.txt
new file mode 100644
index 0000000..545a505
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ChangeContainerItem.txt
@@ -0,0 +1,18 @@
+
+Prototype: ChangeContainerItem( PartyID, slot, action )
+
+Description: Moves an item from PC's inventory into a container or vice versa. If PartyID is 0 then PC is the first selected PC and container is the current container. If PartyID is not 0 then the container is the pile at the feet of that PC.
+
+Parameters:
+PartyID - the PC's position in the party
+slot - the item's inventory or container slot
+action =
+ 0 - put item of PC into container
+ 1 - get item from container and put it in actor's inventory (backpack)
+
+Return value: None
+
+See also: GetContainer, GetSlotItem
+
+
+MD5: b5226a9f5a886c5552438cf1598c72e1
diff --git a/gemrb/docs/en/GUIScript/ChangeItemFlag.txt b/gemrb/docs/en/GUIScript/ChangeItemFlag.txt
new file mode 100644
index 0000000..39c0c5d
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ChangeItemFlag.txt
@@ -0,0 +1,28 @@
+
+Prototype: GemRB.ChangeItemFlag(PartyID, slot, flags, mode)
+
+Description: Changes the flags of an inventory slot. For example, identifies an item.
+
+Parameters:
+PartyID - the PC's position in the party (1 based)
+slot - inventory slot
+flags - a bitfield, same as the GetSlotItem flags
+IE_INV_ITEM_IDENTIFIED = 0x01 - the item is identified
+IE_INV_ITEM_UNSTEALABLE = 0x02 - the item is unstealable
+IE_INV_ITEM_STOLEN = 0x04 - the item is marked as stolen
+IE_INV_ITEM_UNDROPPABLE = 0x08 - the item is undroppable (dragitem fails)
+IE_INV_ITEM_ACQUIRED = 0x10 - the item was recently acquired
+IE_INV_ITEM_DESTRUCTIBLE = 0x20 - the item is removable
+IE_INV_ITEM_EQUIPPED = 0x40 - the item is equipped
+IE_INV_ITEM_STACKED = 0x80 - the item is a stacked item
+
+mode - binary operation type
+OP_SET = 0 - sets all bits as flag
+OP_AND = 1 - turns all other bits off
+OP_OR = 2 - turns bit on
+OP_XOR = 3 - toggles bit
+OP_NAND = 4 - turns bit off
+
+Return value: Returns false if the item was not found.
+
+See also: GetSlotItem
diff --git a/gemrb/docs/en/GUIScript/ChangeStoreItem.txt b/gemrb/docs/en/GUIScript/ChangeStoreItem.txt
new file mode 100644
index 0000000..67eff27
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ChangeStoreItem.txt
@@ -0,0 +1,23 @@
+
+Prototype: ChangeStoreItem( PartyID, slot, action )
+
+Description: Performs a buy, sell, identify or steal action. Action has the same bit values as IsValidStoreItem.
+
+Parameters:
+PartyID - the PC's position in the party
+slot - the item's inventory or store slot
+action - bitfield
+1 - buy
+2 - sell
+4 - identify
+8 - steal
+Add 0x40 for selection (in case of buy/sell only)
+
+Return value:
+ 0 - failure
+ 2 - success
+
+See also: EnterStore, GetSlotItem, GetStoreItem, IsValidStoreItem
+
+
+MD5: 07c89de8dbd0236f8c206b06abe4b3dc
diff --git a/gemrb/docs/en/GUIScript/CheckFeatCondition.txt b/gemrb/docs/en/GUIScript/CheckFeatCondition.txt
new file mode 100644
index 0000000..835350d
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CheckFeatCondition.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.CheckFeatCondition(partyslot, a_stat, a_value, b_stat, b_value, c_stat, c_value, d_stat, d_value)
+
+Description: Checks if a party character is eligible for a feat, the formula is: (stat[a]>=a or stat[b]>=b) and (stat[c]>=c or stat[d]>=d)
+
+Parameters: partyslot - the characters position in the party
+ a_stat,b_stat, c_stat, d_stat - stat IDs
+ a_value, b_value, c_value, d_value - stat value limits
+
+Return value: bool
+
+See also: GetPlayerStat, SetPlayerStat
+
+
+MD5: b237a66b3abbb49ede75897d3d1ee0da
diff --git a/gemrb/docs/en/GUIScript/CheckVar.txt b/gemrb/docs/en/GUIScript/CheckVar.txt
new file mode 100644
index 0000000..d737eb1
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CheckVar.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.CheckVar(VariableName, Context)
+
+Description: Return (and output on terminal) the value of a Game Variable. It executes the CheckVariable gamescript function in the last actor's context, or, short of that, in the current area's context. If there is no running game, it terminates the script.
+GetGameVar("variable") is effectively the same as CheckVar("variable","GLOBAL").
+
+Parameters: VariableName - must be shorter than 32 bytes
+ Context - must be exactly 6 bytes long
+Special cases for Context: LOCALS, GLOBAL, MYAREA
+
+Return value: numeric
+
+See also: GetGameVar
+
+
+MD5: 872634ab7317e9f381a71f00771f29cd
diff --git a/gemrb/docs/en/GUIScript/ClearActions.txt b/gemrb/docs/en/GUIScript/ClearActions.txt
new file mode 100644
index 0000000..95aea76
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ClearActions.txt
@@ -0,0 +1,9 @@
+
+Prototype: GemRB.ClearActions(Slot)
+
+Description: Stops a player character's movement and any pending action.
+
+Parameters: Slot - actor index in game structure
+
+Return value: N/A
+
diff --git a/gemrb/docs/en/GUIScript/ConvertEdit.txt b/gemrb/docs/en/GUIScript/ConvertEdit.txt
new file mode 100644
index 0000000..cf6b138
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ConvertEdit.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.ConvertEdit(WindowIndex, ControlID[, ScrollBarID])
+
+Description: Converts a TextEdit control into an editable TextArea control. TextAreas have unlimited editbuffer, scrollbar and wordwrap abilities. The optional ScrollBar must already been created.
+
+Parameters:
+WindowIndex - the value returned from LoadWindow
+ControlID - the old control's controlID, usually it will be preserved, but don't rely on it
+ScrollBarID - the optional ScrollBar controlID.
+
+Return value: The new ControlID
+
+See also: CreateTextEdit
diff --git a/gemrb/docs/en/GUIScript/CountEffects.txt b/gemrb/docs/en/GUIScript/CountEffects.txt
new file mode 100644
index 0000000..34a823c
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CountEffects.txt
@@ -0,0 +1,22 @@
+
+Prototype: GemRB.CountEffects(PartyID, opcode, param1, param2[, resref])
+
+Description: Counts the number of effects currently affecting the target.
+If a parameter was set to -1, it will be ignored.
+
+Parameters:
+PartyID - the player character's index in the party
+opcode - the effect opcode (for values see effects.ids)
+param1 - parameter 1 for the opcode
+param2 - parameter 2 for the opcode
+resref - optional resource reference to match the effect
+
+Return value: N/A
+
+Example:
+ res = GemRB.CountEffect (MyChar, "HLA", -1, -1, AbilityName )
+
+The above example returns how many HLA effects were applied on the character.
+
+See also: ApplyEffect
+
diff --git a/gemrb/docs/en/GUIScript/CreateButton.txt b/gemrb/docs/en/GUIScript/CreateButton.txt
new file mode 100644
index 0000000..7ce76ac
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateButton.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.CreateButton(WindowIndex, ControlID, x, y, w, h)
+
+Metaclass Prototype: CreateButton(ControlID, x, y, w, h)
+
+Description: Creates and adds a new Button to a Window.
+
+Parameters:
+WindowIndex - the value returned from LoadWindow
+ControlID - the new control will be available via this controlID
+x,y,w,h - X position, Y position, Width and Height of the control
+
+Return value: N/A
+
+See also: CreateLabel, CreateMapControl, CreateWorldMapControl, CreateTextEdit
+
+MD5: cca3f572a4ebf8b1e9974f83f6e756aa
diff --git a/gemrb/docs/en/GUIScript/CreateCreature.txt b/gemrb/docs/en/GUIScript/CreateCreature.txt
new file mode 100644
index 0000000..1c72b31
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateCreature.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.CreateCreature(PartyID, CreResRef)
+
+Description: Creates Creature in the vicinity of the player character.
+
+Parameters:
+PartyID - the PC's position in the party
+CreResRef - the creature's name (.cre resref)
+
+Return value: N/A
+
+See also: CreateItem
+
diff --git a/gemrb/docs/en/GUIScript/CreateItem.txt b/gemrb/docs/en/GUIScript/CreateItem.txt
new file mode 100644
index 0000000..1e121f9
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateItem.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.CreateItem(PartyID, ItemResRef, [SlotID, Charge0, Charge1, Charge2])
+
+Description: Creates Item in the inventory of the player character.
+
+Parameters:
+PartyID - the PC's position in the party
+ItemResRef - the item's name (.itm resref)
+SlotID - Inventory Slot (-1 means any backpack slot)
+Charge0-2 - The item's stack amount/charges
+
+Return value: N/A
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/CreateLabel.txt b/gemrb/docs/en/GUIScript/CreateLabel.txt
new file mode 100644
index 0000000..118c19c
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateLabel.txt
@@ -0,0 +1,27 @@
+
+Prototype: GemRB.CreateLabel(WindowIndex, ControlID, x, y, w, h, font, text, align)
+
+Metaclass Prototype: CreateLabel(ControlID, x, y, w, h, font, text, align)
+
+Description: Creates and adds a new Label to a Window.
+
+Parameters:
+WindowIndex - the value returned from LoadWindow
+ControlID - the new control will be available via this controlID
+x,y,w,h - X position, Y position, Width and Height of the control
+font - a .bam resref which must be listed in fonts.2da too
+text - initial text of the label (must be string)
+align - label text alignment
+
+Return value: N/A
+
+Example:
+ GemRB.CreateLabel(StartWindow, 0x0fff0000, 0,415,640,30, "EXOFONT", "", 1)
+ Label=GemRB.GetControl(StartWindow, 0x0fff0000)
+ GemRB.SetText(StartWindow, Label,GEMRB_VERSION)
+The above lines add the GemRB version string to the PST main screen.
+
+See also: CreateButton, SetText
+
+
+MD5: aaeaa42947bb1bc3cde0719ad5e08b51
diff --git a/gemrb/docs/en/GUIScript/CreateLabelOnButton.txt b/gemrb/docs/en/GUIScript/CreateLabelOnButton.txt
new file mode 100644
index 0000000..3bad183
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateLabelOnButton.txt
@@ -0,0 +1,18 @@
+
+Prototype: GemRB.CreateLabelOnButton(WindowIndex, ButtonControlID, NewID, font, align)
+
+Metaclass Prototype: CreateLabelOnButton(NewID, font, align)
+
+Description: Creates and adds a new Label to a Window, based on the dimensions of an existing button. If the NewID is the same as the old button ID, the old button will be converted to this Label (the old button will be removed).
+
+Parameters:
+WindowIndex - the value returned from LoadWindow
+ButtonControlID - the button control to be copied/converted
+NewID - the new control will be available via this controlID
+font - a .bam resref which must be listed in fonts.2da too
+align - label text alignment
+
+Return value: N/A
+
+See also: CreateButton, CreateLabel
+
diff --git a/gemrb/docs/en/GUIScript/CreateMapControl.txt b/gemrb/docs/en/GUIScript/CreateMapControl.txt
new file mode 100644
index 0000000..d07339d
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateMapControl.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.CreateMapControl(WindowIndex, ControlID, x, y, w, h, [LabelID, FlagResRef [, Flag2ResRef]]
+
+Metaclass Prototype: CreateMapControl(ControlID, x, y, w, h, [LabelID, FlagResRef [, Flag2ResRef]]
+
+Description: Creates and adds a new Area Map Control to a Window. If WindowIndex and ControlID (not ControlIndex!) point to a valid control, it will replace that control with the MapControl using the original control's dimensions (x,y,w,h are ignored). It is possible to associate a variable with the MapControl, in this case, the associated variable will enable or disable mapnotes (you must supply a LabelID and the resources for the pins).
+
+Parameters:
+WindowIndex - the value returned from LoadWindow
+ControlID - the new control will be available via this controlID
+x,y,w,h - X position, Y position, Width and Height of the control
+LabelID - associated control ID to display mapnotes, it must be a label
+FlagResRef - Resource Reference for the pins, if no Flag2ResRef is given, this should be a .bam resref. If there is a second resref, then both must be .bmp.
+Flag2ResRef - the readonly mapnotes are marked by this .bam (red pin)
+
+
+Return value: N/A
+
+See also: SetVarAssoc, SetMapnote
+
diff --git a/gemrb/docs/en/GUIScript/CreateMovement.txt b/gemrb/docs/en/GUIScript/CreateMovement.txt
new file mode 100644
index 0000000..acd09b8
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateMovement.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.CreateMovement(Area, Entrance[, Direction])
+
+Description: Moves some or all actors of the current area to the destination area.
+
+Parameters: Area - The area resource reference where the player(s) should arrive.
+ Entrance - The area entrance in the Destination area.
+ Direction - The direction flag (from WMP) to use if the entrance doesn't exist.
+
+Return value: N/A
+
+See also: GetDestinationArea
+
+MD5: b19700ed972c915b0910b40a746a87ee
diff --git a/gemrb/docs/en/GUIScript/CreatePlayer.txt b/gemrb/docs/en/GUIScript/CreatePlayer.txt
new file mode 100644
index 0000000..dd8bc30
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreatePlayer.txt
@@ -0,0 +1,25 @@
+
+Prototype: CreatePlayer(CREResRef, Slot [,Import, VersionOverride] )
+
+Description: Adds an actor (PC) to the current game. It works only after a LoadGame() was executed, and should be used before an EnterGame(). It is also used to import a .chr file as a PC. A new character will need additional SetPlayerStat() and FillPlayerInfo() calls to be a working character.
+
+Parameters: CREResRef - The name of the creature to be used as the actor, usually 'charbase'
+ Slot - The player character's position in the party
+ Import - Set it to 1 if you want to import a .chr instead of creating a new character
+ VersionOverride - Force version of new actor.
+
+Return value: the new player's index in the game structure
+
+Examples:
+ MyChar = GemRB.GetVar("Slot")
+ GemRB.CreatePlayer("charbase", MyChar )
+
+The above example will create a new player character in the slot selected by the Slot variable.
+
+ MyChar = GemRB.GetVar("Slot")
+ ImportName = "avenger"
+ GemRB.CreatePlayer(ImportName, MyChar, 1 )
+The above example would import avenger.chr into the slot selected by the Slot Variable. (If it exists in the Characters directory of the game).
+
+See also: LoadGame, EnterGame, QuitGame, FillPlayerInfo, SetPlayerStat
+
diff --git a/gemrb/docs/en/GUIScript/CreateScrollBar.txt b/gemrb/docs/en/GUIScript/CreateScrollBar.txt
new file mode 100644
index 0000000..3e00475
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateScrollBar.txt
@@ -0,0 +1,19 @@
+v
+Prototype: GemRB.CreateScrollBar(WindowIndex, ControlID, x, y, w, h)
+
+Metaclass Prototype: CreateScrollBar(ControlID, x, y, w, h)
+
+Description: Creates and adds a new ScrollBar to a Window.
+
+Parameters:
+WindowIndex - the window control
+ControlID - the id of the new control
+x - position
+y - position
+w - width
+h - height
+
+Return value: ControlIndex
+
+See also: SetScrollBarSprites, SetDefaultScrollBar, AttachScrollBar
+
diff --git a/gemrb/docs/en/GUIScript/CreateTextEdit.txt b/gemrb/docs/en/GUIScript/CreateTextEdit.txt
new file mode 100644
index 0000000..1ba63da
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateTextEdit.txt
@@ -0,0 +1,19 @@
+
+Prototype: GemRB.CreateTextEdit(WindowIndex, ControlID, x, y, w, h, font, text)
+
+Metaclass Prototype: CreateTextEdit(ControlID, x, y, w, h, font, text)
+
+Description: Creates and adds a new TextEdit field to a Window. Used in PST MapNote editor. The maximum length of the edit field is 500 characters.
+
+Parameters:
+WindowIndex - the value returned from LoadWindow
+ControlID - the new control will be available via this controlID
+x,y,w,h - X position, Y position, Width and Height of the control
+font - font BAM ResRef
+text - initial text
+
+Return value: N/A
+
+See also: CreateLabel, CreateMapControl, CreateWorldMapControl, CreateButton
+
+MD5: dd93ef5d51dda6ab1a2b5050bb0c4e3f
diff --git a/gemrb/docs/en/GUIScript/CreateWindow.txt b/gemrb/docs/en/GUIScript/CreateWindow.txt
new file mode 100644
index 0000000..4714d67
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateWindow.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.CreateWindow(WindowID, X, Y, Width, Height, MosResRef)
+
+Description: Creates a new empty window and returns its index.
+
+Parameters:
+WindowID - the window's ID
+X,Y - the window's position
+Width, Height - the window's dimensions
+MosResRef - the background image (.mos resref)
+
+Return value: a window index
+
+See also:
+
+
+MD5: f28c0c3fb8a9e7d0a279d735c5b94d6f
diff --git a/gemrb/docs/en/GUIScript/CreateWorldMapControl.txt b/gemrb/docs/en/GUIScript/CreateWorldMapControl.txt
new file mode 100644
index 0000000..6984b48
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/CreateWorldMapControl.txt
@@ -0,0 +1,25 @@
+
+Prototype: GemRB.CreateWorldMapControl(WindowIndex, ControlID, x, y, w, h, direction[, font])
+
+Metaclass Prototype: CreateWorldMapControl(ControlID, x, y, w, h, direction[, font])
+
+Description: This command will create a special WorldMapControl, which is currently unavailable via .chu files. If WindowIndex and ControlID (not ControlIndex!) point to a valid control, it will replace that control with the WorldMapControl using the original control's dimensions (x,y,w,h are ignored).
+
+Parameters:
+WindowIndex - the value returned from LoadWindow
+ControlID - the new control will be available via this controlID
+x,y,w,h - X position, Y position, Width and Height of the control
+direction
+font - font uused to display names of map locations
+
+Return value: N/A
+
+Example:
+ Window = GemRB.LoadWindow (0)
+ GemRB.CreateWorldMapControl (Window, 4, 0, 62, 640, 418, Travel, "floattxt")
+ WorldMapControl = GemRB.GetControl (Window, 4)
+
+See also: GetDestinationArea, CreateMovement
+
+
+MD5: ec421a1b6a60206700cad9c9776f78e6
diff --git a/gemrb/docs/en/GUIScript/DeleteControl.txt b/gemrb/docs/en/GUIScript/DeleteControl.txt
new file mode 100644
index 0000000..3a56077
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/DeleteControl.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.DeleteControl(WindowIndex, ControlID)
+
+Metaclass Prototype: DeleteControl(ControlID)
+
+Description: Deletes a control from a Window.
+
+Parameters: WindowIndex - the return value of a previous LoadWindow call.
+ ControlID - a control ID, see the .chu file specification
+
+Return value: N/A
+
+See also: GetControl
+
+
+MD5: dfbf18fa1830fccf5eb61cf2b39b5147
diff --git a/gemrb/docs/en/GUIScript/DeleteSaveGame.txt b/gemrb/docs/en/GUIScript/DeleteSaveGame.txt
new file mode 100644
index 0000000..854a700
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/DeleteSaveGame.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.DeleteSaveGame(SlotCount)
+
+Description: Deletes a saved game folder completely.
+
+Parameters: SlotCount - the index of a saved game
+
+Return value: N/A
+
+See also: GetSaveGameCount
+
diff --git a/gemrb/docs/en/GUIScript/DisplayString.txt b/gemrb/docs/en/GUIScript/DisplayString.txt
new file mode 100644
index 0000000..2fe05d0
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/DisplayString.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.DisplayString(strref, color[, PartyID])
+
+Description: Displays a string in the messagewindow using methods supplied by the core engine.
+
+Parameters: strref - the tlk reference
+ color - a hex packed RGB value
+ PartyID - if supplied, then the PC's name will be displayed too
+
+Return value: N/A
+
+See also: SetText
+
+MD5:
diff --git a/gemrb/docs/en/GUIScript/DragItem.txt b/gemrb/docs/en/GUIScript/DragItem.txt
new file mode 100644
index 0000000..1d7a84a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/DragItem.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.DragItem(PartyID, Slot, ResRef, [Count=0])
+
+Description: Start dragging specified item. If Count is given, it will try to split the item. If an item is already dragged, it won't do anything.
+
+Parameters: PartyID - the PC's position in the party
+ Slot - actor index in game structure
+ ResRef - item name (.itm resref)
+ Count - stack size (0 means all)
+
+Return value: N/A
+
+See also: DropDraggedItem, IsDraggingItem
+
diff --git a/gemrb/docs/en/GUIScript/DrawWindows.txt b/gemrb/docs/en/GUIScript/DrawWindows.txt
new file mode 100644
index 0000000..f0c3f96
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/DrawWindows.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.DrawWindows()
+
+Description: Refreshes the User Interface by redrawing invalidated controls.
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: InvalidateWindow, UnhideGUI
+
diff --git a/gemrb/docs/en/GUIScript/DropDraggedItem.txt b/gemrb/docs/en/GUIScript/DropDraggedItem.txt
new file mode 100644
index 0000000..c20e803
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/DropDraggedItem.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.DropDraggedItem(PartyID, Slot)
+
+Description: Stop dragging specified item. Dropping the item in an invalid slot will result in 0. Partial success may happen if the item was dropped into a stack, but not all items were moved. The dragging status will be removed only after a complete success. Not all inventory slots may carry any type of item. The item could be dropped in an unspecified inventory slot, the ground, or an equippable slot fitting for the item.
+
+Parameters: PartyID - the actor's inparty index
+ Slot - the Inventory Slot
+Special values for Slot:
+ -1 any equippable slot fitting for the item
+ -2 ground
+ -3 any inventory slot
+Return value: integer, the action was: 0 (unsuccessful), 1 (partial success) or 2 (complete success)
+
+See also: DragItem, IsDraggingItem, CanUseItemType
+
diff --git a/gemrb/docs/en/GUIScript/EnableButtonBorder.txt b/gemrb/docs/en/GUIScript/EnableButtonBorder.txt
new file mode 100644
index 0000000..8ef57ec
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/EnableButtonBorder.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.EnableButtonBorder(WindowIndex, ControlIndex, BorderIndex, enabled)
+
+Metaclass Prototype: EnableBorder(BorderIndex, enabled)
+
+Description: Enable or disable specified border/frame/overlay of a button control.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+BorderIndex - 0,1 or 2
+enabled - boolean, true enables the border
+
+Return value: N/A
+
+See also: SetButtonBAM, SetButtonFlags, SetButtonBorder
+
diff --git a/gemrb/docs/en/GUIScript/EnableCheatKeys.txt b/gemrb/docs/en/GUIScript/EnableCheatKeys.txt
new file mode 100644
index 0000000..e0713c4
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/EnableCheatKeys.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.EnableCheatKeys(flag)
+
+Description: Turns the debug keys on or off.
+
+Parameters: flag - boolean
+
+Return value: N/A
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/EndCutSceneMode.txt b/gemrb/docs/en/GUIScript/EndCutSceneMode.txt
new file mode 100644
index 0000000..976e2fb
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/EndCutSceneMode.txt
@@ -0,0 +1,11 @@
+
+Prototype: EndCutSceneMode()
+
+Description: Exits the CutScene Mode. It is similar to the gamescript command of the same name. It gives back the cursor, and shows the game GUI windows hidden by the CutSceneMode() gamescript action. (This is mainly a debug command.)
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: HideGUI, UnhideGUI
+
diff --git a/gemrb/docs/en/GUIScript/EnterGame.txt b/gemrb/docs/en/GUIScript/EnterGame.txt
new file mode 100644
index 0000000..7706a41
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/EnterGame.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.EnterGame()
+
+Description: Starts new game and enters it. It destructs all existing windows, and creates a GameControl window as the 0. window (the GameControl object will be its 0. control). You should already load a game using LoadGame(), otherwise the engine may terminate. The Game won't be entered until the execution of the current script ended, but a LoadGame() may precede EnterGame() in the same function. (SetNextScript too).
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: QuitGame, LoadGame, SetNextScript
+
diff --git a/gemrb/docs/en/GUIScript/EnterStore.txt b/gemrb/docs/en/GUIScript/EnterStore.txt
new file mode 100644
index 0000000..789a285
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/EnterStore.txt
@@ -0,0 +1,11 @@
+
+Prototype: EnterStore( StoreResRef )
+
+Description: Loads a store and sets it as current (used) store.
+
+Parameters: StoreResRef - the store's resource name
+
+Return value: None
+
+See also: GetStore, GetStoreCure, GetStoreDrink, LeaveStore, SetPurchasedAmount
+
diff --git a/gemrb/docs/en/GUIScript/EvaluateString.txt b/gemrb/docs/en/GUIScript/EvaluateString.txt
new file mode 100644
index 0000000..6159052
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/EvaluateString.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.EvaluateString(String)
+
+Description: Evaluates an In-Game Script Trigger in the current Area Script Context. It prints the result. (The command is more useful from the Console than using from scripts)
+
+Parameters: String - a gamescript trigger
+
+Return value: N/A (the trigger's return value is printed)
+
+See also: ExecuteString
+
diff --git a/gemrb/docs/en/GUIScript/ExecuteString.txt b/gemrb/docs/en/GUIScript/ExecuteString.txt
new file mode 100644
index 0000000..8b904e0
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ExecuteString.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.ExecuteString(String[,Slot])
+
+Description: Executes an In-Game Script Action in the current Area Script Context. This means that LOCALS will be treated as the current area's variable.
+
+Parameters: String - a gamescript action
+ Slot - a player slot
+Return value: N/A
+
+Example:
+ GemRB.ExecuteString("ActionOverride([PC], Attack(NearestEnemyOf(Myself)) )")
+
+The above example will force a player (most likely Player1) to attack an enemy, issuing the command as it would come from the current area's script. The current gametype must support the scripting action.
+
+ GemRB.ExecuteString("Attack(NearestEnemyOf(Myself))", 2)
+
+The above example will force Player2 to attack an enemy, as the example will run in that actor's script context.
+
+See also: EvaluateString, gamescripts
+
diff --git a/gemrb/docs/en/GUIScript/ExploreArea.txt b/gemrb/docs/en/GUIScript/ExploreArea.txt
new file mode 100644
index 0000000..625b9d6
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ExploreArea.txt
@@ -0,0 +1,13 @@
+
+Prototype: ExploreArea( [value] )
+
+Description: Fills the explored bitmap with the value given. If there was no value given, it will fill with -1 (all bit set).
+
+Parameters: 0 - undo explore
+ -1 - explore
+ all other values give meaningless results
+
+Return value: None
+
+See also: MoveToArea
+
diff --git a/gemrb/docs/en/GUIScript/FillPlayerInfo.txt b/gemrb/docs/en/GUIScript/FillPlayerInfo.txt
new file mode 100644
index 0000000..25b08f8
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/FillPlayerInfo.txt
@@ -0,0 +1,38 @@
+
+Prototype: GemRB.FillPlayerInfo(Slot[, Portrait1, Portrait2])
+
+Description: Fills basic character info, that is not stored in stats. This command will generate an AnimationID for the character based on the avprefix.2da table, the character must have the stats referenced in the avprefix structure already set. It will also set the player's portraits if given. It will set the actor's area/position according to the "PlayMode" variable and the Slot value (using the startpos.2da table). This command must be called once after a character was created and be [...]
+
+Parameters: Slot - the new character's slot
+ Portrait1 - medium (or large) portrait
+ Portrait2 - small portrait
+The avprefix structure:
+avprefix.2da is a gemrb specific table. Its first row contains the base animationID used for the actor. Its optional additional rows contain other table resrefs which refine the animationID by different player stats. The first row of these tables contain the stat which affects the animationID. The other rows assign cummulative values to the animationID.
+For example:
+
+avprefix.2da
+ RESOURCE
+0 0x6000
+1 avprefr
+2 avprefg
+3 avprefc
+avprefr.2da
+ RACE
+TYPE 201
+HUMAN 0
+ELF 1
+HALF_ELF 1
+GNOME 4
+HALFLING 3
+DWARF 2
+HALFORC 5
+
+Based on the avatar's stat (201 == race) the animationID (0x6000) will be increased by the given values. For example an elf's animationID will be 0x6001. The animationID will be further modified by gender and class.
+
+Return value: N/A
+
+Examples:
+ GemRB.FillPlayerInfo(MyChar, PortraitName+"M", PortraitName+"S")
+
+See also: LoadGame, CreatePlayer, SetPlayerStat, EnterGame
+
diff --git a/gemrb/docs/en/GUIScript/FindTableValue.txt b/gemrb/docs/en/GUIScript/FindTableValue.txt
new file mode 100644
index 0000000..dcac5d3
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/FindTableValue.txt
@@ -0,0 +1,19 @@
+
+Prototype: GemRB.FindTableValue(TableIndex, ColumnIndex, Value[, Start])
+
+Metaclass Prototype: FindValue(ColumnIndex, Value[, Start])
+
+Description: Returns the first rowcount of a field value in a 2DA Table. If Start
+is omitted, the search starts from the beginning. This command doesn't work with
+a string value.
+
+Parameters:
+TableIndex - integer, returned by a previous LoadTable command.
+ColumnIndex - integer, the index of the column in which you look for the value.
+Value - integer, The value to find in the table
+Start - integer, The starting row
+
+Return value: numeric, -1 if the value isn't to be found
+
+See also: LoadTable, GetTableValue
+
diff --git a/gemrb/docs/en/GUIScript/GameControlGetTargetMode.txt b/gemrb/docs/en/GUIScript/GameControlGetTargetMode.txt
new file mode 100644
index 0000000..efd82ae
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameControlGetTargetMode.txt
@@ -0,0 +1,9 @@
+
+Prototype: GemRB.GameControlGetTargetMode()
+
+Description: Returns the current target mode. It could be: talk, attack, spellcast and such.
+
+Return value: numeric (see GameControlSetTargetMode)
+
+See also: GameControlSetScreenFlags
+
diff --git a/gemrb/docs/en/GUIScript/GameControlSetLastActor.txt b/gemrb/docs/en/GUIScript/GameControlSetLastActor.txt
new file mode 100644
index 0000000..e9fd4bb
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameControlSetLastActor.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GameControlSetLastActor(partyID)
+
+Description: Sets LastActor in the GameControl object. The LastActor object is the player character which is currently about to be selected by the player. Its feet circle is flickering.
+
+Parameters: partyID - 0 to delete any previous settings, or the partyID.
+
+Return value: None
+
+See also: GameSelectPCSingle
+
diff --git a/gemrb/docs/en/GUIScript/GameControlSetScreenFlags.txt b/gemrb/docs/en/GUIScript/GameControlSetScreenFlags.txt
new file mode 100644
index 0000000..a93a603
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameControlSetScreenFlags.txt
@@ -0,0 +1,18 @@
+
+Prototype: GemRB.GameControlSetScreenFlags(Mode, Operation)
+
+Description: Sets screen flags, like cutscene mode, disable mouse, etc. Don't confuse it with the saved screen flags set by GameSetScreenFlags.
+
+Parameters:
+Mode - bitfield
+ 1 - disable mouse
+ 2 - center on actor (one time)
+ 4 - center on actor (always)
+ 8 - enable gui
+ 16 - lock scroll
+Operation - see bit_operation.txt
+
+Return value: N/A
+
+See also: GameSetScreenFlags, bit_operation
+
diff --git a/gemrb/docs/en/GUIScript/GameControlSetTargetMode.txt b/gemrb/docs/en/GUIScript/GameControlSetTargetMode.txt
new file mode 100644
index 0000000..2f7db92
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameControlSetTargetMode.txt
@@ -0,0 +1,22 @@
+
+Prototype: GemRB.GameControlSetTargetMode(Mode)
+
+Description: Sets action to be made by the player (the cursor will change). It could be: talk, attack, spellcast.
+
+Parameters:
+ Mode - 0 none
+ 1 talk
+ 2 attack (bash)
+ 4 spellcast
+ 8 defend
+ 16 pick lock
+
+ Also the target type could be added:
+TARGET_MODE_ALLY = 0x100
+TARGET_MODE_ENEMY = 0x200
+TARGET_MODE_NEUTRAL = 0x400
+
+Return value: N/A
+
+See also: GameControlSetScreenFlags, GameControlGetTargetMode
+
diff --git a/gemrb/docs/en/GUIScript/GameGetFirstSelectedPC.txt b/gemrb/docs/en/GUIScript/GameGetFirstSelectedPC.txt
new file mode 100644
index 0000000..5c309f2
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameGetFirstSelectedPC.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GameGetFirstSelectedPC()
+
+Description: Returns index of the first selected PC or 0 if none.
+
+Parameters: N/A
+
+Return value: the first selected PC's position in the party (it will look in the original party order, thus the protagonist will be always first!)
+
+See also: GameSelectPC, GameIsPCSelected
+
diff --git a/gemrb/docs/en/GUIScript/GameGetFormation.txt b/gemrb/docs/en/GUIScript/GameGetFormation.txt
new file mode 100644
index 0000000..a51f6a0
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameGetFormation.txt
@@ -0,0 +1,11 @@
+
+Prototype: GameGetFormation()
+
+Description: Returns current party formation. The formations are stored in the GemRB specific formatio.2da table.
+
+Parameters: N/A
+
+Return value: integer
+
+See also: GameSetFormation
+
diff --git a/gemrb/docs/en/GUIScript/GameGetPartyGold.txt b/gemrb/docs/en/GUIScript/GameGetPartyGold.txt
new file mode 100644
index 0000000..f7dc2d9
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameGetPartyGold.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GameGetPartyGold()
+
+Description: Returns current party gold.
+
+Parameters: N/A
+
+Return value: numeric
+
+See also: GetPlayerStat
+
diff --git a/gemrb/docs/en/GUIScript/GameGetReputation.txt b/gemrb/docs/en/GUIScript/GameGetReputation.txt
new file mode 100644
index 0000000..9deb646
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameGetReputation.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GameGetReputation()
+
+Description: Returns current party's reputation.
+
+Parameters: N/A
+
+Return value: numeric
+
+See also: GetPlayerStat, GameSetReputation
+
diff --git a/gemrb/docs/en/GUIScript/GameGetSelectedPCSingle.txt b/gemrb/docs/en/GUIScript/GameGetSelectedPCSingle.txt
new file mode 100644
index 0000000..cb1e0a4
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameGetSelectedPCSingle.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GameGetSelectedPCSingle(flag)
+
+Description: If flag is 0 or omitted, then returns currently active pc in non-walk environment (i.e. in shops, inventory,...). If flag is set to non-zero, then returns the currently speaking PC.
+If there is no such PC, then returns 0.
+
+Parameters: flag - 0/1
+
+Return value: PartyID (1-10)
+
+See also: GameSelectPC, GameSelectPCSingle
+
+MD5: b19700ed972c915b0910b40a746a87ee
diff --git a/gemrb/docs/en/GUIScript/GameIsBeastKnown.txt b/gemrb/docs/en/GUIScript/GameIsBeastKnown.txt
new file mode 100644
index 0000000..a575d62
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameIsBeastKnown.txt
@@ -0,0 +1,11 @@
+
+Prototype: GameIsBeastKnown(index)
+
+Description: Returns whether beast with given index is known to PCs (works only on PST).
+
+Parameters: index - the beast's index as of beast.ini
+
+Return value: boolean, 1 means beast is known.
+
+See also: GetINIBeastsKey
+
diff --git a/gemrb/docs/en/GUIScript/GameIsPCSelected.txt b/gemrb/docs/en/GUIScript/GameIsPCSelected.txt
new file mode 100644
index 0000000..726e787
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameIsPCSelected.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GameIsPCSelected(Slot)
+
+Description: Returns true if the PC is selected.
+
+Parameters: Slot - the PC's position in the party (1 based)
+
+Return value: boolean, 1 if the PC is selected
+
+See also: GameSelectPC, GameGetFirstSelectedPC
+
diff --git a/gemrb/docs/en/GUIScript/GamePause.txt b/gemrb/docs/en/GUIScript/GamePause.txt
new file mode 100644
index 0000000..abb5017
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GamePause.txt
@@ -0,0 +1,12 @@
+
+Prototype: GemRB.GamePause(pause, quiet)
+
+Description: Pauses or unpauses the current game. This affects all ingame events, including: scripts, animations, movement. It doesn't affect the GUI.
+
+Parameters: pause - int, 1 = pause, 0 = continue, 2 = toggle pause
+ quiet - bool, true = no message, false = game paused/unpaused message
+
+Return value: N/A
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/GameSelectPC.txt b/gemrb/docs/en/GUIScript/GameSelectPC.txt
new file mode 100644
index 0000000..023eb44
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameSelectPC.txt
@@ -0,0 +1,21 @@
+
+Prototype: GemRB.GameSelectPC(PartyID, Selected, [Flags = SELECT_NORMAL])
+
+Description: Selects or deselects a PC. Note: some things use a different PC selection mechanism (dialogs and stores are not unified yet).
+
+Parameters: PartyID - the PC's position in the party, 0 means ALL
+ Selected - boolean
+ Flags - bitflags
+bit 1 - if set deselect all other actors
+bit 2 - do not run SelectionHandler (no GUI feedback)
+
+Return value: N/A
+
+Example:
+def SelectAllOnPress ():
+ GemRB.GameSelectPC (0, 1)
+ return
+The above function is associated to the 'select all' button of the GUI screen.
+
+See also: GameIsPCSelected, GameSelectPCSingle, GameGetSelectedPCSingle, GameGetFirstSelectedPC
+
diff --git a/gemrb/docs/en/GUIScript/GameSelectPCSingle.txt b/gemrb/docs/en/GUIScript/GameSelectPCSingle.txt
new file mode 100644
index 0000000..a1d9226
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameSelectPCSingle.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GameSelectPCSingle(PartyID)
+
+Description: Selects one PC in non-walk environment (i.e. in shops, inventory,...).
+
+Parameters:
+PartyID - the PC's position in the party
+
+Return value: N/A
+
+See also: GameSelectPC, GameGetSelectedPCSingle
+
+MD5: 6ff47eca2a1cc470e3b909a8c1f9fa0b
diff --git a/gemrb/docs/en/GUIScript/GameSetExpansion.txt b/gemrb/docs/en/GUIScript/GameSetExpansion.txt
new file mode 100644
index 0000000..3139b4e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameSetExpansion.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.GameSetExpansion(mode)
+
+Description: Sets the expansion mode. Most games were created in a two in one and could start the game as expansion only (or transfer to the expansion).
+This command selects between these two modes.
+
+-- this command is not working --
+
+Parameters: Mode - 0 or 1
+
+Return value: N/A
+
+See also: LoadGame, GameType(variable)
+
diff --git a/gemrb/docs/en/GUIScript/GameSetFormation.txt b/gemrb/docs/en/GUIScript/GameSetFormation.txt
new file mode 100644
index 0000000..db882ad
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameSetFormation.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GameSetFormation(Formation)
+
+Description: Sets party formation.
+
+Parameters: the row index of formatio.2da
+
+Return value: N/A
+
+See also: GameGetFormation
+
diff --git a/gemrb/docs/en/GUIScript/GameSetPartyGold.txt b/gemrb/docs/en/GUIScript/GameSetPartyGold.txt
new file mode 100644
index 0000000..f4f5f21
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameSetPartyGold.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GameSetPartyGold(Gold)
+
+Description: Sets current party gold.
+
+Parameters: the gold amount to be set
+
+Return value: N/A
+
+See also: GameGetPartyGold
+
diff --git a/gemrb/docs/en/GUIScript/GameSetPartySize.txt b/gemrb/docs/en/GUIScript/GameSetPartySize.txt
new file mode 100644
index 0000000..56f0787
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameSetPartySize.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GameSetPartySize(Size)
+
+Description: Sets the maximum number of PCs. This command works only after a LoadGame(). If the party size was set to 0, then it means unlimited size.
+
+Parameters: Size - must be 0-8
+
+Return value: N/A
+
+See also: GetPartySize
+
diff --git a/gemrb/docs/en/GUIScript/GameSetProtagonistMode.txt b/gemrb/docs/en/GUIScript/GameSetProtagonistMode.txt
new file mode 100644
index 0000000..31eb71f
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameSetProtagonistMode.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.GameSetProtagonistMode(Mode)
+
+Description: Sets the mode the game handles the game over event. This action works only after a LoadGame.
+
+Parameters:
+ Mode - 0 no check
+ 1 game over when protagonist dies
+ 2 game over when whole party is dead
+
+Return value: N/A
+
+See also: LoadGame, GameSetPartySize
+
diff --git a/gemrb/docs/en/GUIScript/GameSetReputation.txt b/gemrb/docs/en/GUIScript/GameSetReputation.txt
new file mode 100644
index 0000000..9b3d46a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameSetReputation.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GameSetReputation(Reputation)
+
+Description: Sets current party's reputation.
+
+Parameters: the reputation amount to be set. (It is divided by ten when displayed).
+
+Return value: N/A
+
+See also: GameGetReputation, IncreaseReputation
+
diff --git a/gemrb/docs/en/GUIScript/GameSetScreenFlags.txt b/gemrb/docs/en/GUIScript/GameSetScreenFlags.txt
new file mode 100644
index 0000000..d7c1b79
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GameSetScreenFlags.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.GameSetScreenFlags(Bits, Operation)
+
+Description: Stores the portrait/options/message window size value in the game object.
+
+Parameters:
+ Bits - This depends on the game.
+ The lowest 2 bits are the message window size
+ Operation - The usual bit operations (see bit_operation.txt)
+
+Return value: N/A
+
+See also: SetVisible, bit_operation
+
diff --git a/gemrb/docs/en/GUIScript/GetAbilityBonus.txt b/gemrb/docs/en/GUIScript/GetAbilityBonus.txt
new file mode 100644
index 0000000..e3af6db
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetAbilityBonus.txt
@@ -0,0 +1,23 @@
+
+Prototype: GemRB.GetAbilityBonus(stat, column, value[, ex])
+
+Description: Returns ability based values from different .2da files. Like strmod, dexmod, etc.
+
+Parameters: stat - a stat value, like IE_STR
+ column - integer, the column index of the value in the .2da file
+
+Column indices:
+
+IE_STR: 0 - To hit, 1 - Damage, 2 - Open doors, 3 - Weight allowance
+IE_INT: 0 - learn spell, 1 - max spell level, 2 - max spell number on level
+IE_DEX: 0 - reaction adjustment, 1 - missile, 2 - AC
+IE_CON: 0 - normal hp, 1 - warrior hp, 2 - minimum hp roll, 3 - hp regen rate, 4 - fatigue
+IE_CHR: 0 - reaction
+IE_LORE: 0 - lore bonus (int+wis based)
+IE_REPUTATION: 0 - reaction (chr+reputation based)
+IE_WIS: 0 - percentile xp bonus
+
+Return value: -9999 if the parameters are illegal, otherwise the required bonus
+
+See also: SetPlayerStat, GetPlayerStat, GetTableValue
+
diff --git a/gemrb/docs/en/GUIScript/GetCharSounds.txt b/gemrb/docs/en/GUIScript/GetCharSounds.txt
new file mode 100644
index 0000000..6b605b3
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetCharSounds.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.GetCharSounds(WindowIndex, ControlIndex)
+
+Metaclass Prototype: GetCharSounds(ControlIndex)
+
+Description: Reads the contents of the sounds subfolder into a TextArea control.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+
+
+Return value: numeric, the number of rows read into the TextArea control.
+Example:
+ TextAreaControl = GemRB.GetControl(SoundWindow, 45)
+ GemRB.SetTextAreaSelectable(SoundWindow, TextAreaControl,1)
+ GemRB.SetVarAssoc(SoundWindow, TextAreaControl, "Sound", 0)
+ RowCount=GemRB.GetCharSounds(SoundWindow, TextAreaControl)
+
+See also: QueryText, GetCharacters
+
diff --git a/gemrb/docs/en/GUIScript/GetCharacters.txt b/gemrb/docs/en/GUIScript/GetCharacters.txt
new file mode 100644
index 0000000..396f3f0
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetCharacters.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.GetCharacters(WindowIndex, ControlIndex)
+
+Metaclass Prototype: GetCharacters(ControlIndex)
+
+Description: Reads the contents of the characters subfolder into a TextArea control.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+
+
+Return value: numeric, the number of rows read into the TextArea control.
+Example:
+ TextAreaControl = GemRB.GetControl(SoundWindow, 45)
+ GemRB.SetTextAreaSelectable(SoundWindow, TextAreaControl,1)
+ GemRB.SetVarAssoc(SoundWindow, TextAreaControl, "Characters", 0)
+ RowCount=GemRB.GetCharSounds(SoundWindow, TextAreaControl)
+
+See also: QueryText, GetCharSounds
+
diff --git a/gemrb/docs/en/GUIScript/GetCombatDetails.txt b/gemrb/docs/en/GUIScript/GetCombatDetails.txt
new file mode 100644
index 0000000..fa148da
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetCombatDetails.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetCombatDetails(pc, leftorright)
+
+Description: Returns the current THAC0 and other data in relation to the equipped weapon.
+
+Parameters:
+pc - position in the party
+leftorright - left or right hand weapon (main or offhand)
+
+Return value: dict: "ToHit", "Flags", "DamageBonus", "Speed", "CriticalBonus", "Style"
+
+See also: IsDualWielding
+
diff --git a/gemrb/docs/en/GUIScript/GetContainer.txt b/gemrb/docs/en/GUIScript/GetContainer.txt
new file mode 100644
index 0000000..a8a1d33
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetContainer.txt
@@ -0,0 +1,15 @@
+
+Prototype: GetContainer( PartyID, autoselect )
+
+Description: Gets the current container's type and other basic header information. The player is always the first selected player. If PartyID is 0 then the default PC is the first multiselected PC. Autoselect will always select a groundpile. If there is no container at the feet of the PC autoselect will create the container.
+
+Parameters:
+ PartyID - the PC's position in the party
+ autoselect - is 1 if you call this function from a player inventory (so you select the pile at their feet)
+
+Return value: dictionary
+"Type" - the container's type, numeric (see IESDP)
+"ItemCount" - the number of items in the container
+
+See also: GetStore, GameGetFirstSelectedPC, GetContainerItem
+
diff --git a/gemrb/docs/en/GUIScript/GetContainerItem.txt b/gemrb/docs/en/GUIScript/GetContainerItem.txt
new file mode 100644
index 0000000..ee6d646
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetContainerItem.txt
@@ -0,0 +1,19 @@
+
+Prototype: GetContainerItem( PartyID, index )
+
+Description: Returns the container item referenced by the index. If PartyID is 0 then the container was opened manually and should be the current container. If PartyID is not 0 then the container is autoselected and should be at the feet of the player.
+
+Parameters:
+ PartyID - the PC's position in the party
+ index - the item's index in the container
+
+Return value: dictionary
+"ItemResRef" - the ResRef of the item
+"ItemName" - the StrRef of the item's name (identified or not)
+"Usages0" - The primary charges of the item (or the item's stack amount if the item is stackable).
+"Usages1" - The secondary charges of the item.
+"Usages2" - The tertiary charges of the item.
+"Flags" - Item flags.
+
+See also: GetContainer, GameGetFirstSelectedPC, GetStoreItem
+
diff --git a/gemrb/docs/en/GUIScript/GetControl.txt b/gemrb/docs/en/GUIScript/GetControl.txt
new file mode 100644
index 0000000..3852268
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetControl.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.GetControl(WindowIndex, ControlID)
+
+Description: Returns a control in a Window.
+
+Parameters: WindowIndex - the return value of a previous LoadWindow call.
+ ControlID - a control ID, see the .chu file specification
+
+Return value: a control index
+
+See also: LoadWindowPack, LoadWindow
+
+
+MD5: eccbc1af698fd8d4e5eec5004433e515
diff --git a/gemrb/docs/en/GUIScript/GetControlObject.txt b/gemrb/docs/en/GUIScript/GetControlObject.txt
new file mode 100644
index 0000000..610e4e0
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetControlObject.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetControlObject(WindowID, ControlID)
+
+Description: Returns a control as an object.
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+
+Return value: GControl
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/GetCurrentArea.txt b/gemrb/docs/en/GUIScript/GetCurrentArea.txt
new file mode 100644
index 0000000..dc77868
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetCurrentArea.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GetCurrentArea()
+
+Description: Returns the name of the current area. It is the same as GetGameString(1). It works only after a LoadGame() was issued.
+
+Parameters: N/A
+
+Return value: string, (ARE resref)
+
+See also: GetGameString
+
diff --git a/gemrb/docs/en/GUIScript/GetDestinationArea.txt b/gemrb/docs/en/GUIScript/GetDestinationArea.txt
new file mode 100644
index 0000000..2dc9e84
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetDestinationArea.txt
@@ -0,0 +1,21 @@
+
+Prototype: GemRB.GetDestinationArea(WindowIndex, ControlIndex[, RndEncounter])
+
+Metaclass Prototype: GetDestinationArea([RndEncounter])
+
+Description: Returns a dictionary of the selected area by the worldmap control.
+If the route is blocked, then Distance will return a negative value and Destination/Entrance won't be set. Random encounters could be optionally evaluated.
+
+Parameters:
+ WindowIndex, ControlIndex - designate a worldmap control
+ RndEncounter - 0/1
+
+Return value: Dictionary
+Target - The target area selected by the player
+Distance - The traveling distance, if it is negative, the way is blocked
+Destination - The area resource reference where the player arrives (if there was a random encounter, it differs from Target)
+Entrance - The area entrance in the Destination area, it could be empty, in this casethe player should appear in middle of the area
+
+See also: CreateWorldMapControl, CreateMovement, accessing_gui_controls
+
+MD5: b19700ed972c915b0910b40a746a87ee
diff --git a/gemrb/docs/en/GUIScript/GetEquippedAmmunition.txt b/gemrb/docs/en/GUIScript/GetEquippedAmmunition.txt
new file mode 100644
index 0000000..79687ff
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetEquippedAmmunition.txt
@@ -0,0 +1,11 @@
+Prototype: GemRB.GetEquippedAmmunition(PartyID)
+
+Description: Returns the equipped ammunition, if any
+
+Parameters: PartyID - the PC's position in the party (1 based)
+
+Return value: If ammunition is equipped, the inventory slot, otherwise -1.
+
+See also: GetEquippedQuickSlot
+
+MD5: f8136021ae5f439ccbd5fa3de99574a1
diff --git a/gemrb/docs/en/GUIScript/GetEquippedQuickSlot.txt b/gemrb/docs/en/GUIScript/GetEquippedQuickSlot.txt
new file mode 100644
index 0000000..5562f13
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetEquippedQuickSlot.txt
@@ -0,0 +1,14 @@
+Prototype: GemRB.GetEquippedQuickSlot(PartyID[, NoTrans])
+
+Description: Returns the quickweapon slot index or the inventory slot.
+
+Parameters:
+ PartyID - the PC's position in the party (1 based)
+ NoTrans - 0 - return the inventory slot
+ 1 - return the quickweapon slot index
+
+Return value: numeric
+
+See also: SetEquippedQuickSlot, GetEquippedAmmunition
+
+MD5: 587dea2203900410b41c8775a9cc4d18
diff --git a/gemrb/docs/en/GUIScript/GetGameString.txt b/gemrb/docs/en/GUIScript/GetGameString.txt
new file mode 100644
index 0000000..dd1eb40
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetGameString.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetGameString(Index)
+
+Description: Returns a system variable of string type referenced by Index.
+
+Parameters: Index
+0 - returns the loading picture's name (MOS resref)
+1 - returns the current area's name (ARE resref)
+
+Return value: string - the referenced system variable
+
+See also: GetSystemVariable, GetToken
+
diff --git a/gemrb/docs/en/GUIScript/GetGameTime.txt b/gemrb/docs/en/GUIScript/GetGameTime.txt
new file mode 100644
index 0000000..4ca892f
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetGameTime.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GetGameTime()
+
+Description: Returns current game time (rounds passed since start).
+
+Parameters: N/A
+
+Return value: numeric
+
+See also: GameGetPartyGold, GetPartySize
+
diff --git a/gemrb/docs/en/GUIScript/GetGameVar.txt b/gemrb/docs/en/GUIScript/GetGameVar.txt
new file mode 100644
index 0000000..b706604
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetGameVar.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetGameVar(VariableName)
+
+Description: Get a Variable value from the Game Global Dictionary. This is what gamescripts know as GLOBAL variable.
+
+Parameters:
+
+Return value:
+
+Example: Chapter = GemRB.GetGameVar("chapter")
+
+See also: GetVar, GetToken, CheckVar
+
diff --git a/gemrb/docs/en/GUIScript/GetINIBeastsKey.txt b/gemrb/docs/en/GUIScript/GetINIBeastsKey.txt
new file mode 100644
index 0000000..24785fc
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetINIBeastsKey.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetINIBeastsKey(Tag, Key, Default)
+
+Description: Returns a Value from the beast.ini File (works only on PST).
+
+Parameters:
+Tag - a section in the beast.ini file
+Key - a field in the section
+Default - default value in case the entry doesn't exist
+
+Return value: string, the entry from the ini file
+
+See also: GetINIQuestsKey
diff --git a/gemrb/docs/en/GUIScript/GetINIPartyCount.txt b/gemrb/docs/en/GUIScript/GetINIPartyCount.txt
new file mode 100644
index 0000000..92ee07f
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetINIPartyCount.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GetINIPartyCount()
+
+Description: Returns the Number of Parties defined in Party.ini (works only on IWD2).
+
+Parameters: N/A
+
+Return value: the number of predefined parties as of party.ini
+
+See also: GetINIPartyKey
+
diff --git a/gemrb/docs/en/GUIScript/GetINIPartyKey.txt b/gemrb/docs/en/GUIScript/GetINIPartyKey.txt
new file mode 100644
index 0000000..eb822ec
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetINIPartyKey.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetINIPartyKey(Tag, Key, Default)
+
+Description: Returns a Value from the party.ini File (works only on IWD2).
+
+Parameters:
+Tag - a section in the party.ini file
+Key - a field in the section
+Default - default value in case the entry doesn't exist
+
+Return value: string, the entry from the ini file
+
+See also: GetINIPartyCount
diff --git a/gemrb/docs/en/GUIScript/GetINIQuestsKey.txt b/gemrb/docs/en/GUIScript/GetINIQuestsKey.txt
new file mode 100644
index 0000000..121eda7
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetINIQuestsKey.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.GetINIQuestsKey(Tag, Key, Default)
+
+Description: Returns a Value from the quests.ini File (works only on PST).
+
+Parameters:
+Tag - a section in the quests.ini file
+Key - a field in the section
+Default - default value in case the entry doesn't exist
+
+Return value: string, the entry from the ini file
+
+See also: GetINIBeastsKey
+
diff --git a/gemrb/docs/en/GUIScript/GetItem.txt b/gemrb/docs/en/GUIScript/GetItem.txt
new file mode 100644
index 0000000..3b157d1
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetItem.txt
@@ -0,0 +1,33 @@
+
+Prototype: GemRB.GetItem(ResRef)
+
+Description: Returns dictionary with the specified item's data.
+
+Parameters: ResRef - the resource reference of the item
+
+Return value: dictionary
+The fields of the dictionary are the following:
+"ItemName" - strref of unidentified name.
+"ItemNameIdentified" - strref of identified name.
+"ItemDesc" - strref of unidentified description.
+"ItemDescIdentified" - strref of identified description.
+"ItemIcon" - the item's icon (.bam resref)
+"StackAmount" - maximum stackable amount
+"Dialog" - item dialog (.dlg resref)
+"DialogName" - the item dialog name
+"Function" - returns special function
+ 0 - no special function
+ 1 - item is a copyable scroll (2. header's 1. feature is 'Learn spell')
+ 2 - item is a drinkable potion
+"Spell" - the spell's strref if the item is a copyable scroll
+"DescIcon" - the description icon
+"BrokenItem" - the replacement item (used for items with broken sounds)
+"Price" - the base item price
+"Type" - the item type (see itemtype.2da)
+"AnimationType" - the item animation ID
+"Exclusion" - the exclusion bit (used by eg. magic armor and rings of protection).
+"LoreToID" - the required lore to identify the item
+"Tooltips" - the item tooltips
+
+See also: GetSlotItem, GetSpell, SetItemIcon
+
diff --git a/gemrb/docs/en/GUIScript/GetJournalEntry.txt b/gemrb/docs/en/GUIScript/GetJournalEntry.txt
new file mode 100644
index 0000000..6723293
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetJournalEntry.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.GetJournalEntry(chapter, index[, section])
+
+Description: Returns dictionary representing journal entry w/ given chapter, section and index. Section will default to zero.
+
+Parameters: chapter - the chapter of the journal entry
+ index - the index of the entry in the given section/chapter
+ section - the section of the journal
+
+Return value: dictionary with the following fields:
+"Text" - strref of the journal entry
+"GameTime" - time of entry
+"Section" - same as the input parameter
+"Chapter" - same as the input parameter
+
+See also: GetJournalSize, SetJournalEntry
+
diff --git a/gemrb/docs/en/GUIScript/GetJournalSize.txt b/gemrb/docs/en/GUIScript/GetJournalSize.txt
new file mode 100644
index 0000000..2b1dc20
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetJournalSize.txt
@@ -0,0 +1,12 @@
+
+Prototype: GemRB.GetJournalSize(chapter[, section])
+
+Description: Returns the number of entries in the given section of journal. Please note that various engines implemented the chapter/sections at various degree. For example PST has none of these. Section will default to zero.
+
+Parameters: chapter - the chapter of the journal page
+ section - 0,1,2 or 4 - general, quest, solved quest or user notes.
+
+Return value: numeric
+
+See also: GetJournalEntry, SetJournalEntry
+
diff --git a/gemrb/docs/en/GUIScript/GetKnownSpell.txt b/gemrb/docs/en/GUIScript/GetKnownSpell.txt
new file mode 100644
index 0000000..82307ac
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetKnownSpell.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.GetKnownSpell(PartyID, SpellType, Level, Index)
+
+Description: Returns dictionary with specified known spell from PC's spellbook.
+
+Parameters:
+PartyID - the PC's position in the party
+SpellType - 0 - priest, 1 - wizard, 2 - innate
+Level - the memorized spell's level
+Index - the memorized spell's index
+
+Return value: dictionary
+The field(s) of the dictionary are the following:
+"SpellResRef" - The name of the spell (.spl resref)
+
+See also: GetMemorizedSpell
+
diff --git a/gemrb/docs/en/GUIScript/GetKnownSpellsCount.txt b/gemrb/docs/en/GUIScript/GetKnownSpellsCount.txt
new file mode 100644
index 0000000..28e293d
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetKnownSpellsCount.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.GetKnownSpellsCount(PartyID, SpellType, Level)
+
+Description: Returns number of known spells of given type and level in a player character's spellbook.
+
+Parameters:
+PartyID - the PC's position in the party
+SpellType - 0 - priest, 1 - wizard, 2 - innate
+Level - the memorized spell's level
+
+Return value: numeric
+
+See also: GetMemorizedSpellsCount, GetKnownSpell
+
diff --git a/gemrb/docs/en/GUIScript/GetMemorizableSpellsCount.txt b/gemrb/docs/en/GUIScript/GetMemorizableSpellsCount.txt
new file mode 100644
index 0000000..d8de78a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetMemorizableSpellsCount.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.GetMemorizableSpellsCount(PartyID, SpellType, Level [,Bonus])
+
+Description: Returns number of memorizable spells of given type and level in a player character's spellbook.
+
+Parameters:
+PartyID - the PC's position in the party
+SpellType - 0 - priest, 1 - wizard, 2 - innate
+Level - the memorized spell's level
+Bonus - whether querying the modified or the base value
+
+Return value: numeric, it returns -1 if the query is invalid (no spellcaster, bad spelltype, too high level).
+
+See also: SetMemorizableSpellsCount
+
diff --git a/gemrb/docs/en/GUIScript/GetMemorizedSpell.txt b/gemrb/docs/en/GUIScript/GetMemorizedSpell.txt
new file mode 100644
index 0000000..fb09652
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetMemorizedSpell.txt
@@ -0,0 +1,18 @@
+
+Prototype: GemRB.GetMemorizedSpell(PartyID, SpellType, Level, Index)
+
+Description: Returns dict with specified memorized spell from PC's spellbook.
+
+Parameters:
+PartyID - the PC's position in the party
+SpellType - 0 - priest, 1 - wizard, 2 - innate
+Level - the memorized spell's level
+Index - the memorized spell's index
+
+Return value: dictionary
+The fields of the dictionary are the following:
+"SpellResRef" - The name of the spell (.spl resref)
+"Flags" - Is the spell castable, or already spent
+
+See also: GetMemorizedSpellsCount
+
diff --git a/gemrb/docs/en/GUIScript/GetMemorizedSpellsCount.txt b/gemrb/docs/en/GUIScript/GetMemorizedSpellsCount.txt
new file mode 100644
index 0000000..53beb6d
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetMemorizedSpellsCount.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.GetMemorizedSpellsCount(PartyID, SpellType, Level)
+
+Description: Returns number of spells of given type and level in selected character's memory.
+
+Parameters:
+PartyID - the PC's position in the party
+SpellType - 0 - priest, 1 - wizard, 2 - innate
+Level - the memorized spell's level
+
+Return value: numeric
+
+See also: GetMemorizedSpell, GetKnownSpellsCount
+
diff --git a/gemrb/docs/en/GUIScript/GetMessageWindowSize.txt b/gemrb/docs/en/GUIScript/GetMessageWindowSize.txt
new file mode 100644
index 0000000..74e2f5e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetMessageWindowSize.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GetMessageWindowSize()
+
+Description: Returns current MessageWindowSize, it works only when a game is loaded.
+
+Parameters: N/A
+
+Return value: int
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/GetPCStats.txt b/gemrb/docs/en/GUIScript/GetPCStats.txt
new file mode 100644
index 0000000..d560c48
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetPCStats.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.GetPCStats(PartyID)
+
+Description: Returns dictionary of PC's performance stats.
+
+Parameters: PartyID - the PC's position in the party (1 based)
+
+Return value: A Python dictionary containing the following items
+"BestKilledName" - strref of killed creature with biggest XP
+"BestKilledXP" - XP value of this creature
+"JoinDate" - date joined the team
+"KillsChapterXP" - total XP from kills gathered in this chapter
+"KillsChapterCount"- total number of kills in this chapter
+"KillsTotalXP" - total XP from kills
+"KillsTotalCount" - total number of kills
+"FavouriteSpell" - spell used the most of the time
+"FavouriteWeapon" - weapon bringing the most kill XP
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/GetPartySize.txt b/gemrb/docs/en/GUIScript/GetPartySize.txt
new file mode 100644
index 0000000..61b1239
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetPartySize.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GetPartySize()
+
+Description: Returns the actual number of PCs (dead included). This command works only after a LoadGame().
+
+Parameters: N/A
+
+Return value: numeric (should be 0-8)
+
+See also: LoadGame, QuitGame, GameSetPartySize
+
diff --git a/gemrb/docs/en/GUIScript/GetPlayerName.txt b/gemrb/docs/en/GUIScript/GetPlayerName.txt
new file mode 100644
index 0000000..705c352
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetPlayerName.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.GetPlayerName(PartyID[, LongOrShort])
+
+Description: Queries the player character's name.
+
+Parameters:
+PartyID - the PC's position in the party (1 based)
+LongOrShort - which name to query
+-1 : default name
+ 0 : shortname
+ 1 : longname
+ 2 : scripting name
+
+Return value: string, it returns ??? if the PC doesn't exist.
+
+See also: SetPlayerName, GetPlayerStat
+
diff --git a/gemrb/docs/en/GUIScript/GetPlayerPortrait.txt b/gemrb/docs/en/GUIScript/GetPlayerPortrait.txt
new file mode 100644
index 0000000..9d93233
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetPlayerPortrait.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetPlayerPortrait(Slot[, SmallOrLarge])
+
+Description: Queries the player's portrait. To set the portrait of a new character you must use FillPlayerInfo().
+
+Parameters:
+Slot - the PC's position in the party
+SmallOrLarge - boolean, specify 1 if you want to get the large portrait
+
+Return value: the player's portrait name (.bmp resref)
+
+See also: FillPlayerInfo
+
diff --git a/gemrb/docs/en/GUIScript/GetPlayerScript.txt b/gemrb/docs/en/GUIScript/GetPlayerScript.txt
new file mode 100644
index 0000000..1418d75
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetPlayerScript.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetPlayerScript(Slot[, Index])
+
+Description: Queries the player's script. You can also set the scripts.
+
+Parameters:
+Slot - the PC's position in the party
+Index - script index (see scrlevel.2da)
+
+Return value: the player's script (.bcs or .baf resref)
+
+See also: SetPlayerScript
+
diff --git a/gemrb/docs/en/GUIScript/GetPlayerStat.txt b/gemrb/docs/en/GUIScript/GetPlayerStat.txt
new file mode 100644
index 0000000..b08d172
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetPlayerStat.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetPlayerStat(Slot, ID[, Base])
+
+Description: Queries a stat of the player character. The stats are listed in ie_stats.py.
+
+Parameters: Slot - actor index in game structure
+ ID - Stat index
+ Base - if set to 1, the function will return the base instead of the modified (current) value
+
+Return value: numeric
+
+See also: SetPlayerStat, GetPlayerName, GetPlayerStates
+
diff --git a/gemrb/docs/en/GUIScript/GetPlayerStates.txt b/gemrb/docs/en/GUIScript/GetPlayerStates.txt
new file mode 100644
index 0000000..9d543d1
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetPlayerStates.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GetPlayerStates(PartyID)
+
+Description: Returns the active spell states on the player. The state descriptions are in the statdesc.2da file which comes with the original games. The values in the character array equal to the corresponding cycle number in states.bam. To reference statdesc.2da, subtract 65 from the values.
+
+Parameters:
+ PartyID - the character's position in the party
+
+Return value: a string whose letters are greater or equal ascii 65.
+
+See also: GetPlayerName, GetPlayerStat
diff --git a/gemrb/docs/en/GUIScript/GetPlayerString.txt b/gemrb/docs/en/GUIScript/GetPlayerString.txt
new file mode 100644
index 0000000..3be37b9
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetPlayerString.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.GetPlayerString(PartyID, StringIndex)
+
+Description: Returns the string reference of a Verbal Constant set in the player.
+The biography string is an example of such a string.
+
+Parameters:
+ PartyID - the character's position in the party
+ StringIndex - the verbal constant's index
+
+Return value: a string reference.
+
+See also: GetPlayerName, GetPlayerStat, GetPlayerScript
+
+See also: sndslot.ids, soundoff.ids (it is a bit unclear which one is it)
diff --git a/gemrb/docs/en/GUIScript/GetPortraits.txt b/gemrb/docs/en/GUIScript/GetPortraits.txt
new file mode 100644
index 0000000..a8edee3
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetPortraits.txt
@@ -0,0 +1,21 @@
+
+Prototype: GemRB.GetPortraits(WindowIndex, ControlIndex, SmallOrLarge)
+
+Metaclass Prototype: GetPortraits(SmallOrLarge)
+
+Description: Reads the contents of the portraits subfolder into a TextArea control.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+SmallOrLarge - 0 means the portraits with 'M' as the suffix, anything else 'S'
+
+
+Return value: numeric, the number of rows read into the TextArea control.
+Example:
+ TextAreaControl = GemRB.GetControl(SoundWindow, 45)
+ GemRB.SetTextAreaSelectable(SoundWindow, TextAreaControl,1)
+ GemRB.SetVarAssoc(SoundWindow, TextAreaControl, "Sound", 0)
+ RowCount=GemRB.GetPortraits(SoundWindow, TextAreaControl, 0)
+
+See also: QueryText, GetCharacters, GetCharSounds
+
diff --git a/gemrb/docs/en/GUIScript/GetRumour.txt b/gemrb/docs/en/GUIScript/GetRumour.txt
new file mode 100644
index 0000000..3144f34
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetRumour.txt
@@ -0,0 +1,12 @@
+
+Prototype: GetRumour( percent, DialogResRef )
+
+Description: Gets a rumour string reference from a rumour dialog.
+
+Parameters: percent - chance of not returning -1
+DialogResRef - a rumour dialog resource
+
+Return value: a string reference
+
+See also: EnterStore, GetStoreDrink, GetStore
+
diff --git a/gemrb/docs/en/GUIScript/GetSaveGameAttrib.txt b/gemrb/docs/en/GUIScript/GetSaveGameAttrib.txt
new file mode 100644
index 0000000..b9a78c9
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetSaveGameAttrib.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.GetSaveGameAttrib(Type, SlotCount)
+
+Description: Returns the name, path, prefix and elapsed game time of the
+passed saved game.
+
+Parameters:
+Type - the queried attribute's name
+ 0 - name
+ 1 - prefix
+ 2 - path
+ 3 - date
+ 4 - elapsed game time
+ 5 - id
+SlotCount - the index of a saved game
+
+Return value: string
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/GetSaveGameCount.txt b/gemrb/docs/en/GUIScript/GetSaveGameCount.txt
new file mode 100644
index 0000000..5c6dc96
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetSaveGameCount.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GetSaveGameCount()
+
+Description: Returns the number of saved games.
+
+Parameters: N/A
+
+Return value: numeric
+
+See also: DeleteSaveGame, GetSaveGameAttrib, SetSaveGamePortrait, SetSaveGamePreview, SaveGame
+
diff --git a/gemrb/docs/en/GUIScript/GetSelectedSize.txt b/gemrb/docs/en/GUIScript/GetSelectedSize.txt
new file mode 100644
index 0000000..36f36de
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetSelectedSize.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.GetSelectedSize()
+
+Description: Returns the number of actors selected in the party.
+
+Parameters:
+
+Return value: int
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/GetSlotItem.txt b/gemrb/docs/en/GUIScript/GetSlotItem.txt
new file mode 100644
index 0000000..033214f
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetSlotItem.txt
@@ -0,0 +1,29 @@
+
+Prototype: GemRB.GetSlotItem(PartyID, slot)
+
+Description: Returns dictionary with the specified actor's inventory slot data.
+
+Parameters:
+PartyID - the PC's position in the party
+slot - the item's inventory slot
+
+Return value: dictionary
+The fields of the dictionary are the following:
+"ItemResRef" - The name of the item (.itm resref)
+"Usages0" - The primary charges of the item (or the item's stack amount if the item is stackable).
+"Usages1" - The secondary charges of the item.
+"Usages2" - The tertiary charges of the item.
+"Flags" - Item flags:
+IE_INV_ITEM_IDENTIFIED = 1, The item is identified.
+IE_INV_ITEM_UNSTEALABLE = 2, The item is unstealable.
+IE_INV_ITEM_STOLEN = 4, The item is stolen.
+IE_INV_ITEM_UNDROPPABLE =8, The item is undroppable.
+IE_INV_ITEM_ACQUIRED = 0x10, The item was recently moved.
+IE_INV_ITEM_DESTRUCTIBLE = 0x20, The item is removable (sellable or destructible).
+IE_INV_ITEM_EQUIPPED = 0x40, The item is currently equipped.
+IE_INV_ITEM_STACKED = 0x80, The item is a stacked item.
+"Header" - Item's extended header assigned to the inventory slot (the
+ ability to use). Only applicable to quickslots.
+
+See also: GetItem, SetItemIcon, ChangeItemFlag
+
diff --git a/gemrb/docs/en/GUIScript/GetSlotType.txt b/gemrb/docs/en/GUIScript/GetSlotType.txt
new file mode 100644
index 0000000..3062d33
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetSlotType.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.GetSlotType(idx)
+
+Description: Returns dictionary of an itemslot type (slottype.2da).
+
+Parameters: idx - a row number of slottype.2da
+
+Return value: dictionary
+"Type" - bitfield, The inventory slot's type.
+"ID" - the gui button's controlID which belongs to this slot.
+"Tip" - the tooltip resref for this slot.
+"ResRef" - the background .bam of the slot.
+
+See also: SetItemIcon
+
diff --git a/gemrb/docs/en/GUIScript/GetSlots.txt b/gemrb/docs/en/GUIScript/GetSlots.txt
new file mode 100644
index 0000000..f5a9f08
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetSlots.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.GetSlots(PartyID, SlotType[, Flag])
+
+Description: Returns the tuple of slots of a PC which match the SlotType criteria. FIXME: This function cannot yet handle PST's diverse slottypes.
+
+Parameters: PartyID - a PC
+SlotType - bitfield, the inventory slot's type (32768 means inventory)
+Flag - >0 - returns filled slots
+ - <0 - returns empty slots
+ - 0 - returns all slots.
+The default is 1.
+
+Return value: tuple
+
+See also: GetSlotType
diff --git a/gemrb/docs/en/GUIScript/GetSpell.txt b/gemrb/docs/en/GUIScript/GetSpell.txt
new file mode 100644
index 0000000..3834640
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetSpell.txt
@@ -0,0 +1,21 @@
+
+Prototype: GemRB.GetSpell(ResRef[, silent])
+
+Description: Returns dictionary with the specified spell's data. If silent
+is set, nothing will be printed to the console.
+
+Parameters: ResRef - the resource reference of the spell.
+
+Return value: dictionary
+The fields of the dictionary are the following:
+"SpellName" - strref of unidentified name.
+"SpellDesc" - strref of unidentified description.
+"SpellbookIcon" - the spell's icon (.bam resref)
+"SpellExclusion" - the excluded schools and alignments
+"SpellDivine" - this field tells divine magics apart
+"SpellSchool" - the spell's school (primary type)
+"SpellType" - the type of text that appears on spell dispelling
+"SpellLevel" - the spell's level
+
+See also: GetItem, SetSpellIcon, spell_structure(IESDP)
+
diff --git a/gemrb/docs/en/GUIScript/GetStore.txt b/gemrb/docs/en/GUIScript/GetStore.txt
new file mode 100644
index 0000000..e8435cb
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetStore.txt
@@ -0,0 +1,24 @@
+
+Prototype: GetStore( )
+
+Description: Gets the basic header information of the current store and returns it in a dictionary. The information is precompiled based on room availability, store type
+
+Parameters: N/A
+
+Return value: dictionary
+"StoreType" - numeric (see IESDP)
+"StoreName" - the StrRef of the store name
+"StoreDrinkCount" - the count of drinks served (tavern)
+"StoreCureCount" - the count of cures served (temple)
+"StoreItemCount" - the count of items sold, in case of PST the availability trigger is also checked
+"StoreCapacity" - the capacity of the store
+"StoreRoomPrices" - a four elements tuple, negative if the room type is unavailable
+"StoreButtons" - a four elements tuple, possible actions
+"StoreFlags" - the store flags if you ever need them, StoreButtons is a digested information, but you might have something else in mind based on these
+"TavernRumour" - ResRef of tavern rumour dialog
+"TempleRumour" - ResRef of temple rumour dialog
+
+See also: EnterStore, GetStoreCure, GetStoreDrink, GetRumour
+
+
+MD5: 1899d9bf9e161d3b1a6ec8b3d9fdff19
diff --git a/gemrb/docs/en/GUIScript/GetStoreCure.txt b/gemrb/docs/en/GUIScript/GetStoreCure.txt
new file mode 100644
index 0000000..42a2713
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetStoreCure.txt
@@ -0,0 +1,14 @@
+
+Prototype: GetStoreCure( index )
+
+Description: Gets the spell resref, price and description of a store cure referenced by the index.
+
+Parameters: index - the number of the cure in the store list
+
+Return value: dictionary
+"CureResRef" - the ResRef of the cure spell
+"Description" - the StrRef of the spell's description
+"Price" - the price of the spell (subtract this from the party gold)
+
+See also: EnterStore, GetStoreDrink, GetStore
+
diff --git a/gemrb/docs/en/GUIScript/GetStoreDrink.txt b/gemrb/docs/en/GUIScript/GetStoreDrink.txt
new file mode 100644
index 0000000..339988b
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetStoreDrink.txt
@@ -0,0 +1,14 @@
+
+Prototype: GetStoreDrink( index )
+
+Description: Gets the name, strength and price of a store drink referenced by the index.
+
+Parameters: index - the number of the drink in the store list
+
+Return value: dictionary
+"DrinkName" - the StrRef of the drink name
+"Strength" - the strength if the drink (affects rumour and intoxication)
+"Price" - the price of the drink (subtract this from the party gold)
+
+See also: EnterStore, GetStoreCure, GetStore
+
diff --git a/gemrb/docs/en/GUIScript/GetStoreItem.txt b/gemrb/docs/en/GUIScript/GetStoreItem.txt
new file mode 100644
index 0000000..9dfb076
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetStoreItem.txt
@@ -0,0 +1,20 @@
+
+Prototype: GetStoreItem( index )
+
+Description: Gets the item resref, price and description of a store item referenced by the index. In case of PST stores the item's availability is also checked against the availability trigger.
+
+Parameters: index - the number of the cure in the store list
+
+Return value: dictionary
+"ItemResRef" - the ResRef of the item
+"ItemName" - the StrRef of the item's name (identified or not)
+"ItemDesc" - the StrRef of the item's description (identified or not)
+"Price" - the price of the item (subtract this from the party gold)
+"Amount" - the amount of item in store (-1 means infinite)
+"Usages0" - The primary charges of the item (or the item's stack amount if the item is stackable).
+"Usages1" - The secondary charges of the item.
+"Usages2" - The tertiary charges of the item.
+"Flags" - Item flags.
+
+See also: EnterStore, GetStoreDrink, GetStoreCure, GetStore, GetSlotItem
+
diff --git a/gemrb/docs/en/GUIScript/GetString.txt b/gemrb/docs/en/GUIScript/GetString.txt
new file mode 100644
index 0000000..133ff4c
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetString.txt
@@ -0,0 +1,23 @@
+
+Prototype: GemRB.GetString(Strref, Flags)
+
+Description: Returns string for given strref. Usually, you don't need to resolve a string before use, you can use SetText with an strref parameter. This command lets you alter the string. For example, if you want to add a level value, you'll need this.
+
+Parameters: Strref - a string index from the dialog.tlk table.
+ Flags - a bitfield
+
+Values for the flags:
+1 - strref on
+2 - play attached sound
+4 - speech (stop previous sound)
+256 - strref off (overrides cfg)
+
+Return value: A string with resolved tokens. To resolve %d's, you must either use StatComment, or do it manually.
+
+Example: GemRB.SetText (Window, Label, GemRB.GetString(12137)+str(level+1) )
+The above example will display "Level: <LEVEL>" in the addressed label.
+
+See also: StatComment, SetText
+
+
+MD5: 27500d4f0d9773b4cdfdc6f615d0b668
diff --git a/gemrb/docs/en/GUIScript/GetSymbolValue.txt b/gemrb/docs/en/GUIScript/GetSymbolValue.txt
new file mode 100644
index 0000000..c005956
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetSymbolValue.txt
@@ -0,0 +1,26 @@
+
+Prototype: GemRB.GetSymbolValue(SymbolIndex, StringVal|IntVal)
+
+Metaclass Prototype: GetValue(StringVal|IntVal)
+
+Description: Returns a field of a IDS Symbol Table.
+
+Parameters:
+ SymbolIndex - returned by a previous LoadSymbol command
+ StringVal - the name of the symbol to resolve (first column of .ids file)
+ IntVal - the value of the symbol to find. (second column of .ids file)
+
+Return value:
+ numeric, if the symbol's name was given (the value of the symbol)
+ string, if the value of the symbol was given (the symbol's name)
+
+Example:
+ align = GemRB.GetPlayerStat (pc, IE_ALIGNMENT)
+ ss = GemRB.LoadSymbol ("ALIGN")
+ sym = GemRB.GetSymbolValue (ss, align)
+The above example will find the symbolic name of the player's alignment.
+
+See also: LoadSymbol, GetTableValue
+
+
+MD5: afa5d5072904c5d0b345e2e08f8e2cbe
diff --git a/gemrb/docs/en/GUIScript/GetSystemVariable.txt b/gemrb/docs/en/GUIScript/GetSystemVariable.txt
new file mode 100644
index 0000000..389100a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetSystemVariable.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.GetSystemVariable(Index)
+
+Description: Returns the named Interface attribute.
+
+Parameters: Index could have the following values:
+SV_BPP = 0 - bpp (color resolution)
+SV_WIDTH = 1 - screen width
+SV_HEIGHT = 2 - screen height
+
+Return value: This function returns -1 if the index is invalid.
+
+See also: GetGameString
+
+
+MD5: b60fafad2a078631a0f53b50b323c8b4
diff --git a/gemrb/docs/en/GUIScript/GetTableColumnCount.txt b/gemrb/docs/en/GUIScript/GetTableColumnCount.txt
new file mode 100644
index 0000000..9862e39
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetTableColumnCount.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.GetTableColumnCount(TableIndex[, Row])
+
+Metaclass Prototype: GetColumnCount([Row])
+
+Description: Returns the number of columns in the specified row in a 2DA Table.
+
+Parameters:
+ TableIndex - returned by a previous LoadTable command.
+ Row - the row of the table, if omitted, defaults to 0
+
+Return value: numeric
+
+See also: LoadTable, GetTableRowCount
+
+
+MD5:
diff --git a/gemrb/docs/en/GUIScript/GetTableColumnIndex.txt b/gemrb/docs/en/GUIScript/GetTableColumnIndex.txt
new file mode 100644
index 0000000..31737a3
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetTableColumnIndex.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.GetTableColumnIndex(TableIndex, ColumnName)
+
+Metaclass Prototype: GetColumnIndex(ColumnName)
+
+Description: Returns the Index of a Column referenced by ColumnName in a 2DA Table.
+
+Parameters: TableIndex - returned by a previous LoadTable command.
+ColumnName - a column label
+
+Return value: numeric, -1 if column doesn't exist
+
+See also: LoadTable, GetTableRowIndex
diff --git a/gemrb/docs/en/GUIScript/GetTableColumnName.txt b/gemrb/docs/en/GUIScript/GetTableColumnName.txt
new file mode 100644
index 0000000..a26d8ca
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetTableColumnName.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.GetTableColumnName(TableIndex, ColumnIndex)
+
+Metaclass Prototype: GetColumnName(ColumnIndex)
+
+Description: Returns the Name of a Column in a 2DA Table.
+
+Parameters: TableIndex - returned by a previous LoadTable command.
+ColumnIndex - the numeric index of the column.
+
+Return value: string
+
+See also: LoadTable, GetTableRowName
+
+
+MD5: 1fec0670e5cbec6f1b45ba5214986bbf
diff --git a/gemrb/docs/en/GUIScript/GetTableRowCount.txt b/gemrb/docs/en/GUIScript/GetTableRowCount.txt
new file mode 100644
index 0000000..82bf6b0
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetTableRowCount.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.GetTableRowCount(TableIndex)
+
+Metaclass Prototype: GetRowCount(TableIndex)
+
+Description: Returns the number of rows in a 2DA Table.
+
+Parameters: TableIndex - returned by a previous LoadTable command.
+
+Return value: numeric
+
+See also: LoadTable, GetTableColumnCount
+
+
+MD5: 3b149161b60a872bde007850fa0f2196
diff --git a/gemrb/docs/en/GUIScript/GetTableRowIndex.txt b/gemrb/docs/en/GUIScript/GetTableRowIndex.txt
new file mode 100644
index 0000000..0e62fc6
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetTableRowIndex.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.GetTableRowIndex(TableIndex, RowName)
+
+Metaclass Prototype: GetRowIndex(RowName)
+
+Description: Returns the Index of a Row referenced by RowName in a 2DA Table.
+
+Parameters: TableIndex - returned by a previous LoadTable command.
+RowName - a row label
+
+Return value: numeric, -1 if row doesn't exist
+
+See also: LoadTable
+
+
+MD5: eb41ba2363ba1262a8edfff8265514da
diff --git a/gemrb/docs/en/GUIScript/GetTableRowName.txt b/gemrb/docs/en/GUIScript/GetTableRowName.txt
new file mode 100644
index 0000000..418776c
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetTableRowName.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.GetTableRowName(TableIndex, RowIndex)
+
+Metaclass Prototype: GetRowName(RowIndex)
+
+Description: Returns the Name of a Row in a 2DA Table.
+
+Parameters: TableIndex - returned by a previous LoadTable command.
+RowIndex - the numeric index of the row.
+
+Return value: string
+
+See also: LoadTable, GetTableColumnName
+
+
+MD5: 0f970b7c96685f7e78874353dc885f37
diff --git a/gemrb/docs/en/GUIScript/GetTableValue.txt b/gemrb/docs/en/GUIScript/GetTableValue.txt
new file mode 100644
index 0000000..3bab167
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetTableValue.txt
@@ -0,0 +1,23 @@
+
+Prototype: GemRB.GetTableValue(TableIndex, RowIndex/RowString, ColIndex/ColString[, Type])
+
+Metaclass Prototype: GetValue(RowIndex/RowString, ColIndex/ColString[, Type])
+
+Description: Returns a field of a 2DA Table. The row and column indices must be of same type (either string or numeric), the return value will be of the same type.
+Type can also be specified.
+
+Parameters: TableIndex - returned by a previous LoadTable command.
+RowIndex, ColIndex - numeric row/column indices
+RowString,ColString - the row/column names as written in the 2da file
+Type - forces a specific return type
+-1 - default
+ 0 - string
+ 1 - int
+ 2 - stat symbol (translated to numeric)
+
+Return value: numeric or string, based on the indices or type
+
+See also: GetSymbolValue, FindTableValue, LoadTable
+
+
+MD5: 27a07ff76472c9880891f7548e1ca4f4
diff --git a/gemrb/docs/en/GUIScript/GetToken.txt b/gemrb/docs/en/GUIScript/GetToken.txt
new file mode 100644
index 0000000..f79d8fc
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetToken.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.GetToken(VariableName)
+
+Description: Get a Variable value from the Token Dictionary. Tokens are string values, both used by the gamescript and the GUI scripts.
+
+Parameters: VariableName - the name of the variable must be shorter than 32 bytes
+
+Return value: string, the value of the token
+
+Example: GemRB.TextAreaAppend(CharGenWindow, TextArea, GemRB.GetToken("CHARNAME"))
+The above example will add the protagonist's name to the TextArea (if the token was set correctly).
+
+See also: SetToken, QueryText
+
+
+MD5: fea89d3ea3aa58100ad165ea6b9cb605
diff --git a/gemrb/docs/en/GUIScript/GetVar.txt b/gemrb/docs/en/GUIScript/GetVar.txt
new file mode 100644
index 0000000..c2ffd99
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/GetVar.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.GetVar(VariableName)
+
+Description: Get a Variable value from the Global Dictionary. Controls could be set up to be associated with such a variable. Even multiple controls could affect the same variable.
+
+Parameters: VariableName - the name of the variable must be shorter than 32 bytes
+
+Return value: numeric, 0 if the variable doesn't exist
+
+Examples:
+ selected = GemRB.GetVar ("SelectedMovie")
+
+See also: SetVar, SetVarAssoc, data_exchange
+
+
+MD5: e1d12a5de0cce9841d6f44622223e79e
diff --git a/gemrb/docs/en/GUIScript/HardEndPL.txt b/gemrb/docs/en/GUIScript/HardEndPL.txt
new file mode 100644
index 0000000..f75bcbe
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/HardEndPL.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.HardEndPL()
+
+Description: Ends a Music Playlist immediately.
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: SoftEndPL
+
diff --git a/gemrb/docs/en/GUIScript/HasControl.txt b/gemrb/docs/en/GUIScript/HasControl.txt
new file mode 100644
index 0000000..e6508b7
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/HasControl.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.HasControl(WindowIndex, ControlID[, ControlType])
+
+Metaclass Prototype: HasControl(ControlID[, ControlType])
+
+Description: Returns true if the control exists.
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+ControlType - (optional) limit to controls of this type (export them from control.h when needed)
+
+Return value: bool
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/HasResource.txt b/gemrb/docs/en/GUIScript/HasResource.txt
new file mode 100644
index 0000000..7ba2dff
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/HasResource.txt
@@ -0,0 +1,9 @@
+
+Prototype: GemRB.HasResource(ResRef, ResType)
+
+Description: Returns true if the resource exists.
+
+Parameters: ResRef - the resource reference (8 characters filename)
+ ResType - the class ID of the resource
+
+Return value: boolean
diff --git a/gemrb/docs/en/GUIScript/HasSpecialItem.txt b/gemrb/docs/en/GUIScript/HasSpecialItem.txt
new file mode 100644
index 0000000..fcaf7de
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/HasSpecialItem.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.HasSpecialItem(pc, itemtype, useup)
+
+Description: Checks if a team member has an item, optionally uses it.
+
+Parameters:
+pc - position in the party
+itemtype - see itemspec.2da (usually 1)
+useup - destroy/remove a charge after use
+
+Return value: bool
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/HasSpecialSpell.txt b/gemrb/docs/en/GUIScript/HasSpecialSpell.txt
new file mode 100644
index 0000000..905c858
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/HasSpecialSpell.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.HasSpecialSpell(pc, itemtype, useup)
+
+Description: Checks if a team member has a spell, optionally uses it.
+
+Parameters:
+pc - position in the party
+itemtype - see splspec.2da
+useup - destroy/remove a charge after use
+
+Return value: bool
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/HideGUI.txt b/gemrb/docs/en/GUIScript/HideGUI.txt
new file mode 100644
index 0000000..bfb6b08
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/HideGUI.txt
@@ -0,0 +1,28 @@
+
+Prototype: GemRB.HideGUI()
+
+Description: Hides the game GUI (all windows except the GameControl window). It is also used before a major change is made on a GUI window. After the changes, an UnhideGUI() command should be issued too. The game GUI window references are stored in the following system variables:
+MessageWindow - contains a TextArea for ingame messages/dialogue
+OptionsWindow - a series of buttons for Inventory/Spellbook/Journal,etc
+PortraitWindow - a series of portrait buttons
+ActionsWindow - a series of buttons to Attack/Talk,etc
+TopWindow - unused (might be removed later)
+OtherWindow - this window usually covers the GameControl, it is used to display maps, inventory, journal, etc.
+FloatWindow - special window which floats on top of the GameControl
+All these windows are associated with a position variable too, these are MessagePosition, OptionsPosition, etc.
+The position value tells the engine the window's relative position to the GameControl GUI. The engine doesn't make any distinction between these windows based on their reference name. The differences come from the position value.
+-1 - no position (floats over GameControl)
+0 - left
+1 - bottom
+2 - right
+3 - top
+4 - bottom (cummulative)
+
+Parameters: N/A
+
+Return value: 1 on success?
+
+See also: UnhideGUI, InvalidateWindow, SetVisible
+
+
+MD5: b95f905bba48f95a878868c7c453d52f
diff --git a/gemrb/docs/en/GUIScript/IncreaseReputation.txt b/gemrb/docs/en/GUIScript/IncreaseReputation.txt
new file mode 100644
index 0000000..ca3d8cc
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/IncreaseReputation.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.IncreaseReputation(Donation)
+
+Description: Increases party's reputation based on Donation. (see reputatio.2da)
+
+Parameters: the gold spent as donation to increase reputation. You have to change the party's gold separately.
+
+Return value: Nonzero if the reputation has been increased. (The amount of increase is multiplied by ten.)
+
+See also: GameGetReputation, GameGetPartyGold, GameSetPartyGold
+
diff --git a/gemrb/docs/en/GUIScript/InvalidateWindow.txt b/gemrb/docs/en/GUIScript/InvalidateWindow.txt
new file mode 100644
index 0000000..6a301d0
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/InvalidateWindow.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.InvalidateWindow(WindowIndex)
+
+Metaclass Prototype: Invalidate()
+
+Description: Invalidates the given Window so it will be redrawn entirely.
+
+Parameters: WindowIndex is the index returned by LoadWindow()
+
+Return value: N/A
+
+See also: LoadWindow
+
+
+MD5: 98ab4933674a41c8ce3d21ab00ac45af
diff --git a/gemrb/docs/en/GUIScript/IsDraggingItem.txt b/gemrb/docs/en/GUIScript/IsDraggingItem.txt
new file mode 100644
index 0000000..2a5be55
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/IsDraggingItem.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.IsDraggingItem()
+
+Description: Returns 1 if the player is dragging an item with the mouse (usually in the inventory screen). Returns 2 if the player is dragging a portrait (rearranging the party).
+
+Parameters: N/A
+
+Return value: boolean
+
+See also: DropDraggedItem, DragItem
+
+
+MD5: 3444790d47084300ab522deaca065d43
diff --git a/gemrb/docs/en/GUIScript/IsDualWielding.txt b/gemrb/docs/en/GUIScript/IsDualWielding.txt
new file mode 100644
index 0000000..7999b07
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/IsDualWielding.txt
@@ -0,0 +1,12 @@
+
+Prototype: GemRB.IsDualWielding(pc)
+
+Description: 1 if the pc is dual wielding; 0 otherwise.
+
+Parameters:
+pc - position in the party
+
+Return value: bool
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/IsValidStoreItem.txt b/gemrb/docs/en/GUIScript/IsValidStoreItem.txt
new file mode 100644
index 0000000..05e5a89
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/IsValidStoreItem.txt
@@ -0,0 +1,18 @@
+
+Prototype: IsValidStoreItem( PartyID, slot[, type] )
+
+Description: Returns if a pc's inventory item or a store item is valid for buying, selling, identifying or stealing. If Type is 1, then this is a store item.
+
+Parameters:
+PartyID - the PC's position in the party
+slot - the item's inventory or store slot
+
+Return value: bitfield
+1 - valid for buy
+2 - valid for sell
+4 - valid for identify
+8 - valid for steal
+0x40 - selected for buy or sell
+
+See also: EnterStore, GetSlotItem, GetStoreItem, ChangeStoreItem
+
diff --git a/gemrb/docs/en/GUIScript/LearnSpell.txt b/gemrb/docs/en/GUIScript/LearnSpell.txt
new file mode 100644
index 0000000..9681a80
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LearnSpell.txt
@@ -0,0 +1,19 @@
+
+Prototype: GemRB.LearnSpell(PartyID, SpellResRef[, Flags])
+
+Description: Tries to learn the specified spell.
+
+Parameters:
+PartyID - the PC's position in the party
+SpellResRef - the spell's Resource Reference
+Flags - bitmap with the following bits (default is 0):
+ 1 - Give XP for learning (Level * 100)
+ 2 - Display message
+ 4 - Check for insufficient stats
+ 8 - Also memorize it
+
+Return value: integer, 0 on success, different values on failure.
+
+See also: MemorizeSpell, RemoveSpell
+
+MD5: 768a8203f408816c5294f8601a7a4b4e
diff --git a/gemrb/docs/en/GUIScript/LeaveContainer.txt b/gemrb/docs/en/GUIScript/LeaveContainer.txt
new file mode 100644
index 0000000..3624752
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LeaveContainer.txt
@@ -0,0 +1,11 @@
+
+Prototype: LeaveContainer( )
+
+Description: Closes the current container by calling 'CloseContainerWindow' in the next update cycle. You cannot call 'CloseContainerWindow' directly, because the core system needs to know if the container subwindow is still open. This function will also remove empty ground piles.
+
+Parameters: -
+
+Return value: None
+
+See also: GetContainer, GetContainerItem, LeaveStore
+
diff --git a/gemrb/docs/en/GUIScript/LeaveParty.txt b/gemrb/docs/en/GUIScript/LeaveParty.txt
new file mode 100644
index 0000000..4a5be5e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LeaveParty.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.LeaveParty(Slot [, Dialog])
+
+Description: Removes the player character from the party.
+
+Parameters: Slot - actor index in game structure
+ Dialog - if set to 1, initiate the dialog.
+ if set to 2, execute "SetLeavePartyDialogFile" and initiate dialog.
+
+Return value: N/A
+
+See also: GetPartySize
+
diff --git a/gemrb/docs/en/GUIScript/LeaveStore.txt b/gemrb/docs/en/GUIScript/LeaveStore.txt
new file mode 100644
index 0000000..3ca4017
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LeaveStore.txt
@@ -0,0 +1,11 @@
+
+Prototype: LeaveStore( )
+
+Description: Saves the current store to the Cache folder and removes it from memory. If there was no active store, this function causes a runtime error.
+
+Parameters: -
+
+Return value: None
+
+See also: EnterStore, GetStore
+
diff --git a/gemrb/docs/en/GUIScript/LoadGame.txt b/gemrb/docs/en/GUIScript/LoadGame.txt
new file mode 100644
index 0000000..4e64f1a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LoadGame.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.LoadGame(index[,version])
+
+Description: Loads a saved game. This must be done before party creation.
+You must set the variable called PlayMode before loading a game (see SetVar).
+The game won't be loaded before the current GUIScript function returns!
+
+Parameters: index - the saved game's index, -1 means new game.
+ version - optional version to override some buggy default savegame versions
+PlayMode (variable) - 0 (single player) ,1 (tutorial) ,2 (multi player)
+
+Return value: N/A
+
+Example: GemRB.SetVar("PlayMode", 0)
+ GemRB.LoadGame(-1, 22)
+
+See also: EnterGame, CreatePlayer, SetVar, SaveGame
+
+
+MD5: a1bc07430d99c420a4e662a67eeda8fb
diff --git a/gemrb/docs/en/GUIScript/LoadMusicPL.txt b/gemrb/docs/en/GUIScript/LoadMusicPL.txt
new file mode 100644
index 0000000..bbee6f8
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LoadMusicPL.txt
@@ -0,0 +1,12 @@
+
+Prototype: LoadMusicPL(MusicPlayListResource, HardEnd)
+
+Description: Loads and starts a Music PlayList.
+
+Parameters: MusicPlayListResource is a .mus resref
+HardEnd - off by default, set to 1 to disable the fading at the end
+
+Return value: N/A
+
+See also: SoftEndPL, HardEndPL
+
diff --git a/gemrb/docs/en/GUIScript/LoadSymbol.txt b/gemrb/docs/en/GUIScript/LoadSymbol.txt
new file mode 100644
index 0000000..542e4ae
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LoadSymbol.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.LoadSymbol(IDSResRef)
+
+Description: Loads a IDS Symbol List. In case it was already loaded, it will return the list's existing reference (won't load it again).
+
+Parameters: IDSResRef - the symbol list's name (.ids resref)
+
+Return value:
+ Symbol table reference index
+
+See also: UnloadSymbol
+
+
+MD5: 40c9dacdfad30df5302c753880a84aa0
diff --git a/gemrb/docs/en/GUIScript/LoadTable.txt b/gemrb/docs/en/GUIScript/LoadTable.txt
new file mode 100644
index 0000000..09a3b8f
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LoadTable.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.LoadTable(2DAResRef, [ignore_error=0])
+
+Description: Loads a 2DA Table. In case it was already loaded, it will return the table's existing reference (won't load it again).
+
+Parameters: 2DAResRef - the table's name (.2da resref)
+ ignore_error - boolean, if set, then the script won't be blocked by missing files.
+
+Return value: GTable
+
+See also: UnloadTable, LoadSymbol
+
+
+MD5: fc03d66804e2be05bed6bfaa56900d66
diff --git a/gemrb/docs/en/GUIScript/LoadWindow.txt b/gemrb/docs/en/GUIScript/LoadWindow.txt
new file mode 100644
index 0000000..cc38a19
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LoadWindow.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.LoadWindow(WindowID)
+
+Description: Returns a Window. You must call LoadWindowPack before using this command. The window won't be displayed.
+ If LoadWindowPack() set nonzero natural screen size with Width and Height
+ parameters, the loaded window is then moved by
+ (screen size - winpack size) / 2
+
+Parameters: a window ID, see the .chu file specification
+
+Return value: GWindow
+
+See also: LoadWindowPack, GetControl, SetVisible, ShowModal, accessing_gui_controls
+
+MD5: c307b6a51ed3bcc0e551bad770b37853
diff --git a/gemrb/docs/en/GUIScript/LoadWindowFrame.txt b/gemrb/docs/en/GUIScript/LoadWindowFrame.txt
new file mode 100644
index 0000000..84e9dfc
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LoadWindowFrame.txt
@@ -0,0 +1,32 @@
+Prototype: GemRB.LoadWindowFrame(MOSResRef_Left, MOSResRef_Right, MOSResRef_Top, MOSResRef_Bottom))
+
+Description: Load the parts of window frame used to decorate windows
+ on higher resolutions.
+
+Parameters:
+ MOSResRef_Left,
+ MOSResRef_Right,
+ MOSResRef_Top,
+ MOSResRef_Bottom: names of MOS files with frame parts
+
+Return value: N/A
+
+Example: (from bg2's Start.py)
+ # Find proper window border for higher resolutions
+ screen_width = GemRB.GetSystemVariable (SV_WIDTH)
+ screen_height = GemRB.GetSystemVariable (SV_HEIGHT)
+ if screen_width == 800:
+ GemRB.LoadWindowFrame ("STON08L", "STON08R", "STON08T", "STON08B")
+ elif screen_width == 1024:
+ GemRB.LoadWindowFrame ("STON10L", "STON10R", "STON10T", "STON10B")
+
+ # Windows in this winpack were originally made and placed
+ # for 640x480 screen size
+ GemRB.LoadWindowPack ("START", 640, 480)
+ StartWindow = GemRB.LoadWindow (0)
+ GemRB.SetWindowFrame (StartWindow)
+
+
+See also: SetWindowFrame, LoadWindowPack
+
+MD5: 22d075f66da06a3c45c66a547719f960
diff --git a/gemrb/docs/en/GUIScript/LoadWindowPack.txt b/gemrb/docs/en/GUIScript/LoadWindowPack.txt
new file mode 100644
index 0000000..f1d4820
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/LoadWindowPack.txt
@@ -0,0 +1,19 @@
+
+Prototype: GemRB.LoadWindowPack(CHUIResRef, [Width=0, Height=0])
+
+Description: Loads a WindowPack into the Window Manager Module. Only one windowpack may be open at a time, but once a window was selected by LoadWindow, you can get a new windowpack.
+
+Parameters:
+ CHUIResRef: the name of the GUI set (.CHU resref)
+ Width, Height: if nonzero, they set the natural screen size for which
+ the windows in the winpack are positioned. LoadWindow() uses this
+ information to automatically reposition loaded windows.
+
+Return value: N/A
+
+Example:
+ LoadWindowPack ("START", 640, 480)
+
+See also: LoadWindow, LoadWindowFrame
+
+MD5: 591f80db15302b2a620238219b61f02a
diff --git a/gemrb/docs/en/GUIScript/Makefile.am b/gemrb/docs/en/GUIScript/Makefile.am
new file mode 100644
index 0000000..3a24843
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/Makefile.am
@@ -0,0 +1,3 @@
+guiscriptdocs_DATA = *.txt
+guiscriptdocsdir = $(docdir)/GUIScript/
+EXTRA_DIST = *.txt
diff --git a/gemrb/docs/en/GUIScript/MemorizeSpell.txt b/gemrb/docs/en/GUIScript/MemorizeSpell.txt
new file mode 100644
index 0000000..c257d56
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/MemorizeSpell.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.MemorizeSpell(PartyID, SpellType, Level, Index)
+
+Description: Sets spell to be memorized on rest.
+
+Parameters:
+PartyID - the PC's position in the party
+SpellType - 0 - priest, 1 - wizard, 2 - innate
+Level - the known spell's level
+Index - the known spell's index
+
+Return value: boolean, 1 on success.
+
+See also: GetKnownSpell, UnmemorizeSpell
+
diff --git a/gemrb/docs/en/GUIScript/ModifyEffect.txt b/gemrb/docs/en/GUIScript/ModifyEffect.txt
new file mode 100644
index 0000000..704d796
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ModifyEffect.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.ModifyEffects(PartyID, opcode, x, y)
+
+Description: Changes/sets the target coordinates of the specified effect.
+This command is used for the farsight spell.
+
+Parameters:
+PartyID - the player character's index in the party
+opcode - the effect opcode (for values see effects.ids)
+x - target x coordinate
+y - target y coordinate
+
+Return value: N/A
+
+See also: ApplyEffect, CountEffects
+
diff --git a/gemrb/docs/en/GUIScript/MoveTAText.txt b/gemrb/docs/en/GUIScript/MoveTAText.txt
new file mode 100644
index 0000000..47abc9c
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/MoveTAText.txt
@@ -0,0 +1,14 @@
+
+Prototype: MoveTAText(srcWin, srcCtrl, dstWin, dstCtrl)
+
+Metaclass Prototype: MoveText(dstCtrl)
+
+Description: Copies a TextArea content to another TextArea. Both parameter pairs must point to a valid TextArea.
+
+Parameters: srcWin, srcCtrl - a window index/control index pair of the source control
+ dstWin, dstCtrl - a window index/control index pair of the destination control
+
+Return value: N/A
+
+See also: SetText
+
diff --git a/gemrb/docs/en/GUIScript/MoveToArea.txt b/gemrb/docs/en/GUIScript/MoveToArea.txt
new file mode 100644
index 0000000..5811680
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/MoveToArea.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.MoveToArea(resref)
+
+Description: Moves the selected actors to the named area.
+
+Parameters: resref - The name of the area.
+
+Return value: N/A
+
+See also: GetCurrentArea
+
diff --git a/gemrb/docs/en/GUIScript/PlayMovie.txt b/gemrb/docs/en/GUIScript/PlayMovie.txt
new file mode 100644
index 0000000..d1542fd
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/PlayMovie.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.PlayMovie(MOVResRef[,flag])
+
+Description: Plays the named movie. Sets the configuration variable MOVResRef to 1. If flag was set to 1 it won't play the movie if the configuration variable was already set.
+
+Parameters: MOVResRef - a .mve resource reference.
+ flag - don't play movie twice
+
+Return value: 0 - movie played
+ -1 - error occurred
+ 1 - movie skipped
+
+See also: SetVar, GetVar
+
+
+MD5: 5aa65188f90931b955b6c91969b3393c
diff --git a/gemrb/docs/en/GUIScript/PlaySound.txt b/gemrb/docs/en/GUIScript/PlaySound.txt
new file mode 100644
index 0000000..1ffcccb
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/PlaySound.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.PlaySound(SoundResource, xpos, ypos, type)
+
+Description: Plays a Sound. If there is a single PC selected, then it will play the sound as if it was said by that PC (EAX).
+
+Parameters: a .wav resref (the format could be raw pcm, wavc or ogg; 8/16 bit; mono/stereo). Use the None python object to simply stop the previous sound.
+ x coordinate of the position where the sound should be played (optional)
+ y coordinate of the position where the sound should be played (optional)
+ type - defaults to 1, use 4 for speeches or other sounds that should stop the previous sounds (optional)
+
+Return value: N/A
+
+See also: LoadMusicPL
+
+
+MD5: 7370df8f931c57fc8317ca7472ea612e
diff --git a/gemrb/docs/en/GUIScript/QueryText.txt b/gemrb/docs/en/GUIScript/QueryText.txt
new file mode 100644
index 0000000..2cfbbf3
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/QueryText.txt
@@ -0,0 +1,23 @@
+
+Prototype: QueryText(WindowIndex, ControlIndex)
+
+Metaclass Prototype: QueryText()
+
+Description: Returns the Text of a TextEdit/TextArea/Label control. In case of a TextArea, it will return the selected row, not the entire textarea.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+
+Return value: string, may be empty
+
+Example:
+ Name = GemRB.QueryText(NameWindow, NameField)
+ GemRB.SetToken("CHARNAME",Name)
+The above example retrieves the character's name typed into the TextEdit control and stores it in a Token (a string variable accessible to gamescripts, the engine core and to the guiscripts too).
+
+ GemRB.SetToken("VoiceSet", GemRB.QueryText(SoundWindow, TextAreaControl))
+The above example sets the VoiceSet token to the value of the selected string in a TextArea control. Later this voiceset could be stored in the character sheet.
+
+See also: SetText, SetToken, accessing_gui_controls
+
+
+MD5: 7bcc3ff81f595b8d73c784ca32b06d5e
diff --git a/gemrb/docs/en/GUIScript/Quit.txt b/gemrb/docs/en/GUIScript/Quit.txt
new file mode 100644
index 0000000..b7253ff
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/Quit.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.Quit()
+
+Description: Quits the GemRB program immediately.
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: QuitGame
+
+
+MD5: 821540a9b347bc2490ac7935df46f481
diff --git a/gemrb/docs/en/GUIScript/QuitGame.txt b/gemrb/docs/en/GUIScript/QuitGame.txt
new file mode 100644
index 0000000..9ddb074
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/QuitGame.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.QuitGame()
+
+Description: Ends the current game session (saved game). To go back to the main screen, you must call SetNextScript. Automatically unloads all existing windows and resets the window variables used by HideGUI().
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: EnterGame, Quit, SetNextScript, HideGUI
+
+
+MD5: ddab42f8f83286b5499855a5bec36bd1
diff --git a/gemrb/docs/en/GUIScript/RemoveEffects.txt b/gemrb/docs/en/GUIScript/RemoveEffects.txt
new file mode 100644
index 0000000..a290d93
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/RemoveEffects.txt
@@ -0,0 +1,12 @@
+
+Prototype: GemRB.RemoveEffects(PartyID, SpellResRef)
+
+Description: Removes all effects created by the spell named SpellResRef.
+This is mostly useful for removing class abilities (CLAB/HLA AP_* entries).
+
+Parameters:
+PartyID - the PC's position in the party
+SpellResRef - a spell resource reference
+
+See also: RemoveSpell, RemoveItem
+
diff --git a/gemrb/docs/en/GUIScript/RemoveItem.txt b/gemrb/docs/en/GUIScript/RemoveItem.txt
new file mode 100644
index 0000000..2560e5d
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/RemoveItem.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.RemoveItem(PartyID, Slot[, Count])
+
+Description: Removes and destructs an item in an actor's inventory. This works even if the item is cursed or indestructible.
+
+Parameters:
+PartyID - the PC's position in the party
+Slot - The inventory slot index of the item
+Count - the number of items
+
+Return value: boolean, 1 on success
+
+See also: CreateItem
+
+MD5:
diff --git a/gemrb/docs/en/GUIScript/RemoveSpell.txt b/gemrb/docs/en/GUIScript/RemoveSpell.txt
new file mode 100644
index 0000000..9d60589
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/RemoveSpell.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.RemoveSpell(PartyID, SpellType, Level, Index)
+
+Description: Unlearns a specified known spell. The original game (bg2) let only mage spells unlearned.
+
+Parameters:
+PartyID - the PC's position in the party
+SpellType - 0 - priest, 1 - wizard, 2 - innate
+Level - the known spell's level
+Index - the known spell's index
+
+Return value: boolean, 1 on success
+
+See also: UnmemorizeSpell, GetKnownSpellsCount, GetKnownSpell, LearnSpell, RemoveEffects
+
diff --git a/gemrb/docs/en/GUIScript/RestParty.txt b/gemrb/docs/en/GUIScript/RestParty.txt
new file mode 100644
index 0000000..b132346
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/RestParty.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.RestParty(flags, hp, movie)
+
+Description: Makes the party rest. It is possible to check various things that may forbid resting (hostile creatures, area flags, party scattered). It is possible to play a movie animation too.
+
+Parameters:
+flags -
+hp - hit points healed, 0 means full healing
+movie - a number 0-7, see restmov.2da
+
+Return value: N/A
+
+See also: StartStore(gamescript)
diff --git a/gemrb/docs/en/GUIScript/RevealArea.txt b/gemrb/docs/en/GUIScript/RevealArea.txt
new file mode 100644
index 0000000..748587a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/RevealArea.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.RevealArea(x, y, radius, type)
+
+Description: Reveals part of the area.
+
+Parameters:
+x - x coordinate of the center point
+y - y coordinate of the center point
+radius - radius of the circle to explore
+type - if positive will make the effect ignore blocked portions of the map
+
+Return value: N/A
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/RewindTA.txt b/gemrb/docs/en/GUIScript/RewindTA.txt
new file mode 100644
index 0000000..46a2893
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/RewindTA.txt
@@ -0,0 +1,13 @@
+
+Prototype: RewindTA(Win, Ctrl, Ticks)
+
+Metaclass Prototype: Rewind(Ticks)
+
+Description: Sets up a textarea control for scrolling text. It clears the textarea and sets TEXTAREA_SMOOTHSCROLL attribute in the textarea.
+
+Parameters: Win, Ctrl - a window index/control index pair of the textarea
+ Ticks - alters the scrolling speed (delay between steps)
+
+Return value: N/A
+
+See also: SetTAHistory, SetTextAreaFlags, SetEvent
diff --git a/gemrb/docs/en/GUIScript/Roll.txt b/gemrb/docs/en/GUIScript/Roll.txt
new file mode 100644
index 0000000..e70e22a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/Roll.txt
@@ -0,0 +1,21 @@
+
+Prototype: GemRB.Roll(Dice, Size, Add)
+
+Description: Calls traditional dice roll calculation.
+
+Parameters: Dice - the number of the dice.
+ Size - the size of the die.
+ Add - add this value directly to the sum
+
+Return value: numeric
+
+Example:
+ dice = 3
+ size = 5
+ v = GemRB.Roll(dice, size, 3)
+The above example generates a 3d5+3 number.
+
+See also:
+
+
+MD5: ad757524b8fb4284f423cd3f5fbb5eb1
diff --git a/gemrb/docs/en/GUIScript/SaveCharacter.txt b/gemrb/docs/en/GUIScript/SaveCharacter.txt
new file mode 100644
index 0000000..1a647ff
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SaveCharacter.txt
@@ -0,0 +1,19 @@
+
+Prototype: GemRB.SaveCharacter(PartyID, filename)
+
+Description: Saves (exports) the designated partymember into the Characters directory of the game. This character is importable later by a special CreatePlayer call.
+
+Parameters:
+ PartyID - the saved character's position in the party
+ filename - the filename of the character
+
+Return value: N/A
+
+Example:
+ pc = GemRB.GameGetSelectedPCSingle ()
+ GemRB.SaveCharacter(pc, ExportFileName)
+
+The above example exports the currently selected character.
+
+See also: CreatePlayer
+
diff --git a/gemrb/docs/en/GUIScript/SaveGame.txt b/gemrb/docs/en/GUIScript/SaveGame.txt
new file mode 100644
index 0000000..9732947
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SaveGame.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SaveGame(position, description)
+
+Description: Saves the current game. This won't be started before the current script returns.
+
+Parameters:
+ pos - the saved game's index, 0 and 1 are reserved
+ description - the string that will also appear in the filename
+
+Return value: N/A
+
+Example:
+ GemRB.SaveGame(0, "QuickSave") #this will make a quicksave
+
+See also: LoadGame, SaveCharacter
+
diff --git a/gemrb/docs/en/GUIScript/SetActionIcon.txt b/gemrb/docs/en/GUIScript/SetActionIcon.txt
new file mode 100644
index 0000000..f3976ba
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetActionIcon.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.SetActionIcon(WindowIndex, ControlIndex, ActionIndex)
+
+Metaclass Prototype: SetActionIcon(ActionIndex)
+
+Description: Sets up an action button based on the guibtact table. The ActionIndex should be less than 32. This action will set the button's image, the tooltip and the push button event handler.
+
+Parameters:
+WindowIndex, ControlIndex - the button's reference
+ActionIndex - the row number in the guibtact.2da file
+
+Return value: N/A
+
+See also: SetSpellIcon, SetItemIcon, SetupControls
diff --git a/gemrb/docs/en/GUIScript/SetAnimation.txt b/gemrb/docs/en/GUIScript/SetAnimation.txt
new file mode 100644
index 0000000..e742814
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetAnimation.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.SetAnimation(WindowIndex, ControlIndex, BAMResRef[, Cycle])
+
+Metaclass Prototype: SetAnimation(BAMResRef[, Cycle])
+
+Description: Sets the animation of a Control (usually a Button) from a BAM file. Optionally an animation cycle could be set too.
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+BAMResRef - resref of the animation
+Cycle - (optional) number of the cycle to use
+
+Return value: N/A
+
+See also: SetAnimationPalette
+
diff --git a/gemrb/docs/en/GUIScript/SetAnimationPalette.txt b/gemrb/docs/en/GUIScript/SetAnimationPalette.txt
new file mode 100644
index 0000000..004c4bb
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetAnimationPalette.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SetAnimationPalette(WindowIndex, ControlIndex, col1, col2, col3, col4, col5, col6, col7, col8)
+
+Metaclass Prototype: SetAnimationPalette(col1, col2, col3, col4, col5, col6, col7, col8)
+
+Description: Sets the palette of an animation already assigned to the button.
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+col1 - col8 - colors for the palette
+
+Return value: N/A
+
+See also: SetAnimation
+
diff --git a/gemrb/docs/en/GUIScript/SetBufferLength.txt b/gemrb/docs/en/GUIScript/SetBufferLength.txt
new file mode 100644
index 0000000..1052f90
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetBufferLength.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SetBufferLength(WindowIndex, ControlIndex, Length)
+
+Metaclass Prototype: SetBufferLength(Length)
+
+Description: Sets the maximum text length of a TextEdit Control. It cannot be more than 65535.
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+Length - the maximum text length
+
+Return value: N/A
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonBAM.txt b/gemrb/docs/en/GUIScript/SetButtonBAM.txt
new file mode 100644
index 0000000..1fa24bb
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonBAM.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.SetButtonBAM(WindowIndex, ControlIndex, BAMResRef, CycleIndex, FrameIndex, col1)
+
+Metaclass Prototype: SetBAM(BAMResRef, CycleIndex, FrameIndex, col1)
+
+Description: Sets the Picture of a Button Control from a BAM file. If the supplied color gradient value is -1, then no palette change, if it is >=0, then it changes the 4-16 palette entries of the bam.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+BAMResRef - the name of the BAM animation (a .bam resref)
+CycleIndex, FrameIndex - the cycle and frame index of the picture in the bam
+col1 - the gradient number, (-1 no gradient)
+
+Return value: N/A
+
+See also: SetButtonPLT, SetButtonPicture, SetButtonSprites
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonBorder.txt b/gemrb/docs/en/GUIScript/SetButtonBorder.txt
new file mode 100644
index 0000000..509b2e2
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonBorder.txt
@@ -0,0 +1,27 @@
+
+Prototype: GemRB.SetButtonBorder(WindowIndex, ControlIndex, BorderIndex, dx1, dy1, dx2, dy2, R, G, B, A, [enabled, filled])
+
+Metaclass Prototype: SetBorder(BorderIndex, dx1, dy1, dx2, dy2, R, G, B, A, [enabled, filled])
+
+Description: Sets border/frame/overlay parameters for a button. This command can be used for drawing a border around a button, or to overlay it with a tint (like with unusable or unidentified item's icons).
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+BorderIndex - 0,1 or 2
+dx1,dy1 - Upper left corner
+dx2,dy2 - Offset from the lower right corner
+RGBA - red,green,blue,opacity components of the border colour
+enabled - 1 means enable it immediately
+filled - 1 means draw it filled (overlays)
+
+Return value: N/A
+
+Examples:
+GemRB.SetButtonBorder (Window, Icon, 0, 0, 0, 0, 0, 0, 0, 0, 160, 0, 1)
+Not known spells are drawn darkened (the whole button will be overlaid).
+
+GemRB.SetButtonBorder (Window, Button, FRAME_PC_SELECTED, 1, 1, 2, 2, 0, 255, 0, 255)
+This will draw a green frame around the portrait.
+
+See also: EnableButtonBorder
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonFlags.txt b/gemrb/docs/en/GUIScript/SetButtonFlags.txt
new file mode 100644
index 0000000..480d574
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonFlags.txt
@@ -0,0 +1,35 @@
+
+Prototype: GemRB.SetButtonFlags(WindowIndex, ControlIndex, Flags, Operation)
+
+Metaclass Prototype: SetFlags(Flags, Operation)
+
+Description: Sets the Display Flags of a Button.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+Flags - various bits altering the behaviour of the control
+IE_GUI_BUTTON_NO_IMAGE = 0x00000001, no button image set by SetButtonSprites
+IE_GUI_BUTTON_PICTURE = 0x00000002, has picture set by other SetButton* commands
+IE_GUI_BUTTON_SOUND = 0x00000004, clicking the button has a sound
+IE_GUI_BUTTON_ALT_SOUND = 0x00000008, clicking the button has a different sound
+IE_GUI_BUTTON_CHECKBOX = 0x00000010, it is a checkbox
+IE_GUI_BUTTON_RADIOBUTTON= 0x00000020, it is a radio button
+IE_GUI_BUTTON_DEFAULT = 0x00000040, it is the default button
+IE_GUI_BUTTON_DRAGGABLE = 0x00000080, the image of the button is draggable?
+Operation - the binary operation to be performed on the
+ current button flags
+OP_SET = 0, sets the value completely
+OP_OR = 1, sets (turns on) the 1 bits of the value
+OP_NAND = 2, resets (turns off) the 1 bits of the value
+
+Return value: N/A
+
+Examples:
+GemRB.SetButtonFlags (window, button, IE_GUI_BUTTON_CHECKBOX, OP_OR)
+This command will make the button behave like a checkbox.
+
+GemRB.SetButtonFlags (Window, Button, IE_GUI_BUTTON_NO_IMAGE, OP_NAND)
+This command will re-enable the images of the button (making it visible).
+
+See also: SetButtonSprites, SetButtonPicture, SetButtonBAM
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonFont.txt b/gemrb/docs/en/GUIScript/SetButtonFont.txt
new file mode 100644
index 0000000..3af39c0
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonFont.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetButtonFont(WindowIndex, ControlIndex, FontResRef)
+
+Metaclass Prototype: SetFont(FontResRef)
+
+Description: Sets font used for drawing button text.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+FontResref - a .bam resref which must be listed in fonts.2da
+
+Return value: N/A
+
+See also: CreateLabel
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonMOS.txt b/gemrb/docs/en/GUIScript/SetButtonMOS.txt
new file mode 100644
index 0000000..1da76df
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonMOS.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetButtonMOS(WindowIndex, ControlIndex, MOSResRef)
+
+Metaclass Prototype: SetMOS(MOSResRef)
+
+Description: Sets the Picture of a Button Control from a MOS file.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+MOSResRef - the name of the picture (a .mos resref)
+
+Return value: N/A
+
+See also: SetButtonBAM, SetButtonPLT, SetButtonPicture
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonOverlay.txt b/gemrb/docs/en/GUIScript/SetButtonOverlay.txt
new file mode 100644
index 0000000..58c9914
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonOverlay.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.SetButtonOverlay(Window, Button, ratio, r1,g1,b1,a1, r2,g2,b2,a2)
+
+Metaclass Prototype: SetOverlay(ratio, r1,g1,b1,a1, r2,g2,b2,a2)
+
+Description: Sets ratio (0-1.0) of height to which button picture will be overlaid in a different colour. The colour will fade from the first rgba values to the second.
+
+Parameters:
+Window, Button - the control's reference
+ClippingRatio - a floating point value from the 0-1 interval
+rgba1 - source colour
+rgba2 - target colour
+
+Return value: N/A
+
+See also: SetButtonPictureClipping
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonPLT.txt b/gemrb/docs/en/GUIScript/SetButtonPLT.txt
new file mode 100644
index 0000000..b1e06c3
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonPLT.txt
@@ -0,0 +1,21 @@
+
+Prototype: GemRB.SetButtonPLT(WindowIndex, ControlIndex, PLTResRef, col1, col2, col3, col4, col5, col6, col7, col8, type)
+
+Metaclass Prototype: SetPLT(PLTResRef, col1, col2, col3, col4, col5, col6, col7, col8, type)
+
+Description: Sets the Picture of a Button Control from a PLT file. Sets up the palette based on the eight given gradient colors.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+PLTResRef - the name of the picture (a .plt resref)
+col1-8 - color gradients
+type - the byte to use from the gradients
+ 0 Body (robe or armour)
+ 1 Weapon
+ 2 Shield
+ 3 Helmet
+
+Return value: N/A
+
+See also: SetButtonBAM
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonPicture.txt b/gemrb/docs/en/GUIScript/SetButtonPicture.txt
new file mode 100644
index 0000000..fd4d3fa
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonPicture.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetButtonPicture(WindowIndex, ControlIndex, PictureResRef, DefaultResRef)
+
+Metaclass Prototype: SetPicture(PictureResRef, DefaultResRef)
+
+Description: Sets the Picture of a Button Control from a BMP file.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+PictureResRef - the name of the picture (a .bmp resref)
+DefaultResRef - an alternate bmp should the picture be nonexistent
+Return value: N/A
+
+See also: SetButtonBAM, SetButtonPLT, SetButtonSprites, SetButtonPictureClipping, SetWindowPicture
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonPictureClipping.txt b/gemrb/docs/en/GUIScript/SetButtonPictureClipping.txt
new file mode 100644
index 0000000..5cb7e14
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonPictureClipping.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetButtonPictureClipping(Window, Button, ClippingRatio)
+
+Metaclass Prototype: SetPictureClipping(ClippingRatio)
+
+Description: Sets percent (0-1.0) of width to which button picture will be clipped. This clipping cannot be used simultaneously with SetButtonOverlay.
+
+Parameters:
+Window, Button - the control's reference
+ClippingRatio - a floating point value from the 0-1 interval
+
+Return value: N/A
+
+See also: SetButtonPicture, SetButtonOverlay
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonSprites.txt b/gemrb/docs/en/GUIScript/SetButtonSprites.txt
new file mode 100644
index 0000000..3b6c204
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonSprites.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.SetButtonSprites(WindowIndex, ControlIndex, ResRef, Cycle, UnpressedFrame, PressedFrame, SelectedFrame, DisabledFrame)
+
+Metaclass Prototype: SetSprites(ResRef, Cycle, UnpressedFrame, PressedFrame, SelectedFrame, DisabledFrame)
+
+Description: Sets the Button's Images. You can disable the images by setting the IE_GUI_BUTTON_NO_IMAGE flag on the control.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+ResRef - a .bam animation resource (.bam resref)
+Cycle - the cycle of the .bam from which all frames of this button will come
+UnpressedFrame - the frame which will be displayed by default
+PressedFrame - the frame which will be displayed when the button is pressed
+SelectedFrame - this is for selected checkboxes
+DisabledFrame - this is for inactivated buttons
+
+Return value: N/A
+
+See also: SetButtonFlags, SetButtonBAM, SetButtonPicture
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonState.txt b/gemrb/docs/en/GUIScript/SetButtonState.txt
new file mode 100644
index 0000000..b439d82
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonState.txt
@@ -0,0 +1,23 @@
+
+Prototype: GemRB.SetButtonState(WindowIndex, ControlIndex, State)
+
+Metaclass Prototype: SetState(State)
+
+Description: Sets the state of a Button Control. Doesn't work if the button
+is a checkbox or a radio button though, their states are handled internally.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+State - the new state of the button:
+IE_GUI_BUTTON_ENABLED = 0x00000000, default state
+IE_GUI_BUTTON_UNPRESSED = 0x00000000, same as above
+IE_GUI_BUTTON_PRESSED = 0x00000001, the button is pressed
+IE_GUI_BUTTON_SELECTED = 0x00000002, the button stuck in pressed state
+IE_GUI_BUTTON_DISABLED = 0x00000003, the button is disabled
+IE_GUI_BUTTON_LOCKED = 0x00000004, the button is inactive (like DISABLED, but processes MouseOver events and draws UNPRESSED bitmap)
+IE_GUI_BUTTON_THIRD = 0x00000005, draws DISABLED bitmap, but it isn't disabled
+IE_GUI_BUTTON_SECOND = 0x00000006, draws PRESSED bitmap, but it isn't shifted
+
+Return value: N/A
+
+See also: SetButtonFlags
+
diff --git a/gemrb/docs/en/GUIScript/SetButtonTextColor.txt b/gemrb/docs/en/GUIScript/SetButtonTextColor.txt
new file mode 100644
index 0000000..2b4c564
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetButtonTextColor.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetButtonTextColor(WindowIndex, ControlIndex, red, green, blue)
+
+Metaclass Prototype: SetTextColor(red, green, blue)
+
+Description: Sets the Text Color of a Button Control.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+red,green,blue - the rgb color values
+
+Return value: N/A
+
+See also: SetLabelUseRGB, SetLabelTextColor
+
diff --git a/gemrb/docs/en/GUIScript/SetControlPos.txt b/gemrb/docs/en/GUIScript/SetControlPos.txt
new file mode 100644
index 0000000..4d755c2
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetControlPos.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SetControlPos(WindowIndex, ControlIndex, X, Y)
+
+Metaclass Prototype: SetPos(X, Y)
+
+Description: Moves a Control.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+X,Y - the new position of the control relative to the window
+
+Return value: N/A
+
+See also: SetControlSize, accessing_gui_controls
+
+
+MD5: fdec6fc5feaa586ba735e4b36c086989
diff --git a/gemrb/docs/en/GUIScript/SetControlSize.txt b/gemrb/docs/en/GUIScript/SetControlSize.txt
new file mode 100644
index 0000000..d4667ae
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetControlSize.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SetControlSize(WindowIndex, ControlIndex, Width, Height)
+
+Metaclass Prototype: SetSize(Width, Height)
+
+Description: Resizes a Control.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+Width, Height - the new dimensions of the control
+
+Return value: N/A
+
+See also: SetControlPos, accessing_gui_controls
+
+
+MD5: e7467f0f874bffc30c31ef6030422c16
diff --git a/gemrb/docs/en/GUIScript/SetControlStatus.txt b/gemrb/docs/en/GUIScript/SetControlStatus.txt
new file mode 100644
index 0000000..4eb4178
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetControlStatus.txt
@@ -0,0 +1,31 @@
+
+Prototype: GemRB.SetControlStatus(WindowIndex, ControlIndex, State)
+
+Metaclass Prototype: SetStatus(State)
+
+Description: Sets the state of a Control. For buttons, this is the same as SetButtonState. You can additionally use 0x80 for a focused control.
+For other controls, this command will set the common Value of the control, which has various uses.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+
+Button States:
+IE_GUI_BUTTON_ENABLED = 0x00000000, default state
+IE_GUI_BUTTON_UNPRESSED = 0x00000000, same as above
+IE_GUI_BUTTON_PRESSED = 0x00000001, the button is pressed
+IE_GUI_BUTTON_SELECTED = 0x00000002, the button stuck in pressed state
+IE_GUI_BUTTON_DISABLED = 0x00000003, the button is disabled
+IE_GUI_BUTTON_LOCKED = 0x00000004, the button is inactive
+
+#Text Edit states
+IE_GUI_EDIT_NUMBER = 0x030000001, the textedit will accept only digits
+
+Map Control States (add 0x09000000 to these):
+IE_GUI_MAP_NO_NOTES = 0, no mapnotes visible
+IE_GUI_MAP_VIEW_NOTES = 1, view notes (no setting)
+IE_GUI_MAP_SET_NOTE = 2, allow setting notes
+
+
+Return value: N/A
+
+See also: SetButtonState
+
diff --git a/gemrb/docs/en/GUIScript/SetDefaultActions.txt b/gemrb/docs/en/GUIScript/SetDefaultActions.txt
new file mode 100644
index 0000000..4805d82
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetDefaultActions.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.SetDefaultActions(qslot, action1, action2, action3)
+
+Description: Sets whether quick slots need an additional translation like in iwd2.
+Also sets up the first three default action types.
+
+
+Parameters: qslot - bool
+ action1-3 - button codes
+
+Return value: N/A
+
+See also: SetupControls
diff --git a/gemrb/docs/en/GUIScript/SetDefaultScrollBar.txt b/gemrb/docs/en/GUIScript/SetDefaultScrollBar.txt
new file mode 100644
index 0000000..664d447
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetDefaultScrollBar.txt
@@ -0,0 +1,13 @@
+
+Prototype: SetDefaultScrollBar(WindowIndex, ControlIndex)
+
+Metaclass Prototype: SetDefaultScrollBar()
+
+Description: Sets a ScrollBar as default on a window. If any control receives mousewheel events, it will be relayed to this ScrollBar, unless there is another attached to the control.
+
+Parameters: WindowIndex, ControlIndex - the ScrollBar control's reference
+
+Return value: N/A
+
+See also: AttachScrollBar
+
diff --git a/gemrb/docs/en/GUIScript/SetEquippedQuickSlot.txt b/gemrb/docs/en/GUIScript/SetEquippedQuickSlot.txt
new file mode 100644
index 0000000..42cadd9
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetEquippedQuickSlot.txt
@@ -0,0 +1,14 @@
+Prototype: GemRB.SetEquippedQuickSlot(PartyID, QWeaponSlot[, ability])
+
+Description: Sets the specified weapon slot as equipped weapon slot.
+
+Parameters:
+PartyID - the PC's position in the party (1 based)
+QWeaponSlot - the quickslot to equip
+ability - optional integer, sets the used ability
+
+Return value: 0 success, -1 silent failure
+
+See also: GetEquippedQuickSlot, SetupQuickSlot
+
+MD5: dde8a3aad7a34d817501e1d8877f5173
diff --git a/gemrb/docs/en/GUIScript/SetEvent.txt b/gemrb/docs/en/GUIScript/SetEvent.txt
new file mode 100644
index 0000000..98ff994
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetEvent.txt
@@ -0,0 +1,43 @@
+
+Prototype: Control.SetEvent(EventMask, PythonFunction)
+
+Description: Sets an event of a control on a window to a python function
+
+Parameters:
+EventMask - a dword describing the event. Its high byte is actually the control's type.
+PythonFunction - the function object
+
+Implemented events:
+IE_GUI_BUTTON_ON_PRESS = 0x00000000, the user pressed the button.
+IE_GUI_MOUSE_OVER_BUTTON = 0x00000001, the user hovered the mouse over the button.
+IE_GUI_MOUSE_ENTER_BUTTON = 0x00000002, the user just moved the mouse onto the button.
+IE_GUI_MOUSE_LEAVE_BUTTON = 0x00000003, the mouse just left the button
+IE_GUI_BUTTON_ON_SHIFT_PRESS = 0x00000004, the button was pressed along with the shift key.
+IE_GUI_BUTTON_ON_RIGHT_PRESS = 0x00000005, the button was right clicked
+IE_GUI_BUTTON_ON_DRAG_DROP = 0x00000006, the button was clicked during a drag&drop action.
+IE_GUI_PROGRESS_END_REACHED = 0x01000000, the progressbar received a 100 percent value.
+IE_GUI_SLIDER_ON_CHANGE = 0x02000000, the slider's knob position has changed.
+IE_GUI_EDIT_ON_CHANGE = 0x03000000, the text in the editbox has changed.
+IE_GUI_TEXTAREA_ON_CHANGE = 0x05000000, the text in the textarea has changed.
+IE_GUI_TEXTAREA_OUT_OF_TEXT = 0x05000001, the smooth scrolling textarea is out of text.
+IE_GUI_LABEL_ON_PRESS = 0x06000000, the label was pressed.
+IE_GUI_SCROLLBAR_ON_CHANGE= 0x07000000, the scrollbar's knob position has changed.
+.... (See GUIDefines.py for more event types)
+
+Return value: N/A
+
+Examples:
+ Bar.SetEvent (IE_GUI_PROGRESS_END_REACHED, EndLoadScreen)
+ ...
+ def EndLoadScreen ():
+ Skull = GemRB.GetControl (LoadScreen, 1)
+ GemRB.SetButtonMOS (LoadScreen, Skull, "GSKULON")
+The above example changes the image on the loadscreen when the progressbar reaches the end.
+
+ Button.SetEvent (IE_GUI_BUTTON_ON_PRESS, Buttons.YesButton)
+The above example sets up the 'YesButton' function from the Buttons module to be called when the button is pressed.
+
+ Button.SetEvent (IE_GUI_MOUSE_OVER_BUTTON, ChaPress)
+The above example shows how to implement 'context sensitive help'. The 'ChaPress' function displays a help text on the screen when you hover the mouse over a button.
+
+See also: GetControl, SetVarAssoc, SetTimedEvent, accessing_gui_controls
diff --git a/gemrb/docs/en/GUIScript/SetFullScreen.txt b/gemrb/docs/en/GUIScript/SetFullScreen.txt
new file mode 100644
index 0000000..0a0bdff
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetFullScreen.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetFullScreen(flag)
+
+Description: Adjusts fullscreen mode.
+
+Parameters:
+flag:
+ -1 : toggle fullscreen mode
+ 0 : set windowed mode
+ 1 : set fullscreen mode
+
+Return value: N/A
+
+See also: SetGamma
+
diff --git a/gemrb/docs/en/GUIScript/SetGamePortraitPreview.txt b/gemrb/docs/en/GUIScript/SetGamePortraitPreview.txt
new file mode 100644
index 0000000..9bc7822
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetGamePortraitPreview.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SetGamePortraitPreview(WindowIndex, ControlIndex, PCSlotCount)
+
+Metaclass Prototype: SetGamePortraitPreview(PCSlotCount)
+
+Description: Sets a current game PC portrait preview bmp onto a button as picture.
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+PCSlotCount - number of pcs
+
+Return value: N/A
+
+See also: SetGamePreview
+
diff --git a/gemrb/docs/en/GUIScript/SetGamePreview.txt b/gemrb/docs/en/GUIScript/SetGamePreview.txt
new file mode 100644
index 0000000..c7cc236
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetGamePreview.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetGamePreview(WindowIndex, ControlIndex)
+
+Metaclass Prototype: SetGamePreview()
+
+Description: Sets current game area preview bmp onto a button as picture.
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+
+Return value: N/A
+
+See also: SetGamePortraitPreview
+
diff --git a/gemrb/docs/en/GUIScript/SetGamma.txt b/gemrb/docs/en/GUIScript/SetGamma.txt
new file mode 100644
index 0000000..613c9a4
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetGamma.txt
@@ -0,0 +1,12 @@
+
+Prototype: GemRB.SetGamma(brightness, contrast)
+
+Description: Adjusts brightness and contrast.
+
+Parameters:
+brightness - value must be 0 .. 40
+contrast - value must be 0 .. 5
+
+Return value: N/A
+
+See also: SetFullScreen
diff --git a/gemrb/docs/en/GUIScript/SetGlobal.txt b/gemrb/docs/en/GUIScript/SetGlobal.txt
new file mode 100644
index 0000000..7c93329
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetGlobal.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.SetGlobal(VariableName, Context, Value)
+
+Description: Sets a gamescript variable to the specificed numeric value.
+
+Parameters:
+VariableName - name of the variable
+Context - LOCALS, GLOBALS or area specific
+Value - value to set
+
+Return value: N/A
+
+See also: SetVar, SetVarAssoc, SetToken, data_exchange
+
diff --git a/gemrb/docs/en/GUIScript/SetInfoTextColor.txt b/gemrb/docs/en/GUIScript/SetInfoTextColor.txt
new file mode 100644
index 0000000..60c8385
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetInfoTextColor.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.SetInfoTextColor(red, green, blue, [alpha])
+
+Description: Sets the color of floating messages in GameControl. Floating messages are in-game messages issued by actors, or information text coming from game objects.
+
+Parameters: red,green,blue,alpha - color code, alpha defaults to 255 (completely opaque)
+
+Return value: N/A
+
+See also: SetLabelTextColor, SetButtonTextColor
+
+
+MD5: e48af04dbba2632c62459f3aba05d0a9
diff --git a/gemrb/docs/en/GUIScript/SetItemIcon.txt b/gemrb/docs/en/GUIScript/SetItemIcon.txt
new file mode 100644
index 0000000..1108df0
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetItemIcon.txt
@@ -0,0 +1,26 @@
+
+Prototype: GemRB.SetItemIcon(WindowIndex, ControlIndex, ITMResRef[, Type, Tooltip, ITM2ResRef])
+
+Metaclass Prototype: SetItemIcon(ITMResRef[, Type, Tooltip, ITM2ResRef])
+
+Description: Sets Item icon image on a Button control.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+ITMResRef - the name of the item (.itm resref)
+Type - 0-5 (the icon's type)
+ 0 - Inventory Icon1
+ 1 - Inventory Icon2
+ 2 - Description Icon (for BG)
+ 3 - No Icon (empty slot)
+ 4 - Activation Icon1
+ 5 - Activation Icon2
+ 6 - Item ability icon for first extended header
+ 7 - Item ability icon for second extended header
+ 8 - etc.
+
+Tooltip - if set to 1, the tooltip for the item will also be set
+ITM2ResRef - if set, a second item to display in the icon. ITM2 is drawn first. The tooltip of ITM is used. Only valid for Type 4 and 5
+Return value: N/A
+
+See also: SetSpellIcon, SetActionIcon
diff --git a/gemrb/docs/en/GUIScript/SetJournalEntry.txt b/gemrb/docs/en/GUIScript/SetJournalEntry.txt
new file mode 100644
index 0000000..d735d00
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetJournalEntry.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.SetJournalEntry(strref[, section, chapter])
+
+Description: Sets a journal journal entry w/ given chapter and section, if section was not given, then it will delete the entry. Chapter is optional, if it is omitted, then the current chapter will be used. If strref is -1, then it will delete the whole journal.
+
+Parameters: strref - strref of the journal entry
+ section - the section of the journal (only if the journal has sections)
+ chapter - the chapter of the journal entry
+
+Return value: NONE
+
+See also: GetJournalEntry
+
diff --git a/gemrb/docs/en/GUIScript/SetLabelTextColor.txt b/gemrb/docs/en/GUIScript/SetLabelTextColor.txt
new file mode 100644
index 0000000..2b074de
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetLabelTextColor.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SetLabelTextColor(WindowIndex, ControlIndex, red, green, blue)
+
+Metaclass Prototype: SetTextColor(red, green, blue)
+
+Description: Sets the Text Color of a Label Control. If the the Font has no own palette, you can set a default palette by this command.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+ red,green,blue - the control's desired text color
+
+Return value: N/A
+
+See also: SetLabelUseRGB
+
+
+MD5: aa639bfaf8d66d2eefba1f96c9a5b6a2
diff --git a/gemrb/docs/en/GUIScript/SetLabelUseRGB.txt b/gemrb/docs/en/GUIScript/SetLabelUseRGB.txt
new file mode 100644
index 0000000..ac28e92
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetLabelUseRGB.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.SetLabelUseRGB(WindowIndex, ControlIndex, status)
+
+Metaclass Prototype: SetUseRGB(status)
+
+Description: Sets a Label control to use the colors coming from the Font. If the font has its own color, you must set this. If a label was set to not use the Font's colors you can alter its color by SetLabelTextColor().
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+status - boolean
+
+Return value: N/A
+
+See also: SetLabelTextColor
+
+
+MD5: 1e4a2ea507466dd58981d8a898254e61
diff --git a/gemrb/docs/en/GUIScript/SetMapnote.txt b/gemrb/docs/en/GUIScript/SetMapnote.txt
new file mode 100644
index 0000000..31e81b1
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetMapnote.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SetMapnote(X, Y, color, text)
+
+Description: Adds or removes a mapnote to the current map (area).
+
+Parameters:
+X,Y: the position of the mapnote
+color: the color index (0-7) of the note (in case of PST it is only 0 or 1)
+text: string, the text of the note. If it's empty, the mapnote is removed.
+
+Return value: N/A
+
+See also: CreateMapControl
+
+
+MD5: eb32f54196b822f64fc53b72c071d64d
diff --git a/gemrb/docs/en/GUIScript/SetMasterScript.txt b/gemrb/docs/en/GUIScript/SetMasterScript.txt
new file mode 100644
index 0000000..ba7de9d
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetMasterScript.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetMasterScript(ScriptResRef, WMPResRef)
+
+Description: Sets the worldmap and master script names. This function is required if you want to alter the worldmap or the master script (simulating the ToB or HoW expansions).
+
+Parameters:
+ScriptResRef - the name of the master script (.bcs resref).
+WMPResRef - the name of the worldmap (.wmp resref).
+
+Return value: N/A
+
+See also: LoadGame
+
+
+MD5: 35143c4d12005fd3162c08cf6851ccfb
diff --git a/gemrb/docs/en/GUIScript/SetMemorizableSpellsCount.txt b/gemrb/docs/en/GUIScript/SetMemorizableSpellsCount.txt
new file mode 100644
index 0000000..174c72a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetMemorizableSpellsCount.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetMemorizableSpellsCount(PartyID, Value, SpellType, Level)
+
+Description: Sets number of memorizable spells of given type and level in a player character's spellbook.
+
+Parameters:
+PartyID - the PC's position in the party
+Value - number of memorizable spells
+SpellType - 0 - priest, 1 - wizard, 2 - innate
+Level - the memorized spell's level
+
+Return value: N/A
+
+See also: GetMemorizableSpellsCount
+
diff --git a/gemrb/docs/en/GUIScript/SetModalState.txt b/gemrb/docs/en/GUIScript/SetModalState.txt
new file mode 100644
index 0000000..ff42de7
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetModalState.txt
@@ -0,0 +1,18 @@
+
+Prototype: GemRB.SetModalState(Slot, Value[, spell])
+
+Description: Sets a player character's modal state. The modal states are listed in ie_modal.py.
+
+Parameters: Slot - actor index in game structure
+ Value - New modal state
+ Spell - the spell resource associated with the state
+
+If spell is not given, a default value from modal.2da will be used.
+
+Return value: N/A
+
+Examples:
+ GemRB.SetModalState (pc, MS_TURNUNDEAD)
+The above example makes the player start the turn undead action.
+
+See also: SetPlayerStat, SetPlayerName
diff --git a/gemrb/docs/en/GUIScript/SetMouseScrollSpeed.txt b/gemrb/docs/en/GUIScript/SetMouseScrollSpeed.txt
new file mode 100644
index 0000000..b342d2b
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetMouseScrollSpeed.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.SetMouseScrollSpeed(speed)
+
+Description: Adjusts the mouse scroll speed.
+
+Parameters:
+speed -
+
+Return value: N/A
+
+See also: SetTooltipDelay
diff --git a/gemrb/docs/en/GUIScript/SetNextScript.txt b/gemrb/docs/en/GUIScript/SetNextScript.txt
new file mode 100644
index 0000000..c8c2fac
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetNextScript.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SetNextScript(scriptname)
+
+Description: Instructs the GUIScript engine to load the script when this script has terminated. (Usually it is the last command before return).
+
+Parameters: scriptname - It is the name of the python script to be executed. The name of the script may not exceed 60 characters.
+
+Return value: N/A
+
+Example: GemRB.SetNextScript("CharGen")
+ return
+
+See also: QuitGame
+
+
+MD5: 5c3c21b6a1f1bc4f00041cc075e35b45
diff --git a/gemrb/docs/en/GUIScript/SetPlayerName.txt b/gemrb/docs/en/GUIScript/SetPlayerName.txt
new file mode 100644
index 0000000..bead6db
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetPlayerName.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.SetPlayerName(Slot, Name[, LongOrShort])
+
+Description: Sets the player name. Each actor has 2 names, this command can set either or both.
+
+Parameters: Slot - numeric, the character's slot
+ Name - string, the name
+ LongOrShort- 0 (both), 1 - (short), 2 - (long)
+
+Return value: N/A
+
+Example:
+ GemRB.SetPlayerName(MyChar, GemRB.GetToken("CHARNAME"), 0)
+In the above example we set the player's name to a previously set Token (Global String).
+
+See also: QueryText, GetToken
+
diff --git a/gemrb/docs/en/GUIScript/SetPlayerScript.txt b/gemrb/docs/en/GUIScript/SetPlayerScript.txt
new file mode 100644
index 0000000..a166f17
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetPlayerScript.txt
@@ -0,0 +1,12 @@
+
+Prototype: GemRB.SetPlayerScript(Slot, Index, ScriptName)
+
+Description: Sets the player character's script. Normally only one of the scripts are customisable via the GUI.
+
+Parameters: Slot - numeric, the character's slot
+ Index - the script index (see scrlevel.2da)
+ ScriptName - the script resource
+
+Return value: N/A
+
+See also: GetPlayerScript
diff --git a/gemrb/docs/en/GUIScript/SetPlayerSound.txt b/gemrb/docs/en/GUIScript/SetPlayerSound.txt
new file mode 100644
index 0000000..53c54ce
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetPlayerSound.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.SetPlayerSound(Slot, SoundFolder)
+
+Description: Sets the player character's soundset.
+
+Parameters: Slot - numeric, the character's slot
+ SoundFolder - string, a folder in Sounds (iwd2 style), or a filename (bg2 style)
+
+Return value: N/A
+
+See also: FillPlayerInfo, SetPlayerString
diff --git a/gemrb/docs/en/GUIScript/SetPlayerStat.txt b/gemrb/docs/en/GUIScript/SetPlayerStat.txt
new file mode 100644
index 0000000..d6eb59e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetPlayerStat.txt
@@ -0,0 +1,19 @@
+
+Prototype: GemRB.SetPlayerStat(Slot, ID, Value[, PCF])
+
+Description: Sets a player character's base stat. The stats are listed in ie_stats.py.
+
+Parameters: Slot - actor index in game structure
+ ID - Stat index
+ Value - New stat value
+ PCF - Set to 0 if you don't want the stat's post-change function to be ran
+
+Return value: N/A
+
+Example:
+ PickedColor=GemRB.GetTableValue(ColorTable, ColorIndex, GemRB.GetVar("Selected"))
+ GemRB.SetPlayerStat (pc, IE_MAJOR_COLOR, PickedColor)
+The above example sets the player's color just picked via the color customisation dialog. ColorTable holds the available colors.
+
+See also: GetPlayerStat, SetPlayerName, ApplyEffect
+
diff --git a/gemrb/docs/en/GUIScript/SetPlayerString.txt b/gemrb/docs/en/GUIScript/SetPlayerString.txt
new file mode 100644
index 0000000..9562262
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetPlayerString.txt
@@ -0,0 +1,12 @@
+Missing function already used in bg2 biography (cg uses a token)
+
+Prototype:
+
+Description:
+
+Parameters:
+
+Return value:
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/SetPurchasedAmount.txt b/gemrb/docs/en/GUIScript/SetPurchasedAmount.txt
new file mode 100644
index 0000000..546765b
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetPurchasedAmount.txt
@@ -0,0 +1,12 @@
+
+Prototype: SetPurchasedAmount( Index, Amount )
+
+Description: Sets the amount of purchased items of a type. If it was 0, then the item will be deselected from the purchase list. This function works only with an active store.
+
+Parameters: Index - the store item's index
+ Amount - a numeric value not less than 0
+
+Return value: None
+
+See also: EnterStore, LeaveStore, SetPurchasedAmount
+
diff --git a/gemrb/docs/en/GUIScript/SetRepeatClickFlags.txt b/gemrb/docs/en/GUIScript/SetRepeatClickFlags.txt
new file mode 100644
index 0000000..852343d
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetRepeatClickFlags.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.SetRepeatClickFlags(value, op)
+
+Description: Sets the mode repeat clicks are handled.
+
+Parameters:
+value - speed, see the GEM_RK* flags in GUIDefines.py
+op - operation
+
+Return value: N/A
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/SetSaveGamePortrait.txt b/gemrb/docs/en/GUIScript/SetSaveGamePortrait.txt
new file mode 100644
index 0000000..8283acb
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetSaveGamePortrait.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.SetSaveGamePortrait(WindowIndex, ControlIndex, SaveSlotCount, PCSlotCount)
+
+Metaclass Prototype: SetSaveGamePortrait(SaveSlotCount, PCSlotCount)
+
+Description: Sets a savegame PC portrait bmp onto a button as picture.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+SlotCount - the index of a saved game
+PCSlotCount - the index of the portrait in the saved game
+
+Return value: N/A
+
+See also: SetSaveGamePreview
+
diff --git a/gemrb/docs/en/GUIScript/SetSaveGamePreview.txt b/gemrb/docs/en/GUIScript/SetSaveGamePreview.txt
new file mode 100644
index 0000000..47c9e40
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetSaveGamePreview.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.SetSaveGamePreview(WindowIndex, ControlIndex, SlotCount)
+
+Metaclass Prototype: SetSaveGamePreview(SlotCount)
+
+Description: Sets a savegame area preview bmp onto a button as picture.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+ SlotCount - the index of a saved game
+
+Return value: N/A
+
+See also: SetSaveGamePortrait
+
diff --git a/gemrb/docs/en/GUIScript/SetScrollBarSprites.txt b/gemrb/docs/en/GUIScript/SetScrollBarSprites.txt
new file mode 100644
index 0000000..4b93c34
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetScrollBarSprites.txt
@@ -0,0 +1,23 @@
+
+Prototype: GemRB.SetScrollBarSprites(WindowIndex, ControlIndex, ResRef, Cycle, UpUnpressedFrame, UpPressedFrame, DownUnpressedFrame, DownPressedFrame, TroughFrame, SliderFrame)
+
+Metaclass Prototype: SetSprites(ResRef, Cycle, UpUnpressedFrame, UpPressedFrame, DownUnpressedFrame, DownPressedFrame, TroughFrame, SliderFrame)
+
+Description: Sets a ScrollBar Sprites Images.
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+ResRef - bam resref
+Cycle - cycle number
+UpUnpressedFrame - frame number
+UpPressedFrame - frame number
+DownUnpressedFrame - frame number
+DownPressedFrame - frame number
+TroughFrame - frame number
+SliderFrame - frame number
+
+Return value:
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/SetSpellIcon.txt b/gemrb/docs/en/GUIScript/SetSpellIcon.txt
new file mode 100644
index 0000000..aef782a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetSpellIcon.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.SetSpellIcon(WindowIndex, ControlIndex, SPLResRef[, Type, Tooltip, Function])
+
+Metaclass Prototype: SetSpellIcon(SPLResRef[, Type, Tooltip, Function])
+
+Description: Sets Spell icon image on a Button control. Type determines the icon type, if set to 1 it will use the Memorised Icon instead of the Spellbook Icon
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+SPLResRef - the name of the spell (.spl resref)
+Type - 0 (default, use parchment background) or 1 (use stone background)
+Tooltip - 0 (default); if 1, set the tooltip "F<n> <spell_name>"
+Function - number to be used in the tooltip above
+
+Return value: N/A
+
+See also: SetItemIcon
+
+
+MD5: b7895d38a8e3f389bb1f702ce99bcf57
diff --git a/gemrb/docs/en/GUIScript/SetTAHistory.txt b/gemrb/docs/en/GUIScript/SetTAHistory.txt
new file mode 100644
index 0000000..216f525
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetTAHistory.txt
@@ -0,0 +1,13 @@
+
+Prototype: SetTAHistory(Win, Ctrl, KeepLines)
+
+Metaclass Prototype: SetHistory(KeepLines)
+
+Description: Sets up a textarea control for holding large amounts of text. If more than 'KeepLines' text scrolls up above, it will be discarded. This works best if TEXTAREA_AUTOSCROLL was set. The text is cut when new text is appended to the buffer, without TEXTAREA_AUTOSCROLL there is no automatic scrolling out of text. This function sets the TEXTAREA_HISTORY flag.
+
+Parameters: Win, Ctrl - a window index/control index pair of the textarea
+ KeepLines - the number of lines to be kept
+
+Return value: N/A
+
+See also: RewindTA, SetTextAreaFlags
diff --git a/gemrb/docs/en/GUIScript/SetText.txt b/gemrb/docs/en/GUIScript/SetText.txt
new file mode 100644
index 0000000..ca43b56
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetText.txt
@@ -0,0 +1,18 @@
+
+Prototype: GemRB.SetText(WindowIndex, ControlIndex, String|Strref)
+
+Metaclass Prototype: SetText(String|Strref)
+
+Description: Sets the Text of a control in a Window. In case of strrefs, any tokens contained by the string will be resolved. (For example the substring "<CHARNAME>" will be replaced by the "CHARNAME" token.) -1 is a special Strref, it will be resolved to the name/version of the running engine.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+String - an arbitrary string
+Strref - a string index from the dialog.tlk table.
+
+Return value:
+0 on success, -1 otherwise
+
+See also: QueryText, DisplayString, GetControl, accessing_gui_controls
+
+MD5: 583a8146b015a25b137c43a036043991
diff --git a/gemrb/docs/en/GUIScript/SetTextAreaFlags.txt b/gemrb/docs/en/GUIScript/SetTextAreaFlags.txt
new file mode 100644
index 0000000..19e5710
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetTextAreaFlags.txt
@@ -0,0 +1,34 @@
+
+Prototype: GemRB.SetTextAreaFlags(WindowIndex, ControlIndex, Flags, Operation)
+
+Metaclass Prototype: SetFlags(Flags, Operation)
+
+Description: Sets the Flags of a TextArea.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+Flags - various bits altering the behaviour of the control
+To make sure the flags are not misplaced, the high byte must be 5 (the textarea control's type).
+
+IE_GUI_TEXTAREA_SELECTABLE - 0x05000001
+The TextArea will be able to return a selected row.
+IE_GUI_TEXTAREA_AUTOSCROLL - 0x05000002
+Some TextAreas are autoscrolling while text was written into them, thus always showing the last content.
+IE_GUI_TEXTAREA_SMOOTHSCROLL - 0x05000004
+The TextArea will slowly scroll its content. If it is out of text, it will call the TEXTAREA_OUT_OF_TEXT callback.
+IE_GUI_TEXTAREA_HISTORY - 0x05000008
+The TextArea will drop some of the scrolled out text.
+
+Operation - could be OP_SET, OP_OR or OP_NAND, default is OP_SET.
+
+Return value: N/A
+
+Example:
+ TextAreaControl = GemRB.GetControl(SoundWindow, 45)
+ GemRB.SetTextAreaFlags(SoundWindow, TextAreaControl,IE_GUI_TEXTAREA_SELECTABLE)
+ GemRB.SetVarAssoc(SoundWindow, TextAreaControl, "Sound", 0)
+ RowCount=GemRB.GetCharSounds(SoundWindow, TextAreaControl)
+The above code will set up the TextArea as a ListBox control, by reading the names of available character soundsets into the TextArea and setting it up as selectable. When the user clicks on row, the "Sound" variable will be assigned a row number.
+
+See also: RewindTA, SetTAHistory, GetCharSounds, GetCharacters, QueryText, accessing_gui_controls
+
+MD5: 30fd7c88972e5fc7e4f41158a28ceaff
diff --git a/gemrb/docs/en/GUIScript/SetTimedEvent.txt b/gemrb/docs/en/GUIScript/SetTimedEvent.txt
new file mode 100644
index 0000000..79a692f
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetTimedEvent.txt
@@ -0,0 +1,12 @@
+
+Prototype: GemRB.SetTimedEvent(FunctionName, rounds)
+
+Description: Sets a timed event to be called by the Game object. If there is no game loaded, this command is ignored. If the game is unloaded, the event won't be called.
+
+Parameters:
+FunctionName - a python function object
+rounds - the time when the function should be called
+
+Return value: N/A
+
+See also: SetEvent
diff --git a/gemrb/docs/en/GUIScript/SetToken.txt b/gemrb/docs/en/GUIScript/SetToken.txt
new file mode 100644
index 0000000..72bcb29
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetToken.txt
@@ -0,0 +1,27 @@
+
+Prototype: GemRB.SetToken(VariableName, Value)
+
+Description: Set/Create a token to be replaced in StrRefs. QueryText() will use the actual value of the token when it encounters "<token>" substrings in a retrieved dialog.tlk entry. Some tokens are hardcoded and you can't affect QueryText() by setting them. Usage of those tokens should be avoided. The hardcoded token list:
+FIGHTERTYPE - always resolves to strref #10174
+RACE - always resolves to the race of the last speaker
+GABBER - always resolves to the longname of the last speaker
+SIRMAAM - strref #27473/#27475 depending on last speaker's gender
+GIRLBOY - strref #27477/#27476 depending on last speaker's gender
+BROTHERSISTER- strref #27478/#27479 ...
+LADYLORD - strref #27481/#27480 ...
+MALEFEMALE - strref #27483/#27482 ...
+HESHE - strref #27485/#27484 ...
+HISHER - strref #27487/#27486 ...
+HIMHER - strref #27487/#27488 ...
+MANWOMAN - strref #27490/#27489 ...
+PRO_* - same as above with protagonist
+
+Parameters:
+ VariableName - the name of the variable must be shorter than 32 bytes
+ Value - string, the value of the token
+
+Return value: N/A
+
+See also: GetToken, QueryText
+
+MD5: fd99519e2b63105ddb7d772476886701
diff --git a/gemrb/docs/en/GUIScript/SetTooltip.txt b/gemrb/docs/en/GUIScript/SetTooltip.txt
new file mode 100644
index 0000000..dff924e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetTooltip.txt
@@ -0,0 +1,26 @@
+
+Prototype: GemRB.SetTooltip(WindowIndex, ControlIndex, String|Strref)
+
+Metaclass Prototype: SetTooltip(String|Strref)
+
+Description: Sets control's tooltip. Any control may have a tooltip.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+String - an arbitrary string
+Strref - a string index from the dialog.tlk table.
+
+Return value:
+0 on success, -1 on error
+
+The tooltip's visual properties must be set in the gemrb.ini file:
+ TooltipFont - Font used to display tooltips
+ TooltipBack - Sprite displayed behind the tooltip text, if any
+ TooltipColor - Tooltip text color (RGBA)
+ TooltipMargin - Space between tooltip text and sides of TooltipBack (x2)
+
+
+
+See also: SetText
+
+
+MD5: 055166c150ec66ca9649006b42114967
diff --git a/gemrb/docs/en/GUIScript/SetTooltipDelay.txt b/gemrb/docs/en/GUIScript/SetTooltipDelay.txt
new file mode 100644
index 0000000..a381204
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetTooltipDelay.txt
@@ -0,0 +1,9 @@
+
+Prototype: GemRB.SetTooltipDelay(time)
+
+Description: Sets the tooltip delay.
+
+Parameters:
+time - 0-10
+
+See also: SetTooltip, SetMouseScrollSpeed
diff --git a/gemrb/docs/en/GUIScript/SetVar.txt b/gemrb/docs/en/GUIScript/SetVar.txt
new file mode 100644
index 0000000..003db0b
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetVar.txt
@@ -0,0 +1,28 @@
+
+Prototype: GemRB.SetVar(VariableName, Value)
+
+Description: Set a Variable of the Global Dictionary. This is an independent dictionary from the gamescript. It contains configuration variables, and provides a flexible interface between guiscript and the engine core. There are some preserved names that are referenced from the core, these are described in different places.
+variable described in
+PlayMode - LoadGame
+*Window - HideGUI
+*Position - HideGUI
+Progress - data_exchange
+
+Parameters: VariableName - the name of the variable must be shorter than 32 bytes
+ Value - numeric, the new value
+
+Return value: N/A
+
+Examples:
+ GemRB.SetVar("ActionsWindow", ActionsWindow)
+ GemRB.SetVar("OptionsWindow", OptionsWindow)
+ GemRB.SetVar("MessageWindow", MessageWindow)
+ GemRB.SetVar("ActionsPosition", 4) #BottomAdded
+ GemRB.SetVar("OptionsPosition", 0) #Left
+ GemRB.SetVar("MessagePosition", 4) #BottomAdded
+The above lines set up some windows of the main game screen.
+
+See also: SetVarAssoc, SetToken, LoadGame, HideGUI, data_exchange
+
+
+MD5: f85bcb143ff3359936e78780da210367
diff --git a/gemrb/docs/en/GUIScript/SetVarAssoc.txt b/gemrb/docs/en/GUIScript/SetVarAssoc.txt
new file mode 100644
index 0000000..e59aa64
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetVarAssoc.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.SetVarAssoc(WindowIndex, ControlIndex, VariableName, LongValue)
+
+Metaclass Prototype: SetVarAssoc(VariableName, LongValue)
+
+Description: It associates a variable name and value with a control. The control uses this associated value differently, depending on the control. See more about this in "data_exchange".
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+Variablename - string, a Global Dictionary Name associated with the control
+LongValue - numeric, a value associated with the control
+
+Return value: N/A
+
+Special: If the 'DialogChoose' variable was set to -1 or 0 during a dialog session, it will terminate (-1) or pick the first available option (0) from the dialog automatically. (0 is used for 'continue', -1 is used for 'end dialogue').
+
+See also: SetButtonFlags, SetVar, GetVar, data_exchange, accessing_gui_controls
+
+
+MD5: df5c51a71e7368c22b2d3e5a0bac311a
diff --git a/gemrb/docs/en/GUIScript/SetVisible.txt b/gemrb/docs/en/GUIScript/SetVisible.txt
new file mode 100644
index 0000000..995521d
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetVisible.txt
@@ -0,0 +1,21 @@
+
+Prototype: GemRB.SetVisible(WindowIndex, Visible)
+
+Metaclass Prototype: SetVisible(Visible)
+
+Description: Sets the Visibility Flag of a Window.
+Window index 0 can be used to manipulate the game control window
+
+Parameters: WindowIndex - the index returned by LoadWindow()
+ Visible:
+WINDOW_INVISIBLE - Window is invisible
+WINDOW_VISIBLE - Window is visible
+WINDOW_GRAYED - Window is shaded
+WINDOW_FRONT - Window is drawn in the foreground
+
+Return value: N/A
+
+See also: ShowModal, SetWindowFrame
+
+
+MD5: cc9b69351e5e62f28a5c65247cae7880
diff --git a/gemrb/docs/en/GUIScript/SetWindowFrame.txt b/gemrb/docs/en/GUIScript/SetWindowFrame.txt
new file mode 100644
index 0000000..00632b6
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetWindowFrame.txt
@@ -0,0 +1,24 @@
+Prototype: GemRB.SetWindowFrame(WindowIndex)
+
+Metaclass Prototype: SetFrame()
+
+Description: Sets Window to have a frame used to fill screen on higher
+ resolutions. At present all windows having frames have the same one.
+
+ To automatically move the windows from the edges and to let GemRB know
+ how much space is there, the LoadWindowPack() function should be called
+ with size parameters.
+
+ Make sure to set the frame only for the window on the bottom, because the
+ frames will erase the whole screen before drawing.
+
+Parameters:
+ WindowIndex - the index returned by LoadWindow()
+
+Return value: N/A
+
+See also: LoadWindowFrame, LoadWindowPack, LoadWindow, SetWindowPos
+
+MD5: 1b3199cc5121101643080fbac127f93d
+
+
diff --git a/gemrb/docs/en/GUIScript/SetWindowPicture.txt b/gemrb/docs/en/GUIScript/SetWindowPicture.txt
new file mode 100644
index 0000000..c4f890e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetWindowPicture.txt
@@ -0,0 +1,23 @@
+
+Prototype: GemRB.SetWindowPicture(WindowIndex, MosResRef)
+
+Metaclass Prototype: SetPicture(MosResRef)
+
+Description: Changes the background of a Window.
+
+Parameters:
+WindowIndex - the index returned by LoadWindow()
+MosResRef - the name of the background image (.mos resref)
+
+Return value: N/A
+
+Example:
+ LoadPic = GemRB.GetGameString (STR_LOADMOS)
+ if LoadPic=="":
+ LoadPic = "GUILS0"+str(GemRB.Roll(1,9,0))
+ GemRB.SetWindowPicture(LoadScreen, LoadPic)
+The above snippet is responsible to generate a random loading screen.
+
+See also: SetButtonPicture
+
+MD5: 62884cae84603d33be1551873d6b93cc
diff --git a/gemrb/docs/en/GUIScript/SetWindowPos.txt b/gemrb/docs/en/GUIScript/SetWindowPos.txt
new file mode 100644
index 0000000..2b68fc6
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetWindowPos.txt
@@ -0,0 +1,36 @@
+
+Prototype: GemRB.SetWindowPos(WindowIndex, X, Y, [Flags=WINDOW_TOPLEFT])
+
+Metaclass Prototype: SetPos(X, Y, [Flags=WINDOW_TOPLEFT])
+
+Description: Moves a Window.
+
+Parameters:
+WindowIndex - the index returned by LoadWindow()
+X,Y - placement of the window, see Flags below for meaning
+ for each flag
+Flags - bitmask of WINDOW_(TOPLEFT|CENTER|ABSCENTER|BOUNDED),
+ used to modify the meaning of X and Y.
+ TOPLEFT : X, Y are coordinates of upper-left corner.
+ CENTER : X, Y are coordinates of window's center.
+ ABSCENTER: window is placed at screen center, moved by X, Y.
+ RELATIVE : window is moved by X, Y.
+ SCALE : window is moved by diff of screen size and X, Y, divided by 2.
+ BOUNDED : the window is kept within screen boundaries.
+
+
+Return value: N/A
+
+Note:
+ All flags except WINDOW_BOUNDED are mutually exclusive
+
+Example:
+ x, y = GemRB.GetVar ("MenuX"), GemRB.GetVar ("MenuY")
+ GemRB.SetWindowPos (Window, x, y, WINDOW_CENTER | WINDOW_BOUNDED)
+
+The above example is from the PST FloatMenuWindow script. It centers
+pie-menu window around the mouse cursor, but keeps it within screen.
+
+See also: SetWindowSize, SetControlPos
+
+MD5: e560702a81119365804f7944792d9b01
diff --git a/gemrb/docs/en/GUIScript/SetWindowSize.txt b/gemrb/docs/en/GUIScript/SetWindowSize.txt
new file mode 100644
index 0000000..025f8f3
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetWindowSize.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.SetWindowSize(WindowIndex, Width, Height)
+
+Metaclass Prototype: SetSize(Width, Height)
+
+Description: Resizes a Window.
+
+Parameters:
+WindowIndex - the index returned by LoadWindow()
+Width, Height - the new dimensions of the window
+
+Return value: N/A
+
+See also: LoadWindow, SetWindowPos, SetControlSize
+
+MD5: 2188d78694c9c447bf881fb37a9bfa4e
diff --git a/gemrb/docs/en/GUIScript/SetWorldMapTextColor.txt b/gemrb/docs/en/GUIScript/SetWorldMapTextColor.txt
new file mode 100644
index 0000000..09e60a6
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetWorldMapTextColor.txt
@@ -0,0 +1,19 @@
+
+Prototype: GemRB.SetWorldMapTextColor(WindowIndex, ControlIndex, which, red, green, blue)
+
+Metaclass Prototype: SetTextColor(which, red, green, blue)
+
+Description: Sets the label colors of a WorldMap Control. "which" selects the color affected and is one of IE_GUI_WMAP_COLOR_(BACKGROUND|NORMAL|SELECTED|NOTVISITED).
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+which - selects the color affected (one of IE_GUI_WMAP_COLOR_(BACKGROUND|NORMAL|SELECTED|NOTVISITED)
+red - red value
+green - green value
+blue - blue value
+
+Return value: N/A
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/SetupControls.txt b/gemrb/docs/en/GUIScript/SetupControls.txt
new file mode 100644
index 0000000..61bfd09
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetupControls.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.SetupControls(WindowIndex, Slot)
+
+Metaclass Prototype: SetupControls(Slot)
+
+Description: Sets up all 12 action buttons for a player character, based on preset preferences and the character's class.
+
+Parameters:
+WindowIndex - the buttons' window index
+Slot - the player character's index in the party
+
+Return value: N/A
+
+See also: SetActionIcon, SetDefaultActions, SetupEquipmentIcons, SetupSpellIcons
diff --git a/gemrb/docs/en/GUIScript/SetupEquipmentIcons.txt b/gemrb/docs/en/GUIScript/SetupEquipmentIcons.txt
new file mode 100644
index 0000000..81c58a9
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetupEquipmentIcons.txt
@@ -0,0 +1,17 @@
+
+Prototype: GemRB.SetupEquipmentIcons(WindowIndex, Slot[, Start])
+
+Metaclass Prototype: SetupEquipmentIcons(Slot[, Start])
+
+Description: Sets up all 12 action buttons for a player character with the usable equipment functions.
+It also sets up the scroll buttons left and right if needed.
+If Start is supplied, it will skip the first few items.
+
+Parameters:
+WindowIndex - the buttons' window index
+Slot - the player character's index in the party
+Start - start the equipment list from this value
+
+Return value: N/A
+
+See also: SetupSpellIcons, SetupControls, UseItem
diff --git a/gemrb/docs/en/GUIScript/SetupQuickSlot.txt b/gemrb/docs/en/GUIScript/SetupQuickSlot.txt
new file mode 100644
index 0000000..a22087a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetupQuickSlot.txt
@@ -0,0 +1,22 @@
+Prototype: GemRB.SetupQuickSlot(PartyID, QuickSlotID, InventorySlot[, AbilityIndex])
+
+Description: Sets up a quickslot to point to a particular inventory slot.
+Also sets the used ability for that given quickslot.
+If the abilityindex is omitted, it will be assumed as 0.
+If the InventorySlot is -1, then it won't be assigned to the quickslot
+(this way you can alter the used Ability index only).
+If the QuickSlotID is 0, then it will try to find the quickslot/weaponslot
+by the InventorySlot, and assign the AbilityIndex to it.
+(Use this if you don't know the exact quick slot, or don't care to find it).
+
+Parameters:
+PartyID - the PC's position in the party (1 based)
+QuickSlotID - the quickslot to set up
+InventorySlot - the inventory slot assigned to this quickslot, this is usually constant
+and taken care by the core
+AbilityIndex - the number of the item extended header to use with this quickslot
+
+Return value: N/A
+
+See also: GetEquippedQuickSlot, SetEquippedQuickSlot
+
diff --git a/gemrb/docs/en/GUIScript/SetupSpellIcons.txt b/gemrb/docs/en/GUIScript/SetupSpellIcons.txt
new file mode 100644
index 0000000..343a4dd
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SetupSpellIcons.txt
@@ -0,0 +1,18 @@
+
+Prototype: GemRB.SetupSpellIcons(WindowIndex, Slot, Type[, Start])
+
+Metaclass Prototype: SetupSpellIcons(Slot, Type[, Start])
+
+Description: Sets up all 12 action buttons for a player character with spell or innate icons.
+It also sets up the scroll buttons left and right if needed.
+If Start is supplied, it will skip the first few items.
+
+Parameters:
+WindowIndex - the buttons' window index
+Slot - the player character's index in the party
+Type - the spell type bitfield (1-mage, 2-priest, 4-innate)
+Start - start the spell list from this value
+
+Return value: N/A
+
+See also: SetupEquipmentIcons, SetupControls, SpellCast
diff --git a/gemrb/docs/en/GUIScript/ShowModal.txt b/gemrb/docs/en/GUIScript/ShowModal.txt
new file mode 100644
index 0000000..e4d873a
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/ShowModal.txt
@@ -0,0 +1,18 @@
+
+Prototype: GemRB.ShowModal(WindowIndex, [Shadow=MODAL_SHADOW_NONE])
+
+Metaclass Prototype: ShowModal([Shadow=MODAL_SHADOW_NONE])
+
+Description: Show a Window on Screen setting the Modal Status. If Shadow is MODAL_SHADOW_GRAY, other windows are grayed. If Shadow is MODAL_SHADOW_BLACK, they are blacked out.
+
+Parameters: WindowIndex - the index returned by LoadWindow()
+Shadow - 0,1 or 2
+MODAL_SHADOW_NONE = 0
+MODAL_SHADOW_GRAY = 1
+MODAL_SHADOW_BLACK = 2
+
+Return value: N/A
+
+See also: SetVisible
+
+MD5: fdde4cda097b7ab5cd23aae7fb1313ec
diff --git a/gemrb/docs/en/GUIScript/SoftEndPL.txt b/gemrb/docs/en/GUIScript/SoftEndPL.txt
new file mode 100644
index 0000000..3fed530
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SoftEndPL.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.SoftEndPL()
+
+Description: Ends the currently playing Music Playlist softly.
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: HardEndPL
+
diff --git a/gemrb/docs/en/GUIScript/SpellCast.txt b/gemrb/docs/en/GUIScript/SpellCast.txt
new file mode 100644
index 0000000..cdf39a6
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SpellCast.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.SpellCast(PartyID, Type, Spell)
+
+Description: Makes PartyID to cast a spell of Type. This handles targeting and executes the appropriate scripting command.
+
+Parameters:
+PartyID - the player character's index in the party
+Type - the spell type bitfield (1-mage, 2-priest, 4-innate)
+Spell - the spell's index in the list
+
+Return value: N/A
+
+See also: SetupSpellIcons
diff --git a/gemrb/docs/en/GUIScript/StatComment.txt b/gemrb/docs/en/GUIScript/StatComment.txt
new file mode 100644
index 0000000..b081a8e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/StatComment.txt
@@ -0,0 +1,20 @@
+
+Prototype: GemRB.StatComment(Strref, X, Y)
+
+Description: Replaces %d's with the values of X and Y in a string referenced by strref. PST uses %d values in place of tokens, thus it requires a special command. (Note that you can safely replace this command with a few Python scripting commands). In other engines use GetString after setting the needed tokens by SetToken (if you need to set them at all).
+
+Parameters: Sttref - a string index from the dialog.tlk table.
+ X,Y - two numerical values to be replaced in place of %d's.
+
+Return value: A string with resolved %d's.
+
+Example:
+def IntPress():
+ GemRB.SetText(NewLifeWindow, TextArea, 18488)
+ GemRB.TextAreaAppend(NewLifeWindow, TextArea, "\n\n"+GemRB.StatComment(GemRB.GetTableValue(StatTable,Int,1),0,0) )
+ return
+
+The above example comes directly from our PST script, it will display the description of the intelligence stat (strref==18488), adding a comment based on the current Int variable. StatTable (a table index) contains the comment strref values associated with an intelligence value.
+
+See also: GetString, SetToken, GetTableValue, SetText, LoadTable, TextAreaAppend
+
diff --git a/gemrb/docs/en/GUIScript/StealFailed.txt b/gemrb/docs/en/GUIScript/StealFailed.txt
new file mode 100644
index 0000000..9a9f460
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/StealFailed.txt
@@ -0,0 +1,12 @@
+
+Prototype: StealFailed()
+
+Description: Triggers the steal failed event for the currently selected actor.
+The event will be triggered in the actor 'owning' the store currently open.
+
+Parameters: None
+
+Return value: N/A
+
+See also: GetStore, EnterStore, LeaveStore, GameGetSelectedPCSingle
+
diff --git a/gemrb/docs/en/GUIScript/SwapPCs.txt b/gemrb/docs/en/GUIScript/SwapPCs.txt
new file mode 100644
index 0000000..fe5e212
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/SwapPCs.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.SwapPCs(idx1, idx2)
+
+Description: Swaps the party order for two player characters.
+
+Parameters:
+idx1 - position in the party
+idx2 - position in the party
+
+Return value: N/A
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/TextAreaAppend.txt b/gemrb/docs/en/GUIScript/TextAreaAppend.txt
new file mode 100644
index 0000000..56639b7
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/TextAreaAppend.txt
@@ -0,0 +1,23 @@
+
+Prototype: GemRB.TextAreaAppend(WindowIndex, ControlIndex, String|Strref [, Row[, Flag]])
+
+Metaclass Prototype: TextAreaAppend(String|Strref [, Row[, Flag]])
+
+Description: Appends the Text to the TextArea Control in the Window. If row is specificed, it can also append text to existing rows.
+
+Parameters:
+WindowIndex, ControlIndex - the control's reference
+String - literal text, it could have embedded colour codes
+Strref - a string index from the dialog.tlk table.
+Row - the row of text to add the text to, if omitted, the text will be added (in a new row) after the last row.
+Flag - the flags for QueryText (if strref resolution is used)
+ 1 - strrefs displayed (even if not enabled by default)
+ 2 - sound (plays the associated sound)
+ 4 - speech (works only with if sound was set)
+ 256 - strrefs not displayed (even if allowed by default)
+
+Return value:
+ Index of the row appended or changed
+
+See also: TextAreaClear, SetText, QueryText
+
diff --git a/gemrb/docs/en/GUIScript/TextAreaClear.txt b/gemrb/docs/en/GUIScript/TextAreaClear.txt
new file mode 100644
index 0000000..f924199
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/TextAreaClear.txt
@@ -0,0 +1,14 @@
+
+Prototype: GemRB.TextAreaClear(WindowIndex, ControlIndex)
+
+Metaclass Prototype: Clear()
+
+Description: Clears the Text from the TextArea Control in the Window.
+
+Parameters: WindowIndex, ControlIndex - the control's reference
+
+Return value: N/A
+
+See also: TextAreaAppend
+
+MD5: 4c563da47e77f8962c62c67a6de8c906
diff --git a/gemrb/docs/en/GUIScript/TextAreaScroll.txt b/gemrb/docs/en/GUIScript/TextAreaScroll.txt
new file mode 100644
index 0000000..0e9d306
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/TextAreaScroll.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.TextAreaScroll(WindowIndex, ControlIndex, offset)
+
+Metaclass Prototype: Scroll(offset)
+
+Description: Scrolls the textarea up or down by offset.
+
+Parameters:
+WindowIndex - the window control id
+ControlID - the id of the target control
+offset - amount to scroll
+
+Return value: N/A
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/UnhideGUI.txt b/gemrb/docs/en/GUIScript/UnhideGUI.txt
new file mode 100644
index 0000000..c184ab8
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/UnhideGUI.txt
@@ -0,0 +1,13 @@
+
+Prototype: GemRB.UnhideGUI()
+
+Description: Shows the Game GUI and redraws windows.
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: HideGUI, InvalidateWindow, SetVisible
+
+
+MD5: da75d42bc5444aff63580ee249c5b858
diff --git a/gemrb/docs/en/GUIScript/UnloadSymbol.txt b/gemrb/docs/en/GUIScript/UnloadSymbol.txt
new file mode 100644
index 0000000..c1f88f2
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/UnloadSymbol.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.UnloadSymbol(SymbolIndex)
+
+Metaclass Prototype: Unload()
+
+Description: Unloads an IDS symbol list.
+
+Parameters: SymbolIndex - returned by a previous LoadSymbol command.
+
+Return value: N/A
+
+See also: LoadSymbol, UnloadTable
+
+
+MD5: 87793ebcb18bf5c2fb1fa011245e470d
diff --git a/gemrb/docs/en/GUIScript/UnloadTable.txt b/gemrb/docs/en/GUIScript/UnloadTable.txt
new file mode 100644
index 0000000..74f8678
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/UnloadTable.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.UnloadTable(TableIndex)
+
+Metaclass Prototype: Unload()
+
+Description: Unloads a 2DA Table.
+
+Parameters: TableIndex - returned by a previous LoadTable command.
+
+Return value: N/A
+
+See also: LoadTable
+
+
+MD5: f281a449838425a6265967006640abbc
diff --git a/gemrb/docs/en/GUIScript/UnloadWindow.txt b/gemrb/docs/en/GUIScript/UnloadWindow.txt
new file mode 100644
index 0000000..46b1fd1
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/UnloadWindow.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.UnloadWindow(WindowIndex)
+
+Metaclass Prototype: Unload()
+
+Description: Unloads a previously loaded Window. EnterGame() and QuitGame() automatically unload all loaded windows.
+
+Parameters: WindowIndex - the index returned by LoadWindow()
+
+Return value: N/A
+
+See also: LoadWindow, EnterGame, QuitGame
+
+
+MD5: 0a64fd889b01eecf2351c4ebb77b3ca0
diff --git a/gemrb/docs/en/GUIScript/UnmemorizeSpell.txt b/gemrb/docs/en/GUIScript/UnmemorizeSpell.txt
new file mode 100644
index 0000000..8cb391c
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/UnmemorizeSpell.txt
@@ -0,0 +1,15 @@
+
+Prototype: GemRB.UnmemorizeSpell(PartyID, SpellType, Level, Index)
+
+Description: Unmemorizes specified memorized spell.
+
+Parameters:
+PartyID - the PC's position in the party
+SpellType - 0 - priest, 1 - wizard, 2 - innate
+Level - the memorized spell's level
+Index - the memorized spell's index
+
+Return value: boolean, 1 on success
+
+See also: MemorizeSpell, GetMemorizedSpellsCount, GetMemorizedSpell
+
diff --git a/gemrb/docs/en/GUIScript/UpdateAmbientsVolume.txt b/gemrb/docs/en/GUIScript/UpdateAmbientsVolume.txt
new file mode 100644
index 0000000..b52e27c
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/UpdateAmbientsVolume.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.UpdateAmbientsVolume()
+
+Description: Updates ambients volume on-the-fly.
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: UpdateMusicVolume
+
diff --git a/gemrb/docs/en/GUIScript/UpdateMusicVolume.txt b/gemrb/docs/en/GUIScript/UpdateMusicVolume.txt
new file mode 100644
index 0000000..e6ab838
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/UpdateMusicVolume.txt
@@ -0,0 +1,11 @@
+
+Prototype: GemRB.UpdateMusicVolume()
+
+Description: Updates music volume on-the-fly.
+
+Parameters: N/A
+
+Return value: N/A
+
+See also: UpdateAmbientsVolume
+
diff --git a/gemrb/docs/en/GUIScript/UseItem.txt b/gemrb/docs/en/GUIScript/UseItem.txt
new file mode 100644
index 0000000..747609f
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/UseItem.txt
@@ -0,0 +1,16 @@
+
+Prototype: GemRB.UseItem(PartyID, Slot, ItemIndex)
+
+Description: Makes PartyID to use an item.
+If slot is negative, then item is the index of the item functionality in the use
+item list. If slot is non-negative, then item is the quick item or weapon in slot.
+This handles targeting and executes the appropriate scripting command.
+
+Parameters:
+PartyID - the player character's index in the party
+Slot - the item's inventory slot, if it is -1 then ItemIndex is used
+ItemIndex - the item index from the SetupEquipmentIcons list, an item may have multiple entries, because of multiple features
+
+Return value: N/A
+
+See also: CanUseItemType, SpellCast, SetupEquipmentIcons
diff --git a/gemrb/docs/en/GUIScript/accessing_gui_controls.txt b/gemrb/docs/en/GUIScript/accessing_gui_controls.txt
new file mode 100644
index 0000000..42638c4
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/accessing_gui_controls.txt
@@ -0,0 +1,11 @@
+Accessing GUI controls.
+
+Description: To access a GUI control, you must know its window index and control index. You must use LoadWindow and GetControl to obtain these indices.
+Usually a GUI command works only on one type of controls. A wrong control type will cause a Runtime Error and terminates the GUI script (not the game or the engine).
+
+Example:
+ StartWindow = GemRB.LoadWindow(7)
+ Label = GemRB.GetControl(StartWindow, 0x0fff0000)
+ GemRB.SetText(StartWindow, Label, GEMRB_VERSION)
+
+In the above example we just load a window whose window ID is 7, and store its window index in StartWindow. We also try to find a control whose control ID is 0xfff0000 and resides on the window pointed by StartWindow.
diff --git a/gemrb/docs/en/GUIScript/bit_operation.txt b/gemrb/docs/en/GUIScript/bit_operation.txt
new file mode 100644
index 0000000..77e36f2
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/bit_operation.txt
@@ -0,0 +1,7 @@
+The following bitwise operators exist in many guiscript commands.
+
+ BM_SET 0 - sets exact value
+ BM_AND 1 - performs the AND operation (turns off unlisted bits)
+ BM_OR 2 - performs the OR operation (turns on listed bits)
+ BM_XOR 3 - performs the XOR operation (toggles the listed bits)
+ BM_NAND 4 - performs the NAND operation (turns off listed bits)
diff --git a/gemrb/docs/en/GUIScript/console.txt b/gemrb/docs/en/GUIScript/console.txt
new file mode 100644
index 0000000..410991c
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/console.txt
@@ -0,0 +1,9 @@
+The 'cheat console'.
+
+Description: You can pop the Console window anytime by pressing the ESC key. Any command you type in will be executed as a GUIScript command. You can use Python variables, issue Python commands as well as the GemRB scripting commands. If a command didn't execute, GemRB will retry its execution by adding "GemRB." to it, thus you can omit it in case you are extremely lazy.
+
+Examples (typed in the console):
+GemRB.Quit() - quit the program
+Quit() - same as above, but shorter
+EnableCheatKeys(1) - enables the debug keys
+
diff --git a/gemrb/docs/en/GUIScript/controls.txt b/gemrb/docs/en/GUIScript/controls.txt
new file mode 100644
index 0000000..8c1ee30
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/controls.txt
@@ -0,0 +1,52 @@
+Button:
+
+Buttons are the most versatile controls in the GUI engine. The following button types exist: checkbox, radiobutton and normal button.
+Checkboxes of the same type are cummulative, and usually hold a bit value.
+Radiobuttons of the same type are mutually exclusive.
+Normal buttons are similar to radiobuttons, but they are not affecting each other.
+
+Progressbar:
+
+This control has no equivalent in the original Infinity Engine. It provides a graphical output of a numeric value, currently used only in LoadScreen.
+
+
+Slider:
+
+A slider is used for limited numeric input.
+
+
+TextEdit:
+
+This is a text input field. It has a configurable maximum input length.
+
+
+TextArea:
+
+Textareas are used in two ways: massive text output, it could be colour coded. Textareas are also used to simulate list controls where you could select a line and receive the row number of the selected item. It is also possible to assign a specific value to each row.
+
+
+Label:
+
+A label is simply a static text control, but it defines a few mouse events. Not so complex as a button.
+
+
+Scrollbar:
+
+A scrollbar is mostly used in connection with a Textarea, but it could be used differently. (See GUILOAD as an example).
+
+
+Worldmap:
+
+This control has a button type placeholder in the original user interface files.
+ The current GemRB chui loader can't handle this type, so you must create it via a CreateWorldMapControl command.
+
+
+Map:
+
+This control has a button type placeholder in the original user interface files. The current GemRB chui loader can't handle this type, so you must create it via a CreateMapControl command.
+
+
+Game:
+
+This control has no equivalent in the original user interface files. It covers the main game area where the actors are placed. The engine enforces that the game control is always the first control of the first window. To achieve this, it will close all windows when entering the game.
+
diff --git a/gemrb/docs/en/GUIScript/data_exchange.txt b/gemrb/docs/en/GUIScript/data_exchange.txt
new file mode 100644
index 0000000..c7e221e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/data_exchange.txt
@@ -0,0 +1,48 @@
+Data exchange between GUI controls and Global Dictionary Names.
+
+Each control has an associated 'Value' which could be assigned to it by the SetVarAssoc command. The controls handle this value according to their type and subtype. To set a button's subtype, use the SetButtonFlags command.
+
+Normal button: the associated variable will be set to the 'Value', other controls are unaffected.
+CheckBox : the associated variable will be or-ed with the value, other controls may change accordingly to the new value.
+RadioButton : the associated variable will be set to the 'Value', other controls are changed accordingly to the new value.
+
+Progressbar: the progressbar will handle Value as a percentage (it can't alter the value).
+
+Slider: the slider will handle Value as a scaling factor (not the actual position!), it will set the associated Variable to Position*Value.
+
+TextEdit: cannot be associated with a variable. You have to use QueryText()
+
+TextArea: cannot be associated with a variable, use the various TextArea commands.
+
+Label: labels work like a normal button, the Value will be assigned to the associated variable when the label was 'pushed'.
+
+ScrollBar: Value will be set based on the scrollbar's knob position. The scrollbar will be redrawn based on Value. If a textarea was associated to the ScrollBar (via the .chu) then the textarea will also be scrolled.
+
+WorldMapControl: whether travel allowed or not (change the cursor)
+
+MapControl: the mapcontrol will use the first bit of the associated value to display mapnotes.
+
+Examples:
+ Progress = 0
+ GemRB.SetVar ("Progress", Progress)
+ Bar = GemRB.GetControl (LoadScreen, 0)
+ GemRB.SetVarAssoc (LoadScreen, Bar, "Progress", Progress)
+Bar is a ProgressBar control, the engine will refresh the "Progress" Global Dictionary Variable as it progresses with the EnterGame command.
+
+ GemRB.SetButtonFlags(FeedbackWindow,THac0RollsB, IE_GUI_BUTTON_CHECKBOX, OP_OR)
+ GemRB.SetVarAssoc(FeedbackWindow,THac0RollsB, "Rolls",1)
+The above commands (from the options screen) will change the "Rolls" variable according to the CheckBox' state.
+
+ BppButtonB1 = GemRB.GetControl(GraphicsWindow, 5)
+ BppButtonB2 = GemRB.GetControl(GraphicsWindow, 6)
+ BppButtonB3 = GemRB.GetControl(GraphicsWindow, 7)
+ GemRB.SetButtonFlags(GraphicsWindow, BppButtonB1, IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ GemRB.SetButtonFlags(GraphicsWindow, BppButtonB2, IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ GemRB.SetButtonFlags(GraphicsWindow, BppButtonB3, IE_GUI_BUTTON_RADIOBUTTON,OP_OR)
+ GemRB.SetVarAssoc(GraphicsWindow, BppButtonB1, "BitsPerPixel",16)
+ GemRB.SetVarAssoc(GraphicsWindow, BppButtonB2, "BitsPerPixel",24)
+ GemRB.SetVarAssoc(GraphicsWindow, BppButtonB3, "BitsPerPixel",32)
+The above commands will set up the BPP selection RadioButton group.
+
+See also: SetVarAssoc, SetVar, GetVar, QueryText, SetButtonFlags, accessing_gui_controls
+
diff --git a/gemrb/docs/en/GUIScript/doc_template.txt b/gemrb/docs/en/GUIScript/doc_template.txt
new file mode 100644
index 0000000..da13f83
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/doc_template.txt
@@ -0,0 +1,13 @@
+
+Prototype:
+
+Metaclass Prototype:
+
+Description:
+
+Parameters:
+
+Return value:
+
+See also:
+
diff --git a/gemrb/docs/en/GUIScript/reserved_functions.txt b/gemrb/docs/en/GUIScript/reserved_functions.txt
new file mode 100644
index 0000000..032ab5e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript/reserved_functions.txt
@@ -0,0 +1,44 @@
+The following functions have a special meaning:
+
+OnLoad():
+ This function is called when the GUI script was loaded by GemRB.
+
+StartTextScreen():
+ This function is called when the engine encountered a TextScreen or an
+ IncrementChapter game script action.
+
+OpenWorldMapWindow():
+ This function is called when the worldmap window must be opened.
+
+UpdatePortraitWindow():
+ This function is called when there was a change in the party, this includes
+ party order and PC hitpoints or state changes that may affect portraits.
+
+SelectionChanged():
+ This function is called when there was a change in selection of a team member.
+
+OpenStoreWindow():
+ This function is called when the StartStore scripting action was called.
+ Or a container item was accessed.
+
+UpdateControlStatus():
+ This function is called when a pane changed on the game screen.
+
+OpenContinueMessageWindow():
+ This function is called when the player may choose to continue a dialog.
+
+OpenEndMessageWindow():
+ This function is called when the player may choose to end a dialog.
+
+DeathWindow():
+ This function is called when the team/protagonist is killed. See also
+ GameSetProtagonistMode().
+
+OpenReformPartyWindow():
+ This function is called when there are too many teammembers.
+
+OpenContainerWindow():
+ This function is called when the player accessed a ground pile or container.
+
+CloseContainerWindow():
+ This function is called when GemRB requests the container window to be closed.
diff --git a/gemrb/docs/en/GUIScript_introduction.txt b/gemrb/docs/en/GUIScript_introduction.txt
new file mode 100644
index 0000000..17d588e
--- /dev/null
+++ b/gemrb/docs/en/GUIScript_introduction.txt
@@ -0,0 +1,275 @@
+GUISCRIPT INTRODUCTION
+----------------------
+
+This file is intended as a brief overview for one of two scripting systems
+in GemRB - GUIScript. You should read it if you want to hack on GemRB's GUI.
+
+
+Contents
+--------
+
+ - Introduction - GUIScript and GameScript
+ - Script execution
+ - Typical GUIScript
+ - GUIScripter's Workflow
+ - Notes
+
+Introduction - GUIScript and GameScript
+---------------------------------------
+
+As said above, there are two scripting systems within GemRB - GUIScript
+and GameScript. What's the difference between them?
+
+The first one, GUIScript, concerns mostly with game GUI, i.e. user
+interaction, opening and closing windows, pushing of character sheet
+buttons, inventory, etc. This functionality was hardwired in the
+original games (probably with Lua scripts compiled-in into the main
+EXE) and so we need to implement it anew. GUIScript scripts have to be
+tailored for each game type, because each game uses more or less
+different GUI, string refs, and so on. We program these scripts in
+Python.
+
+The second one, GameScript, governs almost all AI and "AI" in game,
+effects of opening a trapped chest, annoying an important NPC or
+putting together pieces of a puzzle. These scripts are of course
+totally story (and hence game) dependent, fortunately they are stored
+in game's data files. They are written in special language and
+usually compiled in *.bcs files. Some of them are uncompiled (*.bs
+files) or even stored in dialog.tlk.
+
+This file concerns with the first type of scripts, GUIScripts.
+
+
+Script execution
+----------------
+
+GUIScripts are stored in gemrb/GUIScripts/<gametype>/. First script to
+be run is always named Start.py. Main window with messages, map
+display, character portraits, action buttons etc. is named
+MessageWindow.py. Other scripts are usually named after *.CHU resource
+file they mostly use.
+
+ Example:
+ Planescape: Torment startup script:
+ gemrb/GUIScripts/pst/Start.py
+
+ Baldur's Gate 2 inventory:
+ gemrb/GUIScripts/bg2/GUIINV.py
+
+GemRB API is provided to the scripts by means of GemRB module. The
+module is implemented together with Python interpreter glue in
+GUIScript plugin in gemrb/plugins/GUIScript/GUIScript.cpp.
+
+ Example:
+ import GemRB
+ GemRB.LoadTable ("RACES")
+
+Description of all functions in the huge GemRB module is in
+gemrb/docs/en/GUIScript directory, GUIScript.cpp file itself or obtained
+by typing help(GemRB) in GemRB console (type ESC to open console). In
+that case the help is dumped to stdout.
+
+All scripts to be run have to define OnLoad() function, which is
+called by GemRB each time the script is loaded. Those python files
+without OnLoad() function are merely modules imported into another
+script, usually MessageWindow.py.
+
+The control between scripts is passed with GemRB.SetNextScript()
+function, which causes another script to be loaded. Repeated loading
+of a script causes repeated calling of its OnLoad() function.
+
+All scripts automagically import all symbols from file
+gemrb/GUIScripts/GUIDefines.py, which contains definition of constants
+commonly used in the scripts. Other files in this directory can be
+imported too.
+
+ Example:
+ import GemRB
+ from ie_stats import IE_SAVEVSPOLY
+ def OnLoad():
+ print IE_GUI_BUTTON_NORMAL, IE_SAVEVSPOLY
+ GemRB.SetNextScript("MessageWindow")
+
+
+
+Typical GUIScript
+-----------------
+
+A typical script's task is to open a window, setting button labels and
+callbacks and then return control back to GemRB. The script also
+provides a mean to close the window created.
+
+Since most scripts are modules of MessageWindow.py instead of
+independent scripts, we will use a simplified version of one of them
+for an example
+
+
+ ## Standard header and boilerplate, put in all the scripts.
+
+ # -*-python-*-
+ # GemRB - Infinity Engine Emulator
+ # Copyright (C) 2003 The GemRB Project
+ .....
+
+
+ ## Since this is only a module, GemRB and GUIDefines have to be explicitly
+ ## imported
+
+ ###################################################
+ import GemRB
+ from GUIDefines import *
+ from GUICommon import CloseOtherWindow
+
+ ###################################################
+ JournalWindow = None
+ LogWindow = None
+ QuestsWindow = None
+ ## Function called from a callback in MessageWindow.py
+
+ ###################################################
+ def OpenJournalWindow ():
+ global JournalWindow
+
+ ## This is a common way of closing another window before opening
+ ## our own one (here Journal window). If Journal window already
+ ## exists, it's closed and the control is returned to the caller
+ ## (this implements toggling of a window)
+
+ if CloseOtherWindow (OpenJournalWindow):
+ ## this part is called if we are closing Journal window
+
+ ## Close any opened children windows
+
+ if LogWindow: OpenLogWindow()
+ if QuestsWindow: OpenQuestsWindow()
+
+ ## Freezes the GUI
+ GemRB.HideGUI ()
+
+ ## Free the window resource and delete Journal window
+ ## from window manager
+ GemRB.UnloadWindow(JournalWindow)
+ JournalWindow = None
+ GemRB.SetVar("OtherWindow", -1)
+
+ ## Unfreeze the GUI, redraws screen, reshuffles windows in
+ ## window manager
+
+ GemRB.UnhideGUI ()
+
+ ## Return to the caller
+ return
+
+ ## This part is executed when we did not close Journal window
+ ## and will open it instead
+
+ ## Freeze the GUI
+ GemRB.HideGUI ()
+
+ ## Loads CHU resource file. This one contains windows for Journal,
+ ## Log, and PC/NPC encyclopedia.
+
+ GemRB.LoadWindowPack ("GUIJRNL")
+
+ ## Load window with ID 0 (Main Journal window) from GUIJRNL.CHU file.
+
+ JournalWindow = GemRB.LoadWindow (0)
+
+ ## Add the new window to window manager. "OtherWindow" is name reserved
+ ## for non-floating windows in the center of the screen.
+
+ GemRB.SetVar("OtherWindow", JournalWindow)
+
+ ## Now access some buttons in the new window and assign
+ ## labels and callbacks to them
+
+ ## Comment the button processing with the label of the button for
+ ## easy maintenance
+
+ # Quests
+
+ ## Quest button has ID 0 in GUIJRNL.CHU in window ID 0
+ Button = GemRB.GetControl (JournalWindow, 0)
+
+ ## Set the label for the button to strref 20430 ("Quests")
+ GemRB.SetText (JournalWindow, Button, 20430)
+
+ ## When the button is clicked by mouse, call fn OpenQuestsWindow()
+ GemRB.SetEvent (JournalWindow, Button, IE_GUI_BUTTON_ON_PRESS, "OpenQuestsWindow")
+
+ ## Now some other buttons. Note that "Done" button calls this
+ ## function, which causes closing of the Journal window
+
+ # Beasts
+ Button = GemRB.GetControl (JournalWindow, 1)
+ GemRB.SetText (JournalWindow, Button, 20634)
+ GemRB.SetEvent (JournalWindow, Button, IE_GUI_BUTTON_ON_PRESS, "OpenBeastsWindow")
+
+ # Done
+ Button = GemRB.GetControl (JournalWindow, 3)
+ GemRB.SetText (JournalWindow, Button, 20636)
+ GemRB.SetEvent (JournalWindow, Button, IE_GUI_BUTTON_ON_PRESS, "OpenJournalWindow")
+
+
+ ## Unfreeze the GUI, redraw screen and restart window manager
+ GemRB.UnhideGUI()
+
+
+GUIScripter's Workflow
+----------------------
+
+- Run GemRB and an original game in parallel and notice GUI parts
+ missing or badly done in GemRB.
+
+- Divine the CHU file containing the window resource. It can either be
+ guessed by comparing names of all CHU files in the game to required
+ functionality, from GUIScript already implementing related part of
+ the GUI or by viewing all CHU files in CHU/BAM/MOS viewer (DLTCEP,
+ NearInfinity) and finding the right looking one.
+
+- Find ID of the window to be implemented in the CHU file, usually
+ with DLTCEP or NearInfinity.
+
+- Copy & paste common parts of the script from another one opening
+ similar windows.
+
+- Using CHU viewer find control IDs for the controls you need to
+ configure in the script and add their setting into the script, again
+ copy & pasting from another script. Usually start with magic trinity
+ of GetControl, SetText, SetEvent for each button. Use dummy numbers
+ for now. Beware, NearInfinity displays wrong IDs for labels. You will
+ have to add the label's control ID to "buffer size" << 32 to get the
+ right control ID.
+
+- Write down button labels as they are seen in the original game and
+ find their strrefs using DLTCEP or NI. Alternatively, you can set
+ "Strref On=1" in *.INI of the original games and write down directly
+ the strrefs as they are displayed instead of the labels. This
+ however does not work with Planescape: Torment.
+
+- Put the strrefs into the SetText() functions in the script.
+
+- Now when the trivial parts are done, the real fun can begin.
+ But that's a subject too advanced for this file :-).
+
+
+Notes
+-----
+
+Please try to follow the indentation style of other GUIScript scripts,
+in particular:
+
+ - indent each level with 1 TAB character
+
+ - insert space between function name and opening parenthesis in
+ function calls and around operators and after the hash sign in
+ text comments
+
+ - insert blank lines between logically separated chunks of code,
+ e.g. between setting of different controls
+
+ - insert blank lines after functions and a row of hashes
+
+ - keep naming convention for windows, callback etc.
+
+ - comment your code
diff --git a/gemrb/docs/en/Makefile.am b/gemrb/docs/en/Makefile.am
new file mode 100644
index 0000000..b4b653e
--- /dev/null
+++ b/gemrb/docs/en/Makefile.am
@@ -0,0 +1,6 @@
+SUBDIRS = Tables GUIScript Engine
+docs_DATA = *.txt
+docsdir = $(docdir)/
+EXTRA_DIST = *.txt
+
+CLEANFILES = doxygen/html/* doxygen/latex/*
diff --git a/gemrb/docs/en/Release.txt b/gemrb/docs/en/Release.txt
new file mode 100644
index 0000000..5f698bb
--- /dev/null
+++ b/gemrb/docs/en/Release.txt
@@ -0,0 +1,79 @@
+How to make a release
+*********************
+
+Preparing and testing GIT
+=========================
+
+* Make sure that version numbers in
+ configure.in
+ gemrb/includes/globals.h
+ are correct for the upcoming version.
+
+* Update NEWS with highlights since the last release
+
+* Make sure that GemRB GIT is in compilable and runnable state
+ - download a clean GIT tree and test it.
+ - Make distribution .tar.gz (see Source release below) and test it as well -
+ especially make sure all needed files are included.
+
+* Tag current GIT to version number e.g. v0.9.0
+ i.e. in the gemrb root directory do
+ git tag -a -m "GemRB 0.9.0" v0.9.0
+ git push origin v0.9.0
+* Update from GIT to the tagged version
+ git checkout v0.9.0
+
+* After you're done, update the version with a -git suffix, so it will be
+ easier to tell if people are running release builds or not
+
+Source release
+==============
+
+* on Unix/Linux:
+ make dist
+
+
+Linux binary release
+====================
+
+ ./autogen.sh
+ ./configure --prefix=/usr/local
+ make
+ make install DESTDIR=/tmp/fakeroot
+ cd /tmp/fakeroot/usr/local
+ copy ~/GemRB-binary.cfg over etc/GemRB.cfg, rename to etc/gemrb.cfg
+ strip bin/gemrb lib/* lib/gemrb/*
+ sudo chown -R 0:0 *
+ sudo tar cvzf ../gemrb-0.2.5-linux_i386.tar.gz *
+
+
+Windows binary release
+======================
+
+* FIXME: To be defined....
+
+
+Release and Announcements
+=========================
+
+* Write the release notes if necessary. They are mainly for packagers, so
+create them if there are structural or build related changes. New config
+options should also be mentioned here if they're not part of the changelog.
+
+* Put the tarballs/binaries into Releases on SF
+ - create a new directory in the appropriate section by rightclicking
+ - mark it for upload
+ - upload the release notes
+ - upload the file
+ - mark the release notes as release notes (popup on leftclick)
+ - mark the file to have the release notes and default platforms (popup on leftclick)
+
+* Test the downloads from sourceforge.net
+
+* Announce on homepage, SF, #GemRB in channel and title, Happypenguin and
+ Gibberlings3:
+ - our forum
+ - modding news (Avenger, Theacefes, Grim Squeaker, DavidW and cmorgan have access)
+ (a template is available in admin/announcement.template)
+
+* Run admin/restart_news.sh to restart the NEWS cycle
diff --git a/gemrb/docs/en/Tables/HPBARB.txt b/gemrb/docs/en/Tables/HPBARB.txt
new file mode 100644
index 0000000..943f35d
--- /dev/null
+++ b/gemrb/docs/en/Tables/HPBARB.txt
@@ -0,0 +1,8 @@
+Description: Contains the hp to gain on barbarian level up.
+
+Columns -
+SIDES - number of sides to roll
+ROLL - number of rolls
+MODIFIER - add after the roll
+
+Rows - the level the roll applies to
diff --git a/gemrb/docs/en/Tables/Makefile.am b/gemrb/docs/en/Tables/Makefile.am
new file mode 100644
index 0000000..63decc7
--- /dev/null
+++ b/gemrb/docs/en/Tables/Makefile.am
@@ -0,0 +1,3 @@
+tabledocs_DATA = *.txt
+tabledocsdir = $(docdir)/Tables/
+EXTRA_DIST = *.txt
diff --git a/gemrb/docs/en/Tables/ability.txt b/gemrb/docs/en/Tables/ability.txt
new file mode 100644
index 0000000..bcc2f84
--- /dev/null
+++ b/gemrb/docs/en/Tables/ability.txt
@@ -0,0 +1,10 @@
+Description: This table lists the abilities (strength, intelligence) in the game.
+
+Columns -
+NAME_REF - the name of the ability
+CAP_REF - the capitalized name of the ability
+DESC_REF - the description of the ability
+STAT_ID - the ability stat value
+
+Rows - abilities
+
diff --git a/gemrb/docs/en/Tables/aligns.txt b/gemrb/docs/en/Tables/aligns.txt
new file mode 100644
index 0000000..8435eb3
--- /dev/null
+++ b/gemrb/docs/en/Tables/aligns.txt
@@ -0,0 +1,12 @@
+Description: This table lists the alignments pickable in the game.
+
+Columns -
+NAME_REF - the name of the alignment
+CAP_REF - the capitalized name of the alignment
+DESC_REF - the description of the alignment
+VALUE - the alignment stat value
+COLNAME - the column name for this alignment in other tables
+USABILITY - the item usability flag
+
+Rows - alignments
+
diff --git a/gemrb/docs/en/Tables/areapro.txt b/gemrb/docs/en/Tables/areapro.txt
new file mode 100644
index 0000000..ebee0f6
--- /dev/null
+++ b/gemrb/docs/en/Tables/areapro.txt
@@ -0,0 +1,25 @@
+Description: This table lists resource data for area projectiles. The original engine had these hardcoded in the .exe
+
+Columns -
+ RESOURCE1 - A BAM resref which is drawn in the entire area of effect.
+ RESOURCE2 - A VVC (or BAM) resref which is drawn in the center of effect once.
+ RESOURCE3 - An alternate animation (named recoil without better name)
+ SOUND1 - Explosion sound (played on first explosion)
+ SOUND2 - Area sound (played on subsequent explosions)
+ FLAGS - a bitfield which alters the way the resources are drawn
+
+Values for the FLAGS bitfield -
+ 1 - RESOURCE1 is recoloured by the gradient set in the projectile
+ 2 - fills the entire area of effect randomly, otherwise it is a (spreading) circle
+ 4 - no spreading, the filler graphic is already at its destination, otherwise it starts from the center
+ 8 - RESOURCE2 is recoloured by the gradient set in the projectile
+ 0x10 - repopulate children that vanished
+ 0x20 - the child projectiles need gradient colouring
+ 0x40 - draw RESOURCE3 projectiles too (halves the number of children as well)
+ 0x80 - double the number of child projectiles
+ 0x100 - if none was affected by this projectile, apply a spell of same name on the caster
+
+areapro.2da data could be completely incorporated into the projectile file, it is supplied
+only for some BG2 spells that don't use the extended projectile format.
+
+See projectile.txt for more information.
diff --git a/gemrb/docs/en/Tables/avatars.txt b/gemrb/docs/en/Tables/avatars.txt
new file mode 100644
index 0000000..de8e2af
--- /dev/null
+++ b/gemrb/docs/en/Tables/avatars.txt
@@ -0,0 +1,66 @@
+Description: This table lists the avatar animations. PST has a slightly different animation scheme, this could be turned on by the OneByteAnimationID option (in gemrb.ini). When it is on, the high byte of the animation ID will control the palette flag thus eliminating the need of the PALETTE column.
+
+[ Note: apparently, PST is more complicated than that:
+Foundry gears: 0x2000
+Iron Golem: 0x3000
+Mortuary ghost: 0xD400
+Pillar of Skulls: 0xF000
+black abishai: 0xE000
+
+The only normal one of them is black abishai, the others are all somewhat special. For example, Pillar of Skulls consists of two BAMs, POSMAIN and POSSHAD (the pillar and its shadow, both animated). See ANIMATE.IDS. ]
+
+
+Columns: AT_1 - first armour level animation prefix
+ AT_2 - second armour level animation prefix
+ AT_3 - third armour level animation prefix
+ AT_4 - fourth armour level animation prefix
+ TYPE - the animation scheme
+
+0 - Many files, this is the avatar animation of BG2.
+1 - The whole animation is in one file. Scheme is: [NAME][G1]. There are 16 orientations.
+2 - The whole animation is in four files. Scheme is: [NAME][G1-2][/E].
+3 - The whole animation is in two files. Scheme is: [NAME][G1][/E].
+4 - Many files (simular to 0), but all with G. Scheme is: [NAME][G][1-2][1-6].
+5 - ?
+6 - BG2 style animation. Scheme is:[NAME][ACTIONCODE][/E]. The G1 code contains many sequences. (5 orientations).
+7 - Bird animations, one file, only movement with orientations.
+8 - Six files contain the whole animation. Scheme is:[NAME][G1-3][/E]
+9 - IWD style animation. Scheme is:[NAME][ACTIONTYPE][/E]. The main difference from 6 is that instead of G1, it has separate bams for many actions. (9 orientations).
+10 - The whole animation is in two files [NAME][G1][/E]. Used for some low res static BG1 animations like the horse and cow. It lacks the walking animations of type 3.
+11 - large 2x2 animations (wyvern and balor)
+12 - huge 3x3 dragon animations
+13 - 15 reserved for non pst anims
+16 - full PST style animation. Scheme is:[C/D][ACTIONTYPE][NAME][B]
+17 - stand still animation (no orientations)
+18 - stand still animation with orientations
+19 - full PST animation without different stc/std sequences
+
+PST ACTIONTYPE:
+AT1, AT2
+C2S
+CF1, CF2
+DFB
+GUP Get Up
+HIT Hit
+MS1, MS2
+RUN Run
+S2C
+SF1, SF2
+SP1 .. SP4
+STC Fight Stance?
+STD
+TK1 .. TK6 Talk
+WLK Walk
+
+ SPACE - the personal space of the creature
+ PALETTE - set it to 1 if the animation has its own palette (0 for fake colored avatars).
+Not needed if OneByteAnimation was set.
+ SIZE - Used for selecting possible weapon animation BAMs.
+Not needed if OneByteAnimation was set.
+
+Rows: each row begins with a hexadecimal Animation ID. One or two bytes.
+
+Example: the wyvern animation
+ AT_1 AT_2 AT_3 AT_4 TYPE SPACE PALETTE
+0x1000 MWYV MWYV MWYV MWYV 2 5 1
+
diff --git a/gemrb/docs/en/Tables/avprefix.txt b/gemrb/docs/en/Tables/avprefix.txt
new file mode 100644
index 0000000..50b90c8
--- /dev/null
+++ b/gemrb/docs/en/Tables/avprefix.txt
@@ -0,0 +1,34 @@
+Description: Contains the scheme of the avatar animation IDs based on character stats. The first row, contains the base Animation ID of the avatars. A second column may store the first stance of the generated character (useful only for PST). Additional rows may contain a table ResRef which modifies the animation ID based on a stat. These tables contain a stat number in their first (0.) row. The other rows contain the modifier value based on the stat.
+
+Columns: RESREF - One column, with different values.
+
+Rows: 0 - base Animation ID
+<additional> - table ResRef
+
+Example (BG2):
+ RESOURCE
+0 0x6000
+1 avprefr
+2 avprefg
+3 avprefc
+
+avprefr.2da:
+ RACE
+TYPE 201
+HUMAN 0
+ELF 1
+...
+avprefc.2da:
+ CLASS
+TYPE 202
+FIGHTER 0x100
+MAGE 0x200
+...
+The Animation ID depends on race, gender and class in bg2. The race stat is 201, so that is in the first row.
+
+Example (PST):
+ RESOURCE STANCE
+0 0x6032 18
+
+The Animation ID is constant, but there is a special stance (waking up on the slab).
+
diff --git a/gemrb/docs/en/Tables/cgtable.txt b/gemrb/docs/en/Tables/cgtable.txt
new file mode 100644
index 0000000..c7d8e6d
--- /dev/null
+++ b/gemrb/docs/en/Tables/cgtable.txt
@@ -0,0 +1,6 @@
+Description: This table is used by the casting glow opcode and also when casting a spell (the spell header contains the casting glow code).
+
+Columns: the casting glow resref (VVC or BAM)
+
+Rows: the casting glow code
+
diff --git a/gemrb/docs/en/Tables/classes.txt b/gemrb/docs/en/Tables/classes.txt
new file mode 100644
index 0000000..b51630e
--- /dev/null
+++ b/gemrb/docs/en/Tables/classes.txt
@@ -0,0 +1,15 @@
+Description: This table lists the playable classes in the game.
+
+Columns -
+NAME_REF - the name of the class
+CAP_REF - the capitalized name of the class
+DESC_REF - the description of the class
+SAVE - the saving throw table for this class
+USABILITY - the item usability flag
+MULTI - if multi class, which classes are combined
+ID - the class stat value
+HP - the hp table for the class
+<racecolumns> - the rest of the columns enable or disable the class (2 means partially usable)
+
+Rows - classes
+
diff --git a/gemrb/docs/en/Tables/clowncol.txt b/gemrb/docs/en/Tables/clowncol.txt
new file mode 100644
index 0000000..85e592b
--- /dev/null
+++ b/gemrb/docs/en/Tables/clowncol.txt
@@ -0,0 +1,10 @@
+Description: This table lists the pickable colours for the avatar.
+
+Columns - gradient slots
+
+Rows -
+ MAJOR - the gradient values for the major colour
+ MINOR - the gradient values for the minor colour
+ HAIR - the gradient values for the hair colour
+ SKIN - the gradient values for the skin colour
+
diff --git a/gemrb/docs/en/Tables/containr.txt b/gemrb/docs/en/Tables/containr.txt
new file mode 100644
index 0000000..28f9472
--- /dev/null
+++ b/gemrb/docs/en/Tables/containr.txt
@@ -0,0 +1,11 @@
+Description: This table lists the resources used for container types.
+
+Columns: The BAM and Sound resource references associated with the type.
+
+Rows: Container types. The row index is based on the container type set in the container data of an area.
+
+Example:
+ SOUND BAM
+* * *
+BAG GAM_12A1 CONTSACK
+
diff --git a/gemrb/docs/en/Tables/damage.txt b/gemrb/docs/en/Tables/damage.txt
new file mode 100644
index 0000000..0d7a811
--- /dev/null
+++ b/gemrb/docs/en/Tables/damage.txt
@@ -0,0 +1,10 @@
+Description: This table lists the damage animations.
+
+Columns -
+ MAIN - the damage hit animation
+ SPARKS - additional splash animations
+ GRADIENT - fake coloured animations will be recoloured with this gradient
+
+Rows - damage types
+
+See also: dmgtypes.txt
diff --git a/gemrb/docs/en/Tables/defsound.txt b/gemrb/docs/en/Tables/defsound.txt
new file mode 100644
index 0000000..cc4a801
--- /dev/null
+++ b/gemrb/docs/en/Tables/defsound.txt
@@ -0,0 +1 @@
+Description: This table lists default sounds used by the engine.
diff --git a/gemrb/docs/en/Tables/dmgtype.txt b/gemrb/docs/en/Tables/dmgtype.txt
new file mode 100644
index 0000000..4dbad72
--- /dev/null
+++ b/gemrb/docs/en/Tables/dmgtype.txt
@@ -0,0 +1,11 @@
+Description: This table lists the damage type resistances.
+
+Columns -
+ LOWER - string reference index (there is a second indirection by strings.2da)
+ RESIST_STAT - character stat which provides resistance
+ VALUE - damagetype value
+ IWDMOD - value of the type parameter to the bonus damage modifier effect in iwd
+
+Rows - damage types
+
+See also: damage.txt
diff --git a/gemrb/docs/en/Tables/fistweap.txt b/gemrb/docs/en/Tables/fistweap.txt
new file mode 100644
index 0000000..32a5d2d
--- /dev/null
+++ b/gemrb/docs/en/Tables/fistweap.txt
@@ -0,0 +1,7 @@
+Description: This table contains what resref will be created in the fist slot of a creature.
+This is usually the FIST object, but for the monk class it varies with level.
+It is possible to add other classes which have level based fist weapon.
+
+Columns: The levels
+
+Rows: The classes (use class id as label)
diff --git a/gemrb/docs/en/Tables/fonts.txt b/gemrb/docs/en/Tables/fonts.txt
new file mode 100644
index 0000000..4e2b730
--- /dev/null
+++ b/gemrb/docs/en/Tables/fonts.txt
@@ -0,0 +1,13 @@
+Description: This table lists fonts. Only fonts listed here could be used as a font resource.
+
+Columns: RESREF - the .bam ResRef of the font
+ NEED_PALETTE - if the font has no own palette, set this to 1
+ FIRST_CHAR - usually 0, it contains the first printable character of the font.
+
+Rows: <rowcount> - make sure the 9. font is a 'normal' font. Used for floating texts. (this is a hacked legacy feature, it may go away).
+
+Example (BG2):
+ RESREF NEED_PALETTE FIRST_CHAR
+0 NORMAL 1 0
+1 NUMBER 0 47
+2 INITIALS 0 0
diff --git a/gemrb/docs/en/Tables/formatio.txt b/gemrb/docs/en/Tables/formatio.txt
new file mode 100644
index 0000000..c8e2fd7
--- /dev/null
+++ b/gemrb/docs/en/Tables/formatio.txt
@@ -0,0 +1,10 @@
+Description: This table contains preset party formations.
+
+Columns: X/Y pairs of relative coordinates from the target location. 7-10 pairs can be specified. The coordinates must be given for the north direction. (Other directions will be calculated).
+
+Rows: Formation types.
+
+Example:
+ X1 Y1 X2 Y2 X3 Y3 X4 Y4 X5 Y5 X6 Y6 X7 Y7
+FOLLOW 0 0 0 1 0 2 0 3 0 4 0 5 0 6
+
diff --git a/gemrb/docs/en/Tables/guibtact.txt b/gemrb/docs/en/Tables/guibtact.txt
new file mode 100644
index 0000000..05f2506
--- /dev/null
+++ b/gemrb/docs/en/Tables/guibtact.txt
@@ -0,0 +1,9 @@
+Description: This file describes the action button icons and associated tooltips.
+
+The row names are also part of the function name to run when the button is
+pressed ("Action%sPressed"). For example, clicking the thieving button
+will run "ActionThievingPressed()".
+
+Columns: 1-4: The button BAM Frame numbers for the four button sprites.
+ TOOLTIP: The tooltip strref for the action.
+ RESREF: The resref of the button icon.
diff --git a/gemrb/docs/en/Tables/item_use.txt b/gemrb/docs/en/Tables/item_use.txt
new file mode 100644
index 0000000..5a96e86
--- /dev/null
+++ b/gemrb/docs/en/Tables/item_use.txt
@@ -0,0 +1,7 @@
+Description: This table lists special items that won't be dropped by certain actors.
+
+Columns:
+USER - the scripting name of the playable character
+STRREF - the displayed string
+
+Rows: Item resrefs
diff --git a/gemrb/docs/en/Tables/itemsnd.txt b/gemrb/docs/en/Tables/itemsnd.txt
new file mode 100644
index 0000000..d8dea45
--- /dev/null
+++ b/gemrb/docs/en/Tables/itemsnd.txt
@@ -0,0 +1,9 @@
+Description: This table assigns sounds to item types. The itemtypes must be defined by itemtype.2da. The last four itemtypes are reserved for armourlevel based sounds of body armors.
+
+Columns: Sound resource references for the TAKE and the DROP sound.
+
+Rows: Item types. They must follow the item types in itemtype.2da. There must be four extra item types at the end:
+DEFAULT
+LEATHER
+CHAIN
+PLATE
diff --git a/gemrb/docs/en/Tables/itemspec.txt b/gemrb/docs/en/Tables/itemspec.txt
new file mode 100644
index 0000000..bb08a97
--- /dev/null
+++ b/gemrb/docs/en/Tables/itemspec.txt
@@ -0,0 +1,6 @@
+Description: This table lists special functionality items.
+
+Columns:
+ IDENTIFY - the item could be used as identifying tool. The item's first extended header will be executed.
+
+Rows: Item resrefs
diff --git a/gemrb/docs/en/Tables/itemtype.txt b/gemrb/docs/en/Tables/itemtype.txt
new file mode 100644
index 0000000..d13e75a
--- /dev/null
+++ b/gemrb/docs/en/Tables/itemtype.txt
@@ -0,0 +1,17 @@
+Description: This table assigns item types to inventory slot types. It tells which item may go into which inventory slot. The slot types are the bit values of slottypes.2da
+ 1 - helm (or earring in pst)
+ 2 - armour
+ 4 - shield
+ 8 - gauntlet
+ 16 - ring
+ 32 - amulet
+ 64 - belt
+ 128 - boot
+ 256 - weapon
+ 512 - quiver
+1024 - cloak
+2048 - quick item
+
+Columns: Slot types. They must follow in the same order in any engine, you may add additional columns if needed. (Probably only in PST).
+
+Rows: Item types. They must follow the natural order of item types as of the original engine.
diff --git a/gemrb/docs/en/Tables/itemuse.txt b/gemrb/docs/en/Tables/itemuse.txt
new file mode 100644
index 0000000..133be8e
--- /dev/null
+++ b/gemrb/docs/en/Tables/itemuse.txt
@@ -0,0 +1,10 @@
+Description: This table lists the item usability flags.
+
+Columns:
+STAT - the stat which affects the bits
+FILE - the file which contains the usability values assigned to the stat values
+MCOL - the stat values
+VCOL - the usability values
+WHICH - which bitfield group is affected (could be only 0 or 1)
+
+Rows: Item resrefs
diff --git a/gemrb/docs/en/Tables/magesch.txt b/gemrb/docs/en/Tables/magesch.txt
new file mode 100644
index 0000000..7897694
--- /dev/null
+++ b/gemrb/docs/en/Tables/magesch.txt
@@ -0,0 +1,9 @@
+Description: This table lists the mage schools pickable in the game.
+
+Columns -
+NAME_REF - the name of the mage school
+CAP_REF - the capitalized name of the mage school
+DESC_REF - the description of the mage school
+KIT - the mage school kit value (also used as usability flag)
+
+Rows - mage schools
diff --git a/gemrb/docs/en/Tables/modal.txt b/gemrb/docs/en/Tables/modal.txt
new file mode 100644
index 0000000..a676322
--- /dev/null
+++ b/gemrb/docs/en/Tables/modal.txt
@@ -0,0 +1,15 @@
+Description: This table assigns spell resources to modal actions.
+These modal actions were hardcoded in the original engines.
+
+Columns -
+ SPELL - the spell resource assigned to the modal action.
+ ACTION - the name of the equivalent action in GemRB
+ STR_ON - the string to display when enabling this action/state
+ STR_OFF - the string to display when disabling this state
+ STR_FAILED - the string to display when enabling of this state fails
+ AOESPELL - does the SPELL have an area of effect?
+
+Rows - the modal actions have a somewhat determined order.
+The first row should be left blank.
+
+See modal.ids of the original engine.
diff --git a/gemrb/docs/en/Tables/overlay.txt b/gemrb/docs/en/Tables/overlay.txt
new file mode 100644
index 0000000..0e95152
--- /dev/null
+++ b/gemrb/docs/en/Tables/overlay.txt
@@ -0,0 +1,5 @@
+Description: This table lists the hardcoded overlay animations.
+
+Columns - <N/A>
+
+Rows - resrefs for each overlay type (the slots are mandatory)
diff --git a/gemrb/docs/en/Tables/pathfind.txt b/gemrb/docs/en/Tables/pathfind.txt
new file mode 100644
index 0000000..3052106
--- /dev/null
+++ b/gemrb/docs/en/Tables/pathfind.txt
@@ -0,0 +1,16 @@
+Description: This table describes how the pathfinder must handle the searchmap entries. Also it sets the cost of reaching a neighbouring point.
+
+Columns: <N/A>
+
+Rows: The first row contains 16 bitfields, one for each search map value. The bits make it possible to have an opaque, but passable area (but this isn't too meaningful).
+1 - passable (can walk through)
+2 - travel region
+4 - opaque (can't see through)
+8 - sidewall (blocks sight specially)
+(As you can see 14 marks the travel region).
+The second row contains the cost of walking straight, and the additional cost of walking diagonals.
+
+Example (BG2):
+ 0 1 2 3 4 5 6 7 8 9 a b c d e f
+PASSABLE 4 1 1 1 1 1 1 1 0 1 8 0 0 0 3 1
+COST 10 4
diff --git a/gemrb/docs/en/Tables/pdolls.txt b/gemrb/docs/en/Tables/pdolls.txt
new file mode 100644
index 0000000..9e22f7c
--- /dev/null
+++ b/gemrb/docs/en/Tables/pdolls.txt
@@ -0,0 +1,8 @@
+Description: This table lists the paperdolls linked to each avatar animation ID.
+
+Columns -
+ LEVEL1-4 - the paperdoll for each armour level
+ SIZE - the size suffix (for equipment layers)
+
+Rows - animation IDs
+
diff --git a/gemrb/docs/en/Tables/pictures.txt b/gemrb/docs/en/Tables/pictures.txt
new file mode 100644
index 0000000..0da0aa2
--- /dev/null
+++ b/gemrb/docs/en/Tables/pictures.txt
@@ -0,0 +1,11 @@
+Description: This table lists the pickable portraits and the default colours for the avatar, based on the portrait icon.
+
+Columns -
+ GENDER - male of female portrait (the gender stat)
+ HAIR - the default hair colour
+ SKIN - the default skin colour
+ MAJOR - the default major robe colour
+ MINOR - the default minor robe colour
+
+Rows -
+ portraits
diff --git a/gemrb/docs/en/Tables/polystat.txt b/gemrb/docs/en/Tables/polystat.txt
new file mode 100644
index 0000000..0068f08
--- /dev/null
+++ b/gemrb/docs/en/Tables/polystat.txt
@@ -0,0 +1,4 @@
+Description: This table lists the stats that are copied from a polymorph creature into the target of a polymorph effect. The stats could be given numerically, or literally as they are written in stats.ids
+
+Columns -
+ 0 - the only column, containing stat values (or names).
diff --git a/gemrb/docs/en/Tables/qslots.txt b/gemrb/docs/en/Tables/qslots.txt
new file mode 100644
index 0000000..2253187
--- /dev/null
+++ b/gemrb/docs/en/Tables/qslots.txt
@@ -0,0 +1,11 @@
+Description: This file contains the default action buttons based on class.
+This file is inherited from IWD2, although IWD2 uses a slightly different version.
+The columns of the table contain the nine modifiable actions for each class.
+A value of 100 means an unavailable slot.
+The values may be 0-31, and represent the first 32 rows of guibtact.2da
+
+Columns: the quick slots
+
+Rows: the classes
+
+See also: guibtact.txt
diff --git a/gemrb/docs/en/Tables/races.txt b/gemrb/docs/en/Tables/races.txt
new file mode 100644
index 0000000..8d66a85
--- /dev/null
+++ b/gemrb/docs/en/Tables/races.txt
@@ -0,0 +1,11 @@
+Description: This table lists the playable races in the game.
+
+Columns -
+NAME_REF - the name of the race
+CAP_REF - the capitalized name of the race
+DESC_REF - the description of the race
+ID - the race stat value
+USABILITY - the item usability flag
+
+Rows - races
+
diff --git a/gemrb/docs/en/Tables/randitem.txt b/gemrb/docs/en/Tables/randitem.txt
new file mode 100644
index 0000000..f41ff18
--- /dev/null
+++ b/gemrb/docs/en/Tables/randitem.txt
@@ -0,0 +1,25 @@
+Description: The randitem.2da file serves as a compatibility switch between different engine versions. It contains the ResRef for the gold item and it is the root table for random item tables. Two major random item distributions are allowed: multiple tables or single table. To have a single table, the second row (RND) must contain two ResRefs.
+More rows will follow with the table prefixes. The table prefixes must not exceed 6 characters in length. It may also contain two ResRefs for original game engine single treasure tables (no suffix).
+
+Columns: RESREF - the table names
+ FURY_MODE - use these values in the highest difficulty level
+
+Rows: GOLD - it must be the first row, and contains the gold ResRef. When the system needs the gold item, it will use this ResRef.
+ RND - if numeric, it contains the number of treasure tables which employ a numerical suffix (like rndequ01). If text, it contains table ResRefs.
+<additional> - if RND was numeric, there follows x table suffixes
+
+Example (BG2):
+ RESREF FURY_MODE
+GOLD MISC07
+RND 5 5
+RNDEQU RNDEQUIP RNDEQUIP
+RNDMAG RNDMAGIC RNDMAGIC
+RNDSCR RNDSCROL RNDSCROL
+RNDTRE RNDTREAS RNDTREAS
+RNDWEP RNDWEP RNDWEP
+
+Example (IWD2):
+ RESREF FURY_MODE
+GOLD MISC07
+RND RT_NORM RT_FURY
+
diff --git a/gemrb/docs/en/Tables/restmov.txt b/gemrb/docs/en/Tables/restmov.txt
new file mode 100644
index 0000000..0cb480a
--- /dev/null
+++ b/gemrb/docs/en/Tables/restmov.txt
@@ -0,0 +1,12 @@
+Description: This table lists the resting and day/night switching movies.
+The resting movie index is determined by 3 bits of the areatype. (CITY/FOREST/DUNGEON).
+Day/night switching occurs only for areas marked by the EXTENDED_NIGHT flag (also in areatype).
+The original engine showed only a single switching movie (for a city environment), but
+GemRB allows for 8 different movies (based on the areatype bit combinations).
+
+Columns -
+ resting movies
+ day switching movies
+ night movies
+
+Rows - The movie resource references for each areatype.
diff --git a/gemrb/docs/en/Tables/savegame.txt b/gemrb/docs/en/Tables/savegame.txt
new file mode 100644
index 0000000..7eb0337
--- /dev/null
+++ b/gemrb/docs/en/Tables/savegame.txt
@@ -0,0 +1 @@
+Description: This table lists save game name prefixes used by the SaveGame() action.
diff --git a/gemrb/docs/en/Tables/script.txt b/gemrb/docs/en/Tables/script.txt
new file mode 100644
index 0000000..41e190e
--- /dev/null
+++ b/gemrb/docs/en/Tables/script.txt
@@ -0,0 +1,14 @@
+Description: This table describes how the script file objects and triggers look like.
+
+Columns: <N/A>
+
+Rows:
+ OBJECT_IDS_COUNT - the count and name of the IDS identifiers (ea, general, race...)
+ MAX_OBJECT_NESTING - the maximum number of object filters
+
+ ADDITIONAL_RECT - 1 if the object has additional rectangle parameter
+
+ EXTRA_PARAMETERS_COUNT - the number of IDS after the string parameter
+
+ TRIGGER_POINT - 1 if the triggers contain an extra point parameter
+
diff --git a/gemrb/docs/en/Tables/shtable.txt b/gemrb/docs/en/Tables/shtable.txt
new file mode 100644
index 0000000..84a9ec1
--- /dev/null
+++ b/gemrb/docs/en/Tables/shtable.txt
@@ -0,0 +1,6 @@
+Description: This table is used by the spell hit animation opcode.
+
+Columns: the spell hit animation resref (VVC or BAM)
+
+Rows: the spell hit code
+
diff --git a/gemrb/docs/en/Tables/skills.txt b/gemrb/docs/en/Tables/skills.txt
new file mode 100644
index 0000000..be45c9e
--- /dev/null
+++ b/gemrb/docs/en/Tables/skills.txt
@@ -0,0 +1,9 @@
+Description: This table is used by the character generator (and when leveling up).
+It lists the thieving skills. The first two rows contain the distributable points per level.
+
+Columns - rogue classes or kits
+
+Rows - distributable points and skills
+
+The clskills.2da table's THIEFSKILL column contains the reference to this table.
+Theoretically it is possible to have different tables per class.
diff --git a/gemrb/docs/en/Tables/skillsta.txt b/gemrb/docs/en/Tables/skillsta.txt
new file mode 100644
index 0000000..a70b8e0
--- /dev/null
+++ b/gemrb/docs/en/Tables/skillsta.txt
@@ -0,0 +1 @@
+Description: This table is used by the CheckSkill gamescript trigger to translate skills to stats. The guiscript for character generation in IWD2 also uses it.
diff --git a/gemrb/docs/en/Tables/slottype.txt b/gemrb/docs/en/Tables/slottype.txt
new file mode 100644
index 0000000..f3640b6
--- /dev/null
+++ b/gemrb/docs/en/Tables/slottype.txt
@@ -0,0 +1,20 @@
+Description: This table must have rows equal to the number of itemslots in a creature.
+
+Columns: BITS - connects the itemslots to item types (see itemtype.2da). If a bit is set to 1, the slot may carry a specific item type. -1 means all (inventory slot). The core system uses this column to determine slot types.
+ SCRIPT - usually the ControlID of the slot on the inventory screen, but your script may use it differently. (For example PST is a bit different).
+ ICON - usually a bam ResRef of the background image of the slot in the inventory screen. (Your script may use it differently).
+ STRREF - usually a tooltip StrRef, but your script may use it differently.
+ EFFECT - whether the slot is an equipping slot (for equipping effects)
+ FLAGS - fine tuning of slot mechanics:
+ - 0: normal
+ - 1: this slot cannot be stolen from (used for eg. the fists slot)
+
+Rows: each row represents a creature item slot, even unused or invisible slots must be represented.
+
+Example (BG2):
+ BITS SCRIPT ICON STRREF EFFECT FLAGS
+10 0 0 * 0 2 0
+6 1 13 STONHELM 11999 7 0
+1 2 11 STONARM 11997 1 0
+...
+
diff --git a/gemrb/docs/en/Tables/spells.txt b/gemrb/docs/en/Tables/spells.txt
new file mode 100644
index 0000000..9e1eaa8
--- /dev/null
+++ b/gemrb/docs/en/Tables/spells.txt
@@ -0,0 +1,6 @@
+Description: This table lists the highest .spl learnable at the given level.
+
+Columns -
+1-9 - the level to limit
+
+Rows - the types of spells to limit (MAGE, PRIEST, INNATE)
diff --git a/gemrb/docs/en/Tables/splprot.txt b/gemrb/docs/en/Tables/splprot.txt
new file mode 100644
index 0000000..8440379
--- /dev/null
+++ b/gemrb/docs/en/Tables/splprot.txt
@@ -0,0 +1,41 @@
+Description: This table implements spell resistances based on a simple condition of the target's stats.
+One row defines a certain 'semi-hardcoded' condition. For iwd/iwd2 these conditions were hardcoded into the engine.
+
+Columns -
+STAT - the name or number of the stat
+VALUE - a value which has to match the stat
+RELATION - a symbol for the comparation to be performed
+COMMENT - unused by GemRB, it is just a reminder
+
+Rows - spell immunity conditions used by some iwd effects.
+
+Stat names are coming from stats.ids
+Stats over 255 (0x100 and above) are not real stats, but either virtual stats, or
+composite conditions.
+
+
+0x100 - caster is source of the spell (spell effect won't affect self)
+0x101 - caster isn't source of the spell (spell effect won't affect others)
+0x102 - the personal space of the target, set in avatars.2da (SPACE column)
+0x103 - both rows are true (the two fields designate 2 other rows in this file)
+0x104 - neither rows are true (similar to 0x103)
+0x105 - only the lowest 2 bits of alignment are calculated
+0x106 - area flags (outdoor, forest, etc)
+0x107 - daytime flag
+0x108 - caster and target EA relation (using the EA fields to calculate this)
+
+The relation could be
+0 - less or equal
+1 - equal
+2 - less
+3 - greater
+4 - greater or equal
+5 - not equal
+6 - binary less or equal (all bits of left side are in right side)
+7 - binary more or equal (all bits of right side are in left side)
+8 - binary intersect (left and right side has at least one common bit)
+9 - binary not intersect (left and right side has no common bits)
+10 - binary more (left side has bits that are not in right side)
+11 - binary less (right side has bits that are not in left side)
+
+See also: featreq
diff --git a/gemrb/docs/en/Tables/start.txt b/gemrb/docs/en/Tables/start.txt
new file mode 100644
index 0000000..16b90dc
--- /dev/null
+++ b/gemrb/docs/en/Tables/start.txt
@@ -0,0 +1,12 @@
+Description: This table is a layer of indirection to support various startpos.2da configurations. It simply describes the used row labels for startpos.2da
+
+Columns -
+XPOS - x position labels
+YPOS - y position labels
+AREA - starting area labels
+ROT - optional facing direction labels
+
+Rows -
+NORMAL - data for a normal game starting positions
+TUTORIAL - data for the tutorial starting positions
+EXPANSION - data for the expansion only game starting positions
diff --git a/gemrb/docs/en/Tables/states.txt b/gemrb/docs/en/Tables/states.txt
new file mode 100644
index 0000000..f4ea6ef
--- /dev/null
+++ b/gemrb/docs/en/Tables/states.txt
@@ -0,0 +1,16 @@
+Description: strrefs for character state names.
+
+Columns: (keys) - bitmask for particular state
+ NAME_REF - strref for name of this state
+
+Rows: each row maps one state bit (as a bitmask) to its strref
+
+Example (PST):
+ NAME_REF
+0 67220
+1 59824
+2 59825
+4 59826
+8 59830
+16 59831
+...
diff --git a/gemrb/docs/en/Tables/strings.txt b/gemrb/docs/en/Tables/strings.txt
new file mode 100644
index 0000000..4912e5f
--- /dev/null
+++ b/gemrb/docs/en/Tables/strings.txt
@@ -0,0 +1,5 @@
+Description: This table lists the hardcoded string constants used by the core engine.
+
+Columns - <N/A>
+
+Rows - string references for each slot. (The order is mandatory).
diff --git a/gemrb/docs/en/Tables/table_template.txt b/gemrb/docs/en/Tables/table_template.txt
new file mode 100644
index 0000000..9a10337
--- /dev/null
+++ b/gemrb/docs/en/Tables/table_template.txt
@@ -0,0 +1,7 @@
+Description:
+
+Columns:
+
+Rows:
+
+Example:
diff --git a/gemrb/docs/en/Tables/wildmag.txt b/gemrb/docs/en/Tables/wildmag.txt
new file mode 100644
index 0000000..041674a
--- /dev/null
+++ b/gemrb/docs/en/Tables/wildmag.txt
@@ -0,0 +1,27 @@
+Description: This table lists spells that are used when wild magic surges occur. The original engine had these hardcoded in the .exe.
+
+The row name corresponds to the required roll. The 100th row will be ignored, since a roll of 100 or more signifies a normal cast.
+
+Columns -
+ SPELL - A spell resref (to be cast instead) or one of the special codes below.
+ STRREF - The string to be displayed when the surge occurs (eg. "Oops, is that a Pit Fiend?")
+ COMMENT - unused
+
+The syntax of the SPELL column is as follows:
+ - SPELLREF (use this spell instead)
+ - +SPELLREF (cast this spell first, then the chosen one)
+ - ID.parameter (use hardcoded feature number ID, passing a parameter)
+
+Possible values for ID are -
+ 0 - cast spell //parameter// times
+ 1 - change projectile to parameter
+ 2 - also target (caster)
+ 3 - roll on this wild surge table //parameter// times
+ 4 - change target to parameter
+ 5 - use a random target
+ 6 - change the spell saving throws by //parameter//
+ 7 - cast a random known spell
+ 8 - set the projectile speed to //parameter// percent
+
+2 and 4 can take any effect target type as the parameter.
+
diff --git a/gemrb/docs/en/Tables/wsshield.txt b/gemrb/docs/en/Tables/wsshield.txt
new file mode 100644
index 0000000..427f113
--- /dev/null
+++ b/gemrb/docs/en/Tables/wsshield.txt
@@ -0,0 +1,6 @@
+Description: This table lists the benefits for points in sword/shield proficiency. (BG2-specific)
+
+Columns -
+ACVSMISSILE - bonus to ac vs missile weapons
+
+Rows - number of stars
diff --git a/gemrb/docs/en/Tables/wssingle.txt b/gemrb/docs/en/Tables/wssingle.txt
new file mode 100644
index 0000000..4586f5e
--- /dev/null
+++ b/gemrb/docs/en/Tables/wssingle.txt
@@ -0,0 +1,7 @@
+Description: This table lists the benefits for points in single-weapon proficiency. (BG2-specific)
+
+Columns -
+AC - bonus to ac
+CRITICALHITBONUS - bonus to critical range
+
+Rows - number of stars
diff --git a/gemrb/docs/en/Tables/wstwohnd.txt b/gemrb/docs/en/Tables/wstwohnd.txt
new file mode 100644
index 0000000..4200004
--- /dev/null
+++ b/gemrb/docs/en/Tables/wstwohnd.txt
@@ -0,0 +1,8 @@
+Description: This table lists the benefits for points in two-handed weapon proficiency. (BG2-specific)
+
+Columns -
+DAMAGEBONUS - additional damage
+CRITICALBONUS - critical roll modifier
+PHYSICALSPEED - speed modifier
+
+Rows - number of stars
diff --git a/gemrb/docs/en/Tables/wstwowpn.txt b/gemrb/docs/en/Tables/wstwowpn.txt
new file mode 100644
index 0000000..c656150
--- /dev/null
+++ b/gemrb/docs/en/Tables/wstwowpn.txt
@@ -0,0 +1,7 @@
+Description: This table lists the benefits for points in two-weapon proficiency. (BG2-specific)
+
+Columns -
+THAC0BONUSRIGHT - penalty to the main hand
+THAC0BONUSLEFT - penalty to the off hand
+
+Rows - number of stars
diff --git a/gemrb/docs/en/gemrb_ini.txt b/gemrb/docs/en/gemrb_ini.txt
new file mode 100644
index 0000000..a1a1a9d
--- /dev/null
+++ b/gemrb/docs/en/gemrb_ini.txt
@@ -0,0 +1,322 @@
+GAME-SPECIFIC CONFIGURATION FILE GEMRB.INI
+******************************************
+
+Contents
+--------
+ - Introduction
+ - INI File Format
+ - Options Values Types
+ - List of Options
+
+
+Introduction
+------------
+
+gemrb.ini is a file containing game-specific configuration for GemRB
+engine. It's used for options with simple value types which are
+specific to GemRB and which are not intended to be usually changed by
+a user. More complicated configuration data (tables, etc.) are kept in
+their own files. User specific configuration is in other file (GemRB.cfg).
+
+[FIXME: eventually, this file could be searched for in user's config directory too]
+
+This file's path can be
+ <source_dir>/gemrb/override/<game_type>/gemrb.ini
+ <install_share_dir>/gemrb/override/<game_type>/gemrb.ini
+ <install_dir>/override/<game_type>/gemrb.ini
+
+
+INI File Format
+---------------
+
+gemrb.ini file is in Windows *.INI format. That means it's line
+oriented, consisting of a list of options, each on its own
+line. Options are grouped into sections, each section labeled with
+section name in [brackets]. Comments have to be on their own line, they
+start with a semicolon (;) and last to the end of the line.
+
+Options are in this file described in the format optionname = <type>.
+For the moment all the options have to be in [resources] section.
+
+
+Options Values Types
+--------------------
+These are types of values used in description of particular options.
+
+ resref - resource identifier as defined in KEY file, string of at most
+ 8 characters. The case is not important, but is usually written in
+ all upper-case to be easily distinguished.
+
+ string
+
+ rgba_color - hexadecimal RGBA quadruplet written as #rrggbbaa,
+ e.g. #00ff00ff is opaque bright green.
+
+ number - unsigned integer number
+
+ bool - boolean value, for now either 0 (false) or 1 (true)
+
+
+List of Options
+---------------
+
+CursorBAM = <resref>
+- - - - - - - - - - -
+Name of bitmap resource with cursor images (e.g. PST: CARET)
+
+ScrollCursorBAM = <resref>
+- - - - - - - - - - - - - -
+Name of bitmap resource with scroll cursor images (e.g. BG2: CURSARW)
+
+ButtonFont = <resref>
+- - - - - - - - - - -
+Name of bitmap resource used for button font in dialog windows
+(e.g. BG1: STONESML)
+
+MovieFont = <resref>
+- - - - - - - - - - -
+Name of bitmap resource used for the movie subtitles
+
+TooltipFont = <resref>
+- - - - - - - - - - - -
+Name of bitmap resource with font used to display tooltips
+(e.g. PST: TRMTFONT)
+
+
+TooltipBack = <resref>
+- - - - - - - - - - - -
+Sprite displayed behind the tooltip text, if any. Leave undefined when
+tooltips don't use background bitmap.
+(e.g. BG1: TOOLTIP)
+
+
+TooltipColor = <rgba_color>
+- - - - - - - - - - - - - -
+Tooltip text color. Default is #ffffffff, i.e. opaque white.
+
+
+TooltipMargin = <number>
+- - - - - - - - - - - - -
+Space between tooltip text and sides of TooltipBack (x2)
+
+
+GroundCircleBAM1 = <resref>
+GroundCircleBAM2 = <resref>
+GroundCircleBAM3 = <resref>/<int>
+- - - - - - - - - - - - - - - - -
+Sprites displayed as a ground circle under actors (PST only).
+Actually, there should be this directive for each ground circle
+size, from 1 to 3 (sizes in PST).
+
+If the ResRef is immediately followed by a slash and a number,
+the bitmap is scaled down by this factor. That's used in PST,
+where resources exist for circle sizes 2 and 3 only.
+(e.g. GroundCircleBAM1 = wmpickl/3)
+
+
+INIConfig = <filename>
+- - - - - - - - - - - -
+Name of INI file from the original game. This file is searched for in
+the game root directory.
+
+
+Palette16 = <resref>
+Palette32 = <resref>
+Palette256 = <resref>
+- - - - - - - - - - -
+Palette bitmap resources for various number of colors in a gradient.
+Each pixel line in these files is for one base color. Pixels in the
+row then define the gradient of that color.
+
+
+IgnoreButtonFrames = <bool>
+- - - - - - - - - - - - - -
+If set to 1, buttons will ignore frame numbers as set in button CHU
+resource, and always use frames 0, 1, 2, 3 from their associated
+BAM. (PST, ...)
+
+
+AllStringsTagged = <bool>
+- - - - - - - - - - - - -
+If set to 1, .tlk tokens are always resolved regardless of the token
+flag in dialog.tlk. Normally only BG2 has token flags (0), all other
+games have this option set to 1.
+
+HasDPLAYER = <bool>
+- - - - - - - - - -
+If set to 1, then NPCs will get a default player script (DPLAYER2) set
+when they join the party. PST has no such feature.
+
+HasPickSound = <bool>
+- - - - - - - - - - -
+If set to 1, items have a pick up sound resource reference instead of a
+description icon. PST has this option. (Mutually exclusive with the
+HasDescIcon option).
+
+HasDescIcon = <bool>
+- - - - - - - - - - -
+If set to 1, items have a description icon, instead of a pick up sound.
+BG2 has this option. (Mutually exclusive with the HasPickSound option).
+
+HasEXPTABLE = <bool>
+- - - - - - - - - - -
+If set to 1, then an exptable.2da file is responsible of storing quest
+based experience awards. If the option doesn't exist, then the engine
+will use xplist.2da table for a similar feature. AddXP2da and AddXPVar
+scripting actions use this option to determine which .2da file to use.
+XPList is bg2 specific, while exptable is an iwd2 feature.
+
+SoundFolders = <bool>
+- - - - - - - - - - -
+If set to 1, then there are separate directories for each player
+character soundset. IWD2 specific.
+
+HasSongList = <bool>
+- - - - - - - - - - -
+If set to 1, then the music files are listed in a songlist.2da file.
+Otherwise the music files are listed in a music.2da file. Original
+BG1 and PST doesn't have either of these files (the list is hardcoded
+into the engine), GemRB supplies a music.2da file for them.
+
+UpperButtonText = <bool>
+- - - - - - - - - - - - -
+Set to 1 if button labels should be converted to upper-case (e.g. BG2)
+
+
+LowerLabelText = <bool>
+- - - - - - - - - - - -
+Set to 1 if label text should be converted to lower-case.
+
+HasPartyINI = <bool>
+- - - - - - - - - - -
+If set to 1, then there is a party.ini file describing pre-generated
+parties. Normally only IWD2 has this option set.
+
+HasBeastsINI = <bool>
+- - - - - - - - - - -
+Whether original game data contains beasts.ini and quests.ini files
+with descriptions of monsters and quests (PST only).
+
+ForceStereo = <bool>
+- - - - - - - - - - -
+If set to 1, all acm files will be player as if they are stereo.
+Some early .ACM files (PST, IWD1) were encoded with a faulty channel
+number, this option overrides the channel number.
+
+TeamMovement = <bool>
+- - - - - - - - - - -
+If set to 1, the team always moves to the protagonist (first player).
+This option is 1 for original PST.
+
+OneByteAnimationID = <bool>
+- - - - - - - - - - - - - -
+If set to 1, then only the lower byte of the animation ID will be used
+to index the avatar.2da table. The upper byte is responsible for various
+displaying properties (clown colour, transparency). This option is 1 for
+original PST. /warning/ Some other unrelated, but PST specific, features
+are also controlled by this flag. One such example is the avatar icon
+size in the saved games. If set to 1, then the avatar portrait won't be
+shrunk further. Otherwise the saved portrait bmp's will be shrunk 2:1.
+
+AutomapINI = <bool>
+- - - - - - - - - -
+If set to 1, automap entries will be done PST style.
+
+SmallFog = <bool>
+- - - - - - - - - - -
+Whether fog-of-war maintained in Map::ExploredBitmap is aligned with
+map boundary (SmallFog=1, like in PST) or the fog is half a tile
+larger in each of four cardinal directions (SmallFog=0, e.g. in BG2)
+
+ReverseDoor = <bool>
+- - - - - - - - - - -
+If set to 1, doors will have a reversed open/closed state. PST quirk.
+
+ProtagonistTalks = <bool>
+- - - - - - - - - - - - -
+If set to 1, only protagonist speaks with stranger for the whole party (PST).
+
+HasKaputz = <bool>
+- - - - - - - - - -
+If set to 1, then death variables will be in a separate context.
+This is PST specific.
+
+IWDMapDimensions = <bool>
+- - - - - - - - - - - - -
+If set to 1, then the minimap dimensions are using the IWD specific ratio.
+
+IWD2ScriptName = <bool>
+- - - - - - - - - - - -
+If set to 1, then areas always override the scripting name of the creature.
+Normally there is an area flag for this.
+
+DialogueScrolls = <bool>
+- - - - - - - - - - - - -
+If set to 1, dialogue window behaves as in PS:T - i.e. talk between
+Nameless One and others is appended into the window as it comes. If set to
+0 (default) each new pair of NPC's talk and Nameless One's replies is
+written from the start of the page, like in BG2.
+
+MaximumAbility = 25|40
+- - - - - - - - - - - -
+Determines the maximum value of character abilities. Setting it to absurd
+values might cause problems. Its value should be 40 for IWD2, 25 for all
+other games.
+
+StrrefSaveGame = 0|1
+- - - - - - - - - - -
+Determines whether the GameScript action SaveGame uses the int0 paramater
+to lookup a strref, or to look in savegame.2da for the save game name.
+
+ReverseToHit = 0|1
+- - - - - - - - - -
+Determines whether higher armor class is better (iwd2) or worse (the rest).
+
+RedrawTile = 0|1
+- - - - - - - - -
+Force redraw of some overlay tiles??
+
+CheckAbilities = 0|1
+- - - - - - - - - - -
+Determines if character abilities should matter in item usability checks.
+
+SpellBookIconHack = 0|1
+- - - - - - - - - - - -
+Enables the spell book icon name hack needed for ToB.
+
+DeathOnZeroStat = 0|1
+- - - - - - - - - - -
+Determines whether characters should die if any of their primary stats
+reaches zero.
+
+BreakableWeapons = 0|1
+- - - - - - - - - - - -
+Determines if weapons can randomly break when used (1% chance in bg1).
+
+SelectiveMagicRes = 0|1
+- - - - - - - - - - - -
+Determines whether the magic resistance applies to all effects, even the
+beneficial ones.
+
+HasHideInShadows = 0|1
+- - - - - - - - - - - -
+Enable this if the characters also have the IE_HIDEINSHADOWS stat besides
+IE_STEALTH.
+
+ProperBackstab = 0|1
+- - - - - - - - - - -
+Determines how many checks the backstabbing code does before treating an
+attack as a backstab.
+
+HasSpecificDamageBonus = 0|1
+- - - - - - - - - - - - - - -
+Determines if the game supports specific damage bonus like for example +10%
+extra cold damage.
+
+StealIsAttack = 0|1
+- - - - - - - - - -
+Determines whether a detected stealing attempt causes the shopkeep to turn hostile.
+
+CutsceneAreascripts = 0|1
+- - - - - - - - - -
+Determines whether area scripts should run during cutscenes or not.
diff --git a/gemrb/includes/Makefile.am b/gemrb/includes/Makefile.am
new file mode 100644
index 0000000..eb8c148
--- /dev/null
+++ b/gemrb/includes/Makefile.am
@@ -0,0 +1,2 @@
+#SUBDIRS = sdl
+noinst_HEADERS = *.h
diff --git a/gemrb/includes/RGBAColor.h b/gemrb/includes/RGBAColor.h
new file mode 100644
index 0000000..1f520d0
--- /dev/null
+++ b/gemrb/includes/RGBAColor.h
@@ -0,0 +1,31 @@
+/* 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 RGBACOLOR_H
+#define RGBACOLOR_H
+
+struct RevColor {
+ unsigned char b,g,r,a;
+};
+
+struct Color {
+ unsigned char r,g,b,a;
+};
+
+#endif
diff --git a/gemrb/includes/SClassID.h b/gemrb/includes/SClassID.h
new file mode 100644
index 0000000..b82d71c
--- /dev/null
+++ b/gemrb/includes/SClassID.h
@@ -0,0 +1,89 @@
+/* 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 SClassID.h
+ * Defines ID numbers identifying the various plugins.
+ * Needed for loading the plugins on MS Windows.
+ * @author The GemRB Project
+ */
+
+#ifndef SCLASS_ID_H
+#define SCLASS_ID_H
+
+/** Type of plugin ID numbers */
+typedef unsigned long SClass_ID;
+
+#define IE_2DA_CLASS_ID 0x000003F4
+#define IE_ACM_CLASS_ID 0x00010000
+#define IE_ARE_CLASS_ID 0x000003F2
+#define IE_BAM_CLASS_ID 0x000003E8
+#define IE_BCS_CLASS_ID 0x000003EF
+#define IE_BS_CLASS_ID 0x100003EF
+#define IE_BIF_CLASS_ID 0x00020000
+#define IE_BIO_CLASS_ID 0x000003FE //also .res
+#define IE_BMP_CLASS_ID 0x00000001
+#define IE_PNG_CLASS_ID 0x00000003
+#define IE_CHR_CLASS_ID 0x000003FA
+#define IE_CHU_CLASS_ID 0x000003EA
+#define IE_CRE_CLASS_ID 0x000003F1
+#define IE_DLG_CLASS_ID 0x000003F3
+#define IE_EFF_CLASS_ID 0x000003F8
+#define IE_GAM_CLASS_ID 0x000003F5
+#define IE_IDS_CLASS_ID 0x000003F0
+#define IE_INI_CLASS_ID 0x00000802
+#define IE_ITM_CLASS_ID 0x000003ED
+#define IE_MOS_CLASS_ID 0x000003EC
+#define IE_MUS_CLASS_ID 0x00040000
+#define IE_MVE_CLASS_ID 0x00000002
+#define IE_BIK_CLASS_ID 0x00FFFFFF
+#define IE_OGG_CLASS_ID 0x00000007
+#define IE_PLT_CLASS_ID 0x00000006
+#define IE_PRO_CLASS_ID 0x000003FD
+#define IE_SAV_CLASS_ID 0x00050000
+#define IE_SPL_CLASS_ID 0x000003EE
+#define IE_SRC_CLASS_ID 0x00000803
+#define IE_STO_CLASS_ID 0x000003F6
+#define IE_TIS_CLASS_ID 0x000003EB
+#define IE_TLK_CLASS_ID 0x00060000
+#define IE_TOH_CLASS_ID 0x00070000
+#define IE_TOT_CLASS_ID 0x00080000
+#define IE_VAR_CLASS_ID 0x00090000
+#define IE_VEF_CLASS_ID 0x000003FC
+#define IE_VVC_CLASS_ID 0x000003FB
+#define IE_WAV_CLASS_ID 0x00000004
+#define IE_WED_CLASS_ID 0x000003E9
+#define IE_WFX_CLASS_ID 0x00000005
+#define IE_WMP_CLASS_ID 0x000003F7
+#define IE_SCRIPT_CLASS_ID 0x000D0000
+#define IE_GUI_SCRIPT_CLASS_ID 0x000E0000
+
+typedef unsigned long PluginID;
+enum {
+ PLUGIN_OPCODES_CORE = 0xABCD0001,
+ PLUGIN_OPCODES_ICEWIND,
+ PLUGIN_OPCODES_TORMENT,
+ PLUGIN_RESOURCE_KEY,
+ PLUGIN_RESOURCE_DIRECTORY,
+ PLUGIN_IMAGE_WRITER_BMP,
+ PLUGIN_COMPRESSION_ZLIB
+};
+
+#endif
diff --git a/gemrb/includes/defsounds.h b/gemrb/includes/defsounds.h
new file mode 100644
index 0000000..7ce8dc0
--- /dev/null
+++ b/gemrb/includes/defsounds.h
@@ -0,0 +1,49 @@
+/* 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 defsounds.h
+ * Defines default sound numbers
+ * @author The GemRB Project
+ */
+
+// these symbols should match defsound.2da
+#ifndef IE_SOUNDS_H
+#define IE_SOUNDS_H
+
+#define DS_OPEN 0
+#define DS_CLOSE 1
+#define DS_HOPEN 2
+#define DS_HCLOSE 3
+#define DS_BUTTON_PRESSED 4
+#define DS_WINDOW_OPEN 5
+#define DS_WINDOW_CLOSE 6
+#define DS_OPEN_FAIL 7
+#define DS_CLOSE_FAIL 8
+#define DS_ITEM_GONE 9
+#define DS_FOUNDSECRET 10
+
+#define DS_RAIN 20
+#define DS_SNOW 21
+#define DS_LIGHTNING1 22
+#define DS_LIGHTNING2 23
+#define DS_LIGHTNING3 24
+
+#endif //! IE_SOUNDS_H
diff --git a/gemrb/includes/errors.h b/gemrb/includes/errors.h
new file mode 100644
index 0000000..92e8f3e
--- /dev/null
+++ b/gemrb/includes/errors.h
@@ -0,0 +1,27 @@
+/* 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 ERRORS_H
+#define ERRORS_H
+
+#define GEM_OK 0
+#define GEM_ERROR -1
+#define GEM_EOF -2
+
+#endif
diff --git a/gemrb/includes/exports.h b/gemrb/includes/exports.h
new file mode 100644
index 0000000..0faa7f1
--- /dev/null
+++ b/gemrb/includes/exports.h
@@ -0,0 +1,48 @@
+/* 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 EXPORTS_H
+#define EXPORTS_H
+
+#ifdef WIN32
+# ifdef GEM_BUILD_DLL
+# define GEM_EXPORT __declspec(dllexport)
+# else
+# define GEM_EXPORT __declspec(dllimport)
+# endif
+# define GEM_EXPORT_DLL extern "C" __declspec(dllexport)
+#else
+# if (__GNUC__ >= 3) && (__GNUC_MINOR__ >=4 || __GNUC__ > 3)
+# ifdef GEM_BUILD_DLL
+# define GEM_EXPORT __attribute__ ((visibility("default")))
+# endif
+# define GEM_EXPORT_DLL extern "C" __attribute__ ((visibility("default")))
+# endif
+#endif
+
+#ifndef GEM_EXPORT
+#define GEM_EXPORT
+#endif
+
+#ifndef GEM_EXPORT_DLL
+#define GEM_EXPORT_DLL extern "C"
+#endif
+
+#endif
diff --git a/gemrb/includes/globals.h b/gemrb/includes/globals.h
new file mode 100644
index 0000000..aa2689f
--- /dev/null
+++ b/gemrb/includes/globals.h
@@ -0,0 +1,241 @@
+/* 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 globals.h
+ * Some global definitions and includes
+ * @author The GemRB Project
+ */
+
+
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ie_types.h"
+
+#define VERSION_GEMRB "0.6.4"
+
+#define GEMRB_STRING "GemRB v" VERSION_GEMRB
+
+#ifdef ANDROID
+# define PACKAGE "GemRB"
+# define S_IEXEC S_IXUSR
+# define S_IREAD S_IRUSR
+# define S_IWRITE S_IWUSR
+#endif
+
+#ifndef GLOBALS_ONLY_DEFS
+
+#include "RGBAColor.h"
+#include "SClassID.h"
+#include "errors.h"
+#include "win32def.h"
+
+#include "AnimStructures.h"
+#include "System/DataStream.h"
+#include "Region.h"
+#include "Sprite2D.h"
+
+#include <cstdio>
+#include <cstdlib>
+
+#endif //GLOBALS_ONLY_DEFS
+
+//Global Variables
+
+#define IE_NORMAL 0
+#define IE_SHADED 1
+
+#define IE_STR_STRREFON 1
+#define IE_STR_SOUND 2
+#define IE_STR_SPEECH 4
+#define IE_STR_ALLOW_ZERO 8 //0 strref is allowed
+#define IE_STR_STRREFOFF 256
+#define IE_STR_REMOVE_NEWLINE 0x1000 // gemrb extension: remove extraneus ending newline
+
+// bitflag operations
+// !!! Keep these synchronized with GUIDefines.py !!!
+#define BM_SET 0 //gemrb extension
+#define BM_AND 1
+#define BM_OR 2
+#define BM_XOR 3
+#define BM_NAND 4 //gemrb extension
+
+//IDS Importer Defines
+#define IDS_VALUE_NOT_LOCATED -65535 // GetValue returns this if text is not found in arrays ... this needs to be a unique number that does not exist in the value[] array
+#define GEM_ENCRYPTION_KEY "\x88\xa8\x8f\xba\x8a\xd3\xb9\xf5\xed\xb1\xcf\xea\xaa\xe4\xb5\xfb\xeb\x82\xf9\x90\xca\xc9\xb5\xe7\xdc\x8e\xb7\xac\xee\xf7\xe0\xca\x8e\xea\xca\x80\xce\xc5\xad\xb7\xc4\xd0\x84\x93\xd5\xf0\xeb\xc8\xb4\x9d\xcc\xaf\xa5\x95\xba\x99\x87\xd2\x9d\xe3\x91\xba\x90\xca"
+
+/////feature flags
+#define GF_HAS_KAPUTZ 0 //pst
+#define GF_ALL_STRINGS_TAGGED 1 //bg1, pst, iwd1
+#define GF_HAS_SONGLIST 2 //bg2
+#define GF_TEAM_MOVEMENT 3 //pst
+#define GF_UPPER_BUTTON_TEXT 4 //bg2
+#define GF_LOWER_LABEL_TEXT 5 //bg2
+#define GF_HAS_PARTY_INI 6 //iwd2
+#define GF_SOUNDFOLDERS 7 //iwd2
+#define GF_IGNORE_BUTTON_FRAMES 8 // all?
+#define GF_ONE_BYTE_ANIMID 9 // pst
+#define GF_HAS_DPLAYER 10 // not pst
+#define GF_HAS_EXPTABLE 11 // iwd, iwd2
+#define GF_HAS_BEASTS_INI 12 //pst; also for quests.ini
+#define GF_HAS_DESC_ICON 13 //bg
+#define GF_HAS_PICK_SOUND 14 //pst
+#define GF_IWD_MAP_DIMENSIONS 15 //iwd, iwd2
+#define GF_AUTOMAP_INI 16 //pst
+#define GF_SMALL_FOG 17 //bg1, pst
+#define GF_REVERSE_DOOR 18 //pst
+#define GF_PROTAGONIST_TALKS 19 //pst
+#define GF_HAS_SPELLLIST 20 //iwd2
+#define GF_IWD2_SCRIPTNAME 21 //iwd2, iwd, how
+#define GF_DIALOGUE_SCROLLS 22 //pst
+#define GF_KNOW_WORLD 23 //iwd2
+#define GF_REVERSE_TOHIT 24 //all except iwd2
+#define GF_SAVE_FOR_HALF 25 //pst
+#define GF_CHARNAMEISGABBER 26 //iwd2
+#define GF_MAGICBIT 27 //iwd, iwd2
+#define GF_CHECK_ABILITIES 28 //bg2 (others?)
+#define GF_CHALLENGERATING 29 //iwd2
+#define GF_SPELLBOOKICONHACK 30 //bg2
+#define GF_ENHANCED_EFFECTS 31 //iwd2 (maybe iwd/how too)
+#define GF_DEATH_ON_ZERO_STAT 32 //not in iwd2
+#define GF_SPAWN_INI 33 //pst, iwd, iwd2
+#define GF_IWD2_DEATHVARFORMAT 34 //iwd branch (maybe pst)
+#define GF_RESDATA_INI 35 //pst
+#define GF_OVERRIDE_CURSORPOS 36 //pst, iwd2
+#define GF_BREAKABLE_WEAPONS 37 //only bg1
+#define GF_3ED_RULES 38 //iwd2
+#define GF_LEVELSLOT_PER_CLASS 39 //iwd2
+#define GF_SELECTIVE_MAGIC_RES 40 //bg2, iwd2, (how)
+#define GF_HAS_HIDE_IN_SHADOWS 41 // not in bg1 and pst
+#define GF_AREA_VISITED_VAR 42 //iwd, iwd2
+#define GF_PROPER_BACKSTAB 43 //bg2, iwd2, how?
+#define GF_ONSCREEN_TEXT 44 //pst
+#define GF_SPECIFIC_DMG_BONUS 45 //how, iwd2
+#define GF_STRREF_SAVEGAME 46 //iwd2
+#define GF_WISDOM_BONUS 47 //pst
+#define GF_BIOGRAPHY_RES 48 //iwd branch
+#define GF_NO_BIOGRAPHY 49 //pst
+#define GF_STEAL_IS_ATTACK 50 //bg2 for sure
+#define GF_CUTSCENE_AREASCRIPTS 51 //bg1, maybe more
+#define GF_FLEXIBLE_WMAP 52 //iwd
+#define GF_AUTOSEARCH_HIDDEN 53 //all except iwd2
+#define GF_PST_STATE_FLAGS 54 //pst complicates this
+#define GF_NO_DROP_CAN_MOVE 55 //bg1
+#define GF_JOURNAL_HAS_SECTIONS 56 //bg2
+#define GF_CASTING_SOUNDS 57 //all except pst and bg1
+#define GF_CASTING_SOUNDS2 58 //bg2
+#define GF_FORCE_AREA_SCRIPT 59 //how and iwd2 (maybe iwd1)
+#define GF_AREA_OVERRIDE 60 //pst maze and other hardcode
+#define GF_NO_NEW_VARIABLES 61 //pst
+//update this or bad things can happen
+#define GF_COUNT 62
+
+//the number of item usage fields (used in CREItem and STOItem)
+#define CHARGE_COUNTERS 3
+
+/////AI global defines
+#define AI_UPDATE_TIME 15
+
+/////maximum animation orientation count (used in many places)
+#define MAX_ORIENT 16
+
+/////globally used functions
+
+class Scriptable;
+class Actor;
+
+/* this function will work with pl/cz special characters */
+
+extern unsigned char pl_uppercase[256];
+extern unsigned char pl_lowercase[256];
+
+GEM_EXPORT void strnlwrcpy(char* d, const char *s, int l);
+GEM_EXPORT void strnuprcpy(char* d, const char *s, int l);
+GEM_EXPORT void strnspccpy(char* d, const char *s, int l);
+#ifndef HAVE_STRNLEN
+GEM_EXPORT int strnlen(const char* string, int maxlen);
+#endif
+GEM_EXPORT unsigned char GetOrient(const Point &s, const Point &d);
+GEM_EXPORT unsigned int Distance(const Point pos, const Point pos2);
+GEM_EXPORT unsigned int Distance(const Point pos, Scriptable *b);
+GEM_EXPORT unsigned int SquaredMapDistance(const Point pos, Scriptable *b);
+GEM_EXPORT unsigned int PersonalDistance(const Point pos, Scriptable *b);
+GEM_EXPORT unsigned int SquaredPersonalDistance(const Point pos, Scriptable *b);
+GEM_EXPORT unsigned int Distance(Scriptable *a, Scriptable *b);
+GEM_EXPORT unsigned int SquaredDistance(Scriptable *a, Scriptable *b);
+GEM_EXPORT unsigned int PersonalDistance(Scriptable *a, Scriptable *b);
+GEM_EXPORT unsigned int SquaredPersonalDistance(Scriptable *a, Scriptable *b);
+GEM_EXPORT unsigned int SquaredMapDistance(Scriptable *a, Scriptable *b);
+GEM_EXPORT int EARelation(Scriptable *a, Actor *b);
+GEM_EXPORT bool dir_exists(const char* path);
+GEM_EXPORT int strlench(const char* string, char ch);
+#ifndef HAVE_STRNDUP
+GEM_EXPORT char* strndup(const char* s, size_t l);
+#endif
+
+#ifndef WIN32
+GEM_EXPORT char* strupr(char* string);
+GEM_EXPORT char* strlwr(char* string);
+#endif
+
+#ifdef WIN32
+#define GetTime(store) store = GetTickCount()
+#else
+#include <sys/time.h>
+#define GetTime(store) \
+ { \
+ struct timeval tv; \
+ gettimeofday(&tv, NULL); \
+ store = (tv.tv_usec/1000) + (tv.tv_sec*1000); \
+ }
+#endif
+
+inline int MIN(int a, int b)
+{
+ return (a > b ? b : a);
+}
+
+inline int MAX(int a, int b)
+{
+ return (a < b ? b : a);
+}
+
+inline bool valid_number(const char* string, long& val)
+{
+ char* endpr;
+
+ val = (long) strtoul( string, &endpr, 0 );
+ return ( const char * ) endpr != string;
+}
+
+//we need 32+6 bytes at least, because we store 'context' in the variable
+//name too
+#define MAX_VARIABLE_LENGTH 40
+
+//the maximum supported game CD count
+#define MAX_CD 6
+
+#endif //! GLOBALS_H
+
diff --git a/gemrb/includes/ie_feats.h b/gemrb/includes/ie_feats.h
new file mode 100644
index 0000000..e30f384
--- /dev/null
+++ b/gemrb/includes/ie_feats.h
@@ -0,0 +1,94 @@
+/* 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.
+ *
+ *
+ */
+#define FEAT_AEGIS_OF_RIME 0
+#define FEAT_AMBIDEXTERITY 1
+#define FEAT_AQUA_MORTIS 2
+#define FEAT_ARMORED_ARCANA 3
+#define FEAT_ARMOUR_PROFICIENCY 4
+#define FEAT_ARTERIAL_STRIKE 5
+#define FEAT_BLIND_FIGHT 6
+#define FEAT_BULLHEADED 7
+#define FEAT_CLEAVE 8
+#define FEAT_COMBAT_CASTING 9
+#define FEAT_COURTEOUS_MAGOCRACY 10
+#define FEAT_CRIPPLING_STRIKE 11
+#define FEAT_DASH 12
+#define FEAT_DEFLECT_ARROWS 13
+#define FEAT_DIRTY_FIGHTING 14
+#define FEAT_DISCIPLINE 15
+#define FEAT_DODGE 16
+#define FEAT_ENVENOM_WEAPON 17
+#define FEAT_EXOTIC_BASTARD 18
+#define FEAT_EXPERTISE 19
+#define FEAT_EXTRA_RAGE 20
+#define FEAT_EXTRA_SHAPESHIFTING 21
+#define FEAT_EXTRA_SMITING 22
+#define FEAT_EXTRA_TURNING 23
+#define FEAT_FIENDSLAYER 24
+#define FEAT_FORESTER 25
+#define FEAT_GREAT_FORTITUDE 26
+#define FEAT_HAMSTRING 27
+#define FEAT_HERETICS_BANE 28
+#define FEAT_HEROIC_INSPIRATION 29
+#define FEAT_IMPROVED_CRITICAL 30
+#define FEAT_IMPROVED_EVASION 31
+#define FEAT_IMPROVED_INITIATIVE 32
+#define FEAT_IMPROVED_TURNING 33
+#define FEAT_IRON_WILL 34
+#define FEAT_LIGHTNING_REFLEXES 35
+#define FEAT_LINGERING_SONG 36
+#define FEAT_LUCK_OF_HEROES 37
+#define FEAT_MARTIAL_AXE 38
+#define FEAT_MARTIAL_BOW 39
+#define FEAT_MARTIAL_FLAIL 40
+#define FEAT_MARTIAL_GREATSWORD 41
+#define FEAT_MARTIAL_HAMMER 42
+#define FEAT_MARTIAL_LARGESWORD 43
+#define FEAT_MARTIAL_POLEARM 44
+#define FEAT_MAXIMIZED_ATTACKS 45
+#define FEAT_MERCANTILE_BACKGROUND 46
+#define FEAT_POWER_ATTACK 47
+#define FEAT_PRECISE_SHOT 48
+#define FEAT_RAPID_SHOT 49
+#define FEAT_RESIST_POISON 50
+#define FEAT_SCION_OF_STORMS 51
+#define FEAT_SHIELD_PROF 52
+#define FEAT_SIMPLE_CROSSBOW 53
+#define FEAT_SIMPLE_MACE 54
+#define FEAT_SIMPLE_MISSILE 55
+#define FEAT_SIMPLE_QUARTERSTAFF 56
+#define FEAT_SIMPLE_SMALLBLADE 57
+#define FEAT_SLIPPERY_MIND 58
+#define FEAT_SNAKE_BLOOD 59
+#define FEAT_SPELL_FOCUS_ENCHANTMENT 60
+#define FEAT_SPELL_FOCUS_EVOCATION 61
+#define FEAT_SPELL_FOCUS_NECROMANCY 62
+#define FEAT_SPELL_FOCUS_TRANSMUTE 63
+#define FEAT_SPELL_PENETRATION 64
+#define FEAT_SPIRIT_OF_FLAME 65
+#define FEAT_STRONG_BACK 66
+#define FEAT_STUNNING_FIST 67
+#define FEAT_SUBVOCAL_CASTING 68
+#define FEAT_TOUGHNESS 69
+#define FEAT_TWO_WEAPON_FIGHTING 70
+#define FEAT_WEAPON_FINESSE 71
+#define FEAT_WILDSHAPE_BOAR 72
+#define FEAT_WILDSHAPE_PANTHER 73
+#define FEAT_WILDSHAPE_SHAMBLER 74
diff --git a/gemrb/includes/ie_stats.h b/gemrb/includes/ie_stats.h
new file mode 100644
index 0000000..fa61ca1
--- /dev/null
+++ b/gemrb/includes/ie_stats.h
@@ -0,0 +1,536 @@
+/* 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 ie_stats.h
+ * Definitions of creature stats codes
+ * @author The GemRB Project
+ */
+
+
+// !!! NOTE: keep this file synchronized with gemrb/GUIScripts/ie_stats.py !!!
+
+#ifndef IE_STATS_H
+#define IE_STATS_H
+
+//EA values
+#define EA_INANIMATE 1
+#define EA_PC 2
+#define EA_FAMILIAR 3
+#define EA_ALLY 4
+#define EA_CONTROLLED 5
+#define EA_CHARMED 6
+#define EA_CONTROLLABLE 15
+#define EA_GOODBUTRED 28
+#define EA_GOODBUTBLUE 29
+#define EA_GOODCUTOFF 30
+#define EA_NOTGOOD 31
+#define EA_ANYTHING 126
+#define EA_NEUTRAL 128
+#define EA_NOTNEUTRAL 198
+#define EA_NOTEVIL 199
+#define EA_EVILCUTOFF 200
+#define EA_EVILBUTGREEN 201
+#define EA_EVILBUTBLUE 202
+#define EA_CHARMEDPC 254
+#define EA_ENEMY 255
+
+//GENERAL values
+#define GEN_HUMANOID 1 //charm?
+#define GEN_ANIMAL 2 //charm animals
+#define GEN_DEAD 3 //???
+#define GEN_UNDEAD 4 //turn
+#define GEN_GIANT 5 //???
+#define GEN_FROZEN 6 //???
+#define GEN_MONSTER 255
+
+//GENDER values
+#define SEX_MALE 1
+#define SEX_FEMALE 2
+#define SEX_OTHER 3
+#define SEX_NEITHER 4
+#define SEX_BOTH 5
+#define SEX_SUMMON 6
+#define SEX_ILLUSION 7 // bg2
+#define SEX_EXTRA 8 // bg2
+#define SEX_EXTRA2 0xa // ToB
+#define SEX_MAXEXTRA 0x12 // ToB (extra10)
+
+//alignment values
+#define AL_GE_MASK 3 //good / evil
+#define AL_GOOD 1
+#define AL_GE_NEUTRAL 2
+#define AL_EVIL 3
+#define AL_LC_MASK 0x30 //lawful / chaotic
+#define AL_LAWFUL 0x10
+#define AL_LC_NEUTRAL 0x20
+#define AL_CHAOTIC 0x30
+
+#define AL_LAWFUL_GOOD (AL_LAWFUL|AL_GOOD)
+#define AL_NEUTRAL_GOOD (AL_LC_NEUTRAL|AL_GOOD)
+#define AL_CHAOTIC_GOOD (AL_CHAOTIC|AL_GOOD)
+#define AL_LAWFUL_NEUTRAL (AL_LAWFUL|AL_GE_NEUTRAL)
+#define AL_TRUE_NEUTRAL (AL_LC_NEUTRAL|AL_GE_NEUTRAL)
+#define AL_CHAOTIC_NEUTRAL (AL_CHAOTIC|AL_GE_NEUTRAL)
+#define AL_LAWFUL_EVIL (AL_LAWFUL|AL_EVIL)
+#define AL_NEUTRAL_EVIL (AL_LC_NEUTRAL|AL_EVIL)
+#define AL_CHAOTIC_EVIL (AL_CHAOTIC|AL_EVIL)
+
+//state bits (IE_STATE)
+#define STATE_SLEEP 0x00000001
+#define STATE_BERSERK 0x00000002
+#define STATE_PANIC 0x00000004
+#define STATE_STUNNED 0x00000008
+#define STATE_INVISIBLE 0x00000010
+#define STATE_PST_CURSE 0x00000010
+#define STATE_HELPLESS 0x00000020
+#define STATE_FROZEN 0x00000040
+#define STATE_PETRIFIED 0x00000080
+#define STATE_EXPLODING 0x00000100
+#define STATE_PST_MIRROR 0x00000100
+#define STATE_FLAME 0x00000200
+#define STATE_ACID 0x00000400
+#define STATE_DEAD 0x00000800
+#define STATE_SILENCED 0x00001000
+#define STATE_CHARMED 0x00002000
+#define STATE_POISONED 0x00004000
+#define STATE_HASTED 0x00008000
+#define STATE_CRIT_PROT 0x00008000
+#define STATE_SLOWED 0x00010000
+#define STATE_CRIT_ENH 0x00010000
+#define STATE_INFRA 0x00020000
+#define STATE_BLIND 0x00040000
+//this appears to be a mistake in the original state.ids
+//this flag is the 'deactivate' flag, Activate/Deactivate works on it
+#define STATE_DISEASED 0x00080000
+#define STATE_DEACTIVATED 0x00080000
+#define STATE_FEEBLE 0x00100000
+#define STATE_NONDET 0x00200000
+#define STATE_INVIS2 0x00400000
+#define STATE_EE_DUPL 0x00400000
+#define STATE_BLESS 0x00800000
+#define STATE_CHANT 0x01000000
+#define STATE_DETECT_EVIL 0x01000000
+#define STATE_HOLY 0x02000000
+#define STATE_PST_INVIS 0x02000000
+#define STATE_LUCK 0x04000000
+#define STATE_AID 0x08000000
+#define STATE_CHANTBAD 0x10000000
+#define STATE_ANTIMAGIC 0x10000000
+#define STATE_BLUR 0x20000000
+#define STATE_MIRROR 0x40000000
+#define STATE_EMBALM 0x40000000
+#define STATE_CONFUSED 0x80000000
+
+#define STATE_STILL (STATE_STUNNED | STATE_FROZEN | STATE_PETRIFIED) //not animated
+
+#define STATE_CANTMOVE 0x80180fef
+#define STATE_CANTLISTEN 0x80080fef
+#define STATE_CANTSTEAL 0x00180fc0 //can't steal from
+#define STATE_CANTSEE 0x00080fc0 //can't explore (even itself)
+#define STATE_NOSAVE 0x00000fc0 //don't save these
+
+#define EXTSTATE_PRAYER 0x00000001
+#define EXTSTATE_PRAYER_BAD 0x00000002
+#define EXTSTATE_RECITATION 0x00000004
+#define EXTSTATE_REC_BAD 0x00000008
+#define EXTSTATE_EYE_MIND 0x00000010
+#define EXTSTATE_EYE_SWORD 0x00000020
+#define EXTSTATE_EYE_MAGE 0x00000040
+#define EXTSTATE_EYE_VENOM 0x00000080
+#define EXTSTATE_EYE_SPIRIT 0x00000100
+#define EXTSTATE_EYE_FORT 0x00000200
+#define EXTSTATE_EYE_STONE 0x00000400
+#define EXTSTATE_ANIMAL_RAGE 0x00000800
+#define EXTSTATE_NO_HP 0x00001000 //disable hp info in berserk mode
+#define EXTSTATE_BERSERK 0x00002000
+#define EXTSTATE_NO_BACKSTAB 0x00004000
+#define EXTSTATE_FLOATTEXTS 0x00008000 //weapon chatting (IWD)
+#define EXTSTATE_UNSTUN 0x00010000 //receiving damage will unstun
+#define EXTSTATE_DEAF 0x00020000
+#define EXTSTATE_CHAOTICCMD 0x00040000
+#define EXTSTATE_MISCAST 0x00080000
+#define EXTSTATE_PAIN 0x00100000
+#define EXTSTATE_MALISON 0x00200000
+#define EXTSTATE_BLOODRAGE 0x00400000
+#define EXTSTATE_CATSGRACE 0x00800000
+#define EXTSTATE_MOLD 0x01000000
+#define EXTSTATE_SHROUD 0x02000000
+#define EXTSTATE_NO_WAKEUP 0x80000000 //original HoW engine put this on top of eye_mind
+#define EXTSTATE_SEVEN_EYES 0x000007f0
+
+//Multiclass flags
+#define MC_SHOWLONGNAME 0x0001
+#define MC_REMOVE_CORPSE 0x0002
+#define MC_KEEP_CORPSE 0x0004
+#define MC_WAS_FIGHTER 0x0008
+#define MC_WAS_MAGE 0x0010
+#define MC_WAS_CLERIC 0x0020
+#define MC_WAS_THIEF 0x0040
+#define MC_WAS_DRUID 0x0080
+#define MC_WAS_RANGER 0x0100
+#define MC_WAS_ANY 0x01f8 // MC_WAS_FIGHTER | ... | MC_WAS_RANGER
+#define MC_FALLEN_PALADIN 0x0200
+#define MC_FALLEN_RANGER 0x0400
+#define MC_EXPORTABLE 0x0800
+#define MC_HIDE_HP 0x1000
+#define MC_PLOT_CRITICAL 0x2000 //if dies, it means game over
+#define MC_BEENINPARTY 0x8000
+#define MC_HIDDEN 0x10000 //iwd
+
+#define MC_NO_TALK 0x80000 //ignore dialoginterrupt
+
+//stats
+#define IE_HITPOINTS 0
+#define IE_MAXHITPOINTS 1
+#define IE_ARMORCLASS 2
+#define IE_ACCRUSHINGMOD 3
+#define IE_ACMISSILEMOD 4
+#define IE_ACPIERCINGMOD 5
+#define IE_ACSLASHINGMOD 6
+#define IE_TOHIT 7
+#define IE_NUMBEROFATTACKS 8
+#define IE_SAVEVSDEATH 9
+#define IE_SAVEVSWANDS 10
+#define IE_SAVEVSPOLY 11
+#define IE_SAVEVSBREATH 12
+#define IE_SAVEVSSPELL 13
+#define IE_SAVEFORTITUDE 9
+#define IE_SAVEREFLEX 10
+#define IE_SAVEWILL 11
+#define IE_RESISTFIRE 14
+#define IE_RESISTCOLD 15
+#define IE_RESISTELECTRICITY 16
+#define IE_RESISTACID 17
+#define IE_RESISTMAGIC 18
+#define IE_RESISTMAGICFIRE 19
+#define IE_RESISTMAGICCOLD 20
+#define IE_RESISTSLASHING 21
+#define IE_RESISTCRUSHING 22
+#define IE_RESISTPIERCING 23
+#define IE_RESISTMISSILE 24
+#define IE_LORE 25
+#define IE_LOCKPICKING 26
+#define IE_STEALTH 27
+#define IE_TRAPS 28
+#define IE_PICKPOCKET 29
+#define IE_FATIGUE 30
+#define IE_INTOXICATION 31
+#define IE_LUCK 32
+#define IE_TRACKING 33
+#define IE_LEVEL 34
+#define IE_LEVELFIGHTER 34 //for pst, iwd2
+#define IE_SEX 35
+#define IE_STR 36
+#define IE_STREXTRA 37
+#define IE_INT 38
+#define IE_WIS 39
+#define IE_DEX 40
+#define IE_CON 41
+#define IE_CHR 42
+#define IE_XPVALUE 43
+#define IE_CR 43 //for iwd2, not sure if this is a good idea yet
+#define IE_XP 44
+#define IE_GOLD 45
+#define IE_MORALEBREAK 46
+#define IE_MORALERECOVERYTIME 47
+#define IE_REPUTATION 48
+#define IE_HATEDRACE 49
+#define IE_DAMAGEBONUS 50
+#define IE_SPELLFAILUREMAGE 51
+#define IE_SPELLFAILUREPRIEST 52
+#define IE_SPELLDURATIONMODMAGE 53
+#define IE_SPELLDURATIONMODPRIEST 54
+#define IE_TURNUNDEADLEVEL 55
+#define IE_BACKSTABDAMAGEMULTIPLIER 56
+#define IE_LAYONHANDSAMOUNT 57
+#define IE_HELD 58
+#define IE_POLYMORPHED 59
+#define IE_TRANSLUCENT 60
+#define IE_IDENTIFYMODE 61
+#define IE_ENTANGLE 62
+#define IE_SANCTUARY 63
+#define IE_MINORGLOBE 64
+#define IE_SHIELDGLOBE 65
+#define IE_GREASE 66
+#define IE_WEB 67
+#define IE_LEVEL2 68
+#define IE_LEVELMAGE 68 //pst, iwd2
+#define IE_LEVEL3 69
+#define IE_LEVELTHIEF 69 //pst, iwd2
+#define IE_CASTERHOLD 70
+#define IE_ENCUMBRANCE 71
+#define IE_MISSILEHITBONUS 72
+#define IE_MAGICDAMAGERESISTANCE 73
+#define IE_RESISTPOISON 74
+#define IE_DONOTJUMP 75
+#define IE_AURACLEANSING 76
+#define IE_MENTALSPEED 77
+#define IE_PHYSICALSPEED 78
+#define IE_CASTINGLEVELBONUSMAGE 79
+#define IE_CASTINGLEVELBONUSCLERIC 80
+#define IE_SEEINVISIBLE 81
+#define IE_IGNOREDIALOGPAUSE 82
+#define IE_MINHITPOINTS 83
+#define IE_HITBONUSRIGHT 84
+#define IE_HITBONUSLEFT 85
+#define IE_DAMAGEBONUSRIGHT 86
+#define IE_DAMAGEBONUSLEFT 87
+#define IE_STONESKINS 88
+#define IE_FEAT_BOW 89
+#define IE_FEAT_CROSSBOW 90
+#define IE_FEAT_SLING 91
+#define IE_FEAT_AXE 92
+#define IE_FEAT_MACE 93
+#define IE_FEAT_FLAIL 94
+#define IE_FEAT_POLEARM 95
+#define IE_FEAT_HAMMER 96
+#define IE_FEAT_STAFF 97
+#define IE_FEAT_GREAT_SWORD 98
+#define IE_FEAT_LARGE_SWORD 99
+#define IE_FEAT_SMALL_SWORD 100
+#define IE_FEAT_TOUGHNESS 101
+#define IE_FEAT_ARMORED_ARCANA 102
+#define IE_FEAT_CLEAVE 103
+#define IE_FEAT_ARMOUR 104
+#define IE_FEAT_ENCHANTMENT 105
+#define IE_FEAT_EVOCATION 106
+#define IE_FEAT_NECROMANCY 107
+#define IE_FEAT_TRANSMUTATION 108
+#define IE_FEAT_SPELL_PENETRATION 109
+#define IE_FEAT_EXTRA_RAGE 110
+#define IE_FEAT_EXTRA_SHAPE 111
+#define IE_FEAT_EXTRA_SMITING 112
+#define IE_FEAT_EXTRA_TURNING 113
+#define IE_FEAT_BASTARDSWORD 114
+#define IE_PROFICIENCYBASTARDSWORD 89
+#define IE_PROFICIENCYLONGSWORD 90
+#define IE_PROFICIENCYSHORTSWORD 91
+#define IE_PROFICIENCYAXE 92
+#define IE_PROFICIENCYTWOHANDEDSWORD 93
+#define IE_PROFICIENCYKATANA 94
+#define IE_PROFICIENCYSCIMITAR 95 //wakisashininjato
+#define IE_PROFICIENCYDAGGER 96
+#define IE_PROFICIENCYWARHAMMER 97
+#define IE_PROFICIENCYSPEAR 98
+#define IE_PROFICIENCYHALBERD 99
+#define IE_PROFICIENCYFLAIL 100 //morningstar
+#define IE_PROFICIENCYMACE 101
+#define IE_PROFICIENCYQUARTERSTAFF 102
+#define IE_PROFICIENCYCROSSBOW 103
+#define IE_PROFICIENCYLONGBOW 104
+#define IE_PROFICIENCYSHORTBOW 105
+#define IE_PROFICIENCYDART 106
+#define IE_PROFICIENCYSLING 107
+#define IE_PROFICIENCYBLACKJACK 108
+#define IE_PROFICIENCYGUN 109
+#define IE_PROFICIENCYMARTIALARTS 110
+#define IE_PROFICIENCY2HANDED 111
+#define IE_PROFICIENCYSWORDANDSHIELD 112
+#define IE_PROFICIENCYSINGLEWEAPON 113
+#define IE_PROFICIENCY2WEAPON 114
+#define IE_EXTRAPROFICIENCY1 115
+#define IE_ALCHEMY 115
+#define IE_EXTRAPROFICIENCY2 116
+#define IE_ANIMALS 116
+#define IE_EXTRAPROFICIENCY3 117
+#define IE_BLUFF 117
+#define IE_EXTRAPROFICIENCY4 118
+#define IE_CONCENTRATION 118
+#define IE_EXTRAPROFICIENCY5 119
+#define IE_DIPLOMACY 119
+#define IE_EXTRAPROFICIENCY6 120
+#define IE_INTIMIDATE 120
+#define IE_EXTRAPROFICIENCY7 121
+#define IE_SEARCH 121
+#define IE_EXTRAPROFICIENCY8 122
+#define IE_SPELLCRAFT 122
+#define IE_EXTRAPROFICIENCY9 123
+#define IE_MAGICDEVICE 123
+#define IE_EXTRAPROFICIENCY10 124
+#define IE_EXTRAPROFICIENCY11 125
+#define IE_EXTRAPROFICIENCY12 126
+#define IE_EXTRAPROFICIENCY13 127
+#define IE_EXTRAPROFICIENCY14 128
+#define IE_EXTRAPROFICIENCY15 129
+#define IE_EXTRAPROFICIENCY16 130
+#define IE_EXTRAPROFICIENCY17 131
+#define IE_FEATS1 131
+#define IE_EXTRAPROFICIENCY18 132
+#define IE_FEATS2 132
+#define IE_EXTRAPROFICIENCY19 133
+#define IE_FEATS3 133
+#define IE_EXTRAPROFICIENCY20 134
+#define IE_FREESLOTS 134 //same as above
+#define IE_HIDEINSHADOWS 135
+#define IE_DETECTILLUSIONS 136
+#define IE_SETTRAPS 137
+#define IE_PUPPETMASTERID 138
+#define IE_PUPPETMASTERTYPE 139
+#define IE_PUPPETTYPE 140
+#define IE_PUPPETID 141
+#define IE_CHECKFORBERSERK 142
+#define IE_BERSERKSTAGE1 143
+#define IE_BERSERKSTAGE2 144
+#define IE_DAMAGELUCK 145
+#define IE_CRITICALHITBONUS 146
+#define IE_VISUALRANGE 147
+#define IE_EXPLORE 148
+#define IE_THRULLCHARM 149
+#define IE_SUMMONDISABLE 150
+#define IE_HITBONUS 151
+#define IE_KIT 152
+#define IE_FORCESURGE 153
+#define IE_SURGEMOD 154
+#define IE_IMPROVEDHASTE 155
+#define IE_INTERNAL_0 156
+#define IE_INTERNAL_1 157
+#define IE_INTERNAL_2 158
+#define IE_INTERNAL_3 159
+#define IE_INTERNAL_4 160
+#define IE_INTERNAL_5 161
+#define IE_INTERNAL_6 162
+#define IE_INTERNAL_7 163
+#define IE_INTERNAL_8 164
+#define IE_INTERNAL_9 165
+#define IE_SCRIPTINGSTATE1 156
+#define IE_SCRIPTINGSTATE2 157
+#define IE_SCRIPTINGSTATE3 158
+#define IE_SCRIPTINGSTATE4 159
+#define IE_SCRIPTINGSTATE5 160
+#define IE_SCRIPTINGSTATE6 161
+#define IE_SCRIPTINGSTATE7 162
+#define IE_SCRIPTINGSTATE8 163
+#define IE_SCRIPTINGSTATE9 164
+#define IE_SCRIPTINGSTATE10 165
+//these are genuine bg2 stats found by research
+#define IE_MELEETOHIT 166
+#define IE_MELEEDAMAGE 167
+#define IE_MISSILEDAMAGE 168
+#define IE_NOCIRCLE 169
+#define IE_FISTHIT 170
+#define IE_FISTDAMAGE 171
+#define IE_TITLE1 172
+#define IE_TITLE2 173
+#define IE_DISABLEOVERLAY 174
+#define IE_DISABLEBACKSTAB 175
+//these are clashing with GemRB now
+//176 IE_OPEN_LOCK_BONUS
+//177 IE_MOVE_SILENTLY_BONUS
+//178 IE_FIND_TRAPS_BONUS
+//179 IE_PICK_POCKETS_BONUS
+//180 IE_HIDE_IN_SHADOWS_BONUS
+//181 DETECT_ILLUSIONS_BONUS
+//182 SET_TRAPS_BONUS
+#define IE_ENABLEOFFSCREENAI 183 // bg2 has this on this spot
+#define IE_EXISTANCEDELAY 184 // affects the displaying of EXISTANCE strings
+#define IE_ATTACKNUMBERDOUBLE 185 // used by haste option 2
+#define IE_DISABLECHUNKING 186 // no permanent death
+#define IE_NOTURNABLE 187 // immune to turn
+//the IE sets this stat the same time as stat 150
+//188 IE_SUMMONDISABLE2
+#define IE_CHAOSSHIELD 189 // defense against wild surge
+#define IE_NPCBUMP 190 // allow npcs to be bumped?
+#define IE_CANUSEANYITEM 191
+#define IE_ALWAYSBACKSTAB 192
+#define IE_SEX_CHANGED 193 // modified by opcode 0x47
+#define IE_SPELLFAILUREINNATE 194
+#define IE_NOTRACKING 195 // tracking doesn't detect this
+#define IE_DEADMAGIC 196
+#define IE_DISABLETIMESTOP 197
+#define IE_NOSEQUESTER 198 // this doesn't work in IE, but intended
+#define IE_STONESKINSGOLEM 199
+//actually this stat is not used for level drain
+#define IE_LEVELDRAIN 200
+#define IE_AVATARREMOVAL 201
+
+//GemRB Specific Defines
+//these are temporary only
+#define IE_XP_MAGE 176 // XP2
+#define IE_XP_THIEF 177 // XP3
+#define IE_DIALOGRANGE 178 // iwd2
+#define IE_MOVEMENTRATE 179
+#define IE_MORALE 180 // this has no place
+#define IE_BOUNCE 181 // has projectile bouncing effect
+#define IE_MIRRORIMAGES 182
+//
+
+#define IE_IMMUNITY 203
+#define IE_DISABLEDBUTTON 204
+#define IE_ANIMATION_ID 205 //cd
+#define IE_STATE_ID 206
+#define IE_EXTSTATE_ID 207 //used in how/iwd2
+#define IE_METAL_COLOR 208 //d0
+#define IE_COLORS 208 //same
+#define IE_MINOR_COLOR 209
+#define IE_MAJOR_COLOR 210
+#define IE_SKIN_COLOR 211
+#define IE_LEATHER_COLOR 212
+#define IE_ARMOR_COLOR 213
+#define IE_HAIR_COLOR 214
+#define IE_COLORCOUNT 214 //same
+#define IE_MC_FLAGS 215
+#define IE_CLASSLEVELSUM 216 //iwd2
+#define IE_ALIGNMENT 217
+#define IE_CASTING 218
+#define IE_ARMOR_TYPE 219
+#define IE_TEAM 220
+#define IE_FACTION 221
+#define IE_SUBRACE 222
+#define IE_SPECIES 223
+//temporarily here for iwd2
+#define IE_HATEDRACE2 224
+#define IE_HATEDRACE3 225
+#define IE_HATEDRACE4 226
+#define IE_HATEDRACE5 227
+#define IE_HATEDRACE6 228
+#define IE_HATEDRACE7 229
+#define IE_HATEDRACE8 230
+#define IE_RACE 231
+#define IE_CLASS 232
+#define IE_GENERAL 233
+#define IE_EA 234
+#define IE_SPECIFIC 235
+#define IE_SAVEDXPOS 236
+#define IE_SAVEDYPOS 237
+#define IE_SAVEDFACE 238
+#define IE_USERSTAT 239 //user defined stat
+//These are in IWD2, but in a different place
+//core class levels (fighter, mage, thief are already stored)
+#define IE_LEVELBARBARIAN 240
+#define IE_LEVELBARD 241
+#define IE_LEVELCLERIC 242
+#define IE_LEVELDRUID 243
+#define IE_LEVELMONK 244
+#define IE_LEVELPALADIN 245
+#define IE_LEVELRANGER 246
+#define IE_LEVELSORCEROR 247
+// place for 2 more classes
+#define IE_LEVELCLASS12 248
+#define IE_LEVELCLASS13 249
+// these are iwd2 spell states, iwd2 uses ~180, we have place for 192
+#define IE_SPLSTATE_ID1 250
+#define IE_SPLSTATE_ID2 251
+#define IE_SPLSTATE_ID3 252
+#define IE_SPLSTATE_ID4 253
+#define IE_SPLSTATE_ID5 254
+#define IE_SPLSTATE_ID6 255
+
+#endif // ! IE_STATS_H
diff --git a/gemrb/includes/ie_types.h b/gemrb/includes/ie_types.h
new file mode 100644
index 0000000..23cbac7
--- /dev/null
+++ b/gemrb/includes/ie_types.h
@@ -0,0 +1,70 @@
+/* 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 ie_types.h
+ * Defines data types used to load IE structures
+ * @author The GemRB Project
+ */
+
+
+#ifndef IE_TYPES_H
+#define IE_TYPES_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+//we need this for Windows and Android
+#if defined (WIN32) || defined (ANDROID)
+#define SIZEOF_INT 4
+#define SIZEOF_LONG_INT 4
+#endif
+
+//well msvc and Android likes __int64, and me too
+#ifndef WIN32
+#define __int64 long long
+#endif
+
+typedef unsigned char ieByte;
+typedef signed char ieByteSigned;
+typedef unsigned short ieWord;
+typedef signed short ieWordSigned;
+
+#if (SIZEOF_INT == 4)
+typedef unsigned int ieDword;
+typedef signed int ieDwordSigned;
+#elif (SIZE_LONG_INT == 4)
+typedef unsigned long int ieDword;
+typedef signed long int ieDwordSigned;
+#else
+typedef unsigned long int ieDword;
+typedef signed long int ieDwordSigned;
+#endif
+
+/** string reference into TLK file */
+typedef ieDword ieStrRef;
+
+/** Resource reference */
+typedef char ieResRef[9];
+typedef char ieVariable[33];
+
+#endif //! IE_TYPES_H
+
diff --git a/gemrb/includes/iless.h b/gemrb/includes/iless.h
new file mode 100644
index 0000000..389bd64
--- /dev/null
+++ b/gemrb/includes/iless.h
@@ -0,0 +1,31 @@
+/* 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 ILESS_H
+#define ILESS_H
+
+#include "win32def.h" //for stricmp
+
+struct iless {
+ bool operator () (const char *lhs, const char* rhs) const
+ {
+ return stricmp(lhs, rhs) < 0;
+ }
+};
+
+#endif
diff --git a/gemrb/includes/logging.h b/gemrb/includes/logging.h
new file mode 100644
index 0000000..b993955
--- /dev/null
+++ b/gemrb/includes/logging.h
@@ -0,0 +1,119 @@
+/* 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 logging.h
+ * Logging definitions.
+ * @author The GemRB Project
+ */
+
+
+#ifndef LOGGING_H
+#define LOGGING_H
+
+#include "exports.h"
+
+#ifdef ANDROID
+# include <android/log.h>
+# include "android_log_printf.h"
+#endif
+
+#ifdef WIN32
+# define ADV_TEXT
+# include <conio.h>
+extern GEM_EXPORT HANDLE hConsole;
+# define textcolor(i) SetConsoleTextAttribute(hConsole, i)
+# ifndef __MINGW32__
+# define printf cprintf //broken in mingw !!
+# endif
+#else //WIN32
+# ifndef ANDROID
+# include <config.h>
+# endif
+# include <cstdio>
+# include <cstdlib>
+# define textcolor(i) i
+#endif //WIN32
+
+#ifdef NOCOLOR
+# define DEFAULT printf("%s","");
+# define BLACK printf("%s","");
+# define RED printf("%s","");
+# define GREEN printf("%s","");
+# define BROWN printf("%s","");
+# define BLUE printf("%s","");
+# define MAGENTA printf("%s","");
+# define CYAN printf("%s","");
+# define WHITE printf("%s","");
+# define LIGHT_RED printf("%s","");
+# define LIGHT_GREEN printf("%s","");
+# define YELLOW printf("%s","");
+# define LIGHT_BLUE printf("%s","");
+# define LIGHT_MAGENTA printf("%s","");
+# define LIGHT_CYAN printf("%s","");
+# define LIGHT_WHITE printf("%s","");
+#else
+# ifdef WIN32
+# define BLACK 0
+# define RED FOREGROUND_RED
+# define GREEN FOREGROUND_GREEN
+# define BROWN FOREGROUND_GREEN | FOREGROUND_RED
+# define BLUE FOREGROUND_BLUE
+# define MAGENTA FOREGROUND_RED | FOREGROUND_BLUE
+# define CYAN FOREGROUND_BLUE | FOREGROUND_GREEN
+# define WHITE FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
+# define LIGHT_RED (RED | FOREGROUND_INTENSITY)
+# define LIGHT_GREEN (GREEN | FOREGROUND_INTENSITY)
+# define YELLOW (GREEN | RED | FOREGROUND_INTENSITY)
+# define LIGHT_BLUE (BLUE | FOREGROUND_INTENSITY)
+# define LIGHT_MAGENTA (MAGENTA | FOREGROUND_INTENSITY)
+# define LIGHT_CYAN (CYAN | FOREGROUND_INTENSITY)
+# define LIGHT_WHITE (WHITE | FOREGROUND_INTENSITY)
+# define DEFAULT WHITE
+# else //WIN32
+# define DEFAULT printf("\033[0m");
+# define BLACK printf("\033[0m\033[30;40m");
+# define RED printf("\033[0m\033[31;40m");
+# define GREEN printf("\033[0m\033[32;40m");
+# define BROWN printf("\033[0m\033[33;40m");
+# define BLUE printf("\033[0m\033[34;40m");
+# define MAGENTA printf("\033[0m\033[35;40m");
+# define CYAN printf("\033[0m\033[36;40m");
+# define WHITE printf("\033[0m\033[37;40m");
+# define LIGHT_RED printf("\033[1m\033[31;40m");
+# define LIGHT_GREEN printf("\033[1m\033[32;40m");
+# define YELLOW printf("\033[1m\033[33;40m");
+# define LIGHT_BLUE printf("\033[1m\033[34;40m");
+# define LIGHT_MAGENTA printf("\033[1m\033[35;40m");
+# define LIGHT_CYAN printf("\033[1m\033[36;40m");
+# define LIGHT_WHITE printf("\033[1m\033[37;40m");
+# endif //WIN32
+#endif
+
+#ifndef ANDROID
+# define printBracket(status, color) textcolor(WHITE); printf("["); textcolor(color); printf("%s", status); textcolor(WHITE); printf("]")
+# define printStatus(status, color) printBracket(status, color); printf("\n")
+# define printMessage(owner, message, color) printBracket(owner, LIGHT_WHITE); printf(": "); textcolor(color); printf("%s", message)
+#else
+# define printf android_log_printf
+# define printBracket(status, color)
+# define printStatus(status, color) __android_log_print(ANDROID_LOG_INFO, "GemRB", "[%s]", status)
+# define printMessage(owner, message, color) __android_log_print(ANDROID_LOG_INFO, "GemRB", "%s: %s", owner, message)
+#endif
+
+#endif
diff --git a/gemrb/includes/opcode_params.h b/gemrb/includes/opcode_params.h
new file mode 100644
index 0000000..081dba6
--- /dev/null
+++ b/gemrb/includes/opcode_params.h
@@ -0,0 +1,204 @@
+/* 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 opcode_params.h
+ * Definitions for effect opcode parameters
+ * @author The GemRB Project
+ */
+
+#ifndef IE_OPCODE_PARAMS_H
+#define IE_OPCODE_PARAMS_H
+
+//regen/poison/disease types
+#define RPD_PERCENT 1
+#define RPD_POINTS 2
+#define RPD_SECONDS 3
+//only poison
+#define RPD_ROUNDS 4
+#define RPD_TURNS 5
+//only disease
+#define RPD_STR 4
+#define RPD_DEX 5
+#define RPD_CON 6
+#define RPD_INT 7
+#define RPD_WIS 8
+#define RPD_CHA 9
+#define RPD_SLOW 10
+//HoW specific disease types
+#define RPD_MOLD 11
+#define RPD_MOLD2 12
+//iwd2 specific disease types
+#define RPD_CONTAGION 13
+#define RPD_PEST 14
+#define RPD_DOLOR 15
+
+//appply spell on condition
+#define COND_GOTHIT 0
+#define COND_NEAR 1
+#define COND_HP_HALF 2
+#define COND_HP_QUART 3
+#define COND_HP_LOW 4
+#define COND_HELPLESS 5
+#define COND_POISONED 6
+#define COND_ATTACKED 7
+#define COND_NEAR4 8
+#define COND_NEAR10 9
+#define COND_EVERYROUND 10
+#define COND_TOOKDAMAGE 11
+
+//resources for the seven eyes effect
+#define EYE_MIND 0
+#define EYE_SWORD 1
+#define EYE_MAGE 2
+#define EYE_VENOM 3
+#define EYE_SPIRIT 4
+#define EYE_FORT 5
+#define EYE_STONE 6
+
+//spell states
+#define SS_HOPELESSNESS 0
+#define SS_PROTFROMEVIL 1
+#define SS_ARMOROFFAITH 2
+#define SS_NAUSEA 3
+#define SS_ENFEEBLED 4
+#define SS_FIRESHIELD 5
+#define SS_ICESHIELD 6
+#define SS_HELD 7
+#define SS_DEATHWARD 8
+#define SS_HOLYPOWER 9
+#define SS_GOODCHANT 10
+#define SS_BADCHANT 11
+#define SS_GOODPRAYER 12
+#define SS_BADPRAYER 13
+#define SS_GOODRECIT 14
+#define SS_BADRECIT 15
+#define SS_RIGHTEOUS 16 //allied
+#define SS_RIGHTEOUS2 17 //allied and same alignment
+#define SS_STONESKIN 18
+#define SS_IRONSKIN 19
+#define SS_SANCTUARY 20
+#define SS_RESILIENT 21
+#define SS_BLESS 22
+#define SS_AID 23
+#define SS_BARKSKIN 24
+#define SS_HOLYMIGHT 25
+#define SS_ENTANGLE 26
+#define SS_WEB 27
+#define SS_GREASE 28
+#define SS_FREEACTION 29
+#define SS_ENTROPY 30
+#define SS_STORMSHELL 31
+#define SS_ELEMPROT 32
+#define SS_BERSERK 33
+#define SS_BLOODRAGE 34
+#define SS_NOHPINFO 35
+#define SS_NOAWAKE 36
+#define SS_AWAKE 37
+#define SS_DEAF 38
+#define SS_ANIMALRAGE 39
+#define SS_NOBACKSTAB 40
+#define SS_CHAOTICCMD 41
+#define SS_MISCAST 42
+#define SS_PAIN 43
+#define SS_MALISON 44
+//#define SS_CATSGRACE 45 //used explicitly
+#define SS_MOLDTOUCH 46
+#define SS_FLAMESHROUD 47
+#define SS_EYEMIND 48
+#define SS_EYESWORD 49
+#define SS_EYEMAGE 50
+#define SS_EYEVENOM 51
+#define SS_EYESPIRIT 52
+#define SS_EYEFORTITUDE 53
+#define SS_EYESTONE 54
+#define SS_AEGIS 55
+#define SS_EXECUTIONER 56
+#define SS_ENERGYDRAIN 57
+#define SS_TORTOISE 58
+#define SS_BLINK 59
+#define SS_MINORGLOBE 60
+#define SS_PROTFROMMISS 61
+#define SS_GHOSTARMOR 62
+#define SS_REFLECTION 63
+#define SS_KAI 64
+#define SS_CALLEDSHOT 65
+#define SS_MIRRORIMAGE 66
+#define SS_TURNED 67
+#define SS_BLADEBARRIER 68
+#define SS_POISONWEAPON 69
+#define SS_STUNNINGBLOW 70
+#define SS_QUIVERPALM 71
+#define SS_DOMINATION 72
+#define SS_MAJORGLOBE 73
+#define SS_SHIELD 74
+#define SS_ANTIMAGIC 75
+#define SS_POWERATTACK 76
+//more powerattack
+#define SS_EXPERTISE 81
+//more expertise
+#define SS_ARTERIAL 86
+#define SS_HAMSTRING 87
+#define SS_RAPIDSHOT 88
+#define SS_IRONBODY 89
+#define SS_TENSER 90
+#define SS_SMITEEVIL 91
+#define SS_ALICORNLANCE 92
+#define SS_LIGHTNING 93
+#define SS_CHAMPIONS 94
+#define SS_BONECIRCLE 95
+#define SS_CLOAKOFFEAR 96
+#define SS_PESTILENCE 97
+#define SS_CONTAGION 98
+#define SS_BANE 99
+#define SS_DEFENSIVE 100
+#define SS_DESTRUCTION 101
+#define SS_DOLOROUS 102
+#define SS_DOOM 103
+#define SS_EXALTATION 104
+#define SS_FAERIEFIRE 105
+#define SS_FINDTRAPS 106
+#define SS_GREATERLATH 107
+#define SS_MAGICRESIST 108
+#define SS_NPROTECTION 109
+#define SS_PROTFROMFIRE 110
+#define SS_PROTFROMLIGHTNING 111
+#define SS_ELEMENTAL 112
+#define SS_LATHANDER 113
+#define SS_SLOWPOISON 114
+#define SS_SPELLSHIELD 115
+#define SS_STATICCHARGE 116
+#define SS_ACIDARROW 117
+#define SS_FREEZING 118
+#define SS_PROTFROMACID 119
+#define SS_PROTFROMELEC 120
+#define SS_PFNMISSILES 121
+#define SS_PROTFROMPETR 122
+#define SS_ENFEEBLEMENT 123
+#define SS_SEVENEYES 124
+//#define SS_SOULEATER 125
+
+#define SS_LOWERRESIST 140
+#define SS_LUCK 141
+//tested for this, splstate is wrong or this entry has two uses
+#define SS_DAYBLINDNESS 178
+#define SS_REBUKED 179
+
+#endif //IE_OPCODE_PARAMS_H
diff --git a/gemrb/includes/operatorbool.h b/gemrb/includes/operatorbool.h
new file mode 100644
index 0000000..c9fb188
--- /dev/null
+++ b/gemrb/includes/operatorbool.h
@@ -0,0 +1,12 @@
+#ifndef OPERATORBOOL_H
+#define OPERATORBOOL_H
+// Copied from boost/smart_ptr/detail/operator_bool.hpp
+
+#ifndef _MSC_VER
+#define OPERATOR_BOOL(Class,T,ptr) operator T* Class::*() const { return ptr == NULL ? NULL : &Class::ptr; }
+#else // MSVC6
+ // FIXME: Figure out what version doesn't need this hack.
+#define OPERATOR_BOOL(Class,T,ptr) operator bool() const { return ptr != NULL; }
+#endif
+
+#endif
diff --git a/gemrb/includes/overlays.h b/gemrb/includes/overlays.h
new file mode 100644
index 0000000..b49135c
--- /dev/null
+++ b/gemrb/includes/overlays.h
@@ -0,0 +1,60 @@
+/* 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.
+ *
+ *
+ */
+
+/**
+ * @file overlays.h
+ * the possible hardcoded overlays (they got separate stats or bits)
+ * the numbers are compliant with the internals of IWD2
+ * @author The GemRB Project
+ */
+
+#define OVERLAY_COUNT 32
+
+#define OV_SANCTUARY 0
+#define OV_ENTANGLE 1
+#define OV_WISP 2 //iwd2
+#define OV_SPELLTRAP 2 //bg2
+#define OV_SHIELDGLOBE 3
+#define OV_GREASE 4
+#define OV_WEB 5
+#define OV_MINORGLOBE 6
+#define OV_GLOBE 7
+#define OV_SHROUD 8
+#define OV_ANTIMAGIC 9
+#define OV_RESILIENT 10
+#define OV_NORMALMISS 11
+#define OV_CLOAKFEAR 12
+#define OV_ENTROPY 13
+#define OV_FIREAURA 14
+#define OV_FROSTAURA 15
+#define OV_INSECT 16
+#define OV_STORMSHELL 17
+#define OV_LATH1 18
+#define OV_LATH2 19
+#define OV_GLATH1 20
+#define OV_GLATH2 21
+#define OV_SEVENEYES 22
+#define OV_SEVENEYES2 23
+#define OV_BOUNCE 24 //bouncing
+#define OV_BOUNCE2 25 //bouncing activated
+#define OV_FIRESHIELD1 26
+#define OV_FIRESHIELD2 27
+#define OV_ICESHIELD1 28
+#define OV_ICESHIELD2 29
+#define OV_TORTOISE 30
+#define OV_DEATHARMOR 31
diff --git a/gemrb/includes/plugindef.h b/gemrb/includes/plugindef.h
new file mode 100644
index 0000000..72a74e7
--- /dev/null
+++ b/gemrb/includes/plugindef.h
@@ -0,0 +1,204 @@
+/* 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 plugindef.h
+ * Macros for defining plugins.
+ * @author The GemRB Project
+ *
+ * This file should be included once in each plugin. This file defines several
+ * entry points to the plugin and a set of macros to describe the contents
+ * of the plugin.
+ *
+ * A typical use is
+ * @code
+ * #include "plugindef.h"
+ *
+ * GEMRB_PLUGIN(0xD768B1, "BMP File Reader")
+ * PLUGIN_IE_RESOURCE(BMPImporter, ".bmp", IE_BMP_CLASS_ID)
+ * END_PLUGIN()
+ * @endcode
+ *
+ * The plugin description block should start with a call to GEMRB_PLUGIN and
+ * end with a call to END_PLUGIN, and have only calls to PLUGIN_* macros
+ * defined here in between.
+ *
+ * @def GEMRB_PLUGIN
+ * Starts a plugin declaration block
+ * @param[in] id Arbitrary unique to distinguish loadable modules.
+ * @param[in] desc Description of loadable module.
+ *
+ * PluginMgr will not load multiple plugins with the same id.
+ *
+ * @def PLUGIN_CLASS
+ * Register a class to be accessed through PluginMgr::GetPlugin
+ * @param[in] id Identifier to refer to this class.
+ * @param[in] cls Class to register. Must be a descendent of Plugin.
+ *
+ * PluginMgr will not register multiple classes with the same id, but
+ * will report an error and unload the module.
+ *
+ * @def PLUGIN_DRIVER
+ * Register a class to be accessed through PluginMgr::GetDriver.
+ * @param[in] cls Class to register. Must be a descendent of Plugin.
+ * @param[in] name
+ *
+ * PluginMgr will not register multiple classes with the same name.
+ *
+ * @def PLUGIN_RESOURCE
+ * Registers a resource through ResourceManager.
+ * @param[in] cls Class to register.
+ * @param[in] ext Extension of resource files.
+ *
+ * The class given must derive from a subclass of Resource that
+ * contains a static member ID of type TypeID. Any number of class
+ * extension pairs can be registerd. They will be tried in turn when a
+ * resource of the given subclass is requested.
+ *
+ * If the resource exists in bif files, then \ref{PLUGIN_IE_RESOURCE}
+ * should be used instead.
+ *
+ * @def PLUGIN_IE_RESOURCE
+ * Registers a resource through ResourceManager.
+ * @param[in] ie_id Type id that appears in BIF files.
+ *
+ * See \ref{PLUGIN_RESOURCE} for details. The ie_id will be used when
+ * searching chitin.key.
+ *
+ * @def PLUGIN_INITIALIZER
+ * Registers a function to do global intialization.
+ * @param[in] func Function to call at startup.
+ *
+ * This function is called during Interface initialization.
+ *
+ * @def PLUGIN_CLEANUP
+ * Registers a function to do global cleanup
+ * @param[in] func Function to call at shutdown.
+ *
+ * This function is called during Interface cleanup.
+ *
+ * @def END_PLUGIN
+ * End a plugin declaration block.
+ */
+
+#ifndef PLUGINDEF_H
+#define PLUGINDEF_H
+
+#include "exports.h"
+#include "PluginMgr.h"
+
+template <typename T>
+struct CreatePlugin {
+ static Plugin *func()
+ {
+ return new T();
+ }
+};
+
+template <typename Res>
+struct CreateResource {
+ static Resource* func(DataStream *str)
+ {
+ Res *res = new Res();
+ if (res->Open(str)) {
+ return res;
+ } else {
+ delete res;
+ return NULL;
+ }
+ }
+};
+
+#ifndef STATIC_LINK
+
+#ifdef WIN32
+#include <windows.h>
+
+BOOL APIENTRY DllMain(HANDLE /*hModule*/, DWORD /*ul_reason_for_call*/,
+ LPVOID /*lpReserved*/)
+{
+ return true;
+}
+
+#endif
+
+GEM_EXPORT_DLL const char* GemRBPlugin_Version()
+{
+ return VERSION_GEMRB;
+}
+
+#define GEMRB_PLUGIN(id, desc) \
+ GEM_EXPORT_DLL PluginID GemRBPlugin_ID() { \
+ return id; \
+ } \
+ GEM_EXPORT_DLL const char* GemRBPlugin_Description() { \
+ return desc; \
+ } \
+ GEM_EXPORT_DLL bool GemRBPlugin_Register(PluginMgr *mgr) {
+
+#define PLUGIN_CLASS(id, cls) \
+ if (!mgr->RegisterPlugin(id, &CreatePlugin<cls>::func ))\
+ return false;
+
+#define PLUGIN_DRIVER(cls, name) \
+ mgr->RegisterDriver(&cls::ID, name, &CreatePlugin<cls>::func ); \
+
+#define PLUGIN_RESOURCE(cls, ext) \
+ mgr->RegisterResource(&cls::ID, &CreateResource<cls>::func, ext);
+
+#define PLUGIN_IE_RESOURCE(cls, ext, ie_id) \
+ mgr->RegisterResource(&cls::ID, &CreateResource<cls>::func, ext, ie_id);
+
+#define PLUGIN_INITIALIZER(func) \
+ mgr->RegisterInitializer(func);
+#define PLUGIN_CLEANUP(func) \
+ mgr->RegisterCleanup(func);
+
+ /* mgr is not null (this makes mgr used) */
+#define END_PLUGIN() \
+ return mgr!=0; \
+ }
+
+#else /* STATIC_LINK */
+
+#define GEMRB_PLUGIN(id, desc) \
+ namespace { bool doRegisterPlugin = (
+
+#define PLUGIN_CLASS(id, cls) \
+ PluginMgr::Get()->RegisterPlugin(id, &CreatePlugin<cls>::func ),
+
+#define PLUGIN_DRIVER(cls, name) \
+ PluginMgr::Get()->RegisterDriver(&cls::ID, name, &CreatePlugin<cls>::func ),
+
+#define PLUGIN_RESOURCE(cls, ext) \
+ PluginMgr::Get()->RegisterResource(&cls::ID, &CreateResource<cls>::func, ext),
+
+#define PLUGIN_IE_RESOURCE(cls, ext, ie_id) \
+ PluginMgr::Get()->RegisterResource(&cls::ID, &CreateResource<cls>::func, ext, ie_id),
+
+#define PLUGIN_INITIALIZER(func) \
+ PluginMgr::Get()->RegisterInitializer(func),
+#define PLUGIN_CLEANUP(func) \
+ PluginMgr::Get()->RegisterCleanup(func),
+
+#define END_PLUGIN() \
+ true); }
+
+#endif /* STATIC_LINK */
+
+#endif
diff --git a/gemrb/includes/strrefs.h b/gemrb/includes/strrefs.h
new file mode 100644
index 0000000..e960f71
--- /dev/null
+++ b/gemrb/includes/strrefs.h
@@ -0,0 +1,201 @@
+/* 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 strrefs.h
+ * Defines indices of "standard" strings in strings.2da files
+ * @author The GemRB Project
+ */
+
+
+// these symbols should match strings.2da
+
+#ifndef IE_STRINGS_H
+#define IE_STRINGS_H
+
+#define STR_SCATTERED 0
+#define STR_WHOLEPARTY 1
+#define STR_DOORLOCKED 2
+#define STR_MAGICTRAP 3
+#define STR_NORMALTRAP 4
+#define STR_TRAP 5
+#define STR_CANNOTGO 6
+#define STR_TRAPREMOVED 7
+#define STR_OVERSTOCKED 8
+#define STR_SLEEP 9
+#define STR_AMBUSH 10
+#define STR_CONTLOCKED 11
+#define STR_NOMONEY 12
+#define STR_CURSED 13
+#define STR_SPELLDISRUPT 14
+#define STR_DIED 15
+#define STR_MAYNOTREST 16
+#define STR_CANTRESTMONS 17
+#define STR_CANTSAVEMONS 18
+#define STR_CANTSAVE 19
+#define STR_NODIALOG 20
+#define STR_CANTSAVEDIALOG 21
+#define STR_CANTSAVEDIALOG2 22
+#define STR_CANTSAVEMOVIE 23
+#define STR_TARGETBUSY 24
+#define STR_CANTTALKTRANS 25
+#define STR_GOTGOLD 26
+#define STR_LOSTGOLD 27
+#define STR_GOTXP 28
+#define STR_LOSTXP 29
+#define STR_GOTITEM 30
+#define STR_LOSTITEM 31
+#define STR_GOTREP 32
+#define STR_LOSTREP 33
+#define STR_GOTABILITY 34
+#define STR_GOTSPELL 35
+#define STR_GOTSONG 36
+#define STR_NOTHINGTOSAY 37
+#define STR_JOURNALCHANGE 38
+#define STR_WORLDMAPCHANGE 39
+#define STR_PAUSED 40
+#define STR_UNPAUSED 41
+#define STR_SCRIPTPAUSED 42
+#define STR_AP_UNUSABLE 43
+#define STR_AP_ATTACKED 44
+#define STR_AP_HIT 45
+#define STR_AP_WOUNDED 46
+#define STR_AP_DEAD 47
+#define STR_AP_NOTARGET 48
+#define STR_AP_ENDROUND 49
+#define STR_AP_ENEMY 50
+#define STR_AP_TRAP 51
+#define STR_AP_SPELLCAST 52
+#define STR_AP_RESERVED1 53
+#define STR_AP_RESERVED2 54
+#define STR_AP_RESERVED3 55
+#define STR_AP_RESERVED4 56
+#define STR_CHARMED 57
+#define STR_DIRECHARMED 58
+#define STR_CONTROLLED 59
+#define STR_EVIL 60
+#define STR_GE_NEUTRAL 61
+#define STR_GOOD 62
+#define STR_LAWFUL 63
+#define STR_LC_NEUTRAL 64
+#define STR_CHAOTIC 65
+#define STR_ACTION_CAST 66
+#define STR_ACTION_ATTACK 67
+#define STR_ACTION_TURN 68
+#define STR_ACTION_SONG 69
+#define STR_ACTION_FINDTRAP 70
+#define STR_MAGICWEAPON 71
+#define STR_OFFHAND_USED 72
+#define STR_TWOHANDED_USED 73
+#define STR_CANNOT_USE_ITEM 74
+#define STR_CANT_DROP_ITEM 75
+#define STR_NOT_IN_OFFHAND 76
+#define STR_ITEM_IS_CURSED 77
+#define STR_NO_CRITICAL 78
+#define STR_TRACKING 79
+#define STR_TRACKINGFAILED 80
+#define STR_DOOR_NOPICK 81
+#define STR_CONT_NOPICK 82
+#define STR_CANTSAVECOMBAT 83
+#define STR_CANTSAVENOCTRL 84
+#define STR_LOCKPICK_DONE 85
+#define STR_LOCKPICK_FAILED 86
+#define STR_STATIC_DISS 87
+#define STR_LIGHTNING_DISS 88
+#define STR_UNUSABLEITEM 89 //item has no usable ability
+#define STR_ITEMID 90 //item needs identify
+#define STR_WRONGITEMTYPE 91
+#define STR_ITEMEXCL 92
+#define STR_PICKPOCKET_DONE 93 //done
+#define STR_PICKPOCKET_NONE 94 //no items to steal
+#define STR_PICKPOCKET_FAIL 95 //failed, noticed
+#define STR_PICKPOCKET_EVIL 96 //can't pick hostiles
+#define STR_PICKPOCKET_ARMOR 97 //armor restriction
+#define STR_USING_FEAT 98
+#define STR_STOPPED_FEAT 99
+#define STR_DISARM_DONE 100 //trap disarmed
+#define STR_DISARM_FAIL 101 //trap not disarmed
+#define STR_DOORBASH_DONE 102
+#define STR_DOORBASH_FAIL 103
+#define STR_CONTBASH_DONE 104
+#define STR_CONTBASH_FAIL 105
+#define STR_MAYNOTSETTRAP 106
+#define STR_SNAREFAILED 107
+#define STR_SNARESUCCEED 108
+#define STR_NOMORETRAP 109
+#define STR_DISABLEDMAGE 110
+#define STR_SAVESUCCEED 111
+#define STR_QSAVESUCCEED 112
+#define STR_UNINJURED 113 //uninjured
+#define STR_INJURED1 114
+#define STR_INJURED2 115
+#define STR_INJURED3 116
+#define STR_INJURED4 117 //near death
+#define STR_HOURS 118 //<HOUR> hours
+#define STR_HOUR 119
+#define STR_DAYS 120 //<GAMEDAYS> days
+#define STR_DAY 121
+#define STR_REST 122 //You have rested for <DURATION>
+#define STR_JOURNEY 123 //The journey took <DURATION>
+#define STR_PST_REST 124 //You have rested for <HOUR> <DURATION>
+#define STR_PST_HOUR 125
+#define STR_PST_HOURS 126
+#define STR_DAMAGE_IMMUNITY 127
+#define STR_DAMAGE1 128
+#define STR_DAMAGE2 129
+#define STR_DAMAGE3 130
+#define STR_DMG_POISON 131
+#define STR_DMG_MAGIC 132
+#define STR_DMG_MISSILE 133
+#define STR_DMG_SLASHING 134
+#define STR_DMG_PIERCING 135
+#define STR_DMG_CRUSHING 136
+#define STR_DMG_FIRE 137
+#define STR_DMG_ELECTRIC 138
+#define STR_DMG_COLD 139
+#define STR_DMG_ACID 140
+#define STR_DMG_OTHER 141
+#define STR_GOTQUESTXP 142
+#define STR_LEVELUP 143
+#define STR_INVFULL_ITEMDROP 144
+#define STR_CONTDUP 145
+#define STR_CONTTRIG 146
+#define STR_CONTFAIL 147
+#define STR_SEQDUP 148
+#define STR_CRITICAL_HIT 149
+#define STR_CRITICAL_MISS 150
+#define STR_DEATH 151
+#define STR_BACKSTAB 152
+#define STR_BACKSTAB_BAD 153
+#define STR_BACKSTAB_FAIL 154
+#define STR_CASTER_LVL_INC 155 // caster level bonus (wild mages)
+#define STR_CASTER_LVL_DEC 156
+#define STR_EXPORTED 157 // characters exported (iwd)
+#define STR_PALADIN_FALL 158
+#define STR_RANGER_FALL 159
+#define STR_RES_RESISTED 160
+#define STR_DEADMAGIC_FAIL 161
+#define STR_MISCASTMAGIC 162
+#define STR_WILDSURGE 163
+#define STR_FAMBLOCK 164
+#define STR_FAMPROTAGONIST 165
+#define STRREF_COUNT 166
+
+#endif //! IE_STRINGS_H
diff --git a/gemrb/includes/win32def.h b/gemrb/includes/win32def.h
new file mode 100644
index 0000000..5b676e0
--- /dev/null
+++ b/gemrb/includes/win32def.h
@@ -0,0 +1,88 @@
+/* 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 win32def.h
+ * Some global definitions, mostly for Un*x vs. MS Windows compatibility
+ * @author The GemRB Project
+ */
+
+
+#ifndef WIN32DEF_H
+#define WIN32DEF_H
+
+#include "exports.h"
+
+#ifdef WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+# if _MSC_VER >= 1000
+// 4251 disables the annoying warning about missing dll interface in templates
+# pragma warning( disable: 4251 521 )
+# pragma warning( disable: 4275 )
+//disables annoying warning caused by STL:Map in msvc 6.0
+# if _MSC_VER < 7000
+# pragma warning(disable:4786)
+# endif
+# endif
+
+# if defined(__MINGW32__) && ! defined(HAVE_SNPRINTF)
+# define HAVE_SNPRINTF 1
+# endif
+
+#else //WIN32
+# ifndef ANDROID
+# include <config.h>
+# endif
+# include <cstdio>
+# include <cstdlib>
+# include <cstring>
+
+# define stricmp strcasecmp
+# define strnicmp strncasecmp
+#endif //WIN32
+
+#ifndef HAVE_SNPRINTF
+# ifdef WIN32
+# define snprintf _snprintf
+# define HAVE_SNPRINTF 1
+# else
+# include "System/snprintf.h"
+# endif
+#endif
+
+#include "System/VFS.h"
+
+#ifdef _MSC_VER
+# ifndef round
+# define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
+# endif
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 // pi
+#endif
+#ifndef M_PI_2
+#define M_PI_2 1.57079632679489661923 // pi/2
+#endif
+
+#include "logging.h"
+#endif //! WIN32DEF_H
diff --git a/gemrb/override/CMakeLists.txt b/gemrb/override/CMakeLists.txt
new file mode 100644
index 0000000..20c335e
--- /dev/null
+++ b/gemrb/override/CMakeLists.txt
@@ -0,0 +1,7 @@
+ADD_SUBDIRECTORY( bg1 )
+ADD_SUBDIRECTORY( bg2 )
+ADD_SUBDIRECTORY( how )
+ADD_SUBDIRECTORY( iwd )
+ADD_SUBDIRECTORY( iwd2 )
+ADD_SUBDIRECTORY( pst )
+ADD_SUBDIRECTORY( shared )
diff --git a/gemrb/override/Makefile.am b/gemrb/override/Makefile.am
new file mode 100644
index 0000000..27691ea
--- /dev/null
+++ b/gemrb/override/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = bg1 bg2 how iwd iwd2 pst shared
+EXTRA_DIST = CMakeLists.txt */CMakeLists.txt
diff --git a/gemrb/override/bg1/CMakeLists.txt b/gemrb/override/bg1/CMakeLists.txt
new file mode 100644
index 0000000..f258b1b
--- /dev/null
+++ b/gemrb/override/bg1/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_OVERRIDE (bg1)
\ No newline at end of file
diff --git a/gemrb/override/bg1/Makefile.am b/gemrb/override/bg1/Makefile.am
new file mode 100644
index 0000000..e9c61f4
--- /dev/null
+++ b/gemrb/override/bg1/Makefile.am
@@ -0,0 +1,3 @@
+bg1override_DATA = *.2da *.bmp *.ini *.chu *.ids *.pro
+bg1overridedir = $(moddir)/override/bg1/
+EXTRA_DIST = *.2da *.bmp *.ini *.chu *.ids *.pro
diff --git a/gemrb/override/bg1/ability.2da b/gemrb/override/bg1/ability.2da
new file mode 100644
index 0000000..50604f7
--- /dev/null
+++ b/gemrb/override/bg1/ability.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF STAT_ID
+STRENGTH 11975 9582 1145 36
+DEXTERITY 11977 9584 1151 40
+CONSTITUTION 11978 9583 1178 41
+INTELLIGENCE 11979 9585 1179 38
+WISDOM 11980 9586 1180 39
+CHARISMA 11981 9587 1181 42
diff --git a/gemrb/override/bg1/acidblgr.pro b/gemrb/override/bg1/acidblgr.pro
new file mode 100644
index 0000000..fe37e1c
Binary files /dev/null and b/gemrb/override/bg1/acidblgr.pro differ
diff --git a/gemrb/override/bg1/acidblmu.pro b/gemrb/override/bg1/acidblmu.pro
new file mode 100644
index 0000000..aa9e8df
Binary files /dev/null and b/gemrb/override/bg1/acidblmu.pro differ
diff --git a/gemrb/override/bg1/acidblob.pro b/gemrb/override/bg1/acidblob.pro
new file mode 100644
index 0000000..16ef4f3
Binary files /dev/null and b/gemrb/override/bg1/acidblob.pro differ
diff --git a/gemrb/override/bg1/acidbloc.pro b/gemrb/override/bg1/acidbloc.pro
new file mode 100644
index 0000000..a2e6822
Binary files /dev/null and b/gemrb/override/bg1/acidbloc.pro differ
diff --git a/gemrb/override/bg1/aligns.2da b/gemrb/override/bg1/aligns.2da
new file mode 100644
index 0000000..1047ea6
--- /dev/null
+++ b/gemrb/override/bg1/aligns.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF VALUE COLNAME USABILITY
+LAWFUL_GOOD 7186 9603 1102 0x11 L_G 0x14
+NEUTRAL_GOOD 7183 9606 1105 0x21 N_G 0x24
+CHAOTIC_GOOD 7189 9609 1108 0x31 C_G 0x5
+LAWFUL_NEUTRAL 7188 9604 1104 0x12 L_N 0x18
+TRUE_NEUTRAL 7185 9608 1106 0x22 N_N 0x28
+CHAOTIC_NEUTRAL 7191 9610 1109 0x32 C_N 0x9
+LAWFUL_EVIL 7187 9605 1103 0x13 L_E 0x12
+NEUTRAL_EVIL 7184 9607 1107 0x23 N_E 0x22
+CHAOTIC_EVIL 7190 9611 1110 0x33 C_E 0x3
diff --git a/gemrb/override/bg1/areapro.2da b/gemrb/override/bg1/areapro.2da
new file mode 100644
index 0000000..665f80c
--- /dev/null
+++ b/gemrb/override/bg1/areapro.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+*
+ RESOURCE1 RESOURCE2 RESOURCE3 SOUND1 SOUND2 FLAGS
+FIREBALL SPRING SPBOOM * EFF_M21 * 40
+STINKCLOUD STNKCLDD * * EFF_M18A EFF_M18C 34
+CLOUDKILL STNKCLDD * * EFF_M18A EFF_M18C 34
+ICESTORM STNKCLDD SPBOOM * EFF_M34 * 42
+GREASE GREASEH SPBOOM GREASED EFF_M31B * 42
+WEB WEBENTH SPBOOM WEBENTD EFF_M19 * 42
diff --git a/gemrb/override/bg1/arrow.pro b/gemrb/override/bg1/arrow.pro
new file mode 100644
index 0000000..293014f
Binary files /dev/null and b/gemrb/override/bg1/arrow.pro differ
diff --git a/gemrb/override/bg1/arrowex.pro b/gemrb/override/bg1/arrowex.pro
new file mode 100644
index 0000000..70b7773
Binary files /dev/null and b/gemrb/override/bg1/arrowex.pro differ
diff --git a/gemrb/override/bg1/arrowflb.pro b/gemrb/override/bg1/arrowflb.pro
new file mode 100644
index 0000000..12b5b91
Binary files /dev/null and b/gemrb/override/bg1/arrowflb.pro differ
diff --git a/gemrb/override/bg1/arrowflg.pro b/gemrb/override/bg1/arrowflg.pro
new file mode 100644
index 0000000..45a7bab
Binary files /dev/null and b/gemrb/override/bg1/arrowflg.pro differ
diff --git a/gemrb/override/bg1/arrowfli.pro b/gemrb/override/bg1/arrowfli.pro
new file mode 100644
index 0000000..202c16c
Binary files /dev/null and b/gemrb/override/bg1/arrowfli.pro differ
diff --git a/gemrb/override/bg1/arrowflm.pro b/gemrb/override/bg1/arrowflm.pro
new file mode 100644
index 0000000..ae84563
Binary files /dev/null and b/gemrb/override/bg1/arrowflm.pro differ
diff --git a/gemrb/override/bg1/arrowhvy.pro b/gemrb/override/bg1/arrowhvy.pro
new file mode 100644
index 0000000..1471c2e
Binary files /dev/null and b/gemrb/override/bg1/arrowhvy.pro differ
diff --git a/gemrb/override/bg1/avatars.2da b/gemrb/override/bg1/avatars.2da
new file mode 100644
index 0000000..40fa746
--- /dev/null
+++ b/gemrb/override/bg1/avatars.2da
@@ -0,0 +1,322 @@
+2DA V1.0
+*
+ AT_1 AT_2 AT_3 AT_4 TYPE SPACE PALETTE SIZE
+0x0100 SPCHUNKS SPCHUNKS SPCHUNKS SPCHUNKS 13 0 0 *
+0x0300 SPSMPUFF SPSMPUFF SPSMPUFF SPSMPUFF 13 0 0 *
+0x0400 SKLH SKLH SKLH SKLH 13 0 1 *
+0x0410 GLPHWRDH GLPHWRDH GLPHWRDH GLPHWRDH 13 0 1 *
+0x1000 MWYV MWYV MWYV MWYV 11 3 1 *
+0x1100 MTAN MTAN MTAN MTAN 11 3 1 *
+0x1200 MDR1 MDR1 MDR1 MDR1 12 7 1 *
+0x1201 MDR2 MDR2 MDR2 MDR2 12 7 1 *
+0x1202 MDR3 MDR3 MDR3 MDR3 12 7 1 *
+0x1203 MDR1 MDR1 MDR1 MDR1 12 7 GR *
+0x1204 MDR1 MDR1 MDR1 MDR1 12 7 AQ *
+0x1205 MDR1 MDR1 MDR1 MDR1 12 7 BL *
+0x1206 MDR1 MDR1 MDR1 MDR1 12 7 BR *
+0x1207 MDR1 MDR1 MDR1 MDR1 12 7 MC *
+0x1208 MDR1 MDR1 MDR1 MDR1 12 7 PU *
+0x2000 MSIR MSIR MSIR MSIR 2 2 0 *
+0x2100 UVOL UVOL UVOL UVOL 2 2 1 *
+0x2200 MOGM MOGM MOGM MOGM 2 2 0 S
+0x2300 MDKN MDKN MDKN MDKN 2 2 1 *
+0x3000 MAKH MAKH MAKH MAKH 2 3 1 *
+0x4000 SNOMC SNOMC SNOMC SNOMC 1 2 0 *
+0x4002 SNOMM SNOMM SNOMM SNOMM 1 2 0 *
+0x4010 SNOWC SNOWC SNOWC SNOWC 1 2 0 *
+0x4012 SNOWM SNOWM SNOWM SNOWM 1 2 0 *
+0x4100 SSIMC SSIMC SSIMC SSIMC 1 2 0 *
+0x4101 SSIMS SSIMS SSIMS SSIMS 1 2 0 *
+0x4102 SSIMM SSIMM SSIMM SSIMM 1 2 0 *
+0x4110 SSIWC SSIWC SSIWC SSIWC 1 2 0 *
+0x4112 SSIWM SSIWM SSIWM SSIWM 1 2 0 *
+0x4200 SHMCM SHMCM SHMCM SHMCM 1 2 0 *
+0x4300 MSPLG1 MSPLG1 MSPLG1 MSPLG1 1 2 1 *
+0x4400 LHMC LHMC LHMC LHMC 1 2 0 *
+0x4410 LHFC LHFC LHFC LHFC 1 2 0 *
+0x4500 LFAM LFAM LFAM LFAM 1 2 0 *
+0x4600 LDMF LDMF LDMF LDMF 1 2 0 *
+0x4700 LEMF LEMF LEMF LEMF 1 2 0 *
+0x4710 LEFF LEFF LEFF LEFF 1 2 0 *
+0x4800 LIMC LIMC LIMC LIMC 1 2 0 *
+0x5000 CHMC1 CHMC2 CHMC3 CHMC4 6 2 0 L
+0x5001 CEMC1 CEMC2 CEMC3 CEMC4 6 2 0 M
+0x5002 CDMC1 CDMC2 CDMC3 CDMC4 6 2 0 S
+0x5003 CIMC1 CIMC2 CIMC3 CIMC4 6 2 0 S
+0x5010 CHFC1 CHFC2 CHFC3 CHFC4 6 2 0 M
+0x5011 CEFC1 CEFC2 CEFC3 CEFC4 6 2 0 M
+0x5012 CDMC1 CDMC2 CDMC3 CDMC4 6 2 0 S
+0x5013 CIFC1 CIFC2 CIFC3 CIFC4 6 2 0 S
+0x5100 CHMF1 CHMF2 CHMF3 CHMF4 6 2 0 L
+0x5101 CEMF1 CEMF2 CEMF3 CHMF4 6 2 0 M
+0x5102 CDMF1 CDMF2 CDMF3 CDMF4 6 2 0 S
+0x5103 CIMF1 CIMF2 CIMF3 CIMF4 6 2 0 S
+0x5110 CHFF1 CHFF2 CHFF3 CHFF4 6 2 0 M
+0x5111 CEFF1 CEFF2 CEFF3 CEFF4 6 2 0 M
+0x5112 CDMF1 CDMF2 CDMF3 CDMF4 6 2 0 S
+0x5113 CIFF1 CIFF2 CIFF3 CIFF4 6 2 0 S
+0x5200 CHMW1 CHMW2 CHMW3 CHMW4 6 2 0 L
+0x5201 CEMW1 CEMW2 CEMW3 CEMW4 6 2 0 M
+0x5202 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x5203 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x5210 CHFW1 CHFW2 CHFW3 CHFW4 6 2 0 M
+0x5211 CEFW1 CEFW2 CEFW3 CEFW4 6 2 0 M
+0x5212 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x5213 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x5300 CHMT1 CHMT2 CHMT2 CHMT2 6 2 0 L
+0x5301 CEMT1 CEMT2 CEMT2 CEMT2 6 2 0 M
+0x5302 CDMT1 CDMT2 CDMT2 CDMT2 6 2 0 S
+0x5303 CIMT1 CIMT2 CIMT2 CIMT2 6 2 0 S
+0x5310 CHFT1 CHFT2 CHFT2 CHFT2 6 2 0 M
+0x5311 CEFT1 CEFT2 CEFT2 CEFT2 6 2 0 M
+0x5312 CDMT1 CDMT2 CDMT2 CDMT2 6 2 0 S
+0x5313 CIFT1 CIFT2 CIFT2 CIFT2 6 2 0 S
+0x6000 CHMC1 CHMC2 CHMC3 CHMC4 6 2 0 L
+0x6001 CEMC1 CEMC2 CEMC3 CEMC4 6 2 0 M
+0x6002 CDMC1 CDMC2 CDMC3 CDMC4 6 2 0 S
+0x6003 CIMC1 CIMC2 CIMC3 CIMC4 6 2 0 S
+0x6004 CDMC1 CDMC2 CDMC3 CDMC4 6 2 0 S
+0x6005 CHMC1 CHMC2 CHMC3 CHMC4 6 2 0 L
+0x6010 CHFC1 CHFC2 CHFC3 CHFC4 6 2 0 M
+0x6011 CEFC1 CEFC2 CEFC3 CEFC4 6 2 0 M
+0x6012 CDMC1 CDMC2 CDMC3 CDMC4 6 2 0 S
+0x6013 CIFC1 CIFC2 CIFC3 CIFC4 6 2 0 S
+0x6014 CIFC1 CIFC2 CIFC3 CIFC4 6 2 0 S
+0x6015 CHFC1 CHFC2 CHFC3 CHFC4 6 2 0 L
+0x6100 CHMF1 CHMF2 CHMF3 CHMF4 6 2 0 L
+0x6101 CEMF1 CEMF2 CEMF3 CEMF4 6 2 0 M
+0x6102 CDMF1 CDMF2 CDMF3 CDMF4 6 2 0 S
+0x6103 CIMF1 CIMF2 CIMF3 CIMF4 6 2 0 S
+0x6104 CDMF1 CDMF2 CDMF3 CDMF4 6 2 0 S
+0x6105 CHMF1 CHMF2 CHMF3 CHMF4 6 2 0 L
+0x6110 CHFF1 CHFF2 CHFF3 CHFF4 6 2 0 M
+0x6111 CEFF1 CEFF2 CEFF3 CEFF4 6 2 0 M
+0x6112 CDMF1 CDMF2 CDMF3 CDMF4 6 2 0 S
+0x6113 CIFF1 CIFF2 CIFF3 CIFF4 6 2 0 S
+0x6114 CIFF1 CIFF2 CIFF3 CIFF4 6 2 0 S
+0x6115 CHFF1 CHFF2 CHFF3 CHFF4 6 2 0 L
+0x6200 CHMW1 CHMW2 CHMW3 CHMW4 6 2 0 L
+0x6201 CEMW1 CEMW2 CEMW3 CEMW4 6 2 0 M
+0x6202 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x6203 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x6204 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x6205 CHMW1 CHMW2 CHMW3 CHMW4 6 2 0 L
+0x6210 CHFW1 CHFW2 CHFW3 CHFW4 6 2 0 M
+0x6211 CEFW1 CEFW2 CEFW3 CEFW4 6 2 0 M
+0x6212 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x6213 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x6214 CDMW1 CIFW2 CIFW3 CIFW4 6 2 0 S
+0x6215 CHFW1 CHFW2 CHFW3 CHFW4 6 2 0 L
+0x6300 CHMT1 CHMT2 CHMT2 CHMT2 6 2 0 L
+0x6301 CEMT1 CEMT2 CEMT2 CEMT2 6 2 0 M
+0x6302 CDMT1 CDMT2 CDMT2 CDMT2 6 2 0 S
+0x6303 CIMT1 CIMT2 CIMT2 CIMT2 6 2 0 S
+0x6304 CDMT1 CDMT2 CDMT2 CDMT2 6 2 0 S
+0x6305 CHMT1 CHMT2 CHMT2 CHMT2 6 2 0 L
+0x6310 CHFT1 CHFT2 CHFT2 CHFT2 6 2 0 M
+0x6311 CEFT1 CEFT2 CEFT2 CEFT2 6 2 0 M
+0x6312 CDMT1 CDMT2 CDMT2 CDMT2 6 2 0 S
+0x6313 CIFT1 CIFT2 CIFT2 CIFT2 6 2 0 S
+0x6314 CIFT1 CIFT2 CIFT2 CIFT2 6 2 0 S
+0x6315 CHFT1 CHFT2 CHFT2 CHFT2 6 2 0 L
+0x6400 UDRZ1 UDRZ1 UDRZ1 UDRZ1 6 2 0 *
+0x6401 UELM1 UELM1 UELM1 UELM1 6 2 1 *
+0x6402 CMNK1 CMNK1 CMNK1 CMNK1 6 2 0 *
+0x6403 MSKL1 MSKL1 MSKL1 MSKL1 6 2 1 M
+0x6404 USAR1 USAR1 USAR1 USAR1 6 2 1 *
+0x6405 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 M
+0x6406 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 L
+0x6500 CHMM1 CHMC2 CHMC3 CHMC4 0 2 0 *
+0x6510 CHFM1 CHFC2 CHFC3 CHFC4 0 2 0 *
+0x7000 MOGH MOGH MOGH MOGH 2 2 0 *
+0x7001 MOGN MOGN MOGN MOGN 2 2 0 *
+0x7100 MBAS MBAS MBAS MBAS 2 2 1 *
+0x7101 MBAS MBAS MBAS MBAS 2 2 GR *
+0x7200 MBER MBER MBER MBER 2 2 BL *
+0x7201 MBER MBER MBER MBER 2 2 1 *
+0x7202 MBER MBER MBER MBER 2 2 CA *
+0x7203 MBER MBER MBER MBER 2 2 PO *
+0x7300 MEAE MEAE MEAE MEAE 4 2 1 *
+0x7301 MEAS MEAS MEAS MEAS 4 2 1 *
+0x7302 MEAE MEAE MEAE MEAE 4 2 SH *
+0x7310 MFIE MFIE MFIE MFIE 4 2 1 *
+0x7311 MFIS MFIS MFIS MFIS 4 2 1 *
+0x7320 MAIR MAIR MAIR MAIR 4 2 1 *
+0x7321 MAIS MAIS MAIS MAIS 4 2 1 *
+0x7400 MDOG MDOG MDOG MDOG 2 2 WI *
+0x7401 MDOG MDOG MDOG MDOG 2 2 WA *
+0x7402 MDOG MDOG MDOG MDOG 2 2 MO *
+0x7500 MDOP MDOP MDOP MDOP 2 2 1 *
+0x7501 MDOP MDOP MDOP MDOP 2 2 GR *
+0x7600 METT METT METT METT 2 2 1 *
+0x7701 MGHL MGHL MGHL MGHL 2 2 1 *
+0x7702 MGHL MGHL MGHL MGHL 2 2 RE *
+0x7703 MGHL MGHL MGHL MGHL 2 2 GA *
+0x7704 MSHD MSHD MSHD MSHD 4 2 1 *
+0x7800 MGIB MGIB MGIB MGIB 2 2 1 *
+0x7900 MSLI MSLI MSLI MSLI 3 3 GR *
+0x7901 MSLI MSLI MSLI MSLI 3 3 OL *
+0x7902 MSLI MSLI MSLI MSLI 3 3 MU *
+0x7903 MSLI MSLI MSLI MSLI 3 3 OC *
+0x7904 MSLI MSLI MSLI MSLI 3 3 1 *
+0x7A00 MSPI MSPI MSPI MSPI 2 2 GI *
+0x7A01 MSPI MSPI MSPI MSPI 3 2 HU *
+0x7A02 MSPI MSPI MSPI MSPI 3 2 PH *
+0x7A03 MSPI MSPI MSPI MSPI 3 2 SW *
+0x7A04 MSPI MSPI MSPI MSPI 3 2 WR *
+0x7B00 MWLF MWLF MWLF MWLF 2 2 1 *
+0x7B01 MWLF MWLF MWLF MWLF 2 2 WO *
+0x7B02 MWLF MWLF MWLF MWLF 2 2 DI *
+0x7B03 MWLF MWLF MWLF MWLF 2 2 WI *
+0x7B04 MWLF MWLF MWLF MWLF 2 2 VA *
+0x7B05 MWLF MWLF MWLF MWLF 2 2 DR *
+0x7B06 MWLS MWLS MWLS MWLS 2 2 1 *
+0x7C00 MXVT MXVT MXVT MXVT 2 2 0 *
+0x7C01 MTAS MTAS MTAS MTAS 2 2 1 *
+0x7D00 MZOM MZOM MZOM MZOM 2 2 0 *
+0x7E00 MWER MWER MWER MWER 2 2 1 *
+0x7E01 MGWE MGWE MGWE MGWE 2 2 1 *
+0x7F00 MTRO MTRO MTRO MTRO 4 2 1 *
+0x7F01 MMIN MMIN MMIN MMIN 4 2 1 *
+0x7F02 MBEH MBEH MBEH MBEH 4 3 1 *
+0x7F03 MIMP MIMP MIMP MIMP 4 2 1 *
+0x7F04 MIGO MIGO MIGO MIGO 4 4 1 *
+0x7F05 MDJI MDJI MDJI MDJI 4 2 1 *
+0x7F06 MDJL MDJL MDJL MDJL 4 2 1 *
+0x7F07 MGLC MGLC MGLC MGLC 4 2 1 *
+0x7F08 MOTY MOTY MOTY MOTY 4 4 1 *
+0x7F09 MSAH MSAH MSAH MSAH 4 2 1 *
+0x7F0A MGCP MGCP MGCP MGCP 4 2 1 *
+0x7F0B MGCL MGCL MGCL MGCL 4 2 1 *
+0x7F0C MKUO MKUO MKUO MKUO 4 2 1 *
+0x7F0D MLIC MLIC MLIC MLIC 4 2 1 *
+0x7F0E MDLI MDLI MDLI MDLI 4 2 1 *
+0x7F0F MTRS MTRS MTRS MTRS 4 2 1 *
+0x7F10 MRAK MRAK MRAK MRAK 4 2 1 *
+0x7F11 MUMB MUMB MUMB MUMB 4 2 1 *
+0x7F12 MVAM MVAM MVAM MVAM 4 2 1 *
+0x7F13 MSNK MSNK MSNK MSNK 4 2 1 *
+0x7F14 MGIT MGIT MGIT MGIT 4 2 1 *
+0x7F15 MBES MBES MBES MBES 4 2 1 *
+0x7F16 AMOO AMOO AMOO AMOO 4 3 1 *
+0x7F17 ARAB ARAB ARAB ARAB 4 1 1 *
+0x7F18 ADER ADER ADER ADER 4 2 1 *
+0x7F19 MDSW MDSW MDSW MDSW 4 2 1 *
+0x7F20 AGRO AGRO AGRO AGRO 4 2 1 *
+0x7F21 APHE APHE APHE APHE 4 2 1 *
+0x7F22 MVAF MVAF MVAF MVAF 4 2 1 *
+0x7F23 MSAT MSAT MSAT MSAT 4 2 1 *
+0x7F24 NPIR NPIR NPIR NPIR 4 2 1 *
+0x7F27 MDRO MDRO MDRO MDRO 4 2 1 *
+0x7F28 MKUL MKUL MKUL MKUL 4 2 1 *
+0x7F29 MFDR MFDR MFDR MFDR 4 2 1 *
+0x7F2A NSAI NSAI NSAI NSAI 4 2 1 *
+0x7F2C NSOL NSOL NSOL NSOL 4 2 1 *
+0x7F2D MWFM MWFM MWFM MWFM 4 2 1 *
+0x7F2E MRAV MRAV MRAV MRAV 4 3 1 *
+0x7F2F MSPS MSPS MSPS MSPS 4 2 1 *
+0x7F30 NBOH NBOH NBOH NBOH 4 2 1 *
+0x7F31 NELL NELL NELL NELL 4 2 1 *
+0x7F32 MSLY MSLY MSLY MSLY 4 2 1 *
+0x7F35 MMIS MMIS MMIS MMIS 4 2 1 *
+0x7F36 NSHD NSHD NSHD NSHD 4 2 1 *
+0x7F37 NIRE NIRE NIRE NIRE 4 2 1 *
+0x8000 MGNL MGNL MGNL MGNL 2 2 0 *
+0x8100 MHOB MHOB MHOB MHOB 2 2 0 *
+0x8200 MKOB MKOB MKOB MKOB 2 2 0 *
+0x9000 MOGR MOGR MOGR MOGR 5 2 0 *
+0xA000 MWYV MWYV MWYV MWYV 8 3 1 *
+0xA100 MCAR MCAR MCAR MCAR 8 3 1 *
+0xB000 ACOW ACOW ACOW ACOW 10 3 1 *
+0xB100 AHRS AHRS AHRS AHRS 10 3 1 *
+0xB200 NBEGL NBEGL NBEGL NBEGL 3 2 0 *
+0xB210 NPROL NPROL NPROL NPROL 3 2 0 *
+0xB300 NBOYL NBOYL NBOYL NBOYL 3 2 0 *
+0xB310 NGRLL NGRLL NGRLL NGRLL 3 2 0 *
+0xB400 NFAML NFAML NFAML NFAML 3 2 0 *
+0xB410 NFAWL NFAWL NFAWL NFAWL 3 2 0 *
+0xB500 NSIML NSIML NSIML NSIML 3 2 0 *
+0xB510 NSIWL NSIWL NSIWL NSIWL 3 2 0 *
+0xB600 NNOML NNOML NNOML NNOML 3 2 0 *
+0xB610 NNOWL NNOWL NNOWL NNOWL 3 2 0 *
+0xB700 NSLVL NSLVL NSLVL NSLVL 3 2 0 *
+0xC000 ABAT ABAT ABAT ABAT 3 1 1 *
+0xC100 ACAT ACAT ACAT ACAT 3 1 1 *
+0xC200 ACHK ACHK ACHK ACHK 3 1 1 *
+0xC300 ARAT ARAT ARAT ARAT 3 1 1 *
+0xC400 ASQU ASQU ASQU ASQU 3 1 1 *
+0xC500 ABAT ABAT ABAT ABAT 3 1 1 *
+0xC600 NBEGH NBEGH NBEGH NBEGH 3 2 0 *
+0xC610 NPROH NPROH NPROH NPROH 3 2 0 *
+0xC700 NBOYH NBOYH NBOYH NBOYH 3 2 0 *
+0xC710 NGRLH NGRLH NGRLH NGRLH 3 2 0 *
+0xC800 NFAMH NFAMH NFAMH NFAMH 3 2 0 *
+0xC810 NFAWH NFAWH NFAWH NFAWH 3 2 0 *
+0xC900 NSIMH NSIMH NSIMH NSIMH 3 2 0 *
+0xC910 NSIWH NSIWH NSIWH NSIWH 3 2 0 *
+0xCA00 NNOMH NNOMH NNOMH NNOMH 3 2 0 *
+0xCA10 NNOWH NNOWH NNOWH NNOWH 3 2 0 *
+0xCB00 NSLVH NSLVH NSLVH NSLVH 3 2 0 *
+0xD000 AEAGG1 AEAGG1 AEAGG1 AEAGG1 7 0 1 *
+0xD100 AGULG1 AGULG1 AGULG1 AGULG1 7 0 1 *
+0xD200 AVULG1 AVULG1 AVULG1 AVULG1 7 0 1 *
+0xD300 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xD400 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xE000 MCYC MCYC MCYC MCYC 9 4 1 *
+0xE010 METN METN METN METN 9 4 1 *
+0xE020 MTAN MTAN MTAN MTAN 9 4 1 *
+0xE040 MHIS MHIS MHIS MHIS 9 2 1 *
+0xE050 MLER MLER MLER MLER 9 2 1 *
+0xE060 MLIC MLIC MLIC MLIC 9 2 1 *
+0xE070 MMIN MMIN MMIN MMIN 9 2 1 *
+0xE080 MMUM MMUM MMUM MMUM 9 2 1 *
+0xE090 MMER MMER MMER MMER 9 2 1 *
+0xE0A0 MTIC MTIC MTIC MTIC 9 2 1 *
+0xE0B0 MTRO MTRO MTRO MTRO 9 2 1 *
+0xE0C0 MTSN MTSN MTSN MTSN 9 2 1 *
+0xE0D0 MUMB MUMB MUMB MUMB 9 2 1 *
+0xE0E0 MCOR MCOR MCOR MCOR 9 2 1 *
+0xE0F0 MGIC MGIC MGIC MGIC 9 2 1 *
+0xE0F1 MGLA MGLA MGLA MGLA 9 2 1 *
+0xE0F2 MWAV MWAV MWAV MWAV 9 2 1 *
+0xE200 MBET MBET MBET MBET 9 2 1 *
+0xE210 MBFI MBFI MBFI MBFI 9 2 1 *
+0xE220 MBBM MBBM MBBM MBBM 9 2 1 *
+0xE230 MBRH MBRH MBRH MBRH 9 5 1 *
+0xE300 MGHO MGHO MGHO MGHO 9 2 1 *
+0xE310 MGH2 MGH2 MGH2 MGH2 9 2 1 *
+0xE320 MGH3 MGH3 MGH3 MGH3 9 2 1 *
+0xE400 MGO1 MGO1 MGO1 MGO1 9 2 1 *
+0xE410 MGO2 MGO2 MGO2 MGO2 9 2 1 *
+0xE420 MGO3 MGO3 MGO3 MGO3 9 2 1 *
+0xE430 MGO4 MGO4 MGO4 MGO4 9 2 1 *
+0xE500 MLIZ MLIZ MLIZ MLIZ 9 2 1 *
+0xE510 MLI2 MLI2 MLI2 MLI2 9 2 1 *
+0xE520 MLI3 MLI3 MLI3 MLI3 9 2 1 *
+0xE600 MMYC MMYC MMYC MMYC 9 2 1 *
+0xE610 MMY2 MMY2 MMY2 MMY2 9 2 1 *
+0xE700 MNO1 MNO1 MNO1 MNO1 9 2 1 *
+0xE710 MNO2 MNO2 MNO2 MNO2 9 2 1 *
+0xE720 MNO3 MNO3 MNO3 MNO3 9 2 1 *
+0xE800 MOR1 MOR1 MOR1 MOR1 9 2 1 *
+0xE810 MOR2 MOR2 MOR2 MOR2 9 2 1 *
+0xE820 MOR3 MOR3 MOR3 MOR3 9 2 1 *
+0xE830 MOR4 MOR4 MOR4 MOR4 9 2 1 *
+0xE840 MOR5 MOR5 MOR5 MOR5 9 2 1 *
+0xE900 MSAL MSAL MSAL MSAL 9 2 1 *
+0xE910 MSA2 MSA2 MSA2 MSA2 9 2 1 *
+0xEA00 MSHR MSHR MSHR MSHR 9 2 1 *
+0xEA10 MSH1 MSH1 MSH1 MSH1 9 2 1 *
+0xEA20 MSH2 MSH2 MSH2 MSH2 9 2 1 *
+0xEB00 MSKT MSKT MSKT MSKT 9 2 1 *
+0xEB10 MSKA MSKA MSKA MSKA 9 2 1 *
+0xEB20 MSKB MSKB MSKB MSKB 9 2 1 *
+0xEC00 MWIG MWIG MWIG MWIG 9 2 1 *
+0xEC10 MWI2 MWI2 MWI2 MWI2 9 2 1 *
+0xEC20 MWI3 MWI3 MWI3 MWI3 9 2 1 *
+0xED00 MYU1 MYU1 MYU1 MYU1 9 2 1 *
+0xED10 MYU2 MYU2 MYU2 MYU2 9 2 1 *
+0xED20 MYU3 MYU3 MYU3 MYU3 9 2 1 *
+0xEE00 MZO2 MZO2 MZO2 MZO2 9 2 1 *
+0xEE10 MZO3 MZO3 MZO3 MZO3 9 2 1 *
+0xEF00 MWWE MWWE MWWE MWWE 9 2 1 *
diff --git a/gemrb/override/bg1/avprefc.2da b/gemrb/override/bg1/avprefc.2da
new file mode 100644
index 0000000..d674f23
--- /dev/null
+++ b/gemrb/override/bg1/avprefc.2da
@@ -0,0 +1,25 @@
+2DA V1.0
+*
+ CLASS
+TYPE 232
+MAGE 0x200
+FIGHTER 0x100
+CLERIC 0
+THIEF 0x300
+BARD 0x300
+PALADIN 0x100
+FIGHTER_MAGE 0x100
+FIGHTER_CLERIC 0x100
+FIGHTER_THIEF 0x100
+FIGHTER_MAGE_THIEF 0x100
+DRUID 0
+RANGER 0x100
+MAGE_THIEF 0x300
+CLERIC_MAGE 0
+CLERIC_THIEF 0
+FIGHTER_DRUID 0x100
+FIGHTER_MAGE_CLERIC 0x100
+CLERIC_RANGER 0
+SORCERER 0x200
+MONK 0x500
+BARBARIAN 0x100
diff --git a/gemrb/override/bg1/avprefr.2da b/gemrb/override/bg1/avprefr.2da
new file mode 100644
index 0000000..53ea155
--- /dev/null
+++ b/gemrb/override/bg1/avprefr.2da
@@ -0,0 +1,11 @@
+2DA V1.0
+*
+ RACE
+TYPE 231
+HUMAN 0
+ELF 1
+HALF_ELF 1
+DWARF 2
+HALFLING 3
+GNOME 4
+HALFORC 5
diff --git a/gemrb/override/bg1/axe.pro b/gemrb/override/bg1/axe.pro
new file mode 100644
index 0000000..2220a75
Binary files /dev/null and b/gemrb/override/bg1/axe.pro differ
diff --git a/gemrb/override/bg1/axeex.pro b/gemrb/override/bg1/axeex.pro
new file mode 100644
index 0000000..475b824
Binary files /dev/null and b/gemrb/override/bg1/axeex.pro differ
diff --git a/gemrb/override/bg1/bloodclr.2da b/gemrb/override/bg1/bloodclr.2da
new file mode 100644
index 0000000..1b33a30
--- /dev/null
+++ b/gemrb/override/bg1/bloodclr.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+0
+ VALUE MIN MAX
+NORMAL 0x2f 0x1000 0xffff
+PURPLE 0x2d 0x4300 0x43ff
+MSKEL 0x25 0x5403 0x5403
+DOOMG 0x22 0x5405 0x5406
+MSKEL 0x25 0x6403 0x6403
+DOOMG 0x22 0x6405 0x6406
+YELLOW 0x32 0x7300 0x73ff
+PURPLE 0x3c 0x7500 0x75ff
+YELLOW 0x32 0x7600 0x76ff
+BLACK 0x66 0x7700 0x77ff
+SLI_GR 7 0x7900 0x7900
+SLI_OL 0x24 0x7901 0x7901
+SLI_MU 0x33 0x7902 0x7902
+SLI_OC 0x25 0x7903 0x7903
+SLIME 0x1b 0x7904 0x7904
+SPIDER 0x33 0x7a00 0x7aff
+ZOMBIE 0x25 0x7c00 0x7cff
+MTRO 0x33 0x7f00 0x7f00
+MMIN 0x3d 0x7f01 0x7f03
+MIGO 0x1a 0x7f04 0x7f04
+MGLC 0x5d 0x7f07 0x7f07
+MTRS 0x32 0x7f0f 0x7f0f
+MBES 0x3d 0x7f15 0x7f15
+MCAR 0x38 0xa100 0xa1ff
+UNDEAD 0x66 0xe300 0xe3ff
+MWWE 0x38 0xef00 0xefff
diff --git a/gemrb/override/bg1/bolt.pro b/gemrb/override/bg1/bolt.pro
new file mode 100644
index 0000000..1cfd4f5
Binary files /dev/null and b/gemrb/override/bg1/bolt.pro differ
diff --git a/gemrb/override/bg1/boltex.pro b/gemrb/override/bg1/boltex.pro
new file mode 100644
index 0000000..e963523
Binary files /dev/null and b/gemrb/override/bg1/boltex.pro differ
diff --git a/gemrb/override/bg1/bullet.pro b/gemrb/override/bg1/bullet.pro
new file mode 100644
index 0000000..cb89cea
Binary files /dev/null and b/gemrb/override/bg1/bullet.pro differ
diff --git a/gemrb/override/bg1/bulletex.pro b/gemrb/override/bg1/bulletex.pro
new file mode 100644
index 0000000..4a039f5
Binary files /dev/null and b/gemrb/override/bg1/bulletex.pro differ
diff --git a/gemrb/override/bg1/cgtable.2da b/gemrb/override/bg1/cgtable.2da
new file mode 100644
index 0000000..ff76876
--- /dev/null
+++ b/gemrb/override/bg1/cgtable.2da
@@ -0,0 +1,20 @@
+2DA V1.0
+*
+ RESREF
+UNUSED0 *
+UNUSED1 *
+UNUSED2 *
+UNUSED3 *
+UNUSED4 *
+UNUSED5 *
+UNUSED6 *
+UNUSED7 *
+UNUSED8 *
+NECROMANCY CGNecrom
+ALTERATION CGAltera
+ENCHANTMENT CGEnchan
+ABJURATION CGAbjura
+ILLUSION CGIllusi
+CONJURATION CGConjur
+INVOCATION CGInvoca
+DIVINATION CGDivina
diff --git a/gemrb/override/bg1/chromorb.pro b/gemrb/override/bg1/chromorb.pro
new file mode 100644
index 0000000..c39d719
Binary files /dev/null and b/gemrb/override/bg1/chromorb.pro differ
diff --git a/gemrb/override/bg1/classes.2da b/gemrb/override/bg1/classes.2da
new file mode 100644
index 0000000..1b36120
--- /dev/null
+++ b/gemrb/override/bg1/classes.2da
@@ -0,0 +1,21 @@
+2DA V1.0
+*
+ NAME_REF DESC_REF CAP_REF SAVE MULTI ID HP USABILITY MC_WAS_ID HUMAN ELF HALF_ELF DWARF HALFLING GNOME HALFORC
+FIGHTER 7201 9556 1076 SAVEWAR 0 2 HPWAR 0x800 0x0008 1 1 1 1 1 1 1
+RANGER 7200 9557 1077 SAVEWAR 0 12 HPWAR 0x200000 0x0100 1 1 1 0 0 0 0
+PALADIN 7217 9558 1078 SAVEWAR 0 6 HPWAR 0x100000 -1 1 0 1 0 0 0 0
+CLERIC 7204 9559 1079 SAVEPRS 0 3 HPPRS 128 0x0020 1 1 1 1 1 1 1
+DRUID 7210 9560 1080 SAVEPRS 0 11 HPPRS 0x40000000 0x0080 1 1 1 1 0 0 0
+MAGE 7203 9563 1081 SAVEWIZ 0 1 HPWIZ 0x40000 0x0010 1 1 1 0 0 2 0
+THIEF 7202 9561 1082 SAVEROG 0 4 HPROG 0x400000 0x0040 1 1 1 1 1 1 1
+BARD 7206 9562 1083 SAVEROG 0 5 HPROG 64 -1 1 1 1 1 1 1 1
+FIGHTER_THIEF 7205 9572 1052 * 10 9 * 0x20000 -1 0 1 1 1 1 1 1
+FIGHTER_CLERIC 7211 9573 1053 * 6 8 * 0x4000 -1 0 1 1 1 1 1 1
+FIGHTER_MAGE 7213 9574 1056 * 3 7 * 0x2000 -1 0 1 1 0 0 2 0
+MAGE_THIEF 7216 9575 1057 * 9 13 * 0x80000 -1 0 1 1 0 0 2 0
+CLERIC_MAGE 7207 9577 1058 * 5 14 * 256 -1 0 1 1 0 0 2 0
+CLERIC_THIEF 7209 9578 1065 * 12 15 * 512 -1 0 1 1 1 1 1 1
+FIGHTER_DRUID 7212 9579 1066 * 1026 16 * 0x1000 -1 0 1 1 0 0 0 0
+CLERIC_RANGER 7208 9580 1073 * 2052 18 * 1024 -1 0 1 1 0 0 0 0
+FIGHTER_MAGE_THIEF 7215 9576 1074 * 11 10 * 0x10000 -1 0 1 1 0 0 0 0
+FIGHTER_MAGE_CLERIC 7214 9581 1075 * 7 17 * 0x8000 -1 0 1 1 0 0 0 0
diff --git a/gemrb/override/bg1/cloud.pro b/gemrb/override/bg1/cloud.pro
new file mode 100644
index 0000000..444db21
Binary files /dev/null and b/gemrb/override/bg1/cloud.pro differ
diff --git a/gemrb/override/bg1/cloudkil.pro b/gemrb/override/bg1/cloudkil.pro
new file mode 100644
index 0000000..d3f51d1
Binary files /dev/null and b/gemrb/override/bg1/cloudkil.pro differ
diff --git a/gemrb/override/bg1/clowncol.2da b/gemrb/override/bg1/clowncol.2da
new file mode 100644
index 0000000..8e413b8
--- /dev/null
+++ b/gemrb/override/bg1/clowncol.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+*
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+HAIR 0 1 2 3 4 5 6 7 79 80 81 82 110 111 * * * * * * * * * * * * * * * * *
+SKIN 8 9 10 11 12 13 14 15 16 17 18 19 20 83 84 85 86 87 88 89 90 105 106 107 108 109 112 113 114 * *
+MAJOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
+MINOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
diff --git a/gemrb/override/bg1/clskills.2da b/gemrb/override/bg1/clskills.2da
new file mode 100644
index 0000000..68c4e97
--- /dev/null
+++ b/gemrb/override/bg1/clskills.2da
@@ -0,0 +1,24 @@
+2DA V1.0
+*
+ DRUIDSPELL CLERICSPELL MAGESPELL STARTXP BARDSKILL THIEFSKILL LAYHANDS TURNLEVEL BOOKTYPE HATERACE SAVEBONUS
+UNUSED * * * * * * * 0 0 * *
+MAGE * * MXSPLWIZ 89000 * * * 0 1 * 0
+FIGHTER * * * 89000 * * * 0 0 * 0
+CLERIC * MXSPLPRS * 89000 * * * 1 1 * 0
+THIEF * * * 89000 * SKILLS * 0 0 * 0
+BARD * * MXSPLBRD 89000 SKILLBRD * * 0 1 * 0
+PALADIN * MXSPLPAL * 89000 * * PALADIN 5 1 * 2
+FIGHTER_MAGE * * MXSPLWIZ 89000 * * * 0 1 * 0
+FIGHTER_CLERIC * MXSPLPRS * 89000 * * * 0 1 * 0
+FIGHTER_THIEF * * * 89000 * SKILLS * 0 0 * 0
+FIGHTER_MAGE_THIEF * * MXSPLWIZ 89000 * SKILLS * 0 1 * 0
+DRUID MXSPLPRS * * 89000 * * * 0 1 * 0
+RANGER MXSPLRAN * * 89000 * * * 0 1 HATERACE 0
+MAGE_THIEF * * MXSPLWIZ 89000 * SKILLS * 0 1 * 0
+CLERIC_MAGE * MXSPLPRS MXSPLWIZ 89000 * * * 0 1 * 0
+CLERIC_THIEF * MXSPLPRS * 89000 * SKILLS * 0 1 * 0
+FIGHTER_DRUID MXSPLPRS * * 89000 * * * 0 1 * 0
+FIGHTER_MAGE_CLERIC * MXSPLPRS MXSPLWIZ 89000 * * * 0 1 * 0
+CLERIC_RANGER MXSPLRAN MXSPLPRS * 89000 * * * 0 1 HATERACE 0
+SORCERER * * MXSPLSRC 89000 * * * 0 2 * 0
+MONK * * * 89000 * SKILLS * 0 0 * 0
diff --git a/gemrb/override/bg1/dagger.pro b/gemrb/override/bg1/dagger.pro
new file mode 100644
index 0000000..284c351
Binary files /dev/null and b/gemrb/override/bg1/dagger.pro differ
diff --git a/gemrb/override/bg1/daggerex.pro b/gemrb/override/bg1/daggerex.pro
new file mode 100644
index 0000000..e33a595
Binary files /dev/null and b/gemrb/override/bg1/daggerex.pro differ
diff --git a/gemrb/override/bg1/damage.2da b/gemrb/override/bg1/damage.2da
new file mode 100644
index 0000000..179722a
--- /dev/null
+++ b/gemrb/override/bg1/damage.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+*
+ MAIN SPARKS GRADIENT
+CRIT BLOODCR * 47
+SMALL BLOODS * 47
+MEDIUM BLOODM * 47
+LARGE BLOODL * 47
+FIRES SPFIRIMP SPBURN 19
+FIREM SPFIRIMP SPBURN 19
+FIREL SPFIRIMP SPBURN 19
+SPARKS SPSHKIMP SPSPARKS -1
+SPARKM SPSHKIMP SPSPARKS -1
+SPARKL SPSHKIMP SPSPARKS -1
+ICES FIRIMP * 71
+ICEM FIRIMP * 71
+ICEL FIRIMP * 71
diff --git a/gemrb/override/bg1/dart.pro b/gemrb/override/bg1/dart.pro
new file mode 100644
index 0000000..a8ec832
Binary files /dev/null and b/gemrb/override/bg1/dart.pro differ
diff --git a/gemrb/override/bg1/dartex.pro b/gemrb/override/bg1/dartex.pro
new file mode 100644
index 0000000..b09ac20
Binary files /dev/null and b/gemrb/override/bg1/dartex.pro differ
diff --git a/gemrb/override/bg1/defsound.2da b/gemrb/override/bg1/defsound.2da
new file mode 100644
index 0000000..22cfee3
--- /dev/null
+++ b/gemrb/override/bg1/defsound.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+*
+ RESREF
+OPEN AMB_D03A
+CLOSE AMB_D03B
+HOPEN AMB_D04A
+HCLOSE AMB_D04B
+BUTTON1 GAM_09
+BUTTON2 GAM_03
+BUTTON3 GAM_04
+OPENFAIL *
+CLOSEFAIL *
+ITEM_GONE EFF_M02
+SECRET ACT_09
+11 *
+12 *
+13 *
+14 *
+15 *
+16 *
+17 *
+18 *
+19 *
+LIGHTNING1 AMB_E13A
+LIGHTNING2 AMB_E13B
+LIGHTNING3 AMB_E13F
+RAIN AMB_E11
+SNOW AMB_E02B
+
diff --git a/gemrb/override/bg1/dualclas.2da b/gemrb/override/bg1/dualclas.2da
new file mode 100644
index 0000000..8d40d90
--- /dev/null
+++ b/gemrb/override/bg1/dualclas.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+0
+ FIGHTER CLERIC MAGE THIEF DRUID RANGER
+MAGE 1 1 0 1 0 0
+FIGHTER 0 1 1 1 1 0
+CLERIC 1 0 1 1 0 1
+THIEF 1 1 1 0 0 0
+BARD 0 0 0 0 0 0
+PALADIN 0 0 0 0 0 0
+DRUID 1 0 0 0 0 0
+RANGER 0 1 0 0 0 0
+FIGHTER_MAGE 0 0 0 0 0 0
+FIGHTER_CLERIC 0 0 0 0 0 0
+FIGHTER_THIEF 0 0 0 0 0 0
+FIGHTER_MAGE_THIEF 0 0 0 0 0 0
+MAGE_THIEF 0 0 0 0 0 0
+CLERIC_MAGE 0 0 0 0 0 0
+CLERIC_THIEF 0 0 0 0 0 0
+FIGHTER_DRUID 0 0 0 0 0 0
+FIGHTER_MAGE_CLERIC 0 0 0 0 0 0
+CLERIC_RANGER 0 0 0 0 0 0
+ABJURER 1 1 0 1 0 0
+CONJURER 1 1 0 1 0 0
+DIVINER 1 1 0 1 0 0
+ENCHANTER 1 1 0 1 0 0
+ILLUSIONIST 1 1 0 1 0 0
+INVOKER 1 1 0 1 0 0
+NECROMANCER 1 1 0 1 0 0
+TRANSMUTER 1 1 0 1 0 0
diff --git a/gemrb/override/bg1/effects.ids b/gemrb/override/bg1/effects.ids
new file mode 100644
index 0000000..e122471
--- /dev/null
+++ b/gemrb/override/bg1/effects.ids
@@ -0,0 +1,319 @@
+IDS
+0x0 ACVsDamageTypeModifier
+0x1 AttacksPerRoundModifier
+0x2 Cure:Sleep
+0x3 State:Berserk
+0x4 Cure:Berserk
+0x5 State:Charmed
+0x6 CharismaModifier
+0x7 Color:SetPalette
+0x8 Color:SetRGB
+0x9 Color:PulseRGB
+0xa ConstitutionModifier
+0xb Cure:Poison
+0xc Damage
+0xd Death
+0xe Cure:Defrost
+0xf DexterityModifier
+0x10 State:Hasted
+0x11 CurrentHPModifier
+0x12 MaximumHPModifier
+0x13 IntelligenceModifier
+0x14 State:Invisible
+0x15 LoreModifier
+0x16 LuckModifier
+0x17 MoraleModifier
+0x18 State:Panic
+0x19 State:Poisoned
+0x1a RemoveCurse
+0x1b AcidResistanceModifier
+0x1c ColdResistanceModifier
+0x1d ElectricityResistanceModifier
+0x1e FireResistanceModifier
+0x1f MagicDamageResistanceModifier
+0x20 Cure:Death
+0x21 SaveVsDeathModifier
+0x22 SaveVsWandsModifier
+0x23 SaveVsPolyModifier
+0x24 SaveVsBreathModifier
+0x25 SaveVsSpellsModifier
+0x26 State:Silenced
+0x27 State:Helpless
+0x28 State:Slowed
+0x29 Sparkle
+0x2a WizardSpellSlotsModifier
+0x2b Cure:Petrification
+0x2c StrengthModifier
+0x2d State:Stun
+0x2e Cure:Stun
+0x2f Cure:Invisible
+0x30 Cure:Silence
+0x31 WisdomModifier
+0x32 Color:BriefRGB
+0x33 Color:DarkenRGB
+0x34 Color:GlowRGB
+0x35 AnimationIDModifier
+0x36 ToHitModifier
+0x37 KillCreatureType
+0x38 Alignment:Invert
+0x39 Alignment:Change
+0x3a DispelEffects
+0x3b StealthModifier
+0x3c MiscastMagicModifier
+0x3d AlchemyModifier
+0x3e PriestSpellSlotsModifier
+0x3f State:Infravision
+0x40 Cure:Infravision
+0x41 State:Blur
+0x42 TransparencyModifier
+0x43 SummonCreature
+0x44 UnsummonCreature
+0x45 State:NonDetection
+0x46 Cure:NonDetection
+0x47 SexModifier
+0x48 AIIdentifierModifier
+0x49 DamageBonusModifier
+0x4a State:Blind
+0x4b Cure:Blind
+0x4c State:Feeblemind
+0x4d Cure:Feeblemind
+0x4e State:Diseased
+0x4f Cure:Disease
+0x50 State:Deafness
+0x51 Cure:Deafness
+0x52 SetAIScript
+0x53 Protection:Projectile
+0x54 MagicalFireResistanceModifier
+0x55 MagicalColdResistanceModifier
+0x56 SlashingResistanceModifier
+0x57 CrushingResistanceModifier
+0x58 PiercingResistanceModifier
+0x59 MissilesResistanceModifier
+0x5a OpenLocksModifier
+0x5b FindTrapsModifier
+0x5c PickPocketsModifier
+0x5d FatigueModifier
+0x5e IntoxicationModifier
+0x5f TrackingModifier
+0x60 LevelModifier
+0x61 StrengthBonusModifier
+0x62 State:Regenerating
+0x63 SpellDurationModifier
+0x64 Protection:Creature
+0x65 Protection:Opcode
+0x66 Protection:SpellLevel
+0x67 ChangeName
+0x68 ExperienceModifier
+0x69 GoldModifier
+0x6a MoraleBreakModifier
+0x6b PortraitChange
+0x6c ReputationModifier
+0x6d State:HoldNoIcon
+0x6e RetreatFrom
+0x6f Item:CreateMagic
+0x70 Item:Remove
+0x71 Item:Equip
+0x72 Dither
+0x73 DetectAlignment
+0x74 Cure:Invisible2
+0x75 Reveal:Area
+0x76 Reveal:Creatures
+0x77 MirrorImage
+0x78 Protection:Weapons
+0x79 VisualAnimationEffect
+0x7a Item:CreateInventory
+0x7b Item:RemoveInventory
+0x7c DimensionDoor
+0x7d Unlock
+0x7e MovementRateModifier
+0x7f MonsterSummoning
+0x80 State:Confused
+0x81 AidNonCumulative
+0x82 BlessNonCumulative
+0x83 ChantNonCumulative
+0x84 HolyNonCumulative
+0x85 LuckNonCumulative
+0x86 State:Petrification
+0x87 Polymorph
+0x88 ForceVisible
+0x89 ChantBadNonCumulative
+0x8a AnimationStateChange
+0x8b DisplayString
+0x8c CastingGlow
+0x8d VisualSpellHit
+0x8e Icon:Display
+0x8f Item:CreateInSlot
+0x90 DisableButton
+0x91 DisableCasting
+0x92 Spell:Cast
+0x93 Spell:Learn
+0x94 Spell:CastPoint
+0x95 Identify
+0x96 FindTraps
+0x97 ReplaceCreature
+0x98 PlayMovie
+0x99 Overlay:Sanctuary
+0x9a Overlay:Entangle
+0x9b Overlay:MinorGlobe
+0x9c Overlay:ShieldGlobe
+0x9d Overlay:Web
+0x9e Overlay:Grease
+0x9f MirrorImageModifier
+0xa0 Cure:Sanctuary
+0xa1 Cure:Panic
+0xa2 Cure:Hold
+0xa3 FreeAction
+0xa4 Cure:Intoxication
+0xa5 PauseTarget
+0xa6 MagicResistanceModifier
+0xa7 MissileHitModifier
+0xa8 RemoveCreature
+0xa9 Icon:Disable
+0xaa DamageAnimation
+0xab Spell:Add
+0xac Spell:Remove
+0xad PoisonResistanceModifier
+0xae PlaySound
+0xaf State:Hold
+0xb0 MovementRateModifier2
+0xb1 ApplyEffect
+0xb2 ToHitVsCreature
+0xb3 DamageVsCreature
+0xb4 CantUseItem
+0xb5 CantUseItemType
+0xb6 ApplyEffectItem
+0xb7 ApplyEffectItemType
+0xb8 DontJumpModifier
+0xb9 State:Hold2
+0xba MoveToArea
+0xbb Variable:StoreLocalVariable
+0xbc AuraCleansingModifier
+0xbd CastingSpeedModifier
+0xbe AttackSpeedModifier
+0xbf CastingLevelModifier
+0xc0 FindFamiliar
+0xc1 InvisibleDetection
+0xc2 IgnoreDialogPause
+0xc3 FamiliarBond
+0xc4 FamiliarMarker
+0xc5 Bounce:Projectile
+0xc6 Bounce:Opcode
+0xc7 Bounce:SpellLevel
+0xc8 Bounce:SpellLevelDec
+0xc9 Protection:SpellLevelDec
+0xca Bounce:School
+0xcb Bounce:SecondaryType
+0xcc Protection:School
+0xcd Protection:SecondaryType
+0xce Protection:Spell
+0xcf Bounce:Spell
+0xd0 MinimumHPModifier
+0xd1 PowerWordKill
+0xd2 PowerWordStun
+0xd3 State:Imprisonment
+0xd4 Cure:Imprisonment
+0xd5 Maze
+0xd6 CastFromList
+0xd7 PlayVisualEffect
+0xd8 LevelDrainModifier
+0xd9 PowerWordSleep
+0xda StoneskinModifier
+0xdb ACVsCreatureType
+0xdc DispelSchool
+0xdd DispelSecondaryType
+0xde RandomTeleport
+0xdf Protection:SchoolDec
+0xe0 Cure:LevelDrain
+0xe1 Reveal:Magic
+0xe2 Protection:SecondaryTypeDec
+0xe3 Bounce:SchoolDec
+0xe4 Bounce:SecondaryTypeDec
+0xe5 DispelSchoolOne
+0xe6 DispelSecondaryTypeOne
+0xe7 TimeStop
+0xe8 CastSpellOnCondition
+0xe9 Proficiency
+0xea CreateContingency
+0xeb WingBuffet
+0xec ProjectImage
+0xed PuppetMarker
+0xee Disintegrate
+0xef Farsee
+0xf0 Icon:Remove
+0xf1 ControlCreature
+0xf2 Cure:Confusion
+0xf3 DrainItems
+0xf4 DrainSpells
+0xf5 CheckForBerserkModifier
+0xf6 BerserkStage1Modifier
+0xf7 BerserkStage2Modifier
+0xf8 SetMeleeEffect
+0xf9 SetRangedEffect
+0xfa DamageLuckModifier
+0xfb ChangeBardSong
+0xfc SetTrap
+0xfd SetMapNote
+0xfe RemoveMapNote
+0xff Item:CreateDays
+0x100 Sequencer:Store
+0x101 Sequencer:Create
+0x102 Sequencer:Activate
+0x103 SpellTrap
+0x104 *Crash*
+0x105 RestoreSpells
+0x106 VisualRangeModifier
+0x107 BackstabModifier
+0x108 DropWeapon
+0x109 ModifyGlobalVariable
+0x10a RemoveImmunity
+0x10b Protection:String
+0x10c ExploreModifier
+0x10d ScreenShake
+0x10e Cure:CasterHold
+0x10f SummonDisable
+0x110 ApplyEffectRepeat
+0x111 RemoveProjectile
+0x112 TeleportToTarget
+0x113 HideInShadowsModifier
+0x114 DetectIllusionsModifier
+0x115 SetTrapsModifier
+0x116 ToHitBonusModifier
+0x117 RenableButton
+0x118 ForceSurgeModifier
+0x119 WildSurgeModifier
+0x11a ScriptingState
+0x11b ApplyEffectCurse
+0x11c MeleeHitModifier
+0x11d MeleeDamageModifier
+0x11e MissileDamageModifier
+0x11f NoCircleState
+0x120 FistHitModifier
+0x121 FistDamageModifier
+0x122 TitleModifier
+0x123 DisableOverlay
+0x124 Protection:Backstab
+0x125 OffscreenAIModifier
+0x126 ExistanceDelayModifier
+0x127 DisableChunk
+0x128 Protection:Animation
+0x129 Protection:Turn
+0x12a Cutscene2
+0x12b ChaosShieldModifier
+0x12c NPCBump
+0x12d CriticalHitModifier
+0x12e CanUseAnyItem
+0x12f AlwaysBackstab
+0x130 MassRaiseDead
+0x131 OffhandHitModifier
+0x132 RightHitModifier
+0x133 Reveal:Tracks
+0x134 Protection:Tracking
+0x135 ModifyLocalVariable
+0x136 TimelessState
+0x137 GenerateWish
+0x138 *Crash*
+0x139 HLA
+0x13a StoneSkin2Modifier
+0x13b AvatarRemovalModifier
+0x13c MagicalRest
+0x13d State:Haste2
diff --git a/gemrb/override/bg1/fireball.pro b/gemrb/override/bg1/fireball.pro
new file mode 100644
index 0000000..ede63e1
Binary files /dev/null and b/gemrb/override/bg1/fireball.pro differ
diff --git a/gemrb/override/bg1/fireblic.pro b/gemrb/override/bg1/fireblic.pro
new file mode 100644
index 0000000..f9b920f
Binary files /dev/null and b/gemrb/override/bg1/fireblic.pro differ
diff --git a/gemrb/override/bg1/firebolt.pro b/gemrb/override/bg1/firebolt.pro
new file mode 100644
index 0000000..a7b3b25
Binary files /dev/null and b/gemrb/override/bg1/firebolt.pro differ
diff --git a/gemrb/override/bg1/firebtbl.pro b/gemrb/override/bg1/firebtbl.pro
new file mode 100644
index 0000000..5599064
Binary files /dev/null and b/gemrb/override/bg1/firebtbl.pro differ
diff --git a/gemrb/override/bg1/firestor.pro b/gemrb/override/bg1/firestor.pro
new file mode 100644
index 0000000..76e10b2
Binary files /dev/null and b/gemrb/override/bg1/firestor.pro differ
diff --git a/gemrb/override/bg1/fistweap.2da b/gemrb/override/bg1/fistweap.2da
new file mode 100644
index 0000000..e492835
--- /dev/null
+++ b/gemrb/override/bg1/fistweap.2da
@@ -0,0 +1,3 @@
+2DA V1.0
+FIST
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
diff --git a/gemrb/override/bg1/fonts.2da b/gemrb/override/bg1/fonts.2da
new file mode 100644
index 0000000..bfde90b
--- /dev/null
+++ b/gemrb/override/bg1/fonts.2da
@@ -0,0 +1,14 @@
+2DA V1.0
+NORMAL
+ RESREF NEED_PALETTE FIRST_CHAR
+0 NORMAL 1 0
+1 TOOLFONT 1 0
+2 NUMBER 0 47
+3 INITIALS 0 0
+4 NUMBER2 0 47
+5 NUMBER3 0 47
+6 REALMS 0 0
+7 STONEBIG 0 0
+8 STONESML 0 0
+9 STATES 0 0
+10 STATES2 0 0
diff --git a/gemrb/override/bg1/formatio.2da b/gemrb/override/bg1/formatio.2da
new file mode 100644
index 0000000..c369656
--- /dev/null
+++ b/gemrb/override/bg1/formatio.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+-10
+# generated by make_formation.py, do not edit
+ X1 Y1 X2 Y2 X3 Y3 X4 Y4 X5 Y5 X6 Y6 X7 Y7 X8 Y8 X9 Y9 X10 Y10 X11 Y11 X12 Y12 X13 Y13 X14 Y14 X15 Y15 X16 Y16 X17 Y17 X18 Y18 X19 Y19 X20 Y20
+FOLLOW 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
+T 0 0 48 0 -48 0 0 48 0 84 0 120 0 156 0 192 0 228 0 264 0 300 0 336 0 372 0 408 0 444 0 480 0 516 0 552 0 588 0 624
+GATHER 0 -36 48 -24 -48 -24 48 24 -48 24 0 36 48 48 -48 48 0 72 48 72 -48 72 0 108 48 96 -48 96 0 144 48 120 -48 120 0 180 48 144 -48 144
+4AND2 0 0 64 0 -64 0 128 0 0 48 64 48 -64 48 128 48 0 96 64 96 -64 96 128 96 0 144 64 144 -64 144 128 144 0 192 64 192 -64 192 128 192
+3BY2 0 0 64 0 -64 0 0 48 64 48 -64 48 0 96 64 96 -64 96 0 144 64 144 -64 144 0 192 64 192 -64 192 0 240 64 240 -64 240 0 288 64 288
+PROTECT 0 0 0 -36 -64 0 64 0 -32 48 32 48 0 24 0 48 0 72 0 96 0 120 0 144 0 168 0 192 0 216 0 240 0 264 0 288 0 312 0 336
+2BY3 -24 0 24 0 -24 48 24 48 -24 84 24 84 -24 120 24 120 -24 156 24 156 -24 192 24 192 -24 228 24 228 -24 264 24 264 -24 300 24 300 -24 336 24 336
+RANK -32 0 32 0 -96 0 96 0 -160 0 160 0 -224 0 224 0 -288 0 288 0 -352 0 352 0 -416 0 416 0 -480 0 480 0 -544 0 544 0 -608 0 608 0
+V 0 0 64 0 -15 48 49 48 -30 96 34 96 -45 144 19 144 -60 192 4 192 -75 240 -11 240 -90 288 -26 288 -105 336 -41 336 -120 384 -56 384 -135 432 -71 432
+WEDGE 0 0 64 36 -64 36 -124 72 124 72 0 72 0 144 -124 144 124 144 0 180 -124 180 124 180 0 216 -124 216 124 216 0 252 -124 252 124 252 0 288 -124 288
+S 0 0 64 24 0 48 64 72 0 96 64 120 0 144 64 168 0 192 64 216 0 240 64 264 0 288 64 312 0 336 64 360 0 384 64 408 0 432 64 456
+LINE 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
diff --git a/gemrb/override/bg1/gametime.2da b/gemrb/override/bg1/gametime.2da
new file mode 100644
index 0000000..54ac077
--- /dev/null
+++ b/gemrb/override/bg1/gametime.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+0
+ DURATION
+ROUND_SECONDS 6
+TURN_SECONDS 60
diff --git a/gemrb/override/bg1/gaze.pro b/gemrb/override/bg1/gaze.pro
new file mode 100644
index 0000000..632263a
Binary files /dev/null and b/gemrb/override/bg1/gaze.pro differ
diff --git a/gemrb/override/bg1/gemprjtl.ids b/gemrb/override/bg1/gemprjtl.ids
new file mode 100644
index 0000000..d28770d
--- /dev/null
+++ b/gemrb/override/bg1/gemprjtl.ids
@@ -0,0 +1,207 @@
+IDS V1.0
+1 ARROW
+2 ARROWEX
+3 ARROWFLM
+4 ARROWHVY
+5 ARROW
+6 AXE
+7 AXEEX
+8 AXEFLM
+9 AXE
+10 AXE
+11 BOLT
+12 BOLTEX
+13 ARROWHVY
+14 BOLT
+15 BOLT
+16 BULLET
+17 BULLETEX
+18 <flamingbullet>
+19 BULLET
+20 BULLET
+21 SPBRNHND
+22 SKYBOLT
+23 CHROMORB
+24 SPCONECO
+25 SPCONEFI
+26 DAGGER
+27 DAGGEREX
+28 <flamingdagger>
+29 DAGGER
+30 DAGGER
+31 DART
+32 DARTEX
+33 <flamingdart>
+34 DART
+35 DART
+36 MAGICMIS
+37 FIREBALL
+38 <icefragments>
+39 LIGHTB
+40 STONE
+41 SLEEP
+42 <skeleton>
+43 SPSMPUFF
+44 SPSMKJET
+45 SPSMOLD
+46 SPARKLBL
+47 SPARKLGO
+48 SPARKLPU
+49 SPARKLIC
+50 SPARKLST
+51 SPARKLBK
+52 SPARKLCH
+53 SPARKLRE
+54 SPARKLGR
+55 SPEAR
+56 SPEAREX
+57 <flamingspear>
+58 SPEAR
+59 SPEAR
+60 <starsprite>
+61 <stoned>
+62 <webtravel>
+63 WEB
+64 GAZE
+65 HLYMITE
+66 FLMSTRK
+67 MAGICMIS
+68 SPMAGMIS
+69 SPMAGMIS
+70 SPMAGMIS
+71 SPMAGMIS
+72 SPMAGMIS
+73 SPMAGMIS
+74 SPMAGMIS
+75 SPMAGMIS
+76 SPMAGMIS
+77 SPMAGMIS
+78 INVTRAV
+79 FIREBOLT
+80 SKYBOLT
+81 SKYBOLT2
+82 SKYBOLT2
+83 SKYBOLT2
+84 SKYBOLT2
+85 SKYBOLT2
+86 SKYBOLT2
+87 SKYBOLT2
+88 SKYBOLT2
+89 SKYBOLT2
+90 SKYBOLT2
+91 FIRESTOR
+92 LIGHTSTO
+93 INAREA
+94 CLOUD
+95 TRAPSKUL
+96 COLRSPRY
+97 ICESTORM
+98 SPFIREWL
+99 TRAPGLYP
+100 GREASE
+101 ARROWFLG
+102 ARROWFLB
+103 FIREBLGR
+104 FIREBTBL
+105 <potion>
+106 <potionexplode>
+107 ACIDBLOB
+108 SPSCORCH
+109 SPDIMDR
+110 CGNECROM
+111 CGALTERA
+112 CGENCHAN
+113 CGABJURA
+114 CGILLUSI
+115 CGCONJUR
+116 CGINVOCA
+117 CGDIVINA
+118 SHAIR
+119 SHEARTH
+120 SHWATER
+121 SHAIR1
+122 SHEARTH1
+123 SHWATER1
+124 SHAIR2
+125 SHEARTH2
+126 SHWATER2
+127 SHAIR3
+128 SHEARTH3
+129 SHWATER3
+130 SHAIR4
+131 SHEARTH4
+132 SHWATER4
+133 SHAIR5
+134 SHEARTH5
+135 SHWATER5
+136 SHAIR6
+137 SHEARTH6
+138 SHWATER6
+139 SHAIR7
+140 SHEARTH7
+141 SHWATER7
+142 SPBOOM1
+143 SPBOOM2
+144 SPBOOM3
+145 FLMSTRK
+146 HLYMITE
+147 SHAIR7
+148 SPKLARBL
+149 SPKLARGO
+150 SPKLARPU
+151 SPKLARIC
+152 SPKLARST
+153 SPKLARBK
+154 SPKLARCH
+155 SPKLARRE
+156 SPKLARGR
+157 INAREAPA
+158 INAREANP
+159 SPARBLPA
+160 SPARGOPA
+161 SPARPUPA
+162 SPARICPA
+163 SPARSTPA
+164 SPARBKPA
+165 SPARCHPA
+166 SPARREPA
+167 SPARGRPA
+168 SPARBLNP
+169 SPARGONP
+170 SPARPUNP
+171 SPARICNP
+172 SPARSTNP
+173 SPARBKNP
+174 SPARCHNP
+175 SPARRENP
+176 SPARGRNP
+177 SPARMANP
+178 SPARORNP
+179 SPARMAPA
+180 SPARORPA
+181 SPKLARMA
+182 SPKLAROR
+183 SPARKLMA
+184 SPARKLOR
+185 INAREANS
+186 CLOUDKIL
+187 ARROWFLI
+188 COW
+189 HOLD
+190 SPSCORIC
+191 ACIDBLMU
+192 ACIDBLGR
+193 ACIDBLOC
+194 REDHOLY
+195 SHAREA
+196 SHAREA1
+197 SHAREA
+198 SHAREA2
+199 SHAREA3
+200 SHAREA4
+201 SHAREA5
+202 SHAREA6
+203 FIREBLIC
+204 INAREASM
+205 LIGHTB
+206 LIGHTBNB
diff --git a/gemrb/override/bg1/gemrb.ini b/gemrb/override/bg1/gemrb.ini
new file mode 100644
index 0000000..6544362
--- /dev/null
+++ b/gemrb/override/bg1/gemrb.ini
@@ -0,0 +1,80 @@
+; 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.ini - game type-specific settings for GemRB engine
+
+[resources]
+
+; Bitmap resource for cursors
+CursorBAM = CAROT
+
+; Bitmap resource for scroll cursor arrow
+ScrollCursorBAM = CURSARW
+
+; Bitmap resource for dialog buttons font
+ButtonFont = STONESML
+
+; Font used to display tooltips
+TooltipFont = TOOLFONT
+
+; Sprite displayed behind the tooltip text, if any
+TooltipBack = TOOLTIP
+
+; Tooltip text color (RGBA)
+TooltipColor = #f0b08000
+
+; Space between tooltip text and sides of TooltipBack (x2)
+#TooltipMargin = 10
+TooltipMargin = 5
+
+; INI file from the original games
+INIConfig = baldur.ini
+
+; Palette bitmaps in various widths
+Palette16 = MPALETTE
+Palette32 = MPALETTE
+Palette256 = MPAL256
+
+MaximumAbility = 25
+IgnoreButtonFrames = 0
+AllStringsTagged = 1
+HasDPLAYER = 1
+HasPickSound = 0
+HasDescIcon = 1
+HasEXPTABLE = 0
+SoundFolders = 0
+HasSongList = 0
+UpperButtonText = 1
+ReverseToHit = 1
+LowerLabelText = 1
+HasPartyINI = 0
+HasBeastsINI = 0
+ForceStereo = 1
+IWDMapDimensions = 0
+SmallFog = 1
+DialogueScrolls = 0
+RedrawTile = 1
+DeathOnZeroStat = 1
+BreakableWeapons = 1
+HasSpecificDamageBonus = 0
+CutsceneAreascripts = 1
+FlexibleWorldmap = 0
+AutoSearchHidden = 1
+PSTStateFlags = 0
+NoDropCanMove = 1
diff --git a/gemrb/override/bg1/grease.pro b/gemrb/override/bg1/grease.pro
new file mode 100644
index 0000000..bf273b6
Binary files /dev/null and b/gemrb/override/bg1/grease.pro differ
diff --git a/gemrb/override/bg1/guibtact.2da b/gemrb/override/bg1/guibtact.2da
new file mode 100644
index 0000000..2077854
--- /dev/null
+++ b/gemrb/override/bg1/guibtact.2da
@@ -0,0 +1,37 @@
+2DA V1.0
+100
+ 1 2 3 4 TOOLTIP RESREF
+Stealth 30 31 32 33 4968 guibtact
+Thieving 26 27 28 29 4971 guibtact
+Cast 12 13 52 53 4688 guibtact
+QSpell1 8 9 32 33 4938 guibtbut
+QSpell2 10 11 34 35 4938 guibtbut
+QSpell3 12 13 36 37 4938 guibtbut
+Turn 8 9 10 11 4974 guibtact
+Talk 4 5 6 7 4933 guibtact
+UseItem 18 19 56 57 4978 guibtact
+QItem1 14 15 38 39 4937 guibtbut
+QItem4 16 17 40 41 4937 guibtbut
+QItem2 18 19 42 43 4937 guibtbut
+QItem3 20 21 44 45 4937 guibtbut
+Innate 38 39 54 55 4954 guibtact
+Defend 0 1 2 3 15925 guibtact
+Attack 14 15 16 17 4666 guibtact
+QWeapon1 0 1 24 25 4950 guibtbut
+QWeapon2 2 3 26 27 4950 guibtbut
+QWeapon3 4 5 28 29 4950 guibtbut
+QWeapon4 6 7 30 31 4950 guibtbut
+BardSong 22 23 24 25 11798 guibtact
+Stop 58 59 60 61 15924 guibtact
+Search 34 35 36 37 4927 guibtact
+23 * * * * * *
+24 * * * * * *
+25 * * * * * *
+26 * * * * * *
+27 * * * * * *
+28 * * * * * *
+29 * * * * * *
+30 * * * * * *
+31 * * * * * *
+Left 44 45 100 100 100 guibtact
+Right 43 44 100 100 100 guibtact
diff --git a/gemrb/override/bg1/guils.chu b/gemrb/override/bg1/guils.chu
new file mode 100644
index 0000000..a855008
Binary files /dev/null and b/gemrb/override/bg1/guils.chu differ
diff --git a/gemrb/override/bg1/haterace.2da b/gemrb/override/bg1/haterace.2da
new file mode 100644
index 0000000..7ca328a
--- /dev/null
+++ b/gemrb/override/bg1/haterace.2da
@@ -0,0 +1,13 @@
+2DA V1.0
+4294967296
+ STRREF IDS STRREF_HELP
+CRAWLER 15940 104 15988
+ETTERCAP 15939 107 15990
+GHOUL 15946 108 15991
+GIBBERLING 15931 109 15994
+GNOLL 15932 110 15995
+HOBGOBLIN 15930 111 15996
+KOBOLD 15929 112 15997
+OGRE 15933 113 15998
+SKELETON 15937 115 15999
+SPIDER 15941 116 16000
diff --git a/gemrb/override/bg1/icestorm.pro b/gemrb/override/bg1/icestorm.pro
new file mode 100644
index 0000000..32d2a98
Binary files /dev/null and b/gemrb/override/bg1/icestorm.pro differ
diff --git a/gemrb/override/bg1/inarea.pro b/gemrb/override/bg1/inarea.pro
new file mode 100644
index 0000000..ec6a50d
Binary files /dev/null and b/gemrb/override/bg1/inarea.pro differ
diff --git a/gemrb/override/bg1/inareasm.pro b/gemrb/override/bg1/inareasm.pro
new file mode 100644
index 0000000..6545bce
Binary files /dev/null and b/gemrb/override/bg1/inareasm.pro differ
diff --git a/gemrb/override/bg1/item_use.2da b/gemrb/override/bg1/item_use.2da
new file mode 100644
index 0000000..a79cbc5
--- /dev/null
+++ b/gemrb/override/bg1/item_use.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+*
+ USER STRREF FLAG
+MISC84 * 10218 3
+MISC88 ALORA 10219 2
+MISC89 * 10222 3
+SW1H13 XAN 10220 2
+AROW14 ELDOTH 10221 2
+
diff --git a/gemrb/override/bg1/itemsnd.2da b/gemrb/override/bg1/itemsnd.2da
new file mode 100644
index 0000000..3ee1fa6
--- /dev/null
+++ b/gemrb/override/bg1/itemsnd.2da
@@ -0,0 +1,46 @@
+2DA V1.0
+*
+ TAKE DROP
+MISC GAM_21A GAM_21B
+AMULET GAM_14A GAM_14B
+ARMOR GAM_21A GAM_21B
+BELT GAM_18A GAM_18B
+BOOT GAM_18A GAM_18B
+ARROW GAM_20A GAM_20B
+BRACER GAM_18A GAM_18B
+HELMET GAM_22A GAM_22B
+KEY GAM_23A GAM_23B
+POTION GAM_38A GAM_38B
+RING GAM_43A GAM_43B
+SCROLL GAM_25A GAM_25B
+SHIELD GAM_26A GAM_26B
+FOOD GAM_21A GAM_21B
+BULLET GAM_27A GAM_27B
+BOW GAM_28A GAM_28B
+DAGGER GAM_29A GAM_29B
+MACE GAM_30A GAM_30B
+SLING GAM_31A GAM_31B
+SMSWORD GAM_41A GAM_41B
+BGSWORD GAM_41A GAM_41B
+HAMMER GAM_33A GAM_33B
+MSTAR GAM_34A GAM_34B
+FLAIL GAM_34A GAM_34B
+DART GAM_21A GAM_21B
+AXE GAM_33A GAM_33B
+STAFF GAM_39A GAM_39B
+XBOW GAM_40A GAM_40B
+FIST GAM_21A GAM_21B
+SPEAR GAM_39A GAM_39B
+POLEARM GAM_32A GAM_32B
+BOLT GAM_20A GAM_20B
+CLOAK GAM_36A GAM_36B
+COIN GAM_37A GAM_37B
+GEM GAM_24A GAM_24B
+WAND GAM_39A GAM_39B
+BAG GAM_21A GAM_21B
+BROKEN1 GAM_21A GAM_21B
+BROKEN2 GAM_21A GAM_21B
+DEFAULT GAM_21A GAM_21B
+LEATHER GAM_15A GAM_15B
+CHAIN GAM_16A GAM_16B
+PLATE GAM_17A GAM_17B
diff --git a/gemrb/override/bg1/itemtype.2da b/gemrb/override/bg1/itemtype.2da
new file mode 100644
index 0000000..8bf7d35
--- /dev/null
+++ b/gemrb/override/bg1/itemtype.2da
@@ -0,0 +1,42 @@
+2DA V1.0
+0
+ HELMET ARMOR SHIELD GLOVES RING AMULET BELT BOOTS WEAPON QUIVER CLOAK QUICK SCROLL BAG POTION
+MISC 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+AMULET 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+ARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+BELT 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
+BOOT 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
+ARROW 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BRACER 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
+HELMET 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+KEY 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+POTION 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
+RING 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
+SCROLL 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0
+SHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+FOOD 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BULLET 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DAGGER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MACE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SLING 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SMSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BGSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+HAMMER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MSTAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FLAIL 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DART 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+AXE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+STAFF 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+XBOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FIST 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SPEAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+POLEARM 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BOLT 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+CLOAK 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
+COIN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+GEM 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+WAND 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BAG 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
+BROKEN1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BROKEN2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/gemrb/override/bg1/itemuse.2da b/gemrb/override/bg1/itemuse.2da
new file mode 100644
index 0000000..b61e2b2
--- /dev/null
+++ b/gemrb/override/bg1/itemuse.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+0
+ STAT FILE MCOL VCOL WHICH
+0 ALIGNMENT aligns 3 5 0
+1 RACE races 3 4 0
+2 CLASS classes 5 7 0
diff --git a/gemrb/override/bg1/k_m_e.2da b/gemrb/override/bg1/k_m_e.2da
new file mode 100644
index 0000000..dee1416
--- /dev/null
+++ b/gemrb/override/bg1/k_m_e.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ KIT
+1 24
+2 25
diff --git a/gemrb/override/bg1/k_m_g.2da b/gemrb/override/bg1/k_m_g.2da
new file mode 100644
index 0000000..cc3fdca
--- /dev/null
+++ b/gemrb/override/bg1/k_m_g.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ KIT
+1 26
diff --git a/gemrb/override/bg1/k_m_h.2da b/gemrb/override/bg1/k_m_h.2da
new file mode 100644
index 0000000..dfbaec3
--- /dev/null
+++ b/gemrb/override/bg1/k_m_h.2da
@@ -0,0 +1,11 @@
+2DA V1.0
+*
+ KIT
+1 22
+2 23
+3 24
+4 25
+5 26
+6 27
+7 28
+8 29
diff --git a/gemrb/override/bg1/k_m_he.2da b/gemrb/override/bg1/k_m_he.2da
new file mode 100644
index 0000000..3f74f49
--- /dev/null
+++ b/gemrb/override/bg1/k_m_he.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+*
+ KIT
+1 23
+2 24
+3 25
+4 29
diff --git a/gemrb/override/bg1/kitlist.2da b/gemrb/override/bg1/kitlist.2da
new file mode 100644
index 0000000..3362bad
--- /dev/null
+++ b/gemrb/override/bg1/kitlist.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+*
+ ROWNAME LOWER MIXED HELP ABILITIES PROFICIENCY UNUSABLE CLASS
+0 RESERVE * * * * * * *
+1 ABJURER 597 502 9564 CLABMA02 21 0x00000040 1
+2 CONJURER 2179 504 9565 CLABMA06 22 0x00000080 1
+3 DIVINER 2846 2012 9566 CLABMA05 23 0x00000100 1
+4 ENCHANTER 2861 2022 9567 CLABMA09 24 0x00000200 1
+5 ILLUSIONIST 2862 12785 9568 CLABMA08 25 0x00000400 1
+6 INVOKER 3015 12786 9569 CLABMA07 26 0x00000800 1
+7 NECROMANCER 12744 12787 9570 CLABMA03 27 0x00001000 1
+8 TRANSMUTER 12745 12788 9571 CLABMA04 28 0x00002000 1
diff --git a/gemrb/override/bg1/kittable.2da b/gemrb/override/bg1/kittable.2da
new file mode 100644
index 0000000..c3272e2
--- /dev/null
+++ b/gemrb/override/bg1/kittable.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ HUMAN DWARF GNOME ELF HALF_ELF HALFLING HALFORC
+MAGE K_M_H K_M_D K_M_G K_M_E K_M_HE K_M_HL K_M_HO
diff --git a/gemrb/override/bg1/lightsto.pro b/gemrb/override/bg1/lightsto.pro
new file mode 100644
index 0000000..7b502b1
Binary files /dev/null and b/gemrb/override/bg1/lightsto.pro differ
diff --git a/gemrb/override/bg1/magesch.2da b/gemrb/override/bg1/magesch.2da
new file mode 100644
index 0000000..3e8697b
--- /dev/null
+++ b/gemrb/override/bg1/magesch.2da
@@ -0,0 +1,13 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF KIT
+GENERALIST 18039 9563 9987 0x4000
+ABJURER 597 9564 502 0x40
+CONJURER 2179 9565 504 0x80
+DIVINER 2846 9566 2012 0x100
+ENCHANTER 2861 9567 2022 0x200
+ILLUSIONIST 2862 9568 8421 0x400
+INVOKER 3015 9569 12786 0x800
+NECROMANCER 12744 9570 12787 0x1000
+TRANSMUTER 12745 9571 12788 0x2000
+WILDMAGE 54893 54892 54894 0x8000
diff --git a/gemrb/override/bg1/magicmis.pro b/gemrb/override/bg1/magicmis.pro
new file mode 100644
index 0000000..33ceed1
Binary files /dev/null and b/gemrb/override/bg1/magicmis.pro differ
diff --git a/gemrb/override/bg1/mpal256.bmp b/gemrb/override/bg1/mpal256.bmp
new file mode 100644
index 0000000..9eec246
Binary files /dev/null and b/gemrb/override/bg1/mpal256.bmp differ
diff --git a/gemrb/override/bg1/music.2da b/gemrb/override/bg1/music.2da
new file mode 100644
index 0000000..fc4f9d5
--- /dev/null
+++ b/gemrb/override/bg1/music.2da
@@ -0,0 +1,42 @@
+2DA V1.0
+*
+ 0
+0 *
+1 CDAY1.mus
+2 CDAY2.mus
+3 CNITE.mus
+4 CHANTS.mus
+5 DREAM.mus
+6 Festi.mus
+7 FDay.mus
+8 FNite.mus
+9 PDay.mus
+10 PNite.mus
+11 Temple.mus
+12 Theme.mus
+13 TDay1.mus
+14 TDay2.mus
+15 TNite.mus
+16 Dung1.mus
+17 Dung2.mus
+18 Dung3.mus
+19 BC1.mus
+20 BC2.mus
+21 BD1.mus
+22 BD2.mus
+23 BL1.mus
+24 BL2.mus
+25 BF1.mus
+26 BF2.mus
+27 BP1.mus
+28 BP2.mus
+29 BW1.mus
+30 Bridge.mus
+31 Fort.mus
+32 Chapter.mus
+33 MPO900.mus
+34 TAV1.mus
+35 TAV2.mus
+36 TAV3.mus
+37 TAV4.mus
+38 DEATH.mus
diff --git a/gemrb/override/bg1/overlay.2da b/gemrb/override/bg1/overlay.2da
new file mode 100644
index 0000000..84a944e
--- /dev/null
+++ b/gemrb/override/bg1/overlay.2da
@@ -0,0 +1,35 @@
+2DA V1.0
+*
+ VVC UNDER FLAGS
+SANCTUARY SANCTRY 0 1
+ENTANGLE SPENTACI 0 0
+SPELLTRAP SPMAGGLO 0 0
+SHIELDGLOBE SPSHIELD 0 1
+GREASE GREASED 1 0
+WEB WEBENTD 1 0
+MINORGLOBE MINORGLB 0 1
+GLOBE GOINVUC 0 1
+FLAMESHROUD SOFLAMC 0 0
+ANTIMAGIC AMSHELC 0 0
+RESILIENT ORSPHEC 0 0
+PROTFROMMISS PFNMISC 0 0
+CLOAKOFFEAR COFEARC 0 0
+ENTROPY ESHIELC 0 0
+FIREAURA FIAURAC 0 0
+FROSTAURA FRAURAC 0 0
+INSECT IPLAGUC 0 0
+STORMSHELL SSHELLC 0 0
+LATHANDER1 SOLATC1 0 0
+LATHANDER2 SOLATC2 1 0
+GLATHANDER1 GSOLAC1 0 0
+GLATHANDER2 GSOLAC2 1 0
+SEVENEYES1 SEYESC1 0 0
+SEVENEYES2 SEYESC2 1 0
+BOUNCE SPTURNI2 1 0
+BOUNCE2 SPTURNI 1 0
+FIRESHIELD1 FSHIRC1 0 0
+FIRESHIELD2 FSHIRC2 1 0
+ICESHIELD1 FSHIBC1 0 0
+ICESHIELD2 FSHIBC2 1 0
+TORTOISE TSHELLC 0 0
+DEATHARMOR DARMORC 0 0
diff --git a/gemrb/override/bg1/pathfind.2da b/gemrb/override/bg1/pathfind.2da
new file mode 100644
index 0000000..72b3636
--- /dev/null
+++ b/gemrb/override/bg1/pathfind.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+*
+ 0 1 2 3 4 5 6 7 8 9 a b c d e f
+PASSABLE 8 1 1 1 1 1 1 1 0 1 8 1 0 8 3 1
+COST 10 4
+SOUND * WAL_04 WAL_01 * WAL_05 WAL_06 WAL_01 WAL_03 * WAL_02 * * * * * WAL_04
\ No newline at end of file
diff --git a/gemrb/override/bg1/pdolls.2da b/gemrb/override/bg1/pdolls.2da
new file mode 100644
index 0000000..ec20464
--- /dev/null
+++ b/gemrb/override/bg1/pdolls.2da
@@ -0,0 +1,86 @@
+2DA V1.0
+*
+ LEVEL1 LEVEL2 LEVEL3 LEVEL4 SIZE
+0x5000 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x5001 CEMC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x5002 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x5003 CIMC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x5010 CHFC1INV CHMC2INV CHMC3INV CHMC4INV M
+0x5011 CEFC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x5012 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x5013 CIFC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x5100 CHMF1INV CHMF2INV CHMF3INV CHMC4INV L
+0x5101 CEMF1INV CEMF2INV CEMF3INV CEMC4INV M
+0x5102 CDMF1INV CDMF2INV CDMF3INV CDMC4INV S
+0x5103 CIMF1INV CIMF2INV CIMF3INV CIMC4INV S
+0x5110 CHFF1INV CHFF2INV CHMF3INV CHMC4INV M
+0x5111 CEFF1INV CEFF2INV CEMF3INV CEMC4INV M
+0x5112 CDMF1INV CDMF2INV CDMF3INV CDMC4INV S
+0x5113 CIFF1INV CIFF2INV CIMF3INV CIMC4INV S
+0x5200 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x5201 CEMW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x5202 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5203 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5210 CHFW1INV CHMW2INV CHMW3INV CHMW4INV M
+0x5211 CEFW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x5212 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5213 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5300 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x5301 CEMT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x5302 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x5303 CIMT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x5310 CHFT1INV CHMT2INV CHMF3INV CHMF4INV M
+0x5311 CEFT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x5312 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x5313 CIFT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x6000 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x6001 CEMC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x6002 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x6003 CIMC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x6004 CIMC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x6005 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x6010 CHFC1INV CHFC2INV CHFC3INV CHFC4INV M
+0x6011 CEFC1INV CEFC2INV CEFC3INV CEFC4INV M
+0x6012 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x6013 CIFC1INV CIFC2INV CIFC3INV CIFC4INV S
+0x6014 CIFC1INV CIFC2INV CIFC3INV CIFC4INV S
+0x6015 CHFC1INV CHFC2INV CHFC3INV CHFC4INV M
+0x6100 CHMF1INV CHMF2INV CHMF3INV CHMF4INV L
+0x6101 CEMF1INV CEMF2INV CEMF3INV CEMF4INV M
+0x6102 CDMF1INV CDMF2INV CDMF3INV CDMF4INV S
+0x6103 CIMF1INV CIMF2INV CIMF3INV CIMF4INV S
+0x6104 CIMF1INV CIMF2INV CIMF3INV CIMF4INV S
+0x6105 CHMF1INV CHMF2INV CHMF3INV CHMF4INV L
+0x6110 CHFF1INV CHFF2INV CHFF3INV CHFF4INV M
+0x6111 CEFF1INV CEFF2INV CEFF3INV CEFF4INV M
+0x6112 CDMF1INV CDMF2INV CDMF3INV CDMF4INV S
+0x6113 CIFF1INV CIFF2INV CIFF3INV CIFF4INV S
+0x6114 CIFF1INV CIFF2INV CIFF3INV CIFF4INV S
+0x6115 CHFF1INV CHFF2INV CHFF3INV CHFF4INV M
+0x6200 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x6201 CEMW1INV CEMW2INV CEMW3INV CHMW4INV M
+0x6202 CDMW1INV CDMW2INV CDMW3INV CHMW4INV S
+0x6203 CIMW1INV CIMW2INV CIMW3INV CHMW4INV S
+0x6204 CIMW1INV CIMW2INV CIMW3INV CHMW4INV S
+0x6205 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x6210 CHFW1INV CHFW2INV CHFW3INV CHFW4INV M
+0x6211 CEFW1INV CEFW2INV CEFW3INV CHFW4INV M
+0x6212 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6213 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6214 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6215 CHFW1INV CHFW2INV CHFW3INV CHFW4INV M
+0x6300 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x6301 CEMT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x6302 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x6303 CIMT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x6304 CIMT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x6305 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x6310 CHFT1INV CHFT2INV CHFF3INV CHFF4INV M
+0x6311 CEFT1INV CEFT2INV CEFF3INV CEFF4INV M
+0x6312 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x6313 CIFT1INV CIFT2INV CIFF3INV CIFF4INV S
+0x6314 CIFT1INV CIFT2INV CIFF3INV CIFF4INV S
+0x6315 CHFT1INV CHFT2INV CHFF3INV CHFF4INV M
+0x6402 CMNKINV CHMT2INV CHMF3INV CHMF4INV M
+0x6500 CHMM1INV CHMT2INV CHMF3INV CHMF4INV L
+0x6510 CHFM1INV CHFT2INV CHMF3INV CHMF4INV M
diff --git a/gemrb/override/bg1/pictures.2da b/gemrb/override/bg1/pictures.2da
new file mode 100644
index 0000000..7c4af0f
--- /dev/null
+++ b/gemrb/override/bg1/pictures.2da
@@ -0,0 +1,35 @@
+2DA V1.0
+-1
+ GENDER HAIR SKIN MAJOR MINOR
+AJANTIS 1 3 3 3 3
+CORAN 1 2 12 45 55
+EDWIN 1 91 12 47 41
+ELDOTH 1 0 12 41 36
+GARRICK 1 0 12 41 50
+GENDWRF 1 4 85 39 49
+GENMELF 1 91 10 21 60
+GENMHLF 1 0 85 40 50
+KAGAIN 1 6 85 39 49
+KHALID 1 4 12 48 41
+KIVAN 1 1 84 37 39
+MAN1 1 6 84 52 46
+MAN2 1 2 12 37 45
+MINSC 1 110 84 60 58
+MONTAR 1 91 10 21 60
+QUAYLE 1 110 8 45 66
+TIAX 1 91 12 47 46
+XAN 1 0 12 45 64
+XZAR 1 91 84 54 66
+YESLICK 1 92 85 39 42
+ALORA 2 4 12 60 45
+BRANWE 2 3 12 37 41
+DYNAHEI 2 2 12 60 46
+FALDORN 2 91 84 54 65
+IMOEN 2 4 13 45 61
+JAHEIRA 2 92 12 41 65
+SAFANA 2 4 12 60 60
+SHARTEL 2 4 84 66 21
+SKIE 2 0 12 66 45
+VICONIA 2 79 83 54 60
+WOMAN1 2 3 84 61 46
+WOMAN2 2 0 84 45 58
diff --git a/gemrb/override/bg1/qslots.2da b/gemrb/override/bg1/qslots.2da
new file mode 100644
index 0000000..c9fc3d2
--- /dev/null
+++ b/gemrb/override/bg1/qslots.2da
@@ -0,0 +1,24 @@
+2DA V1.0
+100
+ SLOT1 SLOT2 SLOT3 SLOT4 SLOT5 SLOT6 SLOT7 SLOT8 SLOT9
+DEFAULT 18 19 14 100 8 9 11 12 13
+MAGE 3 4 5 2 8 9 11 12 13
+FIGHTER 18 19 14 100 8 9 11 12 13
+CLERIC 6 3 4 2 8 9 11 12 13
+THIEF 22 0 1 100 8 9 11 12 13
+BARD 20 1 3 2 8 9 11 12 13
+PALADIN 18 14 6 2 8 9 11 12 13
+FIGHTER_MAGE 3 4 5 2 8 9 11 12 13
+FIGHTER_CLERIC 6 3 4 2 8 9 11 12 13
+FIGHTER_THIEF 18 22 0 1 8 9 11 12 13
+FIGHTER_MAGE_THIEF 22 0 1 2 8 9 11 12 13
+DRUID 3 4 5 2 8 9 11 12 13
+RANGER 18 14 0 2 8 9 11 12 13
+MAGE_THIEF 22 0 1 2 8 9 11 12 13
+CLERIC_MAGE 6 3 4 2 8 9 11 12 13
+CLERIC_THIEF 22 0 1 2 8 9 11 12 13
+FIGHTER_DRUID 3 4 5 2 8 9 11 12 13
+FIGHTER_MAGE_CLERIC 6 3 4 2 8 9 11 12 13
+CLERIC_RANGER 6 3 4 2 8 9 11 12 13
+SORCERER 3 4 5 2 8 9 11 12 13
+MONK 18 14 22 0 8 9 11 12 13
diff --git a/gemrb/override/bg1/races.2da b/gemrb/override/bg1/races.2da
new file mode 100644
index 0000000..008ebc3
--- /dev/null
+++ b/gemrb/override/bg1/races.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF ID USABILITY SAVE
+HUMAN 7193 9550 1096 1 0x08000000 *
+ELF 7194 9552 1097 2 0x00800000 *
+HALF_ELF 7197 9555 1098 3 0x02000000 *
+GNOME 7196 9553 1099 6 0x10000000 SAVECNG
+HALFLING 7195 9554 1101 5 0x04000000 SAVECNDH
+DWARF 7182 9551 1100 4 0x01000000 SAVECNDH
diff --git a/gemrb/override/bg1/randitem.2da b/gemrb/override/bg1/randitem.2da
new file mode 100644
index 0000000..4f6dc26
--- /dev/null
+++ b/gemrb/override/bg1/randitem.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+*
+ RESREF FURY_MODE
+GOLD MISC07
+RND 4 4
+RNDEQU RNDEQUIP RNDEQUIP
+RNDMAG RNDMAGIC RNDMAGIC
+RNDSCR RNDSCROL RNDSCROL
+RNDTRE RNDTREAS RNDTREAS
diff --git a/gemrb/override/bg1/savegame.2da b/gemrb/override/bg1/savegame.2da
new file mode 100644
index 0000000..14746de
--- /dev/null
+++ b/gemrb/override/bg1/savegame.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+Auto-Save
+ SLOTNAME QSAVE
+0 Auto-Save 0
+1 Quick-Save 1
+2 Final-Save 0
diff --git a/gemrb/override/bg1/script.2da b/gemrb/override/bg1/script.2da
new file mode 100644
index 0000000..ea6bf26
--- /dev/null
+++ b/gemrb/override/bg1/script.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+0
+ DATA 1 2 3 4 5 6 7
+OBJECT_IDS_COUNT 7 ea general race class specific gender alignmen
+MAX_OBJECT_NESTING 5
+ADDITIONAL_RECT 0
+EXTRA_PARAMETERS_COUNT 0
+TRIGGER_POINT 0
\ No newline at end of file
diff --git a/gemrb/override/bg1/shtable.2da b/gemrb/override/bg1/shtable.2da
new file mode 100644
index 0000000..5851966
--- /dev/null
+++ b/gemrb/override/bg1/shtable.2da
@@ -0,0 +1,42 @@
+2DA V1.0
+*
+ RESREF
+0 SHAIR
+1 SHEARTH
+2 SHWATER
+3 *
+4 SHAIR
+5 SHEARTH
+6 SHWATER
+7 *
+8 SHAIR
+9 SHEARTH
+10 SHWATER
+11 *
+12 SHAIR
+13 SHEARTH
+14 SHWATER
+15 *
+16 SHAIR
+17 SHEARTH
+18 SHWATER
+19 *
+20 SHAIR
+21 SHEARTH
+22 SHWATER
+23 *
+24 SHAIR
+25 SHEARTH
+26 SHWATER
+27 *
+28 SHAIR
+29 SHEARTH
+30 SHWATER
+31 *
+32
+33
+34
+35 FLMSTRK
+36 HLYMITE
+37
+38 SPDIMDR
diff --git a/gemrb/override/bg1/skills.2da b/gemrb/override/bg1/skills.2da
new file mode 100644
index 0000000..a0e2fa4
--- /dev/null
+++ b/gemrb/override/bg1/skills.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ DESC_REF CAP_REF ID THIEF MAGE_THIEF FIGHTER_THIEF CLERIC_THIEF FIGHTER_MAGE_THIEF
+FIRST_LEVEL * * * 40 40 40 40 40
+RATE * * * 40 40 40 40 40
+PICK_POCKETS 9597 9463 29 1 1 1 1 1
+OPEN_LOCKS 9598 9460 26 1 1 1 1 1
+FIND_TRAPS 9599 9462 28 1 1 1 1 1
+STEALTH 9600 9461 27 1 1 1 1 1
diff --git a/gemrb/override/bg1/sleep.pro b/gemrb/override/bg1/sleep.pro
new file mode 100644
index 0000000..06568c9
Binary files /dev/null and b/gemrb/override/bg1/sleep.pro differ
diff --git a/gemrb/override/bg1/slottype.2da b/gemrb/override/bg1/slottype.2da
new file mode 100644
index 0000000..8a14119
--- /dev/null
+++ b/gemrb/override/bg1/slottype.2da
@@ -0,0 +1,42 @@
+2DA V1.0
+*
+ BITS SCRIPT ICON STRREF EFFECT FLAGS
+10 0 0 * 0 2 0
+6 1 13 STONHELM 11999 7 0
+1 2 11 STONARM 11997 1 0
+9 4 26 STONSHIL 12006 6 0
+5 8 12 STONGLET 11998 1 0
+7 16 22 STONRING 12002 1 1
+8 16 23 STONRING 12003 1 1
+0 32 14 STONAMUL 12000 1 0
+2 64 21 STONBELT 12001 1 0
+3 128 25 STONBOOT 12005 1 0
+35 256 1 STONWEAP 12010 4 1
+36 256 2 STONWEAP 12010 4 1
+37 256 3 STONWEAP 12010 4 1
+38 256 4 STONWEAP 12010 4 1
+11 512 15 STONQUIV 12009 5 1
+12 512 16 STONQUIV 12009 5 1
+13 512 17 STONQUIV 12009 5 1
+14 512 0 STONQUIV 12009 5 1
+4 1024 24 STONCLOK 12004 1 0
+15 2048 5 STONITEM 12012 0 1
+16 2048 6 STONITEM 12012 0 1
+17 2048 7 STONITEM 12012 0 1
+18 -1 30 * 12013 0 1
+19 -1 31 * 12013 0 1
+20 -1 32 * 12013 0 1
+21 -1 33 * 12013 0 1
+22 -1 34 * 12013 0 1
+23 -1 35 * 12013 0 1
+24 -1 36 * 12013 0 1
+25 -1 37 * 12013 0 1
+26 -1 38 * 12013 0 1
+27 -1 39 * 12013 0 1
+28 -1 40 * 12013 0 1
+29 -1 41 * 12013 0 1
+30 -1 42 * 12013 0 1
+31 -1 43 * 12013 0 1
+32 -1 44 * 12013 0 1
+33 -1 45 * 12013 0 1
+34 0 0 * 0 3 0
diff --git a/gemrb/override/bg1/sparbknp.pro b/gemrb/override/bg1/sparbknp.pro
new file mode 100644
index 0000000..aba7673
Binary files /dev/null and b/gemrb/override/bg1/sparbknp.pro differ
diff --git a/gemrb/override/bg1/sparbkpa.pro b/gemrb/override/bg1/sparbkpa.pro
new file mode 100644
index 0000000..7a51eed
Binary files /dev/null and b/gemrb/override/bg1/sparbkpa.pro differ
diff --git a/gemrb/override/bg1/sparblnp.pro b/gemrb/override/bg1/sparblnp.pro
new file mode 100644
index 0000000..f858276
Binary files /dev/null and b/gemrb/override/bg1/sparblnp.pro differ
diff --git a/gemrb/override/bg1/sparblpa.pro b/gemrb/override/bg1/sparblpa.pro
new file mode 100644
index 0000000..4124a9d
Binary files /dev/null and b/gemrb/override/bg1/sparblpa.pro differ
diff --git a/gemrb/override/bg1/sparchnp.pro b/gemrb/override/bg1/sparchnp.pro
new file mode 100644
index 0000000..7dd540e
Binary files /dev/null and b/gemrb/override/bg1/sparchnp.pro differ
diff --git a/gemrb/override/bg1/sparchpa.pro b/gemrb/override/bg1/sparchpa.pro
new file mode 100644
index 0000000..101f9a2
Binary files /dev/null and b/gemrb/override/bg1/sparchpa.pro differ
diff --git a/gemrb/override/bg1/spargonp.pro b/gemrb/override/bg1/spargonp.pro
new file mode 100644
index 0000000..234aca6
Binary files /dev/null and b/gemrb/override/bg1/spargonp.pro differ
diff --git a/gemrb/override/bg1/spargopa.pro b/gemrb/override/bg1/spargopa.pro
new file mode 100644
index 0000000..2665825
Binary files /dev/null and b/gemrb/override/bg1/spargopa.pro differ
diff --git a/gemrb/override/bg1/spargrnp.pro b/gemrb/override/bg1/spargrnp.pro
new file mode 100644
index 0000000..a1d4dbb
Binary files /dev/null and b/gemrb/override/bg1/spargrnp.pro differ
diff --git a/gemrb/override/bg1/spargrpa.pro b/gemrb/override/bg1/spargrpa.pro
new file mode 100644
index 0000000..55a741b
Binary files /dev/null and b/gemrb/override/bg1/spargrpa.pro differ
diff --git a/gemrb/override/bg1/sparicnp.pro b/gemrb/override/bg1/sparicnp.pro
new file mode 100644
index 0000000..77ff12b
Binary files /dev/null and b/gemrb/override/bg1/sparicnp.pro differ
diff --git a/gemrb/override/bg1/sparicpa.pro b/gemrb/override/bg1/sparicpa.pro
new file mode 100644
index 0000000..2d8b9d7
Binary files /dev/null and b/gemrb/override/bg1/sparicpa.pro differ
diff --git a/gemrb/override/bg1/sparklbk.pro b/gemrb/override/bg1/sparklbk.pro
new file mode 100644
index 0000000..147bd43
Binary files /dev/null and b/gemrb/override/bg1/sparklbk.pro differ
diff --git a/gemrb/override/bg1/sparklbl.pro b/gemrb/override/bg1/sparklbl.pro
new file mode 100644
index 0000000..4928b0c
Binary files /dev/null and b/gemrb/override/bg1/sparklbl.pro differ
diff --git a/gemrb/override/bg1/sparklch.pro b/gemrb/override/bg1/sparklch.pro
new file mode 100644
index 0000000..c6ad4c8
Binary files /dev/null and b/gemrb/override/bg1/sparklch.pro differ
diff --git a/gemrb/override/bg1/sparklgo.pro b/gemrb/override/bg1/sparklgo.pro
new file mode 100644
index 0000000..748d91d
Binary files /dev/null and b/gemrb/override/bg1/sparklgo.pro differ
diff --git a/gemrb/override/bg1/sparklgr.pro b/gemrb/override/bg1/sparklgr.pro
new file mode 100644
index 0000000..ac152c1
Binary files /dev/null and b/gemrb/override/bg1/sparklgr.pro differ
diff --git a/gemrb/override/bg1/sparklic.pro b/gemrb/override/bg1/sparklic.pro
new file mode 100644
index 0000000..e833132
Binary files /dev/null and b/gemrb/override/bg1/sparklic.pro differ
diff --git a/gemrb/override/bg1/sparklma.pro b/gemrb/override/bg1/sparklma.pro
new file mode 100644
index 0000000..50d7be0
Binary files /dev/null and b/gemrb/override/bg1/sparklma.pro differ
diff --git a/gemrb/override/bg1/sparklor.pro b/gemrb/override/bg1/sparklor.pro
new file mode 100644
index 0000000..dc02305
Binary files /dev/null and b/gemrb/override/bg1/sparklor.pro differ
diff --git a/gemrb/override/bg1/sparklpu.pro b/gemrb/override/bg1/sparklpu.pro
new file mode 100644
index 0000000..ef0f1fd
Binary files /dev/null and b/gemrb/override/bg1/sparklpu.pro differ
diff --git a/gemrb/override/bg1/sparklre.pro b/gemrb/override/bg1/sparklre.pro
new file mode 100644
index 0000000..1a15bfd
Binary files /dev/null and b/gemrb/override/bg1/sparklre.pro differ
diff --git a/gemrb/override/bg1/sparklst.pro b/gemrb/override/bg1/sparklst.pro
new file mode 100644
index 0000000..543ea91
Binary files /dev/null and b/gemrb/override/bg1/sparklst.pro differ
diff --git a/gemrb/override/bg1/sparmanp.pro b/gemrb/override/bg1/sparmanp.pro
new file mode 100644
index 0000000..c76d137
Binary files /dev/null and b/gemrb/override/bg1/sparmanp.pro differ
diff --git a/gemrb/override/bg1/sparmapa.pro b/gemrb/override/bg1/sparmapa.pro
new file mode 100644
index 0000000..6a21bd1
Binary files /dev/null and b/gemrb/override/bg1/sparmapa.pro differ
diff --git a/gemrb/override/bg1/sparornp.pro b/gemrb/override/bg1/sparornp.pro
new file mode 100644
index 0000000..abf33b8
Binary files /dev/null and b/gemrb/override/bg1/sparornp.pro differ
diff --git a/gemrb/override/bg1/sparorpa.pro b/gemrb/override/bg1/sparorpa.pro
new file mode 100644
index 0000000..686dd90
Binary files /dev/null and b/gemrb/override/bg1/sparorpa.pro differ
diff --git a/gemrb/override/bg1/sparpunp.pro b/gemrb/override/bg1/sparpunp.pro
new file mode 100644
index 0000000..23f98f2
Binary files /dev/null and b/gemrb/override/bg1/sparpunp.pro differ
diff --git a/gemrb/override/bg1/sparpupa.pro b/gemrb/override/bg1/sparpupa.pro
new file mode 100644
index 0000000..322a995
Binary files /dev/null and b/gemrb/override/bg1/sparpupa.pro differ
diff --git a/gemrb/override/bg1/sparrenp.pro b/gemrb/override/bg1/sparrenp.pro
new file mode 100644
index 0000000..7718874
Binary files /dev/null and b/gemrb/override/bg1/sparrenp.pro differ
diff --git a/gemrb/override/bg1/sparrepa.pro b/gemrb/override/bg1/sparrepa.pro
new file mode 100644
index 0000000..a5edc18
Binary files /dev/null and b/gemrb/override/bg1/sparrepa.pro differ
diff --git a/gemrb/override/bg1/sparstnp.pro b/gemrb/override/bg1/sparstnp.pro
new file mode 100644
index 0000000..7d4b4c3
Binary files /dev/null and b/gemrb/override/bg1/sparstnp.pro differ
diff --git a/gemrb/override/bg1/sparstpa.pro b/gemrb/override/bg1/sparstpa.pro
new file mode 100644
index 0000000..4d55459
Binary files /dev/null and b/gemrb/override/bg1/sparstpa.pro differ
diff --git a/gemrb/override/bg1/spear.pro b/gemrb/override/bg1/spear.pro
new file mode 100644
index 0000000..bc9288c
Binary files /dev/null and b/gemrb/override/bg1/spear.pro differ
diff --git a/gemrb/override/bg1/spearex.pro b/gemrb/override/bg1/spearex.pro
new file mode 100644
index 0000000..4bb5786
Binary files /dev/null and b/gemrb/override/bg1/spearex.pro differ
diff --git a/gemrb/override/bg1/spfirebl.pro b/gemrb/override/bg1/spfirebl.pro
new file mode 100644
index 0000000..3b44f50
Binary files /dev/null and b/gemrb/override/bg1/spfirebl.pro differ
diff --git a/gemrb/override/bg1/spklarbk.pro b/gemrb/override/bg1/spklarbk.pro
new file mode 100644
index 0000000..f8f12c0
Binary files /dev/null and b/gemrb/override/bg1/spklarbk.pro differ
diff --git a/gemrb/override/bg1/spklarbl.pro b/gemrb/override/bg1/spklarbl.pro
new file mode 100644
index 0000000..eddae9d
Binary files /dev/null and b/gemrb/override/bg1/spklarbl.pro differ
diff --git a/gemrb/override/bg1/spklarch.pro b/gemrb/override/bg1/spklarch.pro
new file mode 100644
index 0000000..82766f4
Binary files /dev/null and b/gemrb/override/bg1/spklarch.pro differ
diff --git a/gemrb/override/bg1/spklargo.pro b/gemrb/override/bg1/spklargo.pro
new file mode 100644
index 0000000..e4323ac
Binary files /dev/null and b/gemrb/override/bg1/spklargo.pro differ
diff --git a/gemrb/override/bg1/spklargr.pro b/gemrb/override/bg1/spklargr.pro
new file mode 100644
index 0000000..38fc137
Binary files /dev/null and b/gemrb/override/bg1/spklargr.pro differ
diff --git a/gemrb/override/bg1/spklaric.pro b/gemrb/override/bg1/spklaric.pro
new file mode 100644
index 0000000..4fd7d7e
Binary files /dev/null and b/gemrb/override/bg1/spklaric.pro differ
diff --git a/gemrb/override/bg1/spklarma.pro b/gemrb/override/bg1/spklarma.pro
new file mode 100644
index 0000000..d8f18c2
Binary files /dev/null and b/gemrb/override/bg1/spklarma.pro differ
diff --git a/gemrb/override/bg1/spklaror.pro b/gemrb/override/bg1/spklaror.pro
new file mode 100644
index 0000000..7e395f4
Binary files /dev/null and b/gemrb/override/bg1/spklaror.pro differ
diff --git a/gemrb/override/bg1/spklarpu.pro b/gemrb/override/bg1/spklarpu.pro
new file mode 100644
index 0000000..e70bb75
Binary files /dev/null and b/gemrb/override/bg1/spklarpu.pro differ
diff --git a/gemrb/override/bg1/spklarre.pro b/gemrb/override/bg1/spklarre.pro
new file mode 100644
index 0000000..29d636c
Binary files /dev/null and b/gemrb/override/bg1/spklarre.pro differ
diff --git a/gemrb/override/bg1/spklarst.pro b/gemrb/override/bg1/spklarst.pro
new file mode 100644
index 0000000..ba5633b
Binary files /dev/null and b/gemrb/override/bg1/spklarst.pro differ
diff --git a/gemrb/override/bg1/splspec.2da b/gemrb/override/bg1/splspec.2da
new file mode 100644
index 0000000..63a3038
--- /dev/null
+++ b/gemrb/override/bg1/splspec.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ IDENTIFY_SILENCE
+SPWI110 1
+SPWI219 2
diff --git a/gemrb/override/bg1/spscorch.pro b/gemrb/override/bg1/spscorch.pro
new file mode 100644
index 0000000..3ad6c32
Binary files /dev/null and b/gemrb/override/bg1/spscorch.pro differ
diff --git a/gemrb/override/bg1/spscoric.pro b/gemrb/override/bg1/spscoric.pro
new file mode 100644
index 0000000..3d7e0b7
Binary files /dev/null and b/gemrb/override/bg1/spscoric.pro differ
diff --git a/gemrb/override/bg1/start.2da b/gemrb/override/bg1/start.2da
new file mode 100644
index 0000000..1805aab
--- /dev/null
+++ b/gemrb/override/bg1/start.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ XPOS YPOS AREA ROT
+NORMAL START_XPOS START_YPOS START_AREA *
diff --git a/gemrb/override/bg1/stone.pro b/gemrb/override/bg1/stone.pro
new file mode 100644
index 0000000..13ce3b0
Binary files /dev/null and b/gemrb/override/bg1/stone.pro differ
diff --git a/gemrb/override/bg1/strings.2da b/gemrb/override/bg1/strings.2da
new file mode 100644
index 0000000..f18b722
--- /dev/null
+++ b/gemrb/override/bg1/strings.2da
@@ -0,0 +1,170 @@
+2DA V1.0
+-1
+ STRREF
+SCATTERED 16457
+WHOLEPARTY 16484
+DOORLOCKED 16485
+MAGICTRAP 16486
+NORMALTRAP 16487
+TRAP 16488
+CANNOTGO 16489
+TRAPREMOVED 16490
+OVERSTOCKED 16491
+SLEEP 16492
+AMBUSH 16493
+CONTLOCKED 16494
+NOMONEY 16495
+CURSED 16496
+SPELLDISRUPT 16497
+DIED 16498
+MAYNOTREST 16499
+CANTRESTMONS 16500
+CANTSAVEMONS 16501
+CANTSAVE 16502
+NODIALOG 10945
+CANTSAVEDIALOG 19253
+CANTSAVEDIALOG2 19254
+CANTSAVEMOVIE 19255
+TARGETBUSY -1
+CANTTALKTRANS -1
+GOTGOLD 17572
+LOSTGOLD 17573
+GOTXP 17574
+LOSTXP 17575
+GOTITEM 17576
+LOSTITEM 17577
+GOTREP 19686
+LOSTREP 19687
+GOTABILITY 10514
+GOTSPELL 10514
+GOTSONG 10514
+NOTHINGTOSAY -1
+JOURNALCHANGE 11359
+WORLDMAPCHANGE 11360
+PAUSED 16321
+UNPAUSED 16322
+SCRIPTPAUSED -1
+AP_UNUSABLE 17113
+AP_ATTACKED 17114
+AP_HIT 17115
+AP_WOUNDED 17116
+AP_DEAD 17117
+AP_NOTARGET 17118
+AP_ENDROUND 10014
+AP_ENEMY 23511
+AP_TRAP -1
+AP_SPELLCAST -1
+AP_RESERVED1 -1
+AP_RESERVED2 -1
+AP_RESERVED3 -1
+AP_RESERVED4 -1
+CHARMED 14672
+DIRECHARMED 14780
+CONTROLLED -1
+EVIL 16505
+GNE_NEUTRAL 16504
+GOOD 16503
+STR_LAWFUL -1
+LNC_NEUTRAL -1
+CHAOTIC -1
+ACTION_CAST 16464
+ACTION_ATTACK 16465
+ACTION_TURN 16466
+ACTION_SONG 16467
+ACTION_FINDTRAP 16468
+MAGICWEAPON 10141
+OFFHAND_USED 9380
+TWOHANDED_USED 9381
+CANNOT_USE_ITEM 9382
+CANT_DROP_ITEM 25697
+NOT_IN_OFFHAND 9375
+ITEM_IS_CURSED 16304
+NO_CRITICAL 20696
+TRACKING -1
+TRACKINGFAILED -1
+DOOR_NOPICK 23169
+CONT_NOPICK 23169
+CANTSAVECOMBAT -1
+CANTSAVENOCTRL -1
+LOCKPICK_DONE 16517
+LOCKPICK_FAILED 16518
+STATIC_DISSIPATE -1
+LIGHTNING_DISSIPATE -1
+HAS_NO_ABILITY 17317
+NEEDS_IDENTIFY 17316
+WRONG_ITEMTYPE 9375
+HAS_ITEMEXCL 20685
+PICKPOCKET_DONE 10072
+PICKPOCKET_NONE 10070
+PICKPOCKET_FAIL 10069
+PICKPOCKET_EVIL 10068
+PICKPOCKET_ARMOR 10067
+USING_FEAT -1
+STOPPED_FEAT -1
+DISARM_DONE 16520
+DISARM_FAIL 1608
+DOORBASH_DONE 9915
+DOORBASH_FAIL 9913
+CONTBASH_DONE 9916
+CONTBASH_FAIL 9914
+MAYNOTSETTRAP -1
+SNAREFAILED -1
+SNARESUCCEED -1
+NOMORETRAP -1
+DISABLEDMAGE 8856
+SAVESUCCEED 1682
+QSAVESUCCEED 10237
+UNINJURED -1
+INJURED1 -1
+INJURED2 -1
+INJURED3 -1
+INJURED4 -1
+HOURS 10700
+HOUR 10701
+DAYS 10697
+DAY 10698
+REST 10690
+JOURNEY 10689
+PST_REST -1
+PST_HOUR -1
+PST_HOURS -1
+DAMAGE_IMMUNITY -1
+DAMAGE_STR1 14027
+DAMAGE_STR2 -1
+DAMAGE_STR3 -1
+DMG_POISON -1
+DMG_MAGIC -1
+DMG_MISSILE -1
+DMG_SLASHING -1
+DMG_PIERCING -1
+DMG_CRUSHING -1
+DMG_FIRE -1
+DMG_ELECTRIC -1
+DMG_COLD -1
+DMG_ACID -1
+DMG_OTHER -1
+GOTQUESTXP -1
+LEVELUP 17119
+INVFULL_ITEMDROP 24106
+CONT_DUP -1
+CONT_TRIG -1
+CONT_FAIL -1
+SEQ_DUP -1
+CRITICAL_HIT 16462
+CRITICAL_MISS 16463
+DEATH 14026
+BACKSTAB 12128
+BACKSTAB_BAD 10013
+BACKSTAB_FAIL -1
+CASTER_LVL_INC -1
+CASTER_LVL_DEC -1
+CHARS_EXPORTED -1
+PALADIN_FALL 19620
+RANGER_FALL 19621
+RES_RESISTED -1
+DEADMAGIC_FAIL -1
+MISCASTMAGIC -1
+WILDSURGE -1
+FAMBLOCK -1
+FAMPROTAGONIST -1
+
diff --git a/gemrb/override/bg1/trapglyp.pro b/gemrb/override/bg1/trapglyp.pro
new file mode 100644
index 0000000..5542681
Binary files /dev/null and b/gemrb/override/bg1/trapglyp.pro differ
diff --git a/gemrb/override/bg1/trapskul.pro b/gemrb/override/bg1/trapskul.pro
new file mode 100644
index 0000000..5706fec
Binary files /dev/null and b/gemrb/override/bg1/trapskul.pro differ
diff --git a/gemrb/override/bg1/walksnd.2da b/gemrb/override/bg1/walksnd.2da
new file mode 100644
index 0000000..03afb45
--- /dev/null
+++ b/gemrb/override/bg1/walksnd.2da
@@ -0,0 +1,66 @@
+2DA V1.0
+*
+ RESREF MIN MAX RANGE
+NONE * 0x0000 0xffff 0
+WYVERN WAL_17 0x1000 0x10ff 2
+LARGE WAL_30 0x1100 0x13ff 4
+SIRINE WAL_24 0x2000 0x20ff 4
+VOLO SOUND 0x2100 0x21ff 4
+OGREKNIGHT WAL_30 0x2200 0x23ff 4
+DEFAULT SOUND 0x5000 0x6fff 4
+SKELETON WAL_19 0x6403 0x6403 5
+OGRE WAL_30 0x7000 0x70ff 4
+BASILISK WAL_16 0x7100 0x71ff 1
+BEAR WAL_12 0x7200 0x72ff 1
+ELEMENTAL WAL_30 0x7300 0x73ff 4
+ELEM2 WAL_24 0x7301 0x7301 4
+ELEM2 WAL_24 0x7311 0x7311 4
+ELEM2 WAL_24 0x7314 0x7314 4
+ELEM2 WAL_24 0x7321 0x7321 4
+DOG WAL_31 0x7400 0x74ff 1
+DEFAULT SOUND 0x7500 0x77ff 4
+ETTERCAP WAL_24 0x7600 0x76ff 4
+GIBBER WAL_25 0x7800 0x78ff 2
+SLIME WAL_15 0x7900 0x79ff 5
+SPIDER WAL_14 0x7a00 0x7aff 5
+WOLF WAL_31 0x7b00 0x7bff 1
+XVART WAL_26 0x7c00 0x7cff 2
+ZOMBIE WAL_10 0x7d00 0x7dff 4
+WEREWOLF WAL_24 0x7e00 0x7eff 4
+DEFAULT SOUND 0x7f00 0x7fff 4
+TROLL WAL_30 0x7f00 0x7f00 4
+IMP WAL_PS 0x7f03 0x7f03 1
+GOLEM WAL_MT 0x7f04 0x7f04 4
+NONE * 0x7f06 0x7f06 0
+GOLEM WAL_MT 0x7f07 0x7f07 4
+OTYUGH WAL_30 0x7f08 0x7f08 4
+SAHUAGIN WAL_24 0x7f09 0x7f09 4
+GIANTCAT WAL_31 0x7f0a 0x7f0b 1
+KUOLICH WAL_24 0x7f0c 0x7f0d 4
+DEMILICH * 0x7f0e 0x7f0e 0
+TROLL WAL_30 0x7f0f 0x7f0f 4
+UMBERHULK WAL_24 0x7f11 0x7f11 4
+SNAKE * 0x7f13 0x7f13 0
+GITH WAL_24 0x7f14 0x7f14 4
+NONE * 0x7f15 0x7f21 0
+SAHUAGIN WAL_24 0x7f23 0x7f23 4
+KUO WAL_24 0x7f28 0x7f28 4
+FAMILIAR WAL_PS 0x7f2d 0x7f2d 4
+RAVAGER WAL_24 0x7f2e 0x7f2f 4
+SLAYER WAL_24 0x7f32 0x7f32 4
+MIST WAL_24 0x7f35 0x7f35 4
+NONE * 0x7f38 0x7f39 0
+GNOLL WAL_28 0x8000 0x80ff 4
+HOBGOBLIN WAL_29 0x8100 0x81ff 4
+KOBOLD WAL_26 0x8200 0x82ff 4
+IWD WAL_09 0x9000 0x9fff 4
+WYVERN WAL_17 0xa000 0xa0ff 2
+CARRION WAL_13 0xa100 0xa1ff 4
+DEFAULT SOUND 0xc000 0xcfff 4
+BEGGAR WAL_24 0xc600 0xc6ff 4
+CHILDREN WAL_24 0xc700 0xc7ff 4
+SLAVE WAL_24 0xcb00 0xcbff 4
+IWD WAL_09 0xe000 0xefff 4
+GOBLIN WAL_26 0xe400 0xe4ff 2
+NONE * 0xef00 0xefff 0
+
diff --git a/gemrb/override/bg1/weapprof.2da b/gemrb/override/bg1/weapprof.2da
new file mode 100644
index 0000000..d12c39e
--- /dev/null
+++ b/gemrb/override/bg1/weapprof.2da
@@ -0,0 +1,11 @@
+2DA V1.0
+0
+ ID NAME_REF DESC_REF
+LARGE_SWORD 0 8668 9589
+SMALL_SWORD 1 8732 9590
+BOW 2 8733 9591
+SPEAR 3 8734 9592
+BLUNT 4 9400 9593
+SPIKED 5 9401 9594
+AXE 6 9402 9595
+MISSILE 7 9403 9596
diff --git a/gemrb/override/bg1/web.pro b/gemrb/override/bg1/web.pro
new file mode 100644
index 0000000..6b6436a
Binary files /dev/null and b/gemrb/override/bg1/web.pro differ
diff --git a/gemrb/override/bg1/wssingle.2da b/gemrb/override/bg1/wssingle.2da
new file mode 100644
index 0000000..56df669
--- /dev/null
+++ b/gemrb/override/bg1/wssingle.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+0
+ AC CRITICALHITBONUS
+0 0 0
+1 -1 -1
+2 -2 -1
+3 -2 -1
diff --git a/gemrb/override/bg2/CMakeLists.txt b/gemrb/override/bg2/CMakeLists.txt
new file mode 100644
index 0000000..63e7249
--- /dev/null
+++ b/gemrb/override/bg2/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_OVERRIDE (bg2)
\ No newline at end of file
diff --git a/gemrb/override/bg2/HPBARB.2da b/gemrb/override/bg2/HPBARB.2da
new file mode 100644
index 0000000..7f45f4e
--- /dev/null
+++ b/gemrb/override/bg2/HPBARB.2da
@@ -0,0 +1,43 @@
+2DA V1.0
+0
+ SIDES ROLLS MODIFIER
+1 12 1 0
+2 12 1 0
+3 12 1 0
+4 12 1 0
+5 12 1 0
+6 12 1 0
+7 12 1 0
+8 12 1 0
+9 12 1 0
+10 12 0 4
+11 12 0 4
+12 12 0 4
+13 12 0 4
+14 12 0 4
+15 12 0 4
+16 12 0 4
+17 12 0 4
+18 12 0 4
+19 12 0 4
+20 12 0 4
+21 12 0 4
+22 12 0 4
+23 12 0 4
+24 12 0 4
+25 12 0 4
+26 12 0 4
+27 12 0 4
+28 12 0 4
+29 12 0 4
+30 12 0 4
+31 12 0 4
+32 12 0 4
+33 12 0 4
+34 12 0 4
+35 12 0 4
+36 12 0 4
+37 12 0 4
+38 12 0 4
+39 12 0 4
+40 12 0 4
diff --git a/gemrb/override/bg2/Makefile.am b/gemrb/override/bg2/Makefile.am
new file mode 100644
index 0000000..7589bd2
--- /dev/null
+++ b/gemrb/override/bg2/Makefile.am
@@ -0,0 +1,3 @@
+bg2override_DATA = *.2da *.ini *.chu *.ids *.vvc *.spl *.pro
+bg2overridedir = $(moddir)/override/bg2/
+EXTRA_DIST = *.2da *.ini *.chu *.ids *.vvc *.spl *.pro
diff --git a/gemrb/override/bg2/ability.2da b/gemrb/override/bg2/ability.2da
new file mode 100644
index 0000000..2fa0989
--- /dev/null
+++ b/gemrb/override/bg2/ability.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF STAT_ID
+STRENGTH 11975 9582 1145 36
+DEXTERITY 11977 9584 1151 40
+CONSTITUTION 11978 9583 1178 41
+INTELLIGENCE 11979 9585 1179 38
+WISDOM 11980 9586 1180 39
+CHARISMA 11981 9587 1181 42
diff --git a/gemrb/override/bg2/aligns.2da b/gemrb/override/bg2/aligns.2da
new file mode 100644
index 0000000..1047ea6
--- /dev/null
+++ b/gemrb/override/bg2/aligns.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF VALUE COLNAME USABILITY
+LAWFUL_GOOD 7186 9603 1102 0x11 L_G 0x14
+NEUTRAL_GOOD 7183 9606 1105 0x21 N_G 0x24
+CHAOTIC_GOOD 7189 9609 1108 0x31 C_G 0x5
+LAWFUL_NEUTRAL 7188 9604 1104 0x12 L_N 0x18
+TRUE_NEUTRAL 7185 9608 1106 0x22 N_N 0x28
+CHAOTIC_NEUTRAL 7191 9610 1109 0x32 C_N 0x9
+LAWFUL_EVIL 7187 9605 1103 0x13 L_E 0x12
+NEUTRAL_EVIL 7184 9607 1107 0x23 N_E 0x22
+CHAOTIC_EVIL 7190 9611 1110 0x33 C_E 0x3
diff --git a/gemrb/override/bg2/areapro.2da b/gemrb/override/bg2/areapro.2da
new file mode 100644
index 0000000..93f7b6a
--- /dev/null
+++ b/gemrb/override/bg2/areapro.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+*
+ RESOURCE1 RESOURCE2 RESOURCE3 SOUND1 SOUND2 FLAGS
+FIREBALL SPFLAMES SPFIREPI * EFF_M21 * 0
+STINKCLOUD SPHORPUF * * EFF_M18A ARE_M02 7
+CLOUDKILL SPHORPUF * * EFF_M18A ARE_M02 7
+ICESTORM SPICESTM SPBOOM * EFF_M34 ARE_M04 14
+GREASE GREASED * GREASEH EFF_M31B ARE_M01 2
+WEB WEBENTD * WEBENTH EFF_M19 ARE_M03 34
+METEOR SPMETSWM * * EFF_M34 ARE_P14 6
+WILTING SPHORPUF SPHORWIL * * * 2
+WEIRD SPWRDFLD * * EFF_M34 ARE_M11 6
+ENTANGLE SPENTAAI * * EFF_M19 ARE_P01 2
+COLORSPRAY SPCSPRA3 * SPCSPRA2 EFF_M19 * 2
+CONECOLD SPCCOLDE SPCCOLDL * EFF_M19 * 0
+HOLYSMITE SPHLYSM2 SPHLYSM3 * EFF_M34 * 6
+UNHOLY SPUNHBL2 SPUNHBL3 * EFF_M34 * 6
+PRISMATIC SPPRISM2 SPPRISM3 * EFF_M34 * 0
+DRAGON SPRDRASI * * EFF_M21 * 16
+VENGEANCE SPSTRMVA SPSTRMVB * * * 2
+FIREBALL2 SPFLAMES SPFIREPI * EFF_M21 * 0
+FIREBALL3 SPFLAMES SPFIREPI * EFF_M21 * 0
diff --git a/gemrb/override/bg2/at2xlvl.spl b/gemrb/override/bg2/at2xlvl.spl
new file mode 100644
index 0000000..a0ba980
Binary files /dev/null and b/gemrb/override/bg2/at2xlvl.spl differ
diff --git a/gemrb/override/bg2/avatars.2da b/gemrb/override/bg2/avatars.2da
new file mode 100644
index 0000000..7135565
--- /dev/null
+++ b/gemrb/override/bg2/avatars.2da
@@ -0,0 +1,332 @@
+2DA V1.0
+*
+ AT_1 AT_2 AT_3 AT_4 TYPE SPACE PALETTE SIZE
+0x0100 SPCHUNKS SPCHUNKS SPCHUNKS SPCHUNKS 13 0 0 *
+0x0300 SPSMPUFF SPSMPUFF SPSMPUFF SPSMPUFF 13 0 0 *
+0x0400 SKLH SKLH SKLH SKLH 13 0 1 *
+0x0410 GLPHWRDH GLPHWRDH GLPHWRDH GLPHWRDH 13 0 1 *
+0x1000 MWYV MWYV MWYV MWYV 11 3 1 *
+0x1100 MTAN MTAN MTAN MTAN 11 3 1 *
+0x1200 MDR1 MDR1 MDR1 MDR1 12 7 1 *
+0x1201 MDR2 MDR2 MDR2 MDR2 12 7 1 *
+0x1202 MDR3 MDR3 MDR3 MDR3 12 7 1 *
+0x1203 MDR1 MDR1 MDR1 MDR1 12 7 GR *
+0x1204 MDR1 MDR1 MDR1 MDR1 12 7 AQ *
+0x1205 MDR1 MDR1 MDR1 MDR1 12 7 BL *
+0x1206 MDR1 MDR1 MDR1 MDR1 12 7 BR *
+0x1207 MDR1 MDR1 MDR1 MDR1 12 7 MC *
+0x1208 MDR1 MDR1 MDR1 MDR1 12 7 PU *
+0x2000 MSIR MSIR MSIR MSIR 2 2 0 *
+0x2100 UVOL UVOL UVOL UVOL 2 2 1 *
+0x2200 MOGM MOGM MOGM MOGM 2 2 0 S
+0x2300 MDKN MDKN MDKN MDKN 2 2 1 *
+0x3000 MAKH MAKH MAKH MAKH 2 3 1 *
+0x4000 SNOMC SNOMC SNOMC SNOMC 1 2 0 *
+0x4002 SNOMM SNOMM SNOMM SNOMM 1 2 0 *
+0x4010 SNOWC SNOWC SNOWC SNOWC 1 2 0 *
+0x4012 SNOWM SNOWM SNOWM SNOWM 1 2 0 *
+0x4100 SSIMC SSIMC SSIMC SSIMC 1 2 0 *
+0x4101 SSIMS SSIMS SSIMS SSIMS 1 2 0 *
+0x4102 SSIMM SSIMM SSIMM SSIMM 1 2 0 *
+0x4110 SSIWC SSIWC SSIWC SSIWC 1 2 0 *
+0x4112 SSIWM SSIWM SSIWM SSIWM 1 2 0 *
+0x4200 SHMCM SHMCM SHMCM SHMCM 1 2 0 *
+0x4300 MSPLG1 MSPLG1 MSPLG1 MSPLG1 1 2 1 *
+0x4400 LHMC LHMC LHMC LHMC 1 2 0 *
+0x4410 LHFC LHFC LHFC LHFC 1 2 0 *
+0x4500 LFAM LFAM LFAM LFAM 1 2 0 *
+0x4600 LDMF LDMF LDMF LDMF 1 2 0 *
+0x4700 LEMF LEMF LEMF LEMF 1 2 0 *
+0x4710 LEFF LEFF LEFF LEFF 1 2 0 *
+0x4800 LIMC LIMC LIMC LIMC 1 2 0 *
+0x5000 CHMB1 CHMB2 CHMB3 CHMC4 0 2 0 L
+0x5001 CEMB1 CEMB2 CEMB3 CEMC4 0 2 0 M
+0x5002 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x5003 CIMB1 CIMB2 CIMB3 CIMC4 0 2 0 S
+0x5010 CHFB1 CHFB2 CHFB3 CHFC4 0 2 0 N
+0x5011 CEFB1 CEFB2 CEFB3 CEFC4 0 2 0 M
+0x5012 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x5013 CIFB1 CIFB2 CIFB3 CIFC4 0 2 0 S
+0x5100 CHMB1 CHMB2 CHMB3 CHMF4 0 2 0 L
+0x5101 CEMB1 CEMB2 CEMB3 CHMF4 0 2 0 M
+0x5102 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x5103 CIMB1 CIMB2 CIMB3 CIMF4 0 2 0 S
+0x5110 CHFB1 CHFB2 CHFB3 CHFF4 0 2 0 N
+0x5111 CEFB1 CEFB2 CEFB3 CEFF4 0 2 0 M
+0x5112 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x5113 CIFB1 CIFB2 CIFB3 CIFF4 0 2 0 S
+0x5200 CHMW1 CHMW2 CHMW3 CHMW4 0 2 0 L
+0x5201 CEMW1 CEMW2 CEMW3 CEMW4 0 2 0 M
+0x5202 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x5203 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x5210 CHFW1 CHFW2 CHFW3 CHFW4 0 2 0 N
+0x5211 CEFW1 CEFW2 CEFW3 CEFW4 0 2 0 M
+0x5212 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x5213 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x5300 CHMB1 CHMT2 CHMB3 CHMF4 0 2 0 L
+0x5301 CEMB1 CEMT2 CEMB3 CEMF4 0 2 0 M
+0x5302 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x5303 CIMB1 CIMT2 CIMB3 CIMF4 0 2 0 S
+0x5310 CHFB1 CHFT2 CHFB3 CHFF4 0 2 0 N
+0x5311 CEFB1 CEFT2 CEFB3 CEFF4 0 2 0 M
+0x5312 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x5313 CIFB1 CIFT2 CIFB3 CIFF4 0 2 0 S
+0x6000 CHMB1 CHMB2 CHMB3 CHMC4 0 2 0 L
+0x6001 CEMB1 CEMB2 CEMB3 CEMC4 0 2 0 M
+0x6002 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x6003 CIMB1 CIMB2 CIMB3 CIMC4 0 2 0 S
+0x6004 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x6005 CHMB1 CHMB2 CHMB3 CHMC4 0 2 0 L
+0x6010 CHFB1 CHFB2 CHFB3 CHFC4 0 2 0 N
+0x6011 CEFB1 CEFB2 CEFB3 CEFC4 0 2 0 M
+0x6012 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x6013 CIFB1 CIFB2 CIFB3 CIFC4 0 2 0 S
+0x6014 CIFB1 CIFB2 CIFB3 CIFC4 0 2 0 S
+0x6015 CHFB1 CHFB2 CHFB3 CHFC4 0 2 0 N
+0x6100 CHMB1 CHMB2 CHMB3 CHMF4 0 2 0 L
+0x6101 CEMB1 CEMB2 CEMB3 CEMF4 0 2 0 M
+0x6102 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x6103 CIMB1 CIMB2 CIMB3 CIMF4 0 2 0 S
+0x6104 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x6105 CHMB1 CHMB2 CHMB3 CHMF4 0 2 0 L
+0x6110 CHFB1 CHFB2 CHFB3 CHFF4 0 2 0 N
+0x6111 CEFB1 CEFB2 CEFB3 CEFF4 0 2 0 M
+0x6112 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x6113 CIFB1 CIFB2 CIFB3 CIFF4 0 2 0 S
+0x6114 CIFB1 CIFB2 CIFB3 CIFF4 0 2 0 S
+0x6115 CHFB1 CHFB2 CHFB3 CHFF4 0 2 0 N
+0x6200 CHMW1 CHMW2 CHMW3 CHMW4 0 2 0 L
+0x6201 CEMW1 CEMW2 CEMW3 CEMW4 0 2 0 M
+0x6202 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6203 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6204 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6205 CHMW1 CHMW2 CHMW3 CHMW4 0 2 0 L
+0x6210 CHFW1 CHFW2 CHFW3 CHFW4 0 2 0 N
+0x6211 CEFW1 CEFW2 CEFW3 CEFW4 0 2 0 M
+0x6212 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6213 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6214 CDMW1 CIFW2 CIFW3 CIFW4 0 2 0 S
+0x6215 CHFW1 CHFW2 CHFW3 CHFW4 0 2 0 N
+0x6300 CHMB1 CHMT2 CHMB3 CHMF4 0 2 0 L
+0x6301 CEMB1 CEMT2 CEMB3 CEMF4 0 2 0 M
+0x6302 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x6303 CIMB1 CIMT2 CIMB3 CIMF4 0 2 0 S
+0x6304 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x6305 CHMB1 CHMT2 CHMB3 CHMF4 0 2 0 L
+0x6310 CHFB1 CHFT2 CHFB3 CHFF4 0 2 0 N
+0x6311 CEFB1 CEFT2 CEFB3 CEFF4 0 2 0 M
+0x6312 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x6313 CIFB1 CIFT2 CIFB3 CIFF4 0 2 0 S
+0x6314 CIFB1 CIFT2 CIFB3 CIFF4 0 2 0 S
+0x6315 CHFB1 CHFT2 CHFT3 CHFF4 0 2 0 N
+0x6400 UDRZ1 UDRZ1 UDRZ1 UDRZ1 6 2 0 *
+0x6401 UELM1 UELM1 UELM1 UELM1 6 2 1 *
+0x6402 CMNK1 CMNK1 CMNK1 CMNK1 6 2 0 M
+0x6403 MSKL1 MSKL1 MSKL1 MSKL1 6 2 1 M
+0x6404 USAR1 USAR1 USAR1 USAR1 6 2 1 *
+0x6405 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 M
+0x6406 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 L
+0x6500 CHMM1 CHMB2 CHMB3 CHMC4 0 2 0 L
+0x6510 CHFM1 CHFB2 CHFB3 CHFC4 0 2 0 N
+0x7000 MOGH MOGH MOGH MOGH 3 2 0 *
+0x7001 MOGN MOGN MOGN MOGN 3 2 0 *
+0x7100 MBAS MBAS MBAS MBAS 2 2 1 *
+0x7101 MBAS MBAS MBAS MBAS 2 2 GR *
+0x7200 MBER MBER MBER MBER 2 2 BL *
+0x7201 MBER MBER MBER MBER 2 2 1 *
+0x7202 MBER MBER MBER MBER 2 2 CA *
+0x7203 MBER MBER MBER MBER 2 2 PO *
+0x7300 MEAE MEAE MEAE MEAE 4 2 1 *
+0x7301 MEAS MEAS MEAS MEAS 4 2 1 *
+0x7302 MEAE MEAE MEAE MEAE 4 2 SH *
+0x7310 MFIE MFIE MFIE MFIE 4 2 1 *
+0x7311 MFIS MFIS MFIS MFIS 4 2 1 *
+0x7320 MAIR MAIR MAIR MAIR 4 2 1 *
+0x7321 MAIS MAIS MAIS MAIS 4 2 1 *
+0x7400 MDOG MDOG MDOG MDOG 3 2 WI *
+0x7401 MDOG MDOG MDOG MDOG 3 2 WA *
+0x7402 MDOG MDOG MDOG MDOG 3 2 MO *
+0x7500 MDOP MDOP MDOP MDOP 2 2 1 *
+0x7501 MDOP MDOP MDOP MDOP 2 2 GR *
+0x7600 METT METT METT METT 14 2 1 *
+0x7701 MGHL MGHL MGHL MGHL 14 2 1 *
+0x7702 MGHL MGHL MGHL MGHL 14 2 RE *
+0x7703 MGHL MGHL MGHL MGHL 14 2 GA *
+0x7704 MSHD MSHD MSHD MSHD 4 2 1 *
+0x7800 MGIB MGIB MGIB MGIB 14 2 1 *
+0x7900 MSLI MSLI MSLI MSLI 3 3 GR *
+0x7901 MSLI MSLI MSLI MSLI 3 3 OL *
+0x7902 MSLI MSLI MSLI MSLI 3 3 MU *
+0x7903 MSLI MSLI MSLI MSLI 3 3 OC *
+0x7904 MSLI MSLI MSLI MSLI 3 3 1 *
+0x7A00 MSPI MSPI MSPI MSPI 14 2 GI *
+0x7A01 MSPI MSPI MSPI MSPI 3 2 HU *
+0x7A02 MSPI MSPI MSPI MSPI 3 2 PH *
+0x7A03 MSPI MSPI MSPI MSPI 3 2 SW *
+0x7A04 MSPI MSPI MSPI MSPI 3 2 WR *
+0x7B00 MWLF MWLF MWLF MWLF 2 2 1 *
+0x7B01 MWLF MWLF MWLF MWLF 2 2 WO *
+0x7B02 MWLF MWLF MWLF MWLF 2 2 DI *
+0x7B03 MWLF MWLF MWLF MWLF 2 2 WI *
+0x7B04 MWLF MWLF MWLF MWLF 2 2 VA *
+0x7B05 MWLF MWLF MWLF MWLF 2 2 DR *
+0x7B06 MWLS MWLS MWLS MWLS 2 2 1 *
+0x7C00 MXVT MXVT MXVT MXVT 2 2 0 *
+0x7C01 MTAS MTAS MTAS MTAS 2 2 1 *
+0x7D00 MZOM MZOM MZOM MZOM 2 2 0 *
+0x7E00 MWER MWER MWER MWER 2 2 1 *
+0x7E01 MGWE MGWE MGWE MGWE 2 2 1 *
+0x7F00 MTRO MTRO MTRO MTRO 4 2 1 *
+0x7F01 MMIN MMIN MMIN MMIN 4 2 1 *
+0x7F02 MBEH MBEH MBEH MBEH 4 3 1 *
+0x7F03 MIMP MIMP MIMP MIMP 4 2 1 *
+0x7F04 MIGO MIGO MIGO MIGO 15 4 1 *
+0x7F05 MDJI MDJI MDJI MDJI 4 2 1 *
+0x7F06 MDJL MDJL MDJL MDJL 4 2 1 *
+0x7F07 MGLC MGLC MGLC MGLC 4 2 1 *
+0x7F08 MOTY MOTY MOTY MOTY 4 4 1 *
+0x7F09 MSAH MSAH MSAH MSAH 4 2 1 *
+0x7F0A MGCP MGCP MGCP MGCP 4 2 1 *
+0x7F0B MGCL MGCL MGCL MGCL 4 2 1 *
+0x7F0C MKUO MKUO MKUO MKUO 4 2 1 *
+0x7F0D MLIC MLIC MLIC MLIC 4 2 1 *
+0x7F0E MDLI MDLI MDLI MDLI 4 2 1 *
+0x7F0F MTRS MTRS MTRS MTRS 4 2 1 *
+0x7F10 MRAK MRAK MRAK MRAK 4 2 1 *
+0x7F11 MUMB MUMB MUMB MUMB 4 2 1 *
+0x7F12 MVAM MVAM MVAM MVAM 4 2 1 *
+0x7F13 MSNK MSNK MSNK MSNK 4 2 1 *
+0x7F14 MGIT MGIT MGIT MGIT 4 2 1 *
+0x7F15 MBES MBES MBES MBES 4 2 1 *
+0x7F16 AMOO AMOO AMOO AMOO 4 3 1 *
+0x7F17 ARAB ARAB ARAB ARAB 4 2 1 *
+0x7F18 ADER ADER ADER ADER 4 2 1 *
+0x7F19 MDSW MDSW MDSW MDSW 4 2 1 *
+0x7F20 AGRO AGRO AGRO AGRO 4 2 1 *
+0x7F21 APHE APHE APHE APHE 4 2 1 *
+0x7F22 MVAF MVAF MVAF MVAF 4 2 1 *
+0x7F23 MSAT MSAT MSAT MSAT 4 2 1 *
+0x7F24 NPIR NPIR NPIR NPIR 4 2 1 *
+0x7F27 MDRO MDRO MDRO MDRO 4 2 1 *
+0x7F28 MKUL MKUL MKUL MKUL 15 2 1 *
+0x7F29 MFDR MFDR MFDR MFDR 4 2 1 *
+0x7F2A NSAI NSAI NSAI NSAI 4 2 1 *
+0x7F2B MMAX MMAX MMAX MMAX 4 2 1 *
+0x7F2C NSOL NSOL NSOL NSOL 4 2 1 *
+0x7F2D MWFM MWFM MWFM MWFM 4 2 1 *
+0x7F2E MRAV MRAV MRAV MRAV 4 3 1 *
+0x7F2F MSPS MSPS MSPS MSPS 15 2 1 *
+0x7F30 NBOH NBOH NBOH NBOH 4 2 1 *
+0x7F31 NELL NELL NELL NELL 4 2 1 *
+0x7F32 MSLY MSLY MSLY MSLY 4 2 1 *
+0x7F33 MKUR MKUR MKUR MKUR 4 2 1 *
+0x7F34 MDOC MDOC MDOC MDOC 4 2 1 *
+0x7F35 MMIS MMIS MMIS MMIS 4 2 1 *
+0x7F36 NSHD NSHD NSHD NSHD 4 2 1 *
+0x7F37 NIRE NIRE NIRE NIRE 4 2 1 *
+0x7F38 MEYE MEYE MEYE MEYE 2 1 1 *
+0x7F39 MMSTG1 MMSTG2 * * 57 2 1 *
+0x7F3A NIRO NIRO NIRO NIRO 4 2 1 *
+0x7F3B MSOL MSOL MSOL MSOL 4 2 1 *
+0x7F3C MASL MASL MASL MASL 4 2 1 *
+0x7F3D MMEL MMEL MMEL MMEL 4 2 1 *
+0x7F3E MFIG MFIG MFIG MFIG 4 3 1 *
+0x8000 MGNL MGNL MGNL MGNL 2 2 0 *
+0x8100 MHOB MHOB MHOB MHOB 2 2 0 *
+0x8200 MKOB MKOB MKOB MKOB 2 2 0 *
+0x9000 MOGR MOGR MOGR MOGR 5 2 0 *
+0xA000 MWYV MWYV MWYV MWYV 8 3 1 *
+0xA100 MCAR MCAR MCAR MCAR 8 3 1 *
+0xB000 ACOW ACOW ACOW ACOW 10 3 1 *
+0xB100 AHRS AHRS AHRS AHRS 10 3 1 *
+0xB200 NBEGL NBEGL NBEGL NBEGL 3 2 0 *
+0xB210 NPROL NPROL NPROL NPROL 3 2 0 *
+0xB300 NBOYL NBOYL NBOYL NBOYL 3 2 0 *
+0xB310 NGRLL NGRLL NGRLL NGRLL 3 2 0 *
+0xB400 NFAML NFAML NFAML NFAML 3 2 0 *
+0xB410 NFAWL NFAWL NFAWL NFAWL 3 2 0 *
+0xB500 NSIML NSIML NSIML NSIML 3 2 0 *
+0xB510 NSIWL NSIWL NSIWL NSIWL 3 2 0 *
+0xB600 NNOML NNOML NNOML NNOML 3 2 0 *
+0xB610 NNOWL NNOWL NNOWL NNOWL 3 2 0 *
+0xB700 NSLVL NSLVL NSLVL NSLVL 3 2 0 *
+0xC000 ABAT ABAT ABAT ABAT 3 1 1 *
+0xC100 ACAT ACAT ACAT ACAT 3 1 1 *
+0xC200 ACHK ACHK ACHK ACHK 3 1 1 *
+0xC300 ARAT ARAT ARAT ARAT 3 1 1 *
+0xC400 ASQU ASQU ASQU ASQU 3 1 1 *
+0xC500 ABAT ABAT ABAT ABAT 3 1 1 *
+0xC600 NBEGH NBEGH NBEGH NBEGH 3 2 0 *
+0xC610 NPROH NPROH NPROH NPROH 3 2 0 *
+0xC700 NBOYH NBOYH NBOYH NBOYH 3 2 0 *
+0xC710 NGRLH NGRLH NGRLH NGRLH 3 2 0 *
+0xC800 NFAMH NFAMH NFAMH NFAMH 3 2 0 *
+0xC810 NFAWH NFAWH NFAWH NFAWH 3 2 0 *
+0xC900 NSIMH NSIMH NSIMH NSIMH 3 2 0 *
+0xC910 NSIWH NSIWH NSIWH NSIWH 3 2 0 *
+0xCA00 NNOMH NNOMH NNOMH NNOMH 3 2 0 *
+0xCA10 NNOWH NNOWH NNOWH NNOWH 3 2 0 *
+0xCB00 NSLVH NSLVH NSLVH NSLVH 3 2 0 *
+0xD000 AEAGG1 AEAGG1 AEAGG1 AEAGG1 7 0 1 *
+0xD100 AGULG1 AGULG1 AGULG1 AGULG1 7 0 1 *
+0xD200 AVULG1 AVULG1 AVULG1 AVULG1 7 0 1 *
+0xD300 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xD400 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xE000 MCYC MCYC MCYC MCYC 9 4 1 *
+0xE010 METN METN METN METN 9 4 1 *
+0xE020 MTAN MTAN MTAN MTAN 9 4 1 *
+0xE040 MHIS MHIS MHIS MHIS 9 2 1 *
+0xE050 MLER MLER MLER MLER 9 2 1 *
+0xE060 MLIC MLIC MLIC MLIC 9 2 1 *
+0xE070 MMIN MMIN MMIN MMIN 9 2 1 *
+0xE080 MMUM MMUM MMUM MMUM 9 2 1 *
+0xE090 MMER MMER MMER MMER 9 2 1 *
+0xE0A0 MTIC MTIC MTIC MTIC 9 2 1 *
+0xE0B0 MTRO MTRO MTRO MTRO 9 2 1 *
+0xE0C0 MTSN MTSN MTSN MTSN 9 2 1 *
+0xE0D0 MUMB MUMB MUMB MUMB 9 2 1 *
+0xE0E0 MCOR MCOR MCOR MCOR 9 2 1 *
+0xE0F0 MGIC MGIC MGIC MGIC 9 2 1 *
+0xE0F1 MGLA MGLA MGLA MGLA 9 2 1 *
+0xE0F2 MWAV MWAV MWAV MWAV 9 2 1 *
+0xE200 MBET MBET MBET MBET 9 2 1 *
+0xE210 MBFI MBFI MBFI MBFI 9 2 1 *
+0xE220 MBBM MBBM MBBM MBBM 9 2 1 *
+0xE230 MBRH MBRH MBRH MBRH 9 5 1 *
+0xE300 MGHO MGHO MGHO MGHO 9 2 1 *
+0xE310 MGH2 MGH2 MGH2 MGH2 9 2 1 *
+0xE320 MGH3 MGH3 MGH3 MGH3 9 2 1 *
+0xE400 MGO1 MGO1 MGO1 MGO1 9 2 1 *
+0xE410 MGO2 MGO2 MGO2 MGO2 9 2 1 *
+0xE420 MGO3 MGO3 MGO3 MGO3 9 2 1 *
+0xE430 MGO4 MGO4 MGO4 MGO4 9 2 1 *
+0xE500 MLIZ MLIZ MLIZ MLIZ 9 2 1 *
+0xE510 MLI2 MLI2 MLI2 MLI2 9 2 1 *
+0xE520 MLI3 MLI3 MLI3 MLI3 9 2 1 *
+0xE600 MMYC MMYC MMYC MMYC 9 2 1 *
+0xE610 MMY2 MMY2 MMY2 MMY2 9 2 1 *
+0xE700 MNO1 MNO1 MNO1 MNO1 9 2 1 *
+0xE710 MNO2 MNO2 MNO2 MNO2 9 2 1 *
+0xE720 MNO3 MNO3 MNO3 MNO3 9 2 1 *
+0xE800 MOR1 MOR1 MOR1 MOR1 9 2 1 *
+0xE810 MOR2 MOR2 MOR2 MOR2 9 2 1 *
+0xE820 MOR3 MOR3 MOR3 MOR3 9 2 1 *
+0xE830 MOR4 MOR4 MOR4 MOR4 9 2 1 *
+0xE840 MOR5 MOR5 MOR5 MOR5 9 2 1 *
+0xE900 MSAL MSAL MSAL MSAL 9 2 1 *
+0xE910 MSA2 MSA2 MSA2 MSA2 9 2 1 *
+0xEA00 MSHR MSHR MSHR MSHR 9 2 1 *
+0xEA10 MSH1 MSH1 MSH1 MSH1 9 2 1 *
+0xEA20 MSH2 MSH2 MSH2 MSH2 9 2 1 *
+0xEB00 MSKT MSKT MSKT MSKT 9 2 1 *
+0xEB10 MSKA MSKA MSKA MSKA 9 2 1 *
+0xEB20 MSKB MSKB MSKB MSKB 9 2 1 *
+0xEC00 MWIG MWIG MWIG MWIG 9 2 1 *
+0xEC10 MWI2 MWI2 MWI2 MWI2 9 2 1 *
+0xEC20 MWI3 MWI3 MWI3 MWI3 9 2 1 *
+0xED00 MYU1 MYU1 MYU1 MYU1 9 2 1 *
+0xED10 MYU2 MYU2 MYU2 MYU2 9 2 1 *
+0xED20 MYU3 MYU3 MYU3 MYU3 9 2 1 *
+0xEE00 MZO2 MZO2 MZO2 MZO2 9 2 1 *
+0xEE10 MZO3 MZO3 MZO3 MZO3 9 2 1 *
+0xEF00 MWWE MWWE MWWE MWWE 9 2 1 *
diff --git a/gemrb/override/bg2/avprefc.2da b/gemrb/override/bg2/avprefc.2da
new file mode 100644
index 0000000..d674f23
--- /dev/null
+++ b/gemrb/override/bg2/avprefc.2da
@@ -0,0 +1,25 @@
+2DA V1.0
+*
+ CLASS
+TYPE 232
+MAGE 0x200
+FIGHTER 0x100
+CLERIC 0
+THIEF 0x300
+BARD 0x300
+PALADIN 0x100
+FIGHTER_MAGE 0x100
+FIGHTER_CLERIC 0x100
+FIGHTER_THIEF 0x100
+FIGHTER_MAGE_THIEF 0x100
+DRUID 0
+RANGER 0x100
+MAGE_THIEF 0x300
+CLERIC_MAGE 0
+CLERIC_THIEF 0
+FIGHTER_DRUID 0x100
+FIGHTER_MAGE_CLERIC 0x100
+CLERIC_RANGER 0
+SORCERER 0x200
+MONK 0x500
+BARBARIAN 0x100
diff --git a/gemrb/override/bg2/avprefr.2da b/gemrb/override/bg2/avprefr.2da
new file mode 100644
index 0000000..53ea155
--- /dev/null
+++ b/gemrb/override/bg2/avprefr.2da
@@ -0,0 +1,11 @@
+2DA V1.0
+*
+ RACE
+TYPE 231
+HUMAN 0
+ELF 1
+HALF_ELF 1
+DWARF 2
+HALFLING 3
+GNOME 4
+HALFORC 5
diff --git a/gemrb/override/bg2/bloodclr.2da b/gemrb/override/bg2/bloodclr.2da
new file mode 100644
index 0000000..1b33a30
--- /dev/null
+++ b/gemrb/override/bg2/bloodclr.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+0
+ VALUE MIN MAX
+NORMAL 0x2f 0x1000 0xffff
+PURPLE 0x2d 0x4300 0x43ff
+MSKEL 0x25 0x5403 0x5403
+DOOMG 0x22 0x5405 0x5406
+MSKEL 0x25 0x6403 0x6403
+DOOMG 0x22 0x6405 0x6406
+YELLOW 0x32 0x7300 0x73ff
+PURPLE 0x3c 0x7500 0x75ff
+YELLOW 0x32 0x7600 0x76ff
+BLACK 0x66 0x7700 0x77ff
+SLI_GR 7 0x7900 0x7900
+SLI_OL 0x24 0x7901 0x7901
+SLI_MU 0x33 0x7902 0x7902
+SLI_OC 0x25 0x7903 0x7903
+SLIME 0x1b 0x7904 0x7904
+SPIDER 0x33 0x7a00 0x7aff
+ZOMBIE 0x25 0x7c00 0x7cff
+MTRO 0x33 0x7f00 0x7f00
+MMIN 0x3d 0x7f01 0x7f03
+MIGO 0x1a 0x7f04 0x7f04
+MGLC 0x5d 0x7f07 0x7f07
+MTRS 0x32 0x7f0f 0x7f0f
+MBES 0x3d 0x7f15 0x7f15
+MCAR 0x38 0xa100 0xa1ff
+UNDEAD 0x66 0xe300 0xe3ff
+MWWE 0x38 0xef00 0xefff
diff --git a/gemrb/override/bg2/cgtable.2da b/gemrb/override/bg2/cgtable.2da
new file mode 100644
index 0000000..e8fad39
--- /dev/null
+++ b/gemrb/override/bg2/cgtable.2da
@@ -0,0 +1,38 @@
+2DA V1.0
+*
+ RESREF SOUND
+UNUSED0 * *
+UNUSED1 * *
+UNUSED2 * *
+UNUSED3 * *
+UNUSED4 * *
+UNUSED5 * *
+UNUSED6 * *
+UNUSED7 * *
+UNUSED8 * *
+NECROMANCY CGNecrom 07
+ALTERATION CGAltera 08
+ENCHANTMENT CGEnchan 02
+ABJURATION CGAbjura 05
+ILLUSION CGIllusi 01
+CONJURATION CGConjur 03
+INVOCATION CGInvoca 06
+DIVINATION CGDivina 04
+SPARKBLUE *46 05
+SPARKBLACK *51 05
+SPARKBLUE2 *46 05
+SPARKGOLD *47 05
+SPARKGREEN *54 05
+SPARKCHROM *52 05
+SPARKPURP *48 05
+SPARKRED *53 05
+SPARKWHITE *49 05
+SPARKBLUE3 *46 05
+SPARKBLACK2 *51 05
+SPARKBLUE4 *46 05
+SPARKGOLD2 *47 05
+SPARKGREEN2 *54 05
+SPARKCHROM2 *52 05
+SPARKPURP2 *48 05
+SPARKRED2 *53 05
+SPARKWHITE2 *49 05
diff --git a/gemrb/override/bg2/classes.2da b/gemrb/override/bg2/classes.2da
new file mode 100644
index 0000000..32e62fa
--- /dev/null
+++ b/gemrb/override/bg2/classes.2da
@@ -0,0 +1,24 @@
+2DA V1.0
+*
+ NAME_REF DESC_REF CAP_REF SAVE MULTI ID HP USABILITY MC_WAS_ID HUMAN ELF HALF_ELF DWARF HALFLING GNOME HALFORC
+FIGHTER 7201 9556 1076 SAVEWAR 0 2 HPWAR 0x800 0x0008 1 1 1 1 1 1 1
+RANGER 7200 9557 1077 SAVEWAR 0 12 HPWAR 0x200000 0x0100 1 1 1 0 0 0 0
+PALADIN 7217 9558 1078 SAVEWAR 0 6 HPWAR 0x100000 -1 1 0 1 0 0 0 0
+CLERIC 7204 9559 1079 SAVEPRS 0 3 HPPRS 128 0x0020 1 1 1 1 1 1 1
+DRUID 7210 9560 1080 SAVEPRS 0 11 HPPRS 0x40000000 0x0080 1 1 1 1 0 0 0
+MAGE 7203 9563 1081 SAVEWIZ 0 1 HPWIZ 0x40000 0x0010 1 1 1 0 0 2 0
+THIEF 7202 9561 1082 SAVEROG 0 4 HPROG 0x400000 0x0040 1 1 1 1 1 1 1
+BARD 7206 9562 1083 SAVEROG 0 5 HPROG 64 -1 1 1 1 1 1 1 1
+FIGHTER_THIEF 7205 9572 1052 * 10 9 * 0x20000 -1 0 1 1 1 1 1 1
+FIGHTER_CLERIC 7211 9573 1053 * 6 8 * 0x4000 -1 0 1 1 1 1 1 1
+FIGHTER_MAGE 7213 9574 1056 * 3 7 * 0x2000 -1 0 1 1 0 0 2 0
+MAGE_THIEF 7216 9575 1057 * 9 13 * 0x80000 -1 0 1 1 0 0 2 0
+CLERIC_MAGE 7207 9577 1058 * 5 14 * 256 -1 0 1 1 0 0 2 0
+CLERIC_THIEF 7209 9578 1065 * 12 15 * 512 -1 0 1 1 1 1 1 1
+FIGHTER_DRUID 7212 9579 1066 * 1026 16 * 0x1000 -1 0 1 1 0 0 0 0
+CLERIC_RANGER 7208 9580 1073 * 2052 18 * 1024 -1 0 1 1 0 0 0 0
+FIGHTER_MAGE_THIEF 7215 9576 1074 * 11 10 * 0x10000 -1 0 1 1 0 0 0 0
+FIGHTER_MAGE_CLERIC 7214 9581 1075 * 7 17 * 0x8000 -1 0 1 1 0 0 0 0
+SORCERER 45849 45866 45856 SAVEWIZ 0 19 HPWIZ 0x40000 -1 1 1 1 0 0 0 0
+MONK 45851 45867 45858 SAVEMONK 0 20 HPMONK 0x20000000 -1 1 0 0 0 0 0 0
+BARBARIAN 45855 45869 45859 SAVEWAR 0 2 HPBARB 0 -1 1 1 1 1 1 1 1
diff --git a/gemrb/override/bg2/clowncol.2da b/gemrb/override/bg2/clowncol.2da
new file mode 100644
index 0000000..59103ae
--- /dev/null
+++ b/gemrb/override/bg2/clowncol.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+*
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
+HAIR 0 1 2 3 4 5 6 7 79 80 81 82 110 111 * * * * * * * * * * * * * * * * * * * *
+SKIN 8 9 10 11 12 13 14 15 16 17 18 19 20 83 84 85 86 87 88 89 90 105 106 107 108 109 112 113 114 * * * * *
+MAJOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 21 22 23
+MINOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 21 22 23
diff --git a/gemrb/override/bg2/clskills.2da b/gemrb/override/bg2/clskills.2da
new file mode 100644
index 0000000..0141e1b
--- /dev/null
+++ b/gemrb/override/bg2/clskills.2da
@@ -0,0 +1,24 @@
+2DA V1.0
+*
+ DRUIDSPELL CLERICSPELL MAGESPELL STARTXP BARDSKILL THIEFSKILL LAYHANDS TURNLEVEL BOOKTYPE HATERACE ABILITIES STARTXP2 RANGERSKILL SAVEBONUS
+UNUSED * * * * * * * 0 0 * * * * *
+MAGE * * MXSPLWIZ 89000 * * * 0 1 * * 2500000 * 0
+FIGHTER * * * 89000 * * * 0 0 * CLABFI01 2500000 * 0
+CLERIC * MXSPLPRS * 89000 * * * 1 1 * CLABPR01 2500000 * 0
+THIEF * * * 89000 * SKILLS * 0 0 * CLABTH01 2500000 * 0
+BARD * * MXSPLBRD 89000 SKILLBRD * * 0 1 * CLABBA01 2500000 * 0
+PALADIN * MXSPLPAL * 89000 * * PALADIN 5 1 * CLABPA01 2500000 * 2
+FIGHTER_MAGE * * MXSPLWIZ 89000 * * * 0 1 * CLABFI01 2500000 * 0
+FIGHTER_CLERIC * MXSPLPRS * 89000 * * * 1 1 * FI01,PR01 2500000 * 0
+FIGHTER_THIEF * * * 89000 * SKILLS * 0 0 * FI01,TH01 2500000 * 0
+FIGHTER_MAGE_THIEF * * MXSPLWIZ 89000 * SKILLS * 0 1 * FI01,TH01 2500000 * 0
+DRUID MXSPLDRU * * 89000 * * * 0 1 * CLABDR01 2500000 * 0
+RANGER MXSPLRAN * * 89000 * * * 0 1 HATERACE CLABRN01 2500000 SKILLRNG 0
+MAGE_THIEF * * MXSPLWIZ 89000 * SKILLS * 0 1 * CLABTH01 2500000 * 0
+CLERIC_MAGE * MXSPLPRS MXSPLWIZ 89000 * * * 1 1 * CLABPR01 2500000 * 0
+CLERIC_THIEF * MXSPLPRS * 89000 * SKILLS * 1 1 * PR01,TH01 2500000 * 0
+FIGHTER_DRUID MXSPLDRU * * 89000 * * * 0 1 * FI01,DR01 2500000 * 0
+FIGHTER_MAGE_CLERIC * MXSPLPRS MXSPLWIZ 89000 * * * 1 1 * FI01,PR01 2500000 * 0
+CLERIC_RANGER MXSPLRAN MXSPLPRS * 89000 * * * 1 1 HATERACE PR01,RN01 2500000 SKILLRNG 0
+SORCERER * * MXSPLSRC 89000 * * * 0 2 * * 2500000 * 0
+MONK * * * 89000 * SKILLS * 0 0 * CLABMO01 2500000 * 0
diff --git a/gemrb/override/bg2/colrspry.pro b/gemrb/override/bg2/colrspry.pro
new file mode 100644
index 0000000..974c05f
Binary files /dev/null and b/gemrb/override/bg2/colrspry.pro differ
diff --git a/gemrb/override/bg2/comet.pro b/gemrb/override/bg2/comet.pro
new file mode 100644
index 0000000..33d58a1
Binary files /dev/null and b/gemrb/override/bg2/comet.pro differ
diff --git a/gemrb/override/bg2/damage.2da b/gemrb/override/bg2/damage.2da
new file mode 100644
index 0000000..179722a
--- /dev/null
+++ b/gemrb/override/bg2/damage.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+*
+ MAIN SPARKS GRADIENT
+CRIT BLOODCR * 47
+SMALL BLOODS * 47
+MEDIUM BLOODM * 47
+LARGE BLOODL * 47
+FIRES SPFIRIMP SPBURN 19
+FIREM SPFIRIMP SPBURN 19
+FIREL SPFIRIMP SPBURN 19
+SPARKS SPSHKIMP SPSPARKS -1
+SPARKM SPSHKIMP SPSPARKS -1
+SPARKL SPSHKIMP SPSPARKS -1
+ICES FIRIMP * 71
+ICEM FIRIMP * 71
+ICEL FIRIMP * 71
diff --git a/gemrb/override/bg2/defsound.2da b/gemrb/override/bg2/defsound.2da
new file mode 100644
index 0000000..452610d
--- /dev/null
+++ b/gemrb/override/bg2/defsound.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+*
+ RESREF
+OPEN AMB_D03A
+CLOSE AMB_D03B
+HOPEN AMB_D04A
+HCLOSE AMB_D04B
+BUTTON1 GAM_09
+BUTTON2 GAM_03
+BUTTON3 GAM_04
+OPENFAIL AMB_D20
+CLOSEFAIL *
+ITEM_GONE EFF_M02
+SECRET ACT_09
+11 *
+12 *
+13 *
+14 *
+15 *
+16 *
+17 *
+18 *
+19 *
+LIGHTNING1 AMB_E13A
+LIGHTNING2 AMB_E13B
+LIGHTNING3 AMB_E13F
+RAIN AMB_E11
+SNOW AMB_E02B
+
diff --git a/gemrb/override/bg2/denyspl.2da b/gemrb/override/bg2/denyspl.2da
new file mode 100644
index 0000000..fec5d60
--- /dev/null
+++ b/gemrb/override/bg2/denyspl.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+-1
+ AR3020 AR6200 AR4500
+SPIN649 0x11C39 0x11564 0x11564
diff --git a/gemrb/override/bg2/effects.ids b/gemrb/override/bg2/effects.ids
new file mode 100644
index 0000000..403aa9c
--- /dev/null
+++ b/gemrb/override/bg2/effects.ids
@@ -0,0 +1,320 @@
+IDS
+0x0 ACVsDamageTypeModifier
+0x1 AttacksPerRoundModifier
+0x2 Cure:Sleep
+0x3 State:Berserk
+0x4 Cure:Berserk
+0x5 State:Charmed
+0x6 CharismaModifier
+0x7 Color:SetPalette
+0x8 Color:SetRGB
+0x9 Color:PulseRGB
+0xa ConstitutionModifier
+0xb Cure:Poison
+0xc Damage
+0xd Death
+0xe Cure:Defrost
+0xf DexterityModifier
+0x10 State:Hasted
+0x11 CurrentHPModifier
+0x12 MaximumHPModifier
+0x13 IntelligenceModifier
+0x14 State:Invisible
+0x15 LoreModifier
+0x16 LuckModifier
+0x17 MoraleModifier
+0x18 State:Panic
+0x19 State:Poisoned
+0x1a RemoveCurse
+0x1b AcidResistanceModifier
+0x1c ColdResistanceModifier
+0x1d ElectricityResistanceModifier
+0x1e FireResistanceModifier
+0x1f MagicDamageResistanceModifier
+0x20 Cure:Death
+0x21 SaveVsDeathModifier
+0x22 SaveVsWandsModifier
+0x23 SaveVsPolyModifier
+0x24 SaveVsBreathModifier
+0x25 SaveVsSpellsModifier
+0x26 State:Silenced
+0x27 State:Helpless
+0x28 State:Slowed
+0x29 Sparkle
+0x2a WizardSpellSlotsModifier
+0x2b Cure:Petrification
+0x2c StrengthModifier
+0x2d State:Stun
+0x2e Cure:Stun
+0x2f Cure:Invisible
+0x30 Cure:Silence
+0x31 WisdomModifier
+0x32 Color:BriefRGB
+0x33 Color:DarkenRGB
+0x34 Color:GlowRGB
+0x35 AnimationIDModifier
+0x36 ToHitModifier
+0x37 KillCreatureType
+0x38 Alignment:Invert
+0x39 Alignment:Change
+0x3a DispelEffects
+0x3b StealthModifier
+0x3c MiscastMagicModifier
+0x3d AlchemyModifier
+0x3e PriestSpellSlotsModifier
+0x3f State:Infravision
+0x40 Cure:Infravision
+0x41 State:Blur
+0x42 TransparencyModifier
+0x43 SummonCreature
+0x44 UnsummonCreature
+0x45 State:NonDetection
+0x46 Cure:NonDetection
+0x47 SexModifier
+0x48 AIIdentifierModifier
+0x49 DamageBonusModifier
+0x4a State:Blind
+0x4b Cure:Blind
+0x4c State:Feeblemind
+0x4d Cure:Feeblemind
+0x4e State:Diseased
+0x4f Cure:Disease
+0x50 State:Deafness
+0x51 Cure:Deafness
+0x52 SetAIScript
+0x53 Protection:Projectile
+0x54 MagicalFireResistanceModifier
+0x55 MagicalColdResistanceModifier
+0x56 SlashingResistanceModifier
+0x57 CrushingResistanceModifier
+0x58 PiercingResistanceModifier
+0x59 MissilesResistanceModifier
+0x5a OpenLocksModifier
+0x5b FindTrapsModifier
+0x5c PickPocketsModifier
+0x5d FatigueModifier
+0x5e IntoxicationModifier
+0x5f TrackingModifier
+0x60 LevelModifier
+0x61 StrengthBonusModifier
+0x62 State:Regenerating
+0x63 SpellDurationModifier
+0x64 Protection:Creature
+0x65 Protection:Opcode
+0x66 Protection:SpellLevel
+0x67 ChangeName
+0x68 ExperienceModifier
+0x69 GoldModifier
+0x6a MoraleBreakModifier
+0x6b PortraitChange
+0x6c ReputationModifier
+0x6d State:HoldNoIcon
+0x6e RetreatFrom
+0x6f Item:CreateMagic
+0x70 Item:Remove
+0x71 Item:Equip
+0x72 Dither
+0x73 DetectAlignment
+0x74 Cure:Invisible2
+0x75 Reveal:Area
+0x76 Reveal:Creatures
+0x77 MirrorImage
+0x78 Protection:Weapons
+0x79 VisualAnimationEffect
+0x7a Item:CreateInventory
+0x7b Item:RemoveInventory
+0x7c DimensionDoor
+0x7d Unlock
+0x7e MovementRateModifier
+0x7f MonsterSummoning
+0x80 State:Confused
+0x81 AidNonCumulative
+0x82 BlessNonCumulative
+0x83 ChantNonCumulative
+0x84 HolyNonCumulative
+0x85 LuckNonCumulative
+0x86 State:Petrification
+0x87 Polymorph
+0x88 ForceVisible
+0x89 ChantBadNonCumulative
+0x8a AnimationStateChange
+0x8b DisplayString
+0x8c CastingGlow
+0x8d VisualSpellHit
+0x8e Icon:Display
+0x8f Item:CreateInSlot
+0x90 DisableButton
+0x91 DisableCasting
+0x92 Spell:Cast
+0x93 Spell:Learn
+0x94 Spell:CastPoint
+0x95 Identify
+0x96 FindTraps
+0x97 ReplaceCreature
+0x98 PlayMovie
+0x99 Overlay:Sanctuary
+0x9a Overlay:Entangle
+0x9b Overlay:MinorGlobe
+0x9c Overlay:ShieldGlobe
+0x9d Overlay:Web
+0x9e Overlay:Grease
+0x9f MirrorImageModifier
+0xa0 Cure:Sanctuary
+0xa1 Cure:Panic
+0xa2 Cure:Hold
+0xa3 FreeAction
+0xa4 Cure:Intoxication
+0xa5 PauseTarget
+0xa6 MagicResistanceModifier
+0xa7 MissileHitModifier
+0xa8 RemoveCreature
+0xa9 Icon:Disable
+0xaa DamageAnimation
+0xab Spell:Add
+0xac Spell:Remove
+0xad PoisonResistanceModifier
+0xae PlaySound
+0xaf State:Hold
+0xb0 MovementRateModifier2
+0xb1 ApplyEffect
+0xb2 ToHitVsCreature
+0xb3 DamageVsCreature
+0xb4 CantUseItem
+0xb5 CantUseItemType
+0xb6 ApplyEffectItem
+0xb7 ApplyEffectItemType
+0xb8 DontJumpModifier
+0xb9 State:Hold2
+0xba MoveToArea
+0xbb Variable:StoreLocalVariable
+0xbc AuraCleansingModifier
+0xbd CastingSpeedModifier
+0xbe AttackSpeedModifier
+0xbf CastingLevelModifier
+0xc0 FindFamiliar
+0xc1 InvisibleDetection
+0xc2 IgnoreDialogPause
+0xc3 FamiliarBond
+0xc4 FamiliarMarker
+0xc5 Bounce:Projectile
+0xc6 Bounce:Opcode
+0xc7 Bounce:SpellLevel
+0xc8 Bounce:SpellLevelDec
+0xc9 Protection:SpellLevelDec
+0xca Bounce:School
+0xcb Bounce:SecondaryType
+0xcc Protection:School
+0xcd Protection:SecondaryType
+0xce Protection:Spell
+0xcf Bounce:Spell
+0xd0 MinimumHPModifier
+0xd1 PowerWordKill
+0xd2 PowerWordStun
+0xd3 State:Imprisonment
+0xd4 Cure:Imprisonment
+0xd5 Maze
+0xd6 CastFromList
+0xd7 PlayVisualEffect
+0xd8 LevelDrainModifier
+0xd9 PowerWordSleep
+0xda StoneskinModifier
+0xdb ACVsCreatureType
+0xdc DispelSchool
+0xdd DispelSecondaryType
+0xde RandomTeleport
+0xdf Protection:SchoolDec
+0xe0 Cure:LevelDrain
+0xe1 Reveal:Magic
+0xe2 Protection:SecondaryTypeDec
+0xe3 Bounce:SchoolDec
+0xe4 Bounce:SecondaryTypeDec
+0xe5 DispelSchoolOne
+0xe6 DispelSecondaryTypeOne
+0xe7 TimeStop
+0xe8 CastSpellOnCondition
+0xe9 Proficiency
+0xea CreateContingency
+0xeb WingBuffet
+0xec ProjectImage
+0xed PuppetMarker
+0xee Disintegrate
+0xef Farsee
+0xf0 Icon:Remove
+0xf1 ControlCreature
+0xf2 Cure:Confusion
+0xf3 DrainItems
+0xf4 DrainSpells
+0xf5 CheckForBerserkModifier
+0xf6 BerserkStage1Modifier
+0xf7 BerserkStage2Modifier
+0xf8 SetMeleeEffect
+0xf9 SetRangedEffect
+0xfa DamageLuckModifier
+0xfb ChangeBardSong
+0xfc SetTrap
+0xfd SetMapNote
+0xfe RemoveMapNote
+0xff Item:CreateDays
+0x100 Sequencer:Store
+0x101 Sequencer:Create
+0x102 Sequencer:Activate
+0x103 SpellTrap
+0x104 *Crash*
+0x105 RestoreSpells
+0x106 VisualRangeModifier
+0x107 BackstabModifier
+0x108 DropWeapon
+0x109 ModifyGlobalVariable
+0x10a RemoveImmunity
+0x10b Protection:String
+0x10c ExploreModifier
+0x10d ScreenShake
+0x10e Cure:CasterHold
+0x10f SummonDisable
+0x110 ApplyEffectRepeat
+0x111 RemoveProjectile
+0x112 TeleportToTarget
+0x113 HideInShadowsModifier
+0x114 DetectIllusionsModifier
+0x115 SetTrapsModifier
+0x116 ToHitBonusModifier
+0x117 RenableButton
+0x118 ForceSurgeModifier
+0x119 WildSurgeModifier
+0x11a ScriptingState
+0x11b ApplyEffectCurse
+0x11c MeleeHitModifier
+0x11d MeleeDamageModifier
+0x11e MissileDamageModifier
+0x11f NoCircleState
+0x120 FistHitModifier
+0x121 FistDamageModifier
+0x122 TitleModifier
+0x123 DisableOverlay
+0x124 Protection:Backstab
+0x125 OffscreenAIModifier
+0x126 ExistanceDelayModifier
+0x127 DisableChunk
+0x128 Protection:Animation
+0x129 Protection:Turn
+0x12a CutScene2
+0x12b ChaosShieldModifier
+0x12c NPCBump
+0x12d CriticalHitModifier
+0x12e CanUseAnyItem
+0x12f AlwaysBackstab
+0x130 MassRaiseDead
+0x131 OffhandHitModifier
+0x132 RightHitModifier
+0x133 Reveal:Tracks
+0x134 Protection:Tracking
+0x135 ModifyLocalVariable
+0x136 TimelessState
+0x137 GenerateWish
+0x138 *Crash*
+0x139 HLA
+0x13a StoneSkin2Modifier
+0x13b AvatarRemovalModifier
+0x13c MagicalRest
+0x13d State:Haste2
+0x13e ChangeWeather
diff --git a/gemrb/override/bg2/efftext.2da b/gemrb/override/bg2/efftext.2da
new file mode 100644
index 0000000..78829b0
--- /dev/null
+++ b/gemrb/override/bg2/efftext.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+-1
+ EFFECT_NAME STRREF
+25 POISON 14017
+13 DEATH 14026
+134 PETRIFICATION 14127
+#12 DAMAGE 14027
+39 SLEEP 20438
+68 UNSUMMON 14065
+
diff --git a/gemrb/override/bg2/findtrap.spl b/gemrb/override/bg2/findtrap.spl
new file mode 100644
index 0000000..3413deb
Binary files /dev/null and b/gemrb/override/bg2/findtrap.spl differ
diff --git a/gemrb/override/bg2/fistweap.2da b/gemrb/override/bg2/fistweap.2da
new file mode 100644
index 0000000..ccc20c4
--- /dev/null
+++ b/gemrb/override/bg2/fistweap.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+FIST
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
+20 MFIST1 MFIST1 MFIST2 MFIST2 MFIST2 MFIST3 MFIST3 MFIST3 MFIST4 MFIST4 MFIST4 MFIST5 MFIST5 MFIST5 MFIST6 MFIST6 MFIST6 MFIST7 MFIST7 MFIST7 MFIST7 MFIST7 MFIST7 MFIST7 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8 MFIST8
diff --git a/gemrb/override/bg2/fonts.2da b/gemrb/override/bg2/fonts.2da
new file mode 100644
index 0000000..8cb7595
--- /dev/null
+++ b/gemrb/override/bg2/fonts.2da
@@ -0,0 +1,19 @@
+2DA V1.0
+NORMAL
+ RESREF NEED_PALETTE FIRST_CHAR
+0 NORMAL 1 0
+1 FLOATTXT 1 0
+2 NUMBER 0 47
+3 INITIALS 0 0
+4 NUMBER2 0 47
+5 NUMBER3 0 47
+6 REALMS 0 0
+7 STONEBIG 0 0
+8 STONESML 0 0
+9 TOOLFONT 1 0
+10 STONESM2 0 0
+11 TOOLTIP 1 0
+12 STONSML 0 0
+13 GAMETEXT 0 0
+14 STATES 0 0
+15 STATES2 0 0
diff --git a/gemrb/override/bg2/formatio.2da b/gemrb/override/bg2/formatio.2da
new file mode 100644
index 0000000..8403425
--- /dev/null
+++ b/gemrb/override/bg2/formatio.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+-10
+# generated by make_formation.py, do not edit
+ X1 Y1 X2 Y2 X3 Y3 X4 Y4 X5 Y5 X6 Y6 X7 Y7 X8 Y8 X9 Y9 X10 Y10 X11 Y11 X12 Y12 X13 Y13 X14 Y14 X15 Y15 X16 Y16 X17 Y17 X18 Y18 X19 Y19 X20 Y20
+FOLLOW 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
+T 0 0 48 0 -48 0 0 48 0 84 0 120 0 156 0 192 0 228 0 264 0 300 0 336 0 372 0 408 0 444 0 480 0 516 0 552 0 588 0 624
+GATHER 0 -36 48 -24 -48 -24 48 24 -48 24 0 36 48 48 -48 48 0 72 48 72 -48 72 0 108 48 96 -48 96 0 144 48 120 -48 120 0 180 48 144 -48 144
+WAVYLINE -15 72 15 0 -15 24 15 48 15 96 -15 120 15 144 -15 168 15 192 -15 216 15 240 -15 264 15 288 -15 312 15 336 -15 360 15 384 -15 408 15 432 -15 456
+3BY2 0 0 64 0 -64 0 0 48 64 48 -64 48 0 96 64 96 -64 96 0 144 64 144 -64 144 0 192 64 192 -64 192 0 240 64 240 -64 240 0 288 64 288
+PROTECT 0 0 0 -36 -64 0 64 0 -32 48 32 48 0 24 0 48 0 72 0 96 0 120 0 144 0 168 0 192 0 216 0 240 0 264 0 288 0 312 0 336
+2BY3 -24 0 24 0 -24 48 24 48 -24 84 24 84 -24 120 24 120 -24 156 24 156 -24 192 24 192 -24 228 24 228 -24 264 24 264 -24 300 24 300 -24 336 24 336
+RANK -32 0 32 0 -96 0 96 0 -160 0 160 0 -224 0 224 0 -288 0 288 0 -352 0 352 0 -416 0 416 0 -480 0 480 0 -544 0 544 0 -608 0 608 0
+TRIANGLE 0 72 0 0 -32 36 32 36 -64 72 64 72 0 108 -64 108 64 108 0 144 -64 144 64 144 0 180 -64 180 64 180 0 216 -64 216 64 216 0 252 -64 252
+WEDGE 0 0 64 36 -64 36 -124 72 124 72 0 72 0 144 -124 144 124 144 0 180 -124 180 124 180 0 216 -124 216 124 216 0 252 -124 252 124 252 0 288 -124 288
+S 15 0 -15 24 15 48 -15 72 15 96 -15 120 15 144 -15 168 15 192 -15 216 15 240 -15 264 15 288 -15 312 15 336 -15 360 15 384 -15 408 15 432 -15 456
+LINE 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
diff --git a/gemrb/override/bg2/gametime.2da b/gemrb/override/bg2/gametime.2da
new file mode 100644
index 0000000..54ac077
--- /dev/null
+++ b/gemrb/override/bg2/gametime.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+0
+ DURATION
+ROUND_SECONDS 6
+TURN_SECONDS 60
diff --git a/gemrb/override/bg2/gemprjtl.ids b/gemrb/override/bg2/gemprjtl.ids
new file mode 100644
index 0000000..738dc92
--- /dev/null
+++ b/gemrb/override/bg2/gemprjtl.ids
@@ -0,0 +1,78 @@
+IDS V1.0
+8 AXEFLM
+13 ARROWHVY
+21 SPBRNHND
+22 SKYBOLT
+24 SPCONECO
+25 SPCONEFI
+39 LIGHTB
+43 SPSMPUFF
+44 SPSMKJET
+45 SPSMOLD
+65 HLYMITE
+66 FLMSTRK
+67 MAGICMIS
+68 SPMAGMIS
+69 SPMAGMIS
+70 SPMAGMIS
+71 SPMAGMIS
+72 SPMAGMIS
+73 SPMAGMIS
+74 SPMAGMIS
+75 SPMAGMIS
+76 SPMAGMIS
+77 SPMAGMIS
+78 INVTRAV
+96 COLRSPRY
+98 SPFIREWL
+108 SPSCORCH
+109 SPDIMDR
+118 SHAIR
+119 SHEARTH
+120 SHWATER
+121 SHAIR1
+122 SHEARTH1
+123 SHWATER1
+124 SHAIR2
+125 SHEARTH2
+126 SHWATER2
+127 SHAIR3
+128 SHEARTH3
+129 SHWATER3
+130 SHAIR4
+131 SHEARTH4
+132 SHWATER4
+133 SHAIR5
+134 SHEARTH5
+135 SHWATER5
+136 SHAIR6
+137 SHEARTH6
+138 SHWATER6
+139 SHAIR7
+140 SHEARTH7
+141 SHWATER7
+142 SPBOOM1
+143 SPBOOM2
+144 SPBOOM3
+145 FLMSTRK
+146 HLYMITE
+147 SHAIR7
+188 COW
+190 SPSCORIC
+194 REDHOLY
+195 SHAREA
+196 SHAREA1
+197 SHAREA
+198 SHAREA2
+199 SHAREA3
+200 SHAREA4
+201 SHAREA5
+202 SHAREA6
+205 LIGHTB
+206 LIGHTBNB
+207 SPFDEATH
+209 HOLDNECR
+210 FIREBLNS
+241 SPHOLYMT
+254 SPGENHLA
+265 COMET
diff --git a/gemrb/override/bg2/gemrb.ini b/gemrb/override/bg2/gemrb.ini
new file mode 100644
index 0000000..c481423
--- /dev/null
+++ b/gemrb/override/bg2/gemrb.ini
@@ -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.
+;
+
+
+; gemrb.ini - game type-specific settings for GemRB engine
+
+[resources]
+
+; Bitmap resource for cursors
+CursorBAM = CAROT
+
+; Bitmap resource for scroll cursor arrow
+ScrollCursorBAM = CURSARW
+
+; Bitmap resource for dialog buttons font
+ButtonFont = STONESML
+
+; Font used to display subtitles (ToB)
+MovieFont = STONESML
+
+; Font used to display tooltips
+TooltipFont = TOOLFONT
+
+; Sprite displayed behind the tooltip text, if any
+TooltipBack = TOOLSCRL
+
+; Tooltip text color (RGBA)
+TooltipColor = #f0b08000
+
+; Space between tooltip text and sides of TooltipBack (x2)
+#TooltipMargin = 10
+TooltipMargin = 5
+
+; INI file from the original games
+INIConfig = baldur.ini
+
+; Palette bitmaps in various widths
+Palette16 = MPALETTE
+Palette32 = MPALETTE
+Palette256 = MPAL256
+
+MaximumAbility = 25
+IgnoreButtonFrames = 1
+AllStringsTagged = 0
+HasDPLAYER = 1
+HasPickSound = 0
+HasDescIcon = 1
+HasEXPTABLE = 0
+SoundFolders = 0
+HasSongList = 1
+UpperButtonText = 1
+LowerLabelText = 0
+HasPartyINI = 0
+HasBeastsINI = 0
+ForceStereo = 0
+ReverseToHit = 1
+IWDMapDimensions = 0
+SmallFog = 0
+ReverseDoor = 0
+DialogueScrolls = 0
+RedrawTile = 0
+CheckAbilities = 1
+SpellBookIconHack = 1
+DeathOnZeroStat = 1
+BreakableWeapons = 0
+SelectiveMagicRes = 1
+HasHideInShadows = 1
+ProperBackstab = 1
+HasSpecificDamageBonus = 0
+StealIsAttack = 1
+CutsceneAreascripts = 0
+FlexibleWorldmap = 0
+AutoSearchHidden = 1
+PSTStateFlags = 0
+JournalHasSections = 1
+CastingSounds = 1
+EnhancedCastingSounds = 1
diff --git a/gemrb/override/bg2/gender.2da b/gemrb/override/bg2/gender.2da
new file mode 100644
index 0000000..73db5dc
--- /dev/null
+++ b/gemrb/override/bg2/gender.2da
@@ -0,0 +1,23 @@
+2DA V1.0
+*
+ TYPE MALE FEMALE
+SIRMAAM -1 27473 27475
+GIRLBOY -1 27477 27476
+BROTHERSISTER -1 27478 27479
+LADYLORD -1 27481 27480
+MALEFEMALE -1 27482 27483
+HESHE -1 27484 27485
+HISHER -1 27486 27487
+HIMHER -1 27488 27487
+MANWOMAN -1 27489 27490
+SONDAUGHTER -1 70567 70568
+PRO_SIRMAAM 0 27473 27475
+PRO_GIRLBOY 0 27477 27476
+PRO_BROTHERSISTER 0 27478 27479
+PRO_LADYLORD 0 27481 27480
+PRO_MALEFEMALE 0 27482 27483
+PRO_HESHE 0 27484 27485
+PRO_HISHER 0 27486 27487
+PRO_HIMHER 0 27488 27487
+PRO_MANWOMAN 0 27489 27490
+PRO_SONDAUGHTER 0 70567 70568
diff --git a/gemrb/override/bg2/guibtact.2da b/gemrb/override/bg2/guibtact.2da
new file mode 100644
index 0000000..92404af
--- /dev/null
+++ b/gemrb/override/bg2/guibtact.2da
@@ -0,0 +1,37 @@
+2DA V1.0
+-1
+ 1 2 3 4 TOOLTIP RESREF
+Stealth 30 31 32 33 4968 guibtact
+Thieving 26 27 28 29 4971 guibtact
+Cast 12 13 52 53 4688 guibtact
+QSpell1 0 1 2 3 4938 guibtbut
+QSpell2 0 1 2 3 4938 guibtbut
+QSpell3 0 1 2 3 4938 guibtbut
+Turn 8 9 10 11 4974 guibtact
+Talk 4 5 6 7 4933 guibtact
+UseItem 18 19 56 57 4978 guibtact
+QItem1 0 1 2 3 4937 guibtbut
+QItem4 0 1 2 3 4937 guibtbut
+QItem2 0 1 2 3 4937 guibtbut
+QItem3 0 1 2 3 4937 guibtbut
+Innate 38 39 54 55 4954 guibtact
+Defend 0 1 2 3 15925 guibtact
+Attack 14 15 16 17 4666 guibtact
+QWeapon1 0 1 2 3 4950 guibtbut
+QWeapon2 0 1 2 3 4950 guibtbut
+QWeapon3 0 1 2 3 4950 guibtbut
+QWeapon4 0 1 2 3 4950 guibtbut
+BardSong 22 23 24 25 11798 guibtact
+Stop 58 59 60 61 15924 guibtact
+Search 34 35 36 37 4927 guibtact
+23 * * * * * *
+24 * * * * * *
+25 * * * * * *
+26 * * * * * *
+27 * * * * * *
+28 * * * * * *
+29 * * * * * *
+30 * * * * * *
+QItem5 0 1 2 3 4937 guibtbut
+Left 44 45 -1 -1 -1 guibtact
+Right 42 43 -1 -1 -1 guibtact
diff --git a/gemrb/override/bg2/guiid.chu b/gemrb/override/bg2/guiid.chu
new file mode 100644
index 0000000..10dbc6d
Binary files /dev/null and b/gemrb/override/bg2/guiid.chu differ
diff --git a/gemrb/override/bg2/guils.chu b/gemrb/override/bg2/guils.chu
new file mode 100644
index 0000000..1d17785
Binary files /dev/null and b/gemrb/override/bg2/guils.chu differ
diff --git a/gemrb/override/bg2/halvdur.spl b/gemrb/override/bg2/halvdur.spl
new file mode 100644
index 0000000..1950d23
Binary files /dev/null and b/gemrb/override/bg2/halvdur.spl differ
diff --git a/gemrb/override/bg2/hlymite.pro b/gemrb/override/bg2/hlymite.pro
new file mode 100644
index 0000000..c67eabc
Binary files /dev/null and b/gemrb/override/bg2/hlymite.pro differ
diff --git a/gemrb/override/bg2/item_use.2da b/gemrb/override/bg2/item_use.2da
new file mode 100644
index 0000000..584d6d4
--- /dev/null
+++ b/gemrb/override/bg2/item_use.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+*
+ USER STRREF FLAG
+MISC84 * 10218 3
+MISC88 ALORA 10219 2
+MISC89 * 10222 3
+SW1H13 XAN 10220 2
+AROW14 ELDOTH 10221 2
+
diff --git a/gemrb/override/bg2/itemsnd.2da b/gemrb/override/bg2/itemsnd.2da
new file mode 100644
index 0000000..3ee1fa6
--- /dev/null
+++ b/gemrb/override/bg2/itemsnd.2da
@@ -0,0 +1,46 @@
+2DA V1.0
+*
+ TAKE DROP
+MISC GAM_21A GAM_21B
+AMULET GAM_14A GAM_14B
+ARMOR GAM_21A GAM_21B
+BELT GAM_18A GAM_18B
+BOOT GAM_18A GAM_18B
+ARROW GAM_20A GAM_20B
+BRACER GAM_18A GAM_18B
+HELMET GAM_22A GAM_22B
+KEY GAM_23A GAM_23B
+POTION GAM_38A GAM_38B
+RING GAM_43A GAM_43B
+SCROLL GAM_25A GAM_25B
+SHIELD GAM_26A GAM_26B
+FOOD GAM_21A GAM_21B
+BULLET GAM_27A GAM_27B
+BOW GAM_28A GAM_28B
+DAGGER GAM_29A GAM_29B
+MACE GAM_30A GAM_30B
+SLING GAM_31A GAM_31B
+SMSWORD GAM_41A GAM_41B
+BGSWORD GAM_41A GAM_41B
+HAMMER GAM_33A GAM_33B
+MSTAR GAM_34A GAM_34B
+FLAIL GAM_34A GAM_34B
+DART GAM_21A GAM_21B
+AXE GAM_33A GAM_33B
+STAFF GAM_39A GAM_39B
+XBOW GAM_40A GAM_40B
+FIST GAM_21A GAM_21B
+SPEAR GAM_39A GAM_39B
+POLEARM GAM_32A GAM_32B
+BOLT GAM_20A GAM_20B
+CLOAK GAM_36A GAM_36B
+COIN GAM_37A GAM_37B
+GEM GAM_24A GAM_24B
+WAND GAM_39A GAM_39B
+BAG GAM_21A GAM_21B
+BROKEN1 GAM_21A GAM_21B
+BROKEN2 GAM_21A GAM_21B
+DEFAULT GAM_21A GAM_21B
+LEATHER GAM_15A GAM_15B
+CHAIN GAM_16A GAM_16B
+PLATE GAM_17A GAM_17B
diff --git a/gemrb/override/bg2/itemspec.2da b/gemrb/override/bg2/itemspec.2da
new file mode 100644
index 0000000..11c1a16
--- /dev/null
+++ b/gemrb/override/bg2/itemspec.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ IDENTIFY
+SCRL75 1
+MISC3P 1
diff --git a/gemrb/override/bg2/itemtype.2da b/gemrb/override/bg2/itemtype.2da
new file mode 100644
index 0000000..21114ce
--- /dev/null
+++ b/gemrb/override/bg2/itemtype.2da
@@ -0,0 +1,42 @@
+2DA V1.0
+0
+ HELMET ARMOR SHIELD GLOVES RING AMULET BELT BOOTS WEAPON QUIVER CLOAK QUICK SCROLL BAG POTION
+MISC 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+AMULET 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+ARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+BELT 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
+BOOT 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
+ARROW 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BRACER 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
+HELMET 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+KEY 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+POTION 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
+RING 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
+SCROLL 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0
+SHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+FOOD 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BULLET 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DAGGER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MACE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SLING 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SMSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BGSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+HAMMER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MSTAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FLAIL 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DART 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+AXE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+STAFF 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+XBOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FIST 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SPEAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+POLEARM 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BOLT 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+CLOAK 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
+COIN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+GEM 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+WAND 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BAG 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
+BOOK 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+FAMILIAR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/gemrb/override/bg2/itemuse.2da b/gemrb/override/bg2/itemuse.2da
new file mode 100644
index 0000000..24eca3e
--- /dev/null
+++ b/gemrb/override/bg2/itemuse.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+0
+ STAT FILE MCOL VCOL WHICH
+0 KIT kitlist 6 6 1
+1 ALIGNMENT aligns 3 5 0
+2 RACE races 3 4 0
+3 CLASS classes 5 7 0
diff --git a/gemrb/override/bg2/k_bn_d.2da b/gemrb/override/bg2/k_bn_d.2da
new file mode 100644
index 0000000..516f804
--- /dev/null
+++ b/gemrb/override/bg2/k_bn_d.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ KIT
+1 31
diff --git a/gemrb/override/bg2/k_bn_e.2da b/gemrb/override/bg2/k_bn_e.2da
new file mode 100644
index 0000000..516f804
--- /dev/null
+++ b/gemrb/override/bg2/k_bn_e.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ KIT
+1 31
diff --git a/gemrb/override/bg2/k_bn_g.2da b/gemrb/override/bg2/k_bn_g.2da
new file mode 100644
index 0000000..516f804
--- /dev/null
+++ b/gemrb/override/bg2/k_bn_g.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ KIT
+1 31
diff --git a/gemrb/override/bg2/k_bn_h.2da b/gemrb/override/bg2/k_bn_h.2da
new file mode 100644
index 0000000..516f804
--- /dev/null
+++ b/gemrb/override/bg2/k_bn_h.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ KIT
+1 31
diff --git a/gemrb/override/bg2/k_bn_he.2da b/gemrb/override/bg2/k_bn_he.2da
new file mode 100644
index 0000000..516f804
--- /dev/null
+++ b/gemrb/override/bg2/k_bn_he.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ KIT
+1 31
diff --git a/gemrb/override/bg2/k_bn_hl.2da b/gemrb/override/bg2/k_bn_hl.2da
new file mode 100644
index 0000000..516f804
--- /dev/null
+++ b/gemrb/override/bg2/k_bn_hl.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ KIT
+1 31
diff --git a/gemrb/override/bg2/k_bn_ho.2da b/gemrb/override/bg2/k_bn_ho.2da
new file mode 100644
index 0000000..516f804
--- /dev/null
+++ b/gemrb/override/bg2/k_bn_ho.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ KIT
+1 31
diff --git a/gemrb/override/bg2/k_m_e.2da b/gemrb/override/bg2/k_m_e.2da
new file mode 100644
index 0000000..91ca34b
--- /dev/null
+++ b/gemrb/override/bg2/k_m_e.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+*
+ KIT
+1 0
+2 24
+3 25
diff --git a/gemrb/override/bg2/k_m_g.2da b/gemrb/override/bg2/k_m_g.2da
new file mode 100644
index 0000000..cc3fdca
--- /dev/null
+++ b/gemrb/override/bg2/k_m_g.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ KIT
+1 26
diff --git a/gemrb/override/bg2/k_m_h.2da b/gemrb/override/bg2/k_m_h.2da
new file mode 100644
index 0000000..66c82d0
--- /dev/null
+++ b/gemrb/override/bg2/k_m_h.2da
@@ -0,0 +1,13 @@
+2DA V1.0
+*
+ KIT
+1 0
+2 22
+3 23
+4 24
+5 25
+6 26
+7 27
+8 28
+9 29
+10 30
diff --git a/gemrb/override/bg2/k_m_he.2da b/gemrb/override/bg2/k_m_he.2da
new file mode 100644
index 0000000..1b35451
--- /dev/null
+++ b/gemrb/override/bg2/k_m_he.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+*
+ KIT
+1 0
+2 23
+3 24
+4 25
+5 29
+6 30
diff --git a/gemrb/override/bg2/lightb.pro b/gemrb/override/bg2/lightb.pro
new file mode 100644
index 0000000..26b7bb2
Binary files /dev/null and b/gemrb/override/bg2/lightb.pro differ
diff --git a/gemrb/override/bg2/magesch.2da b/gemrb/override/bg2/magesch.2da
new file mode 100644
index 0000000..3e8697b
--- /dev/null
+++ b/gemrb/override/bg2/magesch.2da
@@ -0,0 +1,13 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF KIT
+GENERALIST 18039 9563 9987 0x4000
+ABJURER 597 9564 502 0x40
+CONJURER 2179 9565 504 0x80
+DIVINER 2846 9566 2012 0x100
+ENCHANTER 2861 9567 2022 0x200
+ILLUSIONIST 2862 9568 8421 0x400
+INVOKER 3015 9569 12786 0x800
+NECROMANCER 12744 9570 12787 0x1000
+TRANSMUTER 12745 9571 12788 0x2000
+WILDMAGE 54893 54892 54894 0x8000
diff --git a/gemrb/override/bg2/minorglb.vvc b/gemrb/override/bg2/minorglb.vvc
new file mode 100644
index 0000000..cc2e83c
Binary files /dev/null and b/gemrb/override/bg2/minorglb.vvc differ
diff --git a/gemrb/override/bg2/modal.2da b/gemrb/override/bg2/modal.2da
new file mode 100644
index 0000000..6bcd966
--- /dev/null
+++ b/gemrb/override/bg2/modal.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+*
+ SPELL ACTION STR_ON STR_OFF STR_FAIL AOESPELL
+NONE * * 0 0 0 0
+BARDSONG BARDSONG BattleSong 50705 50706 -1 1
+DETECTTRAPS FINDTRAP FindTraps 50699 50700 -1 1
+STEALTH SNEAK Hide 19944 4188 17120 0
+TURNUNDEAD TURN Turn 50702 50703 -1 1
diff --git a/gemrb/override/bg2/monkbon.2da b/gemrb/override/bg2/monkbon.2da
new file mode 100644
index 0000000..fed4bcf
--- /dev/null
+++ b/gemrb/override/bg2/monkbon.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+0
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
+APR_BONUS 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
+AC_BONUS 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 11 12 12 12 13 13 13 14 14 14 14 14 15 15 15 15 15 16
+ACM_BONUS 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13
\ No newline at end of file
diff --git a/gemrb/override/bg2/overlay.2da b/gemrb/override/bg2/overlay.2da
new file mode 100644
index 0000000..84a944e
--- /dev/null
+++ b/gemrb/override/bg2/overlay.2da
@@ -0,0 +1,35 @@
+2DA V1.0
+*
+ VVC UNDER FLAGS
+SANCTUARY SANCTRY 0 1
+ENTANGLE SPENTACI 0 0
+SPELLTRAP SPMAGGLO 0 0
+SHIELDGLOBE SPSHIELD 0 1
+GREASE GREASED 1 0
+WEB WEBENTD 1 0
+MINORGLOBE MINORGLB 0 1
+GLOBE GOINVUC 0 1
+FLAMESHROUD SOFLAMC 0 0
+ANTIMAGIC AMSHELC 0 0
+RESILIENT ORSPHEC 0 0
+PROTFROMMISS PFNMISC 0 0
+CLOAKOFFEAR COFEARC 0 0
+ENTROPY ESHIELC 0 0
+FIREAURA FIAURAC 0 0
+FROSTAURA FRAURAC 0 0
+INSECT IPLAGUC 0 0
+STORMSHELL SSHELLC 0 0
+LATHANDER1 SOLATC1 0 0
+LATHANDER2 SOLATC2 1 0
+GLATHANDER1 GSOLAC1 0 0
+GLATHANDER2 GSOLAC2 1 0
+SEVENEYES1 SEYESC1 0 0
+SEVENEYES2 SEYESC2 1 0
+BOUNCE SPTURNI2 1 0
+BOUNCE2 SPTURNI 1 0
+FIRESHIELD1 FSHIRC1 0 0
+FIRESHIELD2 FSHIRC2 1 0
+ICESHIELD1 FSHIBC1 0 0
+ICESHIELD2 FSHIBC2 1 0
+TORTOISE TSHELLC 0 0
+DEATHARMOR DARMORC 0 0
diff --git a/gemrb/override/bg2/pathfind.2da b/gemrb/override/bg2/pathfind.2da
new file mode 100644
index 0000000..7180fc2
--- /dev/null
+++ b/gemrb/override/bg2/pathfind.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+*
+ 0 1 2 3 4 5 6 7 8 9 a b c d e f
+PASSABLE 8 1 1 1 1 1 1 1 0 1 8 0 0 8 3 1
+COST 10 4
+SOUND * WAL_04 WAL_MT WAL_02 WAL_05 WAL_06 WAL_01 WAL_03 * WAL_02 * WAL_01 * * * WAL_04
diff --git a/gemrb/override/bg2/pdolls.2da b/gemrb/override/bg2/pdolls.2da
new file mode 100644
index 0000000..5a75653
--- /dev/null
+++ b/gemrb/override/bg2/pdolls.2da
@@ -0,0 +1,106 @@
+2DA V1.0
+*
+ LEVEL1 LEVEL2 LEVEL3 LEVEL4 SIZE
+0x5000 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x5001 CEMC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x5002 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x5003 CIMC1INV CIMC2INV CIMC3INV CIMC4INV H
+0x5010 CHFC1INV CHMC2INV CHMC3INV CHMC4INV N
+0x5011 CEFC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x5012 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x5013 CIFC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x5100 CHMF1INV CHMF2INV CHMF3INV CHMC4INV L
+0x5101 CEMF1INV CEMF2INV CEMF3INV CEMC4INV M
+0x5102 CDMF1INV CDMF2INV CDMF3INV CDMC4INV S
+0x5103 CIMF1INV CIMF2INV CIMF3INV CIMC4INV H
+0x5110 CHFF1INV CHFF2INV CHMF3INV CHMC4INV N
+0x5111 CEFF1INV CEFF2INV CEMF3INV CEMC4INV M
+0x5112 CDMF1INV CDMF2INV CDMF3INV CDMC4INV S
+0x5113 CIFF1INV CIFF2INV CIMF3INV CIMC4INV S
+0x5200 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x5201 CEMW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x5202 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5203 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5210 CHFW1INV CHMW2INV CHMW3INV CHMW4INV N
+0x5211 CEFW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x5212 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5213 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5300 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x5301 CEMT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x5302 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x5303 CIMT1INV CIMT2INV CIMF3INV CIMF4INV H
+0x5310 CHFT1INV CHMT2INV CHMF3INV CHMF4INV N
+0x5311 CEFT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x5312 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x5313 CIFT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x6000 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x6001 CEMC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x6002 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x6003 CIMC1INV CIMC2INV CIMC3INV CIMC4INV H
+0x6004 CIMC1INV CIMC2INV CIMC3INV CIMC4INV H
+0x6005 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x6010 CHFC1INV CHFC2INV CHFC3INV CHFC4INV N
+0x6011 CEFC1INV CEFC2INV CEFC3INV CEFC4INV M
+0x6012 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x6013 CIFC1INV CIFC2INV CIFC3INV CIFC4INV S
+0x6014 CIFC1INV CIFC2INV CIFC3INV CIFC4INV S
+0x6015 CHFC1INV CHFC2INV CHFC3INV CHFC4INV N
+0x6100 CHMF1INV CHMF2INV CHMF3INV CHMF4INV L
+0x6101 CEMF1INV CEMF2INV CEMF3INV CEMF4INV M
+0x6102 CDMF1INV CDMF2INV CDMF3INV CDMF4INV S
+0x6103 CIMF1INV CIMF2INV CIMF3INV CIMF4INV H
+0x6104 CIMF1INV CIMF2INV CIMF3INV CIMF4INV H
+0x6105 CHMF1INV CHMF2INV CHMF3INV CHMF4INV L
+0x6110 CHFF1INV CHFF2INV CHFF3INV CHFF4INV N
+0x6111 CEFF1INV CEFF2INV CEFF3INV CEFF4INV M
+0x6112 CDMF1INV CDMF2INV CDMF3INV CDMF4INV S
+0x6113 CIFF1INV CIFF2INV CIFF3INV CIFF4INV S
+0x6114 CIFF1INV CIFF2INV CIFF3INV CIFF4INV S
+0x6115 CHFF1INV CHFF2INV CHFF3INV CHFF4INV N
+0x6200 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x6201 CEMW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x6202 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6203 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6204 CGMW1INV CGMW2INV CGMW3INV CGMW4INV S
+0x6205 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x6210 CHFW1INV CHFW2INV CHFW3INV CHFW4INV N
+0x6211 CEFW1INV CEFW2INV CEFW3INV CEFW4INV M
+0x6212 CIFT1INV CIFT2INV CIFT3INV CIFT4INV S
+0x6213 CIFT1INV CIFT2INV CIFT3INV CIFT4INV S
+0x6214 CIFT1INV CIFT2INV CIFT3INV CIFT4INV S
+0x6215 CHFW1INV CHFW2INV CHFW3INV CHFW4INV N
+0x6300 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x6301 CEMT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x6302 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x6303 CIMT1INV CIMT2INV CIMF3INV CIMF4INV H
+0x6304 CIMT1INV CIMT2INV CIMF3INV CIMF4INV H
+0x6305 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x6310 CHFT1INV CHFT2INV CHFF3INV CHFF4INV N
+0x6311 CEFT1INV CEFT2INV CEFF3INV CEFF4INV M
+0x6312 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x6313 CIFT1INV CIFT2INV CIFF3INV CIFF4INV S
+0x6314 CIFT1INV CIFT2INV CIFF3INV CIFF4INV S
+0x6315 CHFT1INV CHFT2INV CHFF3INV CHFF4INV N
+0x6402 CMNKINV CHMT2INV CHMF3INV CHMF4INV M
+0x6500 CHMM1INV CHMT2INV CHMF3INV CHMF4INV L
+0x6510 CHFM1INV CHFT2INV CHMF3INV CHMF4INV N
+0x7200 MBER0INV MBER0INV MBER0INV MBER0INV M
+0x7201 MBER1INV MBER1INV MBER1INV MBER1INV M
+0x7300 MEAEINV MEAEINV MEAEINV MEAEINV L
+0x7310 MFIEINV MFIEINV MFIEINV MFIEINV L
+0x7900 MSLI2INV MSLI2INV MSLI2INV MSLI2INV M
+0x7A00 MWYVINV MWYVINV MWYVINV MWYVINV M
+0x7A03 MSPI3INV MSPI3INV MSPI3INV MSPI3INV S
+0x7B00 MWLF0INV MWLF0INV MWLF0INV MWLF0INV S
+0x7B03 MWLF0INV MWLF0INV MWLF0INV MWLF0INV S
+0x7E00 MGWEINV MGWEINV MGWEINV MGWEINV M
+0x7E01 MGWEINV MGWEINV MGWEINV MGWEINV M
+0x7F00 MTROINV MTROINV MTROINV MTROINV L
+0x7F01 MMININV MMININV MMININV MMININV M
+0x7F04 MIGOINV MIGOINV MIGOINV MIGOINV L
+0x7F32 MSLYINV MSLYINV MSLYINV MSLYINV L
+0x8000 MGNLINV MGNLINV MGNLINV MGNLINV M
+0x9000 MOGRINV MOGRINV MOGRINV MOGRINV L
+0xC300 ARATINV ARATINV ARATINV ARATINV S
+0xC400 ASQUINV ASQUINV ASQUINV ASQUINV S
+0xE900 MSALINV MSALINV MSALINV MSALINV L
diff --git a/gemrb/override/bg2/pictures.2da b/gemrb/override/bg2/pictures.2da
new file mode 100644
index 0000000..9c15d34
--- /dev/null
+++ b/gemrb/override/bg2/pictures.2da
@@ -0,0 +1,51 @@
+2DA V1.0
+-1
+ GENDER HAIR SKIN MAJOR MINOR
+NCERND 1 0 85 40 46
+NKELDOR 1 6 12 57 63
+NANOMEN 1 2 84 59 45
+NEDWIN 1 0 10 47 50
+NHAER 1 18 84 103 66
+NHORC 1 0 15 39 38
+NJAN 1 0 84 35 71
+NMINSC 1 110 85 58 50
+NVALYGA 1 0 87 45 57
+NYOSHIM 1 0 84 39 66
+NKORGAN 1 6 85 66 47
+MAN1 1 6 84 52 46
+MAN2 1 2 12 37 45
+GENDWRF 1 4 85 39 49
+GENMELF 1 91 10 21 60
+GENMHLF 1 0 85 40 50
+AJANTIS 1 3 3 3 3
+CORAN 1 2 12 45 55
+EDWIN 1 91 12 47 41
+ELDOTH 1 0 12 41 36
+GARRICK 1 0 12 41 50
+KAGAIN 1 6 85 39 49
+KHALID 1 4 12 48 41
+KIVAN 1 1 84 37 39
+MINSC 1 110 84 60 58
+MONTAR 1 91 10 21 60
+QUAYLE 1 110 8 45 66
+TIAX 1 91 12 47 46
+XAN 1 0 12 45 64
+XZAR 1 91 84 54 66
+YESLICK 1 92 85 39 42
+NNALIA 2 4 13 57 41
+NMAZZY 2 2 12 46 66
+NAERIE 2 3 13 48 50
+NJAHEIR 2 2 109 52 66
+NVICON 2 79 83 58 62
+WOMAN1 2 3 84 61 46
+WOMAN2 2 0 84 45 58
+ALORA 2 4 12 60 45
+BRANWE 2 3 12 37 41
+DYNAHEI 2 2 12 60 46
+FALDORN 2 91 84 54 65
+IMOEN 2 4 13 45 61
+JAHEIRA 2 92 12 41 65
+SAFANA 2 4 12 60 60
+SHARTEL 2 4 84 66 21
+SKIE 2 0 12 66 45
+VICONIA 2 79 83 54 60
diff --git a/gemrb/override/bg2/qslot2.2da b/gemrb/override/bg2/qslot2.2da
new file mode 100644
index 0000000..01b2440
--- /dev/null
+++ b/gemrb/override/bg2/qslot2.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+100
+ CLASS SLOT1 SLOT2 SLOT3 SLOT4 SLOT5 SLOT6 SLOT7 SLOT8 SLOT9 SLOT10 SLOT11 SLOT12
+WIZARDEYE 0xd2 100 100 100 100 100 100 100 100 100 100 100 100
diff --git a/gemrb/override/bg2/qslots.2da b/gemrb/override/bg2/qslots.2da
new file mode 100644
index 0000000..ee67138
--- /dev/null
+++ b/gemrb/override/bg2/qslots.2da
@@ -0,0 +1,24 @@
+2DA V1.0
+100
+ SLOT1 SLOT2 SLOT3 SLOT4 SLOT5 SLOT6 SLOT7 SLOT8 SLOT9
+DEFAULT 3 4 5 2 8 9 11 12 13
+MAGE 3 4 5 2 8 9 11 12 13
+FIGHTER 18 19 14 100 8 9 11 12 13
+CLERIC 6 3 4 2 8 9 11 12 13
+THIEF 22 0 1 100 8 9 11 12 13
+BARD 20 1 3 2 8 9 11 12 13
+PALADIN 18 14 6 2 8 9 11 12 13
+FIGHTER_MAGE 3 4 5 2 8 9 11 12 13
+FIGHTER_CLERIC 6 3 4 2 8 9 11 12 13
+FIGHTER_THIEF 18 22 0 1 8 9 11 12 13
+FIGHTER_MAGE_THIEF 22 0 1 2 8 9 11 12 13
+DRUID 3 4 5 2 8 9 11 12 13
+RANGER 18 14 0 2 8 9 11 12 13
+MAGE_THIEF 22 0 1 2 8 9 11 12 13
+CLERIC_MAGE 6 3 4 2 8 9 11 12 13
+CLERIC_THIEF 22 0 1 2 8 9 11 12 13
+FIGHTER_DRUID 3 4 5 2 8 9 11 12 13
+FIGHTER_MAGE_CLERIC 6 3 4 2 8 9 11 12 13
+CLERIC_RANGER 6 3 4 2 8 9 11 12 13
+SORCERER 3 4 5 2 8 9 11 12 13
+MONK 18 14 22 0 8 9 11 12 13
diff --git a/gemrb/override/bg2/races.2da b/gemrb/override/bg2/races.2da
new file mode 100644
index 0000000..e67424e
--- /dev/null
+++ b/gemrb/override/bg2/races.2da
@@ -0,0 +1,11 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF ID USABILITY SAVE
+HUMAN 7193 9550 1096 1 0x08000000 *
+ELF 7194 9552 1097 2 0x00800000 *
+HALF_ELF 7197 9555 1098 3 0x02000000 *
+GNOME 7196 9553 1099 6 0x10000000 SAVECNG
+HALFLING 7195 9554 1101 5 0x04000000 SAVECNDH
+DWARF 7182 9551 1100 4 0x01000000 SAVECNDH
+HALFORC 53186 53191 53212 7 0x80000000 *
+TIEFLING 8332 -1 8330 153 0 *
diff --git a/gemrb/override/bg2/randitem.2da b/gemrb/override/bg2/randitem.2da
new file mode 100644
index 0000000..cf952d9
--- /dev/null
+++ b/gemrb/override/bg2/randitem.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+*
+ RESREF FURY_MODE
+GOLD MISC07
+RND 5 5
+RNDEQU RNDEQUIP RNDEQUIP
+RNDMAG RNDMAGIC RNDMAGIC
+RNDSCR RNDSCROL RNDSCROL
+RNDTRE RNDTREAS RNDTREAS
+RNDWEP RNDWEP RNDWEP
diff --git a/gemrb/override/bg2/rest.spl b/gemrb/override/bg2/rest.spl
new file mode 100644
index 0000000..7536207
Binary files /dev/null and b/gemrb/override/bg2/rest.spl differ
diff --git a/gemrb/override/bg2/restmov.2da b/gemrb/override/bg2/restmov.2da
new file mode 100644
index 0000000..a40b812
--- /dev/null
+++ b/gemrb/override/bg2/restmov.2da
@@ -0,0 +1,11 @@
+2DA V1.0
+*
+ REST DAY NIGHT
+0 * DAYNITE NITEDAY
+1 RESTINN DAYNITE NITEDAY
+2 RESTCAMP DAYNITE NITEDAY
+3 RESTCAMP DAYNITE NITEDAY
+4 RESTDUNG DAYNITE NITEDAY
+5 RESTDUNG DAYNITE NITEDAY
+6 RESTDUNG DAYNITE NITEDAY
+7 RESTDUNG DAYNITE NITEDAY
diff --git a/gemrb/override/bg2/sanctry.vvc b/gemrb/override/bg2/sanctry.vvc
new file mode 100644
index 0000000..334483c
Binary files /dev/null and b/gemrb/override/bg2/sanctry.vvc differ
diff --git a/gemrb/override/bg2/savegame.2da b/gemrb/override/bg2/savegame.2da
new file mode 100644
index 0000000..7723d85
--- /dev/null
+++ b/gemrb/override/bg2/savegame.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+Auto-Save
+ SLOTNAME QSAVE
+0 Auto-Save 0
+1 Quick-Save 1
+2 Final-Save 0
+3 Auto-Save-TOB 0
+4 Quick-Save-TOB 1
+5 Final-Save-TOB 0
diff --git a/gemrb/override/bg2/script.2da b/gemrb/override/bg2/script.2da
new file mode 100644
index 0000000..9e50229
--- /dev/null
+++ b/gemrb/override/bg2/script.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+0
+ DATA 1 2 3 4 5 6 7
+OBJECT_IDS_COUNT 7 ea general race class specific gender alignmen
+MAX_OBJECT_NESTING 5
+ADDITIONAL_RECT 0
+EXTRA_PARAMETERS_COUNT 0
+TRIGGER_POINT 0
+
diff --git a/gemrb/override/bg2/shtable.2da b/gemrb/override/bg2/shtable.2da
new file mode 100644
index 0000000..a4060c6
--- /dev/null
+++ b/gemrb/override/bg2/shtable.2da
@@ -0,0 +1,43 @@
+2DA V1.0
+*
+ RESREF
+0 SHAIR
+1 SHEARTH
+2 SHWATER
+3 *
+4 SHAIR
+5 SHEARTH
+6 SHWATER
+7 *
+8 SHAIR
+9 SHEARTH
+10 SHWATER
+11 *
+12 SHAIR
+13 SHEARTH
+14 SHWATER
+15 *
+16 SHAIR
+17 SHEARTH
+18 SHWATER
+19 *
+20 SHAIR
+21 SHEARTH
+22 SHWATER
+23 *
+24 SHAIR
+25 SHEARTH
+26 SHWATER
+27 *
+28 SHAIR
+29 SHEARTH
+30 SHWATER
+31 *
+32
+33
+34
+35 FLMSTRK
+36 HLYMITE
+37
+38 SPDIMNDR
+39 SPFINGER
diff --git a/gemrb/override/bg2/skills.2da b/gemrb/override/bg2/skills.2da
new file mode 100644
index 0000000..6a4dccb
--- /dev/null
+++ b/gemrb/override/bg2/skills.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+-1
+ DESC_REF CAP_REF ID THIEF MAGE_THIEF FIGHTER_THIEF CLERIC_THIEF FIGHTER_MAGE_THIEF ASSASIN BOUNTY_HUNTER SWASHBUCKLER MONK
+FIRST_LEVEL * * * 40 40 40 40 40 15 20 40 10
+RATE * * * 25 25 25 25 25 15 20 25 10
+PICK_POCKETS 9597 9463 29 1 1 1 1 1 1 1 1 -1
+OPEN_LOCKS 9598 9460 26 1 1 1 1 1 1 1 1 -1
+FIND_TRAPS 9599 9462 28 1 1 1 1 1 1 1 1 1
+MOVE_SILENTLY 9600 9461 27 1 1 1 1 1 1 1 1 1
+HIDE_IN_SHADOWS 9601 34120 135 1 1 1 1 1 1 1 1 1
+DETECT_ILLUSION 34123 34121 136 1 1 1 1 1 1 1 1 -1
+SET_TRAPS 34124 34122 137 1 1 1 1 1 1 1 1 -1
diff --git a/gemrb/override/bg2/slottype.2da b/gemrb/override/bg2/slottype.2da
new file mode 100644
index 0000000..322ed21
--- /dev/null
+++ b/gemrb/override/bg2/slottype.2da
@@ -0,0 +1,42 @@
+2DA V1.0
+*
+ BITS SCRIPT ICON STRREF EFFECT FLAGS
+10 0 0 * 0 2 0
+6 1 13 STONHELM 11999 7 0
+1 2 11 STONARM 11997 1 0
+9 260 26 STONSHIL 12006 6 0
+5 8 12 STONGLET 11998 1 0
+7 16 22 STONRING 12002 1 1
+8 16 23 STONRING 12003 1 1
+0 32 14 STONAMUL 12000 1 1
+2 64 21 STONBELT 12001 1 0
+3 128 25 STONBOOT 12005 1 0
+35 256 1 STONWEAP 12010 4 1
+36 256 2 STONWEAP 12010 4 1
+37 256 3 STONWEAP 12010 4 1
+38 256 4 STONWEAP 12010 4 1
+11 512 15 STONQUIV 12009 5 1
+12 512 16 STONQUIV 12009 5 1
+13 512 17 STONQUIV 12009 5 1
+14 512 0 STONQUIV 12009 5 1
+4 1024 24 STONCLOK 12004 1 0
+15 2048 5 STONITEM 12012 0 1
+16 2048 6 STONITEM 12012 0 1
+17 2048 7 STONITEM 12012 0 1
+18 -1 30 * 12013 0 1
+19 -1 31 * 12013 0 1
+20 -1 32 * 12013 0 1
+21 -1 33 * 12013 0 1
+22 -1 34 * 12013 0 1
+23 -1 35 * 12013 0 1
+24 -1 36 * 12013 0 1
+25 -1 37 * 12013 0 1
+26 -1 38 * 12013 0 1
+27 -1 39 * 12013 0 1
+28 -1 40 * 12013 0 1
+29 -1 41 * 12013 0 1
+30 -1 42 * 12013 0 1
+31 -1 43 * 12013 0 1
+32 -1 44 * 12013 0 1
+33 -1 45 * 12013 0 1
+34 0 0 * 0 3 0
diff --git a/gemrb/override/bg2/snow.spl b/gemrb/override/bg2/snow.spl
new file mode 100644
index 0000000..dbf5b51
Binary files /dev/null and b/gemrb/override/bg2/snow.spl differ
diff --git a/gemrb/override/bg2/spbrnhnd.pro b/gemrb/override/bg2/spbrnhnd.pro
new file mode 100644
index 0000000..2e25bbc
Binary files /dev/null and b/gemrb/override/bg2/spbrnhnd.pro differ
diff --git a/gemrb/override/bg2/spconeco.pro b/gemrb/override/bg2/spconeco.pro
new file mode 100644
index 0000000..f1f4c1e
Binary files /dev/null and b/gemrb/override/bg2/spconeco.pro differ
diff --git a/gemrb/override/bg2/spells.2da b/gemrb/override/bg2/spells.2da
new file mode 100644
index 0000000..cbfe906
--- /dev/null
+++ b/gemrb/override/bg2/spells.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+0
+ 1 2 3 4 5 6 7 8 9
+MAGE 50 50 50 50 50 50 50 50 19
+PRIEST 50 50 50 50 50 50 20 0 0
diff --git a/gemrb/override/bg2/spentaci.vvc b/gemrb/override/bg2/spentaci.vvc
new file mode 100644
index 0000000..f0a34dc
Binary files /dev/null and b/gemrb/override/bg2/spentaci.vvc differ
diff --git a/gemrb/override/bg2/spfdeath.pro b/gemrb/override/bg2/spfdeath.pro
new file mode 100644
index 0000000..ae48e54
Binary files /dev/null and b/gemrb/override/bg2/spfdeath.pro differ
diff --git a/gemrb/override/bg2/spfirewl.pro b/gemrb/override/bg2/spfirewl.pro
new file mode 100644
index 0000000..9d566c1
Binary files /dev/null and b/gemrb/override/bg2/spfirewl.pro differ
diff --git a/gemrb/override/bg2/spgenhla.pro b/gemrb/override/bg2/spgenhla.pro
new file mode 100644
index 0000000..13220c4
Binary files /dev/null and b/gemrb/override/bg2/spgenhla.pro differ
diff --git a/gemrb/override/bg2/spholymt.pro b/gemrb/override/bg2/spholymt.pro
new file mode 100644
index 0000000..93ea232
Binary files /dev/null and b/gemrb/override/bg2/spholymt.pro differ
diff --git a/gemrb/override/bg2/splspec.2da b/gemrb/override/bg2/splspec.2da
new file mode 100644
index 0000000..6411a22
--- /dev/null
+++ b/gemrb/override/bg2/splspec.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+*
+ IDENTIFY_SILENCE
+SPWI110 1
+SPWI219 2
+SPIN649 2
+SPCL412 2
+SPCL414 2
diff --git a/gemrb/override/bg2/spscorch.pro b/gemrb/override/bg2/spscorch.pro
new file mode 100644
index 0000000..f9cfc1f
Binary files /dev/null and b/gemrb/override/bg2/spscorch.pro differ
diff --git a/gemrb/override/bg2/spscoric.pro b/gemrb/override/bg2/spscoric.pro
new file mode 100644
index 0000000..1c9eb84
Binary files /dev/null and b/gemrb/override/bg2/spscoric.pro differ
diff --git a/gemrb/override/bg2/spshield.vvc b/gemrb/override/bg2/spshield.vvc
new file mode 100644
index 0000000..8508acb
Binary files /dev/null and b/gemrb/override/bg2/spshield.vvc differ
diff --git a/gemrb/override/bg2/start.2da b/gemrb/override/bg2/start.2da
new file mode 100644
index 0000000..b985524
--- /dev/null
+++ b/gemrb/override/bg2/start.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+*
+ XPOS YPOS AREA ROT
+NORMAL START_XPOS START_YPOS START_AREA *
+TUTORIAL START_XPOS_TUTORIAL START_YPOS_TUTORIAL START_AREA_TUTORIAL *
+EXPANSION START_XPOS_MP START_YPOS_MP START_AREA_MP *
diff --git a/gemrb/override/bg2/strings.2da b/gemrb/override/bg2/strings.2da
new file mode 100644
index 0000000..26ba8c0
--- /dev/null
+++ b/gemrb/override/bg2/strings.2da
@@ -0,0 +1,169 @@
+2DA V1.0
+-1
+ STRREF
+SCATTERED 16457
+WHOLEPARTY 16484
+DOORLOCKED 16485
+MAGICTRAP 16486
+NORMALTRAP 16487
+TRAP 16488
+CANNOTGO 16489
+TRAPREMOVED 16490
+OVERSTOCKED 16491
+SLEEP 16492
+AMBUSH 16493
+CONTLOCKED 15874
+NOMONEY 16495
+CURSED 16496
+SPELLDISRUPT 16497
+DIED 16498
+MAYNOTREST 16499
+CANTRESTMONS 16500
+CANTSAVEMONS 16501
+CANTSAVE 16502
+NODIALOG 10945
+CANTSAVEDIALOG 19253
+CANTSAVEDIALOG2 19254
+CANTSAVEMOVIE 19255
+TARGETBUSY 61393
+CANTTALKTRANS 62158
+GOTGOLD 17572
+LOSTGOLD 17573
+GOTXP 17574
+LOSTXP 17575
+GOTITEM 17576
+LOSTITEM 17577
+GOTREP 19686
+LOSTREP 19687
+GOTABILITY 10514
+GOTSPELL 61354
+GOTSONG 10514
+NOTHINGTOSAY 32092
+JOURNALCHANGE 11359
+WORLDMAPCHANGE 11360
+PAUSED 16321
+UNPAUSED 16322
+SCRIPTPAUSED 54381
+AP_UNUSABLE 17113
+AP_ATTACKED 17114
+AP_HIT 17115
+AP_WOUNDED 17116
+AP_DEAD 17117
+AP_NOTARGET 17118
+AP_ENDROUND 10014
+AP_ENEMY 23511
+AP_TRAP 58225
+AP_SPELLCAST 31874
+AP_RESERVED1 -1
+AP_RESERVED2 -1
+AP_RESERVED3 -1
+AP_RESERVED4 -1
+CHARMED 14672
+DIRECHARMED 14780
+CONTROLLED 61394
+EVIL 16505
+GNE_NEUTRAL 16504
+GOOD 16503
+STR_LAWFUL -1
+LNC_NEUTRAL -1
+CHAOTIC -1
+ACTION_CAST 16464
+ACTION_ATTACK 16465
+ACTION_TURN 16466
+ACTION_SONG 16467
+ACTION_FINDTRAP 16468
+MAGICWEAPON 10141
+OFFHAND_USED 9380
+TWOHANDED_USED 9381
+CANNOT_USE_ITEM 9382
+CANT_DROP_ITEM 25697
+NOT_IN_OFFHAND 26342
+ITEM_IS_CURSED 16304
+NO_CRITICAL 20696
+TRACKING 67807
+TRACKINGFAILED 19534
+DOOR_NOPICK 23169
+CONT_NOPICK 23169
+CANTSAVECOMBAT 3041
+CANTSAVENOCTRL 54401
+LOCKPICK_DONE 16517
+LOCKPICK_FAILED 16518
+STATIC_DISSIPATE -1
+LIGHTNING_DISSIPATE -1
+HAS_NO_ABILITY 17317
+NEEDS_IDENTIFY 17316
+WRONG_ITEMTYPE 9375
+HAS_ITEMEXCL 20685
+PICKPOCKET_DONE 10072
+PICKPOCKET_NONE 55369
+PICKPOCKET_FAIL 10069
+PICKPOCKET_EVIL 10068
+PICKPOCKET_ARMOR 10067
+USING_FEAT -1
+STOPPED_FEAT -1
+DISARM_DONE 16520
+DISARM_FAIL 1608
+DOORBASH_DONE 9915
+DOORBASH_FAIL 9913
+CONTBASH_DONE 9916
+CONTBASH_FAIL 9914
+MAYNOTSETTRAP 31891
+SNAREFAILED 56378
+SNARESUCCEED 3799
+NOMORETRAP 72712
+DISABLEDMAGE 8856
+SAVESUCCEED 1682
+QSAVESUCCEED 10237
+UNINJURED 38551
+INJURED1 38552
+INJURED2 38553
+INJURED3 38554
+INJURED4 38555
+HOURS 10700
+HOUR 10701
+DAYS 10697
+DAY 10698
+REST 10690
+JOURNEY 10689
+PST_REST -1
+PST_HOUR -1
+PST_HOURS -1
+DAMAGE_IMMUNITY 5199
+DAMAGE_STR1 14027
+DAMAGE_STR2 3805
+DAMAGE_STR3 -1
+DMG_POISON -1
+DMG_MAGIC -1
+DMG_MISSILE -1
+DMG_SLASHING -1
+DMG_PIERCING -1
+DMG_CRUSHING -1
+DMG_FIRE -1
+DMG_ELECTRIC -1
+DMG_COLD -1
+DMG_ACID -1
+DMG_OTHER -1
+GOTQUESTXP 46858
+LEVELUP 17119
+INVFULL_ITEMDROP 32879
+CONT_DUP 32876
+CONT_TRIG 36936
+CONT_FAIL 36937
+SEQ_DUP 39722
+CRITICAL_HIT 16462
+CRITICAL_MISS 16463
+DEATH 14026
+BACKSTAB 12128
+BACKSTAB_BAD 10013
+BACKSTAB_FAIL 3042
+CASTER_LVL_INC 73544
+CASTER_LVL_DEC 73545
+CHARS_EXPORTED -1
+PALADIN_FALL 19620
+RANGER_FALL 19621
+RES_RESISTED -1
+DEADMAGIC_FAIL 70306
+MISCASTMAGIC 467
+WILDSURGE 52260
+FAMBLOCK 8537
+FAMPROTAGONIST 8538
diff --git a/gemrb/override/bg2/walksnd.2da b/gemrb/override/bg2/walksnd.2da
new file mode 100644
index 0000000..03afb45
--- /dev/null
+++ b/gemrb/override/bg2/walksnd.2da
@@ -0,0 +1,66 @@
+2DA V1.0
+*
+ RESREF MIN MAX RANGE
+NONE * 0x0000 0xffff 0
+WYVERN WAL_17 0x1000 0x10ff 2
+LARGE WAL_30 0x1100 0x13ff 4
+SIRINE WAL_24 0x2000 0x20ff 4
+VOLO SOUND 0x2100 0x21ff 4
+OGREKNIGHT WAL_30 0x2200 0x23ff 4
+DEFAULT SOUND 0x5000 0x6fff 4
+SKELETON WAL_19 0x6403 0x6403 5
+OGRE WAL_30 0x7000 0x70ff 4
+BASILISK WAL_16 0x7100 0x71ff 1
+BEAR WAL_12 0x7200 0x72ff 1
+ELEMENTAL WAL_30 0x7300 0x73ff 4
+ELEM2 WAL_24 0x7301 0x7301 4
+ELEM2 WAL_24 0x7311 0x7311 4
+ELEM2 WAL_24 0x7314 0x7314 4
+ELEM2 WAL_24 0x7321 0x7321 4
+DOG WAL_31 0x7400 0x74ff 1
+DEFAULT SOUND 0x7500 0x77ff 4
+ETTERCAP WAL_24 0x7600 0x76ff 4
+GIBBER WAL_25 0x7800 0x78ff 2
+SLIME WAL_15 0x7900 0x79ff 5
+SPIDER WAL_14 0x7a00 0x7aff 5
+WOLF WAL_31 0x7b00 0x7bff 1
+XVART WAL_26 0x7c00 0x7cff 2
+ZOMBIE WAL_10 0x7d00 0x7dff 4
+WEREWOLF WAL_24 0x7e00 0x7eff 4
+DEFAULT SOUND 0x7f00 0x7fff 4
+TROLL WAL_30 0x7f00 0x7f00 4
+IMP WAL_PS 0x7f03 0x7f03 1
+GOLEM WAL_MT 0x7f04 0x7f04 4
+NONE * 0x7f06 0x7f06 0
+GOLEM WAL_MT 0x7f07 0x7f07 4
+OTYUGH WAL_30 0x7f08 0x7f08 4
+SAHUAGIN WAL_24 0x7f09 0x7f09 4
+GIANTCAT WAL_31 0x7f0a 0x7f0b 1
+KUOLICH WAL_24 0x7f0c 0x7f0d 4
+DEMILICH * 0x7f0e 0x7f0e 0
+TROLL WAL_30 0x7f0f 0x7f0f 4
+UMBERHULK WAL_24 0x7f11 0x7f11 4
+SNAKE * 0x7f13 0x7f13 0
+GITH WAL_24 0x7f14 0x7f14 4
+NONE * 0x7f15 0x7f21 0
+SAHUAGIN WAL_24 0x7f23 0x7f23 4
+KUO WAL_24 0x7f28 0x7f28 4
+FAMILIAR WAL_PS 0x7f2d 0x7f2d 4
+RAVAGER WAL_24 0x7f2e 0x7f2f 4
+SLAYER WAL_24 0x7f32 0x7f32 4
+MIST WAL_24 0x7f35 0x7f35 4
+NONE * 0x7f38 0x7f39 0
+GNOLL WAL_28 0x8000 0x80ff 4
+HOBGOBLIN WAL_29 0x8100 0x81ff 4
+KOBOLD WAL_26 0x8200 0x82ff 4
+IWD WAL_09 0x9000 0x9fff 4
+WYVERN WAL_17 0xa000 0xa0ff 2
+CARRION WAL_13 0xa100 0xa1ff 4
+DEFAULT SOUND 0xc000 0xcfff 4
+BEGGAR WAL_24 0xc600 0xc6ff 4
+CHILDREN WAL_24 0xc700 0xc7ff 4
+SLAVE WAL_24 0xcb00 0xcbff 4
+IWD WAL_09 0xe000 0xefff 4
+GOBLIN WAL_26 0xe400 0xe4ff 2
+NONE * 0xef00 0xefff 0
+
diff --git a/gemrb/override/bg2/wildmag.2da b/gemrb/override/bg2/wildmag.2da
new file mode 100644
index 0000000..05e01ea
--- /dev/null
+++ b/gemrb/override/bg2/wildmag.2da
@@ -0,0 +1,103 @@
+2DA V1.0
+*
+ SPELL STRREF COMMENT
+1 SPWM102 0x13b *
+2 SPWM102 0x13b *
+3 SPWM103 0x13c *
+4 SPWM104 0x13d *
+5 SPWM105 0x13e *
+6 1.155 0x13f change_spell_to_area_effect
+7 SPWM107 0x140 *
+8 SPWM108 0x141 *
+9 SPWM109 0x142 *
+10 SPWM110 0x14c *
+11 SPWM111 0x14e *
+12 SPWM112 0x14f *
+13 SPWM113 0x153 *
+14 SPWM114 0x154 *
+15 SPWM115 0x15d *
+16 SPWM183 0x15f *
+17 SPWM117 0x167 *
+18 SPWM118 0x168 *
+19 SPWI523 0x17a *
+20 SPWM120 0x17d *
+21 2.1 0x17e self_is_also_targeted
+22 SPWM122 0x180 *
+23 SPWM123 0x184 *
+24 3.2 0x185 roll_twice
+25 SPWM125 0x186 *
+26 SPWM126 0x187 *
+27 SPPR211 0x189 *
+28 SPWM128 0x18b *
+29 SPWI206 0x18c *
+30 SPWM130 0x18d *
+31 4.1 0x18e self_becomes_target
+32 SPWI206 0x191 *
+33 SPWI105 0x192 *
+34 SPWM134 0x195 *
+35 DUMMY 0x197 do_nothing
+36 SPWM136 0x198 *
+37 SPPR207 0x1a1 *
+38 SPPR207 0x1a1 *
+39 1.23 0x1a2 projectile_changed_to_chromatic_orb
+40 SPWM141 0x1a4 *
+41 SPWM141 0x1a4 *
+42 SPWM142 0x1a5 *
+43 SPWM142 0x1a5 *
+44 SPWM145 0x1c6 *
+45 SPWM145 0x1c6 *
+46 3.2 0x185 roll_twice
+47 SPWM113 0x329 *
+48 5 0x334 random_target
+49 REST 0x342 refreshed
+50 SPWI407 0x34e *
+51 SNOW 0x356 start_snow_if_outside_otherwise_roll_twice_more?
+52 SPWM155 0x36b *
+53 SPWM153 0x363 *
+54 SPWM154 0x36a *
+55 SPWM155 0x36b *
+56 +HALVDUR 0x36c duration_halved
+57 SPWM157 0x36d *
+58 8.50 0x36e projectile_speed_halved
+59 SPWM159 0x37a *
+60 6.-20 0x391 no_save_(-20_save)
+61 SPWM114 0x393 *
+62 SPWM162 0x395 *
+63 3.4 0x3a3 roll_four_times
+64 SPWM164 0x5a6 *
+65 7 0x5aa random_spell
+66 SPWI308 0x5ab *
+67 SPWM167 0x5ac *
+68 SPWM168 0x5ad *
+69 SPWM111 0x5ae *
+70 SPWM118 0x5b6 *
+71 SPWI304 0x5b9 *
+72 SPWI604 0x5ba *
+73 +REST 0x5bb refreshed_and_spell_cast
+74 SPWM168 0x5bd *
+75 SPWM128 0x5be *
+76 SPWI523 0x5bf *
+77 SPWM122 0x5c1 *
+78 SPWM178 0x5c2 *
+79 SPWM179 0x5c4 *
+80 SPWM136 0x5c6 *
+81 SPWM120 0x5c8 *
+82 SPWM141 0x5c9 *
+83 SPPR211 0x5cb *
+84 SPPR211 0x5cb *
+85 SPWM107 0x5cc *
+86 SPWM186 0x5cf *
+87 SPWM187 0x21f6 *
+88 SPWM188 0x21f7 *
+89 SPWM128 0x5be *
+90 1.155 0x2203 change_projectile_to_area_effect
+91 SPWM191 0x225b *
+92 SPWM153 0x2260 *
+93 SPWM122 0x2277 *
+94 SPWM115 0x2279 *
+95 SPWM117 0x227b *
+96 0.2 0x22c3 cast_spell_twice
+97 6.4 0x227e easier_save_(+4_save)
+98 SPWM198 0x2289 *
+99 +AT2XLVL 0x2296 spell_cast_at_double_level
+100 0.1 0x227D no_change
diff --git a/gemrb/override/bg2/wishcode.2da b/gemrb/override/bg2/wishcode.2da
new file mode 100644
index 0000000..1bf59ec
--- /dev/null
+++ b/gemrb/override/bg2/wishcode.2da
@@ -0,0 +1,28 @@
+2DA V1.0
+*
+ RESREF MIN MAX
+1 SPWISH01 10 25
+2 SPWISH02 10 25
+3 SPWISH03 10 25
+4 SPWISH04 10 25
+5 SPWISH05 0 25
+6 SPWISH06 10 25
+7 SPWISH07 0 25
+8 SPWISH08 15 25
+9 SPWISH09 0 25
+10 SPWISH10 0 25
+11 SPWISH11 0 25
+12 SPWISH12 0 25
+13 SPWISH13 0 25
+14 SPWISH14 0 25
+15 SPWISH15 15 25
+16 SPWISH16 0 25
+17 SPWISH17 0 25
+18 SPWISH18 0 17
+19 SPWISH19 0 9
+20 SPWISH20 0 17
+21 SPWISH21 0 17
+22 SPWISH22 0 9
+23 SPWISH23 0 9
+24 SPWISH24 0 9
+25 SPWISH25 0 25
diff --git a/gemrb/override/bg2/wsshield.2da b/gemrb/override/bg2/wsshield.2da
new file mode 100644
index 0000000..e24b2f5
--- /dev/null
+++ b/gemrb/override/bg2/wsshield.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+0
+ ACVSMISSLE
+0 0
+1 -2
+2 -4
+3 -4
diff --git a/gemrb/override/bg2/wssingle.2da b/gemrb/override/bg2/wssingle.2da
new file mode 100644
index 0000000..56df669
--- /dev/null
+++ b/gemrb/override/bg2/wssingle.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+0
+ AC CRITICALHITBONUS
+0 0 0
+1 -1 -1
+2 -2 -1
+3 -2 -1
diff --git a/gemrb/override/bg2/wstwohnd.2da b/gemrb/override/bg2/wstwohnd.2da
new file mode 100644
index 0000000..5c83ad4
--- /dev/null
+++ b/gemrb/override/bg2/wstwohnd.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+0
+ DAMAGEBONUS CRITICALHITBONUS PHYSICALSPEED
+0 0 0 0
+1 1 -1 -2
+2 1 -1 -4
+3 1 -1 -4
diff --git a/gemrb/override/bg2/wstwowpn.2da b/gemrb/override/bg2/wstwowpn.2da
new file mode 100644
index 0000000..3e81cc5
--- /dev/null
+++ b/gemrb/override/bg2/wstwowpn.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+0
+ THAC0BONUSRIGHT THAC0BONUSLEFT
+0 -4 -8
+1 -2 -6
+2 0 -4
+3 0 -2
diff --git a/gemrb/override/how/CMakeLists.txt b/gemrb/override/how/CMakeLists.txt
new file mode 100644
index 0000000..727794c
--- /dev/null
+++ b/gemrb/override/how/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_OVERRIDE (how)
\ No newline at end of file
diff --git a/gemrb/override/how/Makefile.am b/gemrb/override/how/Makefile.am
new file mode 100644
index 0000000..801744d
--- /dev/null
+++ b/gemrb/override/how/Makefile.am
@@ -0,0 +1,3 @@
+howoverride_DATA = *.2da *.ini *.chu *.ids *.spl *.pro *.bcs
+howoverridedir = $(moddir)/override/how/
+EXTRA_DIST = *.2da *.ini *.chu *.ids *.spl *.pro *.bcs
diff --git a/gemrb/override/how/ability.2da b/gemrb/override/how/ability.2da
new file mode 100644
index 0000000..2fa0989
--- /dev/null
+++ b/gemrb/override/how/ability.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF STAT_ID
+STRENGTH 11975 9582 1145 36
+DEXTERITY 11977 9584 1151 40
+CONSTITUTION 11978 9583 1178 41
+INTELLIGENCE 11979 9585 1179 38
+WISDOM 11980 9586 1180 39
+CHARISMA 11981 9587 1181 42
diff --git a/gemrb/override/how/abjurap.pro b/gemrb/override/how/abjurap.pro
new file mode 100644
index 0000000..9f72db3
Binary files /dev/null and b/gemrb/override/how/abjurap.pro differ
diff --git a/gemrb/override/how/abjurh.pro b/gemrb/override/how/abjurh.pro
new file mode 100644
index 0000000..4927bf6
Binary files /dev/null and b/gemrb/override/how/abjurh.pro differ
diff --git a/gemrb/override/how/abjurt.pro b/gemrb/override/how/abjurt.pro
new file mode 100644
index 0000000..092eeb8
Binary files /dev/null and b/gemrb/override/how/abjurt.pro differ
diff --git a/gemrb/override/how/acidblgr.pro b/gemrb/override/how/acidblgr.pro
new file mode 100644
index 0000000..fe37e1c
Binary files /dev/null and b/gemrb/override/how/acidblgr.pro differ
diff --git a/gemrb/override/how/acidblmu.pro b/gemrb/override/how/acidblmu.pro
new file mode 100644
index 0000000..aa9e8df
Binary files /dev/null and b/gemrb/override/how/acidblmu.pro differ
diff --git a/gemrb/override/how/acidblob.pro b/gemrb/override/how/acidblob.pro
new file mode 100644
index 0000000..16ef4f3
Binary files /dev/null and b/gemrb/override/how/acidblob.pro differ
diff --git a/gemrb/override/how/acidbloc.pro b/gemrb/override/how/acidbloc.pro
new file mode 100644
index 0000000..a2e6822
Binary files /dev/null and b/gemrb/override/how/acidbloc.pro differ
diff --git a/gemrb/override/how/acidh.pro b/gemrb/override/how/acidh.pro
new file mode 100644
index 0000000..de3952a
Binary files /dev/null and b/gemrb/override/how/acidh.pro differ
diff --git a/gemrb/override/how/adhwil.pro b/gemrb/override/how/adhwil.pro
new file mode 100644
index 0000000..07e10dd
Binary files /dev/null and b/gemrb/override/how/adhwil.pro differ
diff --git a/gemrb/override/how/adhwilh.pro b/gemrb/override/how/adhwilh.pro
new file mode 100644
index 0000000..387a674
Binary files /dev/null and b/gemrb/override/how/adhwilh.pro differ
diff --git a/gemrb/override/how/alance.pro b/gemrb/override/how/alance.pro
new file mode 100644
index 0000000..9336ca7
Binary files /dev/null and b/gemrb/override/how/alance.pro differ
diff --git a/gemrb/override/how/aligns.2da b/gemrb/override/how/aligns.2da
new file mode 100644
index 0000000..1047ea6
--- /dev/null
+++ b/gemrb/override/how/aligns.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF VALUE COLNAME USABILITY
+LAWFUL_GOOD 7186 9603 1102 0x11 L_G 0x14
+NEUTRAL_GOOD 7183 9606 1105 0x21 N_G 0x24
+CHAOTIC_GOOD 7189 9609 1108 0x31 C_G 0x5
+LAWFUL_NEUTRAL 7188 9604 1104 0x12 L_N 0x18
+TRUE_NEUTRAL 7185 9608 1106 0x22 N_N 0x28
+CHAOTIC_NEUTRAL 7191 9610 1109 0x32 C_N 0x9
+LAWFUL_EVIL 7187 9605 1103 0x13 L_E 0x12
+NEUTRAL_EVIL 7184 9607 1107 0x23 N_E 0x22
+CHAOTIC_EVIL 7190 9611 1110 0x33 C_E 0x3
diff --git a/gemrb/override/how/altera.pro b/gemrb/override/how/altera.pro
new file mode 100644
index 0000000..cb466b3
Binary files /dev/null and b/gemrb/override/how/altera.pro differ
diff --git a/gemrb/override/how/alteranp.pro b/gemrb/override/how/alteranp.pro
new file mode 100644
index 0000000..952660b
Binary files /dev/null and b/gemrb/override/how/alteranp.pro differ
diff --git a/gemrb/override/how/alterap.pro b/gemrb/override/how/alterap.pro
new file mode 100644
index 0000000..2196be2
Binary files /dev/null and b/gemrb/override/how/alterap.pro differ
diff --git a/gemrb/override/how/alteras.pro b/gemrb/override/how/alteras.pro
new file mode 100644
index 0000000..81ad729
Binary files /dev/null and b/gemrb/override/how/alteras.pro differ
diff --git a/gemrb/override/how/alterh.pro b/gemrb/override/how/alterh.pro
new file mode 100644
index 0000000..4ef79d3
Binary files /dev/null and b/gemrb/override/how/alterh.pro differ
diff --git a/gemrb/override/how/altert.pro b/gemrb/override/how/altert.pro
new file mode 100644
index 0000000..2a5f332
Binary files /dev/null and b/gemrb/override/how/altert.pro differ
diff --git a/gemrb/override/how/area1np.pro b/gemrb/override/how/area1np.pro
new file mode 100644
index 0000000..b617ed5
Binary files /dev/null and b/gemrb/override/how/area1np.pro differ
diff --git a/gemrb/override/how/area1p.pro b/gemrb/override/how/area1p.pro
new file mode 100644
index 0000000..e4b1d13
Binary files /dev/null and b/gemrb/override/how/area1p.pro differ
diff --git a/gemrb/override/how/area2.pro b/gemrb/override/how/area2.pro
new file mode 100644
index 0000000..a9eea19
Binary files /dev/null and b/gemrb/override/how/area2.pro differ
diff --git a/gemrb/override/how/area2np.pro b/gemrb/override/how/area2np.pro
new file mode 100644
index 0000000..6739976
Binary files /dev/null and b/gemrb/override/how/area2np.pro differ
diff --git a/gemrb/override/how/area3p.pro b/gemrb/override/how/area3p.pro
new file mode 100644
index 0000000..4356697
Binary files /dev/null and b/gemrb/override/how/area3p.pro differ
diff --git a/gemrb/override/how/area4np.pro b/gemrb/override/how/area4np.pro
new file mode 100644
index 0000000..d042aeb
Binary files /dev/null and b/gemrb/override/how/area4np.pro differ
diff --git a/gemrb/override/how/areapro.2da b/gemrb/override/how/areapro.2da
new file mode 100644
index 0000000..de40d48
--- /dev/null
+++ b/gemrb/override/how/areapro.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+*
+ RESOURCE1 RESOURCE2 RESOURCE3 SOUND1 SOUND2 FLAGS
+FIREBALL SPRING SPBOOM * EFF_M21 * 32
+STINKCLOUD SCLOUDA SCLOUDX SCLOUDR RNG_M01 ARE_M02 2
+CLOUDKILL CLOUDKA CLOUDKX CLOUDKR RNG_M01 ARE_M02 2
+ICESTORM ISTORMA ISTORMX * * ARE_M04 14
+GREASE GREASEA GREASEX GREASEH EFF_M31B ARE_M01 2
+WEB WEBENTD * WEBENTH EFF_M19 ARE_M03 34
+METEOR SPMETSWM * * EFF_M34 ARE_P14 2
+WILTING SPHORPUF SPHORWIL * EFF_M34 * 1
+WEIRD SPWRDFLD * * EFF_M34 ARE_M11 2
+ENTANGLE ENTANGA ENTANGX * * ARE_P01 2
+COLORSPRAY SPCSPRA3 * SPCSPRA2 EFF_M19 * 2
+CONECOLD SPCCOLDE SPCCOLDL * EFF_M19 * 0
+HOLYSMITE SPHLYSM2 SPHLYSM3 * EFF_M34 * 6
+UNHOLY SPUNHBL2 SPUNHBL3 * EFF_M34 * 0
+PRISMATIC SPPRISM2 SPPRISM3 * EFF_M34 * 0
+DRAGON DBREATHA DBREATHX * ARE_M21 * 16
+VENGEANCE SPSTRMVA SPSTRMVB * * * 2
+FIREBALL2 SPRING SPFIREPI * EFF_M21 * 32
+FIREBALL3 SPRING SPFIREPI * EFF_M21 * 32
diff --git a/gemrb/override/how/armorh.pro b/gemrb/override/how/armorh.pro
new file mode 100644
index 0000000..cceb813
Binary files /dev/null and b/gemrb/override/how/armorh.pro differ
diff --git a/gemrb/override/how/arrow.pro b/gemrb/override/how/arrow.pro
new file mode 100644
index 0000000..293014f
Binary files /dev/null and b/gemrb/override/how/arrow.pro differ
diff --git a/gemrb/override/how/arrowex.pro b/gemrb/override/how/arrowex.pro
new file mode 100644
index 0000000..70b7773
Binary files /dev/null and b/gemrb/override/how/arrowex.pro differ
diff --git a/gemrb/override/how/arrowflb.pro b/gemrb/override/how/arrowflb.pro
new file mode 100644
index 0000000..12b5b91
Binary files /dev/null and b/gemrb/override/how/arrowflb.pro differ
diff --git a/gemrb/override/how/arrowflg.pro b/gemrb/override/how/arrowflg.pro
new file mode 100644
index 0000000..45a7bab
Binary files /dev/null and b/gemrb/override/how/arrowflg.pro differ
diff --git a/gemrb/override/how/arrowfli.pro b/gemrb/override/how/arrowfli.pro
new file mode 100644
index 0000000..202c16c
Binary files /dev/null and b/gemrb/override/how/arrowfli.pro differ
diff --git a/gemrb/override/how/arrowflm.pro b/gemrb/override/how/arrowflm.pro
new file mode 100644
index 0000000..ae84563
Binary files /dev/null and b/gemrb/override/how/arrowflm.pro differ
diff --git a/gemrb/override/how/arrowhvy.pro b/gemrb/override/how/arrowhvy.pro
new file mode 100644
index 0000000..1471c2e
Binary files /dev/null and b/gemrb/override/how/arrowhvy.pro differ
diff --git a/gemrb/override/how/ascorch.pro b/gemrb/override/how/ascorch.pro
new file mode 100644
index 0000000..9ec8567
Binary files /dev/null and b/gemrb/override/how/ascorch.pro differ
diff --git a/gemrb/override/how/astorm.pro b/gemrb/override/how/astorm.pro
new file mode 100644
index 0000000..d6ec20f
Binary files /dev/null and b/gemrb/override/how/astorm.pro differ
diff --git a/gemrb/override/how/asumm1.pro b/gemrb/override/how/asumm1.pro
new file mode 100644
index 0000000..0671b04
Binary files /dev/null and b/gemrb/override/how/asumm1.pro differ
diff --git a/gemrb/override/how/asumm1h.pro b/gemrb/override/how/asumm1h.pro
new file mode 100644
index 0000000..42cf78c
Binary files /dev/null and b/gemrb/override/how/asumm1h.pro differ
diff --git a/gemrb/override/how/asumm1x.pro b/gemrb/override/how/asumm1x.pro
new file mode 100644
index 0000000..e3e49c2
Binary files /dev/null and b/gemrb/override/how/asumm1x.pro differ
diff --git a/gemrb/override/how/asumm2h.pro b/gemrb/override/how/asumm2h.pro
new file mode 100644
index 0000000..86ab54a
Binary files /dev/null and b/gemrb/override/how/asumm2h.pro differ
diff --git a/gemrb/override/how/asumm3h.pro b/gemrb/override/how/asumm3h.pro
new file mode 100644
index 0000000..4bf942b
Binary files /dev/null and b/gemrb/override/how/asumm3h.pro differ
diff --git a/gemrb/override/how/avatars.2da b/gemrb/override/how/avatars.2da
new file mode 100644
index 0000000..7dc5963
--- /dev/null
+++ b/gemrb/override/how/avatars.2da
@@ -0,0 +1,417 @@
+2DA V1.0
+*
+ AT_1 AT_2 AT_3 AT_4 TYPE SPACE PALETTE SIZE
+0x0100 SPCHUNKS SPCHUNKS SPCHUNKS SPCHUNKS 13 0 0 *
+0x0300 SPSMPUFF SPSMPUFF SPSMPUFF SPSMPUFF 13 0 0 *
+0x0400 SKLH SKLH SKLH SKLH 13 0 1 *
+0x0410 GLPHWRDH GLPHWRDH GLPHWRDH GLPHWRDH 13 0 1 *
+0x1000 MWYV MWYV MWYV MWYV 11 5 1 *
+0x1001 MWYV MWYV MWYV MWYV 11 5 1 *
+0x1100 MTAN MTAN MTAN MTAN 11 5 1 *
+0x1200 MDR1 MDR1 MDR1 MDR1 12 5 1 *
+0x1201 MDR2 MDR2 MDR2 MDR2 12 5 1 *
+0x1202 MDR3 MDR3 MDR3 MDR3 12 5 1 *
+0x1203 MDR1 MDR1 MDR1 MDR1 12 7 GR *
+0x1204 MDR1 MDR1 MDR1 MDR1 12 7 AQ *
+0x1205 MDR1 MDR1 MDR1 MDR1 12 7 BL *
+0x1206 MDR1 MDR1 MDR1 MDR1 12 7 BR *
+0x1207 MDR1 MDR1 MDR1 MDR1 12 7 MC *
+0x1208 MDR1 MDR1 MDR1 MDR1 12 7 PU *
+0x2000 MSIR MSIR MSIR MSIR 2 2 0 *
+0x2100 UVOL UVOL UVOL UVOL 2 2 1 *
+0x2200 MOGM MOGM MOGM MOGM 2 2 0 S
+0x2300 MDKN MDKN MDKN MDKN 2 2 1 *
+0x3000 MAKH MAKH MAKH MAKH 2 3 1 *
+0x4000 SNOMC SNOMC SNOMC SNOMC 1 2 0 *
+0x4002 SNOMM SNOMM SNOMM SNOMM 1 2 0 *
+0x4010 SNOWC SNOWC SNOWC SNOWC 1 2 0 *
+0x4012 SNOWM SNOWM SNOWM SNOWM 1 2 0 *
+0x4100 SSIMC SSIMC SSIMC SSIMC 1 2 0 *
+0x4101 SSIMS SSIMS SSIMS SSIMS 1 2 0 *
+0x4102 SSIMM SSIMM SSIMM SSIMM 1 2 0 *
+0x4110 SSIWC SSIWC SSIWC SSIWC 1 2 0 *
+0x4112 SSIWM SSIWM SSIWM SSIWM 1 2 0 *
+0x4200 SHMCM SHMCM SHMCM SHMCM 1 2 0 *
+0x4300 MSPLG1 MSPLG1 MSPLG1 MSPLG1 1 2 1 *
+0x4400 LHMC LHMC LHMC LHMC 1 2 0 *
+0x4410 LHFC LHFC LHFC LHFC 1 2 0 *
+0x4500 LFAM LFAM LFAM LFAM 1 2 0 *
+0x4600 LDMF LDMF LDMF LDMF 1 2 0 *
+0x4700 LEMF LEMF LEMF LEMF 1 2 0 *
+0x4710 LEFF LEFF LEFF LEFF 1 2 0 *
+0x4800 LIMC LIMC LIMC LIMC 1 2 0 *
+0x5000 CHMB1 CHMB2 CHMB3 CHMC4 0 2 0 L
+0x5001 CEMB1 CEMB2 CEMB3 CEMC4 0 2 0 M
+0x5002 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x5003 CIMB1 CIMB2 CIMB3 CIMC4 0 2 0 S
+0x5010 CHFB1 CHFB2 CHFB3 CHFC4 0 2 0 M
+0x5011 CEFB1 CEFB2 CEFB3 CEFC4 0 2 0 M
+0x5012 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x5013 CIFB1 CIFB2 CIFB3 CIFC4 0 2 0 S
+0x5100 CHMB1 CHMB2 CHMB3 CHMF4 0 2 0 L
+0x5101 CEMB1 CEMB2 CEMB3 CHMF4 0 2 0 M
+0x5102 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x5103 CIMB1 CIMB2 CIMB3 CIMF4 0 2 0 S
+0x5110 CHFB1 CHFB2 CHFB3 CHFF4 0 2 0 M
+0x5111 CEFB1 CEFB2 CEFB3 CEFF4 0 2 0 M
+0x5112 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x5113 CIFB1 CIFB2 CIFB3 CIFF4 0 2 0 S
+0x5200 CHMW1 CHMW2 CHMW3 CHMW4 0 2 0 L
+0x5201 CEMW1 CEMW2 CEMW3 CEMW4 0 2 0 M
+0x5202 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x5203 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x5210 CHFW1 CHFW2 CHFW3 CHFW4 0 2 0 M
+0x5211 CEFW1 CEFW2 CEFW3 CEFW4 0 2 0 M
+0x5212 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x5213 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x5300 CHMB1 CHMT2 CHMB3 CHMF4 0 2 0 L
+0x5301 CEMB1 CEMT2 CEMB3 CEMF4 0 2 0 M
+0x5302 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x5303 CIMB1 CIMT2 CIMB3 CIMF4 0 2 0 S
+0x5310 CHFB1 CHFT2 CHFB3 CHFF4 0 2 0 M
+0x5311 CEFB1 CEFT2 CEFB3 CEFF4 0 2 0 M
+0x5312 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x5313 CIFB1 CIFT2 CIFB3 CIFF4 0 2 0 S
+0x6000 CHMB1 CHMB2 CHMB3 CHMC4 0 2 0 L
+0x6001 CEMB1 CEMB2 CEMB3 CEMC4 0 2 0 M
+0x6002 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x6003 CIMB1 CIMB2 CIMB3 CIMC4 0 2 0 S
+0x6004 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x6010 CHFB1 CHFB2 CHFB3 CHFC4 0 2 0 M
+0x6011 CEFB1 CEFB2 CEFB3 CEFC4 0 2 0 M
+0x6012 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 S
+0x6013 CIFB1 CIFB2 CIFB3 CIFC4 0 2 0 S
+0x6014 CIFB1 CIFB2 CIFB3 CIFC4 0 2 0 S
+0x6100 CHMB1 CHMB2 CHMB3 CHMF4 0 2 0 L
+0x6101 CEMB1 CEMB2 CEMB3 CEMF4 0 2 0 M
+0x6102 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x6103 CIMB1 CIMB2 CIMB3 CIMF4 0 2 0 S
+0x6104 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x6110 CHFB1 CHFB2 CHFB3 CHFF4 0 2 0 M
+0x6111 CEFB1 CEFB2 CEFB3 CEFF4 0 2 0 M
+0x6112 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 S
+0x6113 CIFB1 CIFB2 CIFB3 CIFF4 0 2 0 S
+0x6114 CIFB1 CIFB2 CIFB3 CIFF4 0 2 0 S
+0x6200 CHMW1 CHMW2 CHMW3 CHMW4 0 2 0 L
+0x6201 CEMW1 CEMW2 CEMW3 CEMW4 0 2 0 M
+0x6202 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6203 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6204 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6210 CHFW1 CHFW2 CHFW3 CHFW4 0 2 0 M
+0x6211 CEFW1 CEFW2 CEFW3 CEFW4 0 2 0 M
+0x6212 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6213 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6214 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 S
+0x6300 CHMB1 CHMT2 CHMB3 CHMF4 0 2 0 L
+0x6301 CEMB1 CEMT2 CEMB3 CEMF4 0 2 0 M
+0x6302 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x6303 CIMB1 CIMT2 CIMB3 CIMF4 0 2 0 S
+0x6304 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x6310 CHFB1 CHFT2 CHFB3 CHFF4 0 2 0 M
+0x6311 CEFB1 CEFT2 CEFB3 CEFF4 0 2 0 M
+0x6312 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 S
+0x6313 CIFB1 CIFT2 CIFB3 CIFF4 0 2 0 S
+0x6314 CIFB1 CIFT2 CIFB3 CIFF4 0 2 0 S
+0x6400 UDRZ1 UDRZ1 UDRZ1 UDRZ1 6 2 0 *
+0x6401 UELM1 UELM1 UELM1 UELM1 6 2 1 *
+0x6402 CMNK1 CMNK1 CMNK1 CMNK1 6 2 0 *
+0x6403 MSKL1 MSKL1 MSKL1 MSKL1 6 2 0 M
+0x6404 USAR1 USAR1 USAR1 USAR1 6 2 1 *
+0x6405 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 M
+0x6406 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 L
+0x6500 CHMM1 CHMB2 CHMB3 CHMC4 0 2 0 M
+0x6510 CHFM1 CHFB2 CHFB3 CHFC4 0 2 0 M
+0x7000 MOGH MOGH MOGH MOGH 2 2 0 *
+0x7001 MOGN MOGN MOGN MOGN 2 2 0 *
+0x7100 MBAS MBAS MBAS MBAS 2 2 1 *
+0x7101 MBAS MBAS MBAS MBAS 2 2 GR *
+0x7200 MBER MBER MBER MBER 3 2 BL *
+0x7201 MBER MBER MBER MBER 3 2 1 *
+0x7202 MBER MBER MBER MBER 3 2 CA *
+0x7203 MBER MBER MBER MBER 3 2 PO *
+0x7300 MEAE MEAE MEAE MEAE 4 2 1 *
+0x7301 MEAS MEAS MEAS MEAS 4 2 1 *
+0x7302 MEAE MEAE MEAE MEAE 4 2 SH *
+0x7310 MFIE MFIE MFIE MFIE 4 2 1 *
+0x7311 MFIS MFIS MFIS MFIS 4 2 1 *
+0x7320 MAIR MAIR MAIR MAIR 4 2 1 *
+0x7321 MAIS MAIS MAIS MAIS 4 2 1 *
+0x7400 MDOG MDOG MDOG MDOG 2 2 WI *
+0x7401 MDOG MDOG MDOG MDOG 2 2 WA *
+0x7402 MDOG MDOG MDOG MDOG 2 2 MO *
+0x7500 MDOP MDOP MDOP MDOP 3 2 1 *
+0x7501 MDOP MDOP MDOP MDOP 3 2 GR *
+0x7600 METT METT METT METT 2 2 1 *
+0x7701 MGHL MGHL MGHL MGHL 2 2 1 *
+0x7702 MGHL MGHL MGHL MGHL 2 2 RE *
+0x7703 MGHL MGHL MGHL MGHL 2 2 GA *
+0x7704 MSHD MSHD MSHD MSHD 4 2 1 *
+0x7800 MGIB MGIB MGIB MGIB 2 2 1 *
+0x7900 MSLI MSLI MSLI MSLI 3 3 GR *
+0x7901 MSLI MSLI MSLI MSLI 3 3 OL *
+0x7902 MSLI MSLI MSLI MSLI 3 3 MU *
+0x7903 MSLI MSLI MSLI MSLI 3 3 OC *
+0x7904 MSLI MSLI MSLI MSLI 3 3 1 *
+0x7A00 MSPI MSPI MSPI MSPI 3 2 GI *
+0x7A01 MSPI MSPI MSPI MSPI 3 2 HU *
+0x7A02 MSPI MSPI MSPI MSPI 3 2 PH *
+0x7A03 MSPI MSPI MSPI MSPI 3 2 SW *
+0x7A04 MSPI MSPI MSPI MSPI 3 2 WR *
+0x7B00 MWLF MWLF MWLF MWLF 2 2 1 *
+0x7B01 MWLF MWLF MWLF MWLF 2 2 WO *
+0x7B02 MWLF MWLF MWLF MWLF 2 2 DI *
+0x7B03 MWLF MWLF MWLF MWLF 2 2 WI *
+0x7B04 MWLF MWLF MWLF MWLF 2 2 VA *
+0x7B05 MWLF MWLF MWLF MWLF 2 2 DR *
+0x7B06 MWLS MWLS MWLS MWLS 2 2 1 *
+0x7C00 MXVT MXVT MXVT MXVT 2 2 0 *
+0x7C01 MTAS MTAS MTAS MTAS 2 2 0 *
+0x7D00 MZOM MZOM MZOM MZOM 2 2 0 *
+0x7E00 MWER MWER MWER MWER 2 2 1 *
+0x7E01 MGWE MGWE MGWE MGWE 2 2 1 *
+0x7F00 MTRO MTRO MTRO MTRO 4 2 1 *
+0x7F01 MMIN MMIN MMIN MMIN 4 2 1 *
+0x7F02 MBEH MBEH MBEH MBEH 4 3 1 *
+0x7F03 MIMP MIMP MIMP MIMP 4 2 1 *
+0x7F04 MIGO MIGO MIGO MIGO 4 2 1 *
+0x7F05 MDJI MDJI MDJI MDJI 4 2 1 *
+0x7F06 MDJL MDJL MDJL MDJL 4 2 1 *
+0x7F07 MGLC MGLC MGLC MGLC 4 3 1 *
+0x7F08 MOTY MOTY MOTY MOTY 4 4 1 *
+0x7F09 MSAH MSAH MSAH MSAH 4 2 1 *
+0x7F0A MGCP MGCP MGCP MGCP 4 2 1 *
+0x7F0B MGCL MGCL MGCL MGCL 4 2 1 *
+0x7F0C MKUO MKUO MKUO MKUO 4 2 1 *
+0x7F0D MLIC MLIC MLIC MLIC 4 2 1 *
+0x7F0E MDLI MDLI MDLI MDLI 4 2 1 *
+0x7F0F MTRS MTRS MTRS MTRS 4 2 1 *
+0x7F10 MRAK MRAK MRAK MRAK 4 2 1 *
+0x7F11 MUMB MUMB MUMB MUMB 4 2 1 *
+0x7F12 MVAM MVAM MVAM MVAM 4 2 1 *
+0x7F13 MSNK MSNK MSNK MSNK 4 2 1 *
+0x7F14 MGIT MGIT MGIT MGIT 4 2 1 *
+0x7F15 MBES MBES MBES MBES 4 2 1 *
+0x7F16 AMOO AMOO AMOO AMOO 4 3 1 *
+0x7F17 ARAB ARAB ARAB ARAB 4 1 1 *
+0x7F18 ADER ADER ADER ADER 4 2 1 *
+0x7F19 MDSW MDSW MDSW MDSW 4 2 1 *
+0x7F20 AGRO AGRO AGRO AGRO 4 2 1 *
+0x7F21 APHE APHE APHE APHE 4 2 1 *
+0x7F22 MVAF MVAF MVAF MVAF 4 2 1 *
+0x7F23 MSAT MSAT MSAT MSAT 4 3 1 *
+0x7F24 NPIR NPIR NPIR NPIR 4 2 1 *
+0x7F27 MDRO MDRO MDRO MDRO 4 2 1 *
+0x7F28 MKUL MKUL MKUL MKUL 4 3 1 *
+0x7F29 MFDR MFDR MFDR MFDR 4 2 1 *
+0x7F2A NSAI NSAI NSAI NSAI 4 2 1 *
+0x7F2C NSOL NSOL NSOL NSOL 4 2 1 *
+0x7F2D MWFM MWFM MWFM MWFM 4 2 1 *
+0x7F2E MRAV MRAV MRAV MRAV 4 3 1 *
+0x7F2F MSPS MSPS MSPS MSPS 4 2 1 *
+0x7F30 NBOH NBOH NBOH NBOH 4 2 1 *
+0x7F31 NELL NELL NELL NELL 4 2 1 *
+0x7F32 MSLY MSLY MSLY MSLY 4 2 1 *
+0x7F35 MMIS MMIS MMIS MMIS 4 3 1 *
+0x7F36 NSHD NSHD NSHD NSHD 4 2 1 *
+0x7F37 NIRE NIRE NIRE NIRE 4 2 1 *
+0x8000 MGNL MGNL MGNL MGNL 2 2 0 *
+0x8100 MHOB MHOB MHOB MHOB 2 2 0 *
+0x8200 MKOB MKOB MKOB MKOB 2 2 0 *
+0x9000 MOGR MOGR MOGR MOGR 5 2 0 *
+0xA000 MWYV MWYV MWYV MWYV 8 3 1 *
+0xA100 MCAR MCAR MCAR MCAR 8 3 1 *
+0xB000 ACOW ACOW ACOW ACOW 10 3 1 *
+0xB100 AHRS AHRS AHRS AHRS 10 3 1 *
+0xB200 NBEGL NBEGL NBEGL NBEGL 2 2 0 *
+0xB210 NPROL NPROL NPROL NPROL 2 2 0 *
+0xB300 NBOYL NBOYL NBOYL NBOYL 3 2 0 *
+0xB310 NGRLL NGRLL NGRLL NGRLL 3 2 0 *
+0xB400 NFAML NFAML NFAML NFAML 2 2 0 *
+0xB410 NFAWL NFAWL NFAWL NFAWL 2 2 0 *
+0xB500 NSIML NSIML NSIML NSIML 2 2 0 *
+0xB510 NSIWL NSIWL NSIWL NSIWL 2 2 0 *
+0xB600 NNOML NNOML NNOML NNOML 2 2 0 *
+0xB610 NNOWL NNOWL NNOWL NNOWL 2 2 0 *
+0xB700 NSVLL NSVLL NSIWL NSIWL 2 2 0 *
+0xC000 ABAT ABAT ABAT ABAT 2 1 1 *
+0xC100 ACAT ACAT ACAT ACAT 2 1 1 *
+0xC200 ACHK ACHK ACHK ACHK 2 1 1 *
+0xC300 ARAT ARAT ARAT ARAT 2 1 1 *
+0xC400 ASQU ASQU ASQU ASQU 2 1 1 *
+0xC500 ABAT ABAT ABAT ABAT 2 1 1 *
+0xC600 NBEGH NBEGH NBEGH NBEGH 2 2 0 *
+0xC610 NPROH NPROH NPROH NPROH 2 2 0 *
+0xC700 NBOYH NBOYH NBOYH NBOYH 3 2 0 *
+0xC710 NGRLH NGRLH NGRLH NGRLH 3 2 0 *
+0xC800 NFAMH NFAMH NFAMH NFAMH 2 2 0 *
+0xC810 NFAWH NFAWH NFAWH NFAWH 2 2 0 *
+0xC900 NSIMH NSIMH NSIMH NSIMH 2 2 0 *
+0xC910 NSIWH NSIWH NSIWH NSIWH 2 2 0 *
+0xCA00 NNOMH NNOMH NNOMH NNOMH 2 2 0 *
+0xCA10 NNOWH NNOWH NNOWH NNOWH 2 2 0 *
+0xCB00 NSLVH NSLVH NSLVH NSLVH 2 2 0 *
+0xD000 AEAGG1 AEAGG1 AEAGG1 AEAGG1 7 0 1 *
+0xD100 AGULG1 AGULG1 AGULG1 AGULG1 7 0 1 *
+0xD200 AVULG1 AVULG1 AVULG1 AVULG1 7 0 1 *
+0xD300 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xD400 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xE000 MBET MBET MBET MBET 9 2 1 *
+0xE010 MBBM MBBM MBBM MBBM 16 2 1 *
+0xE020 MBBR MBBR MBBR MBBR 9 2 1 *
+0xE030 MBFI MBFI MBFI MBFI 9 2 1 *
+0xE040 MBRH MBRH MBRH MBRH 9 5 1 *
+0xE050 MREM MREM MREM MREM 9 4 1 *
+0xE060 MLIC MLIC MLIC MLIC 9 2 1 *
+0xE068 MHOH MHOH MHOH MHOH 9 2 1 *
+0xE070 MMIN MMIN MMIN MMIN 9 2 1 *
+0xE090 MMER MMER MMER MMER 9 2 1 *
+0xE0A0 MTIC MTIC MTIC MTIC 9 2 1 *
+0xE0B0 MTRO MTRO MTRO MTRO 9 2 1 *
+0xE0C0 MTSN MTSN MTSN MTSN 9 2 1 *
+0xE0D0 MUMB MUMB MUMB MUMB 9 2 1 *
+0xE0E0 MCOR MCOR MCOR MCOR 9 3 1 *
+0xE0F0 MGLA MGLA MGLA MGLA 9 2 1 *
+0xE100 MCYC MCYC MCYC MCYC 9 4 1 *
+0xE110 METN METN METN METN 9 4 1 *
+0xE130 MGFR MGFR MGFR MGFR 9 4 1 *
+0xE140 MGVE MGVE MGVE MGVE 9 4 1 *
+0xE150 MGFO MGFO MGFO MGFO 9 4 1 *
+0xE200 MELA MELA MELA MELA 9 4 1 *
+0xE210 MELE MELE MELE MELE 9 4 1 *
+0xE220 MELF MELF MELF MELF 9 4 1 *
+0xE230 MELW MELW MELW MELW 9 4 1 *
+0xE240 MHAR MHAR MHAR MHAR 9 2 1 *
+0xE250 MWWE MWWE MWWE MWWE 9 2 1 *
+0xE260 MFEY MFEY MFEY MFEY 9 2 1 *
+0xE270 MDTR MDTR MDTR MDTR 9 3 1 *
+0xE280 MFE2 MFE2 MFE2 MFE2 9 3 1 *
+0xE290 MEW2 MEW2 MEW2 MEW2 9 2 1 *
+0xE300 MGH2 MGH2 MGH2 MGH2 9 2 1 *
+0xE310 MGH3 MGH3 MGH3 MGH3 9 2 1 *
+0xE320 MWIG MWIG MWIG MWIG 9 2 1 *
+0xE330 MZO2 MZO2 MZO2 MZO2 9 2 1 *
+0xE340 MZO3 MZO3 MZO3 MZO3 16 2 1 *
+0xE350 MWI2 MWI2 MWI2 MWI2 9 2 1 *
+0xE360 MWI3 MWI3 MWI3 MWI3 9 2 1 *
+0xE380 MMUM MMUM MMUM MMUM 9 2 1 *
+0xE380 MMUM MMUM MMUM MMUM 9 2 1 *
+0xE390 MHIS MHIS MHIS MHIS 9 2 1 *
+0xE3A0 MDRD MDRD MDRD MDRD 9 2 1 *
+0xE3B0 MWAV MWAV MWAV MWAV 9 2 1 *
+0xE400 MGO1 MGO1 MGO1 MGO1 9 2 1 *
+0xE410 MGO2 MGO2 MGO2 MGO2 16 2 1 *
+0xE420 MGO3 MGO3 MGO3 MGO3 9 2 1 *
+0xE430 MGO4 MGO4 MGO4 MGO4 16 2 1 *
+0xE440 MSVI MSVI MSVI MSVI 9 2 1 *
+0xE450 MSV2 MSV2 MSV2 MSV2 9 2 1 *
+0xE460 MGWO MGWO MGWO MGWO 9 2 1 *
+0xE470 MGOC MGOC MGOC MGOC 9 2 1 *
+0xE480 MGW2 MGW2 MGW2 MGW2 9 2 1 *
+0xE490 MGO5 MGO5 MGO5 MGO5 9 2 1 *
+0xE500 MLIZ MLIZ MLIZ MLIZ 9 2 1 *
+0xE510 MGIR MGIR MGIR MGIR 9 3 1 *
+0xE520 MGIC MGIC MGIC MGIC 9 2 1 *
+0xE600 MMYC MMYC MMYC MMYC 9 2 1 *
+0xE610 MMY2 MMY2 MMY2 MMY2 9 2 1 *
+0xE620 MSKB MSKB MSKB MSKB 9 2 1 *
+0xE700 MNO1 MNO1 MNO1 MNO1 9 2 1 *
+0xE710 MNO2 MNO2 MNO2 MNO2 9 2 1 *
+0xE718 MTRO MTRO MTRO MTRO 9 2 1 *
+0xE720 MNO3 MNO3 MNO3 MNO3 9 2 1 *
+0xE730 MTSN MTSN MTSN MTSN 9 2 1 *
+0xE750 MUMB MUMB MUMB MUMB 9 2 1 *
+0xE760 MYET MYET MYET MYET 9 2 1 *
+0xE770 MBA4 MBA4 MBA4 MBA4 9 2 1 *
+0xE780 MBA5 MBA5 MBA5 MBA5 9 2 1 *
+0xE790 MBA6 MBA6 MBA6 MBA6 9 2 1 *
+0xE7A0 MBAI MBAI MBAI MBAI 9 2 1 *
+0xE7B0 MBOA MBOA MBOA MBOA 9 2 1 *
+0xE7C0 MABW MABW MABW MABW 9 2 1 *
+0xE7D0 MMAL MMAL MMAL MMAL 9 2 1 *
+0xE7E0 MSCR MSCR MSCR MSCR 9 2 1 *
+0xE7F0 MUM2 MUM2 MUM2 MUM2 9 2 1 *
+0xE800 MOR6 MOR6 MOR6 MOR6 9 2 1 *
+0xE810 MOR1 MOR1 MOR1 MOR1 9 2 1 *
+0xE820 MOR2 MOR2 MOR2 MOR2 16 2 1 *
+0xE830 MOR3 MOR3 MOR3 MOR3 9 2 1 *
+0xE840 MOR4 MOR4 MOR4 MOR4 16 2 1 *
+0xE850 MOR5 MOR5 MOR5 MOR5 9 2 1 *
+0xE860 MNO1 MNO1 MNO1 MNO1 9 2 1 *
+0xE870 MNO2 MNO2 MNO2 MNO2 9 2 1 *
+0xE880 MNO3 MNO3 MNO3 MNO3 9 2 1 *
+0xE890 MLI3 MLI3 MLI3 MLI3 9 2 1 *
+0xE8A0 MYU3 MYU3 MYU3 MYU3 9 2 1 *
+0xE8B0 MYUH MYUH MYUH MYUH 9 2 1 *
+0xE8C0 MBUG MBUG MBUG MBUG 9 2 1 *
+0xE8D0 MNOS MNOS MNOS MNOS 9 2 1 *
+0xE8E0 MBU2 MBU2 MBU2 MBU2 9 2 1 *
+0xE8F0 MOR7 MOR7 MOR7 MOR7 9 2 1 *
+0xE900 MSAL MSAL MSAL MSAL 16 2 1 *
+0xE908 MSH1 MSH1 MSH1 MSH1 16 2 1 *
+0xE910 MSA2 MSA2 MSA2 MSA2 16 2 1 *
+0xE918 MSH2 MSH2 MSH2 MSH2 16 2 1 *
+0xE920 MGHO MGHO MGHO MGHO 9 2 1 *
+0xEA00 MSHR MSHR MSHR MSHR 9 2 1 *
+0xEA10 MSH1 MSH1 MSH1 MSH1 16 2 1 *
+0xEA20 MCRD MCRD MCRD MCRD 9 2 1 *
+0xEB00 MSKT MSKT MSKT MSKT 9 2 1 *
+0xEB00 MANI MANI MANI MANI 9 3 1 *
+0xEB10 MSKA MSKA MSKA MSKA 9 2 1 *
+0xEB10 MANI MANI MANI MANI 9 2 1 *
+0xEB20 MAN2 MAN2 MAN2 MAN2 9 2 1 *
+0xEB30 MBE1 MBE1 MBE1 MBE1 9 2 1 *
+0xEB40 MBE2 MBE2 MBE2 MBE2 9 2 1 *
+0xEB51 MSEE MSEE MSEE MSEE 9 2 1 *
+0xEB52 MFIR MFIR MFIR MFIR 9 2 1 *
+0xEB60 MLIC MLIC MLIC MLIC 9 2 1 *
+0xEB70 MLER MLER MLER MLER 9 2 1 *
+0xEB80 MMAN MMAN MMAN MMAN 9 2 1 *
+0xEB90 MMYC MMYC MMYC MMYC 9 2 1 *
+0xEBA0 MMY2 MMY2 MMY2 MMY2 9 2 1 *
+0xEBB0 MSHR MSHR MSHR MSHR 9 2 1 *
+0xEBC0 MTAN MTAN MTAN MTAN 9 4 1 *
+0xEBD0 MSAL MSAL MSAL MSAL 16 2 1 *
+0xEBE0 MSA2 MSA2 MSA2 MSA2 16 2 1 *
+0xEBF0 MARU MARU MARU MARU 9 2 1 *
+0xEC00 MWDR MWDR MWDR MWDR 9 2 1 *
+0xEC10 MCHY MCHY MCHY MCHY 9 2 1 *
+0xEC20 MSHE MSHE MSHE MSHE 9 2 1 *
+0xEC30 MCHI MCHI MCHI MCHI 9 2 1 *
+0xEC40 MDH1 MDH1 MDH1 MDH1 9 2 1 *
+0xEC50 MDH2 MDH2 MDH2 MDH2 9 2 1 *
+0xED00 MYU1 MYU1 MYU1 MYU1 9 2 1 *
+0xED09 MCOR MCOR MCOR MCOR 9 3 1 *
+0xED10 MYU2 MYU2 MYU2 MYU2 9 2 1 *
+0xED19 MGLA MGLA MGLA MGLA 9 2 1 *
+0xED20 MYU3 MYU3 MYU3 MYU3 9 2 1 *
+0xED20 MLEM MLEM MLEM MLEM 9 2 1 *
+0xEE00 MWEB MWEB MWEB MWEB 9 2 1 *
+0xEE10 MWRA MWRA MWRA MWRA 9 2 1 *
+0xEF00 MISA MISA MISA MISA 9 2 1 *
+0xEF10 MMAD MMAD MMAD MMAD 9 2 1 *
+0xEF20 MWOR MWOR MWOR MWOR 9 2 1 *
+0xEF50 MKG1 MKG1 MKG1 MKG1 9 2 1 *
+0xEF60 MKG2 MKG2 MKG2 MKG2 9 2 1 *
+0xEF70 MKG3 MKG3 MKG3 MKG3 9 2 1 *
+0xEF90 MWIL MWIL MWIL MWIL 9 2 1 *
+0xEFA0 MGEN MGEN MGEN MGEN 9 2 1 *
+0xEFB0 MGEN MGEN MGEN MGEN 9 3 1 *
+0xEFC0 MGEN MGEN MGEN MGEN 9 4 1 *
+0xEFD0 MGEN MGEN MGEN MGEN 9 5 1 *
+0xEFE0 MGEN MGEN MGEN MGEN 9 1 1 *
+0xEFF0 MGEN MGEN MGEN MGEN 9 6 1 *
+0xF000 MSKA MSKA MSKA MSKA 9 2 1 *
+0xF010 MSKT MSKT MSKT MSKT 9 2 1 *
+0xF020 MWI4 MWI4 MWI4 MWI4 9 2 1 *
+0xF100 MYU1 MYU1 MYU1 MYU1 9 2 1 *
+0xF110 MYU2 MYU2 MYU2 MYU2 9 2 1 *
+0xF200 MLIZ MLIZ MLIZ MLIZ 9 2 1 *
+0xF210 MLI2 MLI2 MLI2 MLI2 9 2 1 *
+0xF300 MGFI MGFI MGFI MGFI 9 4 1 *
+0xF400 MSAH MSAH MSAH MSAH 9 2 1 *
+0xF410 MSAT MSAT MSAT MSAT 9 2 1 *
+0xF500 MDRM MDRM MDRM MDRM 9 4 1 *
+0xF510 MDRF MDRF MDRF MDRF 9 4 1 *
+0xF770 MBA1 MBA1 MBA1 MBA1 9 2 1 *
+0xF780 MBA2 MBA2 MBA2 MBA2 9 2 1 *
+0xF790 MBA3 MBA3 MBA3 MBA3 9 2 1 *
diff --git a/gemrb/override/how/avprefc.2da b/gemrb/override/how/avprefc.2da
new file mode 100644
index 0000000..37d6684
--- /dev/null
+++ b/gemrb/override/how/avprefc.2da
@@ -0,0 +1,25 @@
+2DA V1.0
+*
+ PREFIX
+TYPE 232
+FIGHTER 0x100
+RANGER 0x100
+PALADIN 0x100
+CLERIC 0
+DRUID 0
+MAGE 0x200
+THIEF 0x300
+BARD 0x300
+FIGHTER_MAGE 0x100
+FIGHTER_CLERIC 0x100
+FIGHTER_THIEF 0x100
+FIGHTER_MAGE_THIEF 0x100
+MAGE_THIEF 0x300
+CLERIC_MAGE 0
+CLERIC_THIEF 0
+FIGHTER_DRUID 0x100
+FIGHTER_MAGE_CLERIC 0x100
+CLERIC_RANGER 0
+SORCERER 0x200
+MONK 0x500
+BARBARIAN 0x100
diff --git a/gemrb/override/how/avprefr.2da b/gemrb/override/how/avprefr.2da
new file mode 100644
index 0000000..bd50876
--- /dev/null
+++ b/gemrb/override/how/avprefr.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+*
+ RACE
+TYPE 231
+HUMAN 0
+ELF 1
+HALF_ELF 1
+GNOME 3
+HALFLING 3
+DWARF 2
diff --git a/gemrb/override/how/axe.pro b/gemrb/override/how/axe.pro
new file mode 100644
index 0000000..2220a75
Binary files /dev/null and b/gemrb/override/how/axe.pro differ
diff --git a/gemrb/override/how/axeex.pro b/gemrb/override/how/axeex.pro
new file mode 100644
index 0000000..475b824
Binary files /dev/null and b/gemrb/override/how/axeex.pro differ
diff --git a/gemrb/override/how/baldur.bcs b/gemrb/override/how/baldur.bcs
new file mode 100644
index 0000000..2a77629
--- /dev/null
+++ b/gemrb/override/how/baldur.bcs
@@ -0,0 +1,74 @@
+SC
+CR
+CO
+TR
+16399 1 0 0 0 "GLOBALFORGE_ON" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+TR
+TR
+16397 0 0 0 0 "" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004a"OB
+TR
+TR
+16397 0 0 0 0 "" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004b"OB
+TR
+TR
+16397 0 0 0 0 "" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004c"OB
+TR
+CO
+RS
+RE
+100AC
+398OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004a"OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+0 0 0 0 0"AR6013" "" AC
+AC
+398OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004b"OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+0 0 0 0 0"AR6013" "" AC
+AC
+398OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004c"OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+0 0 0 0 0"AR6013" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 1 0 0 0 "GLOBALFORGE_ON" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+TR
+TR
+16397 0 0 0 0 "" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004"OB
+TR
+CO
+RS
+RE
+100AC
+398OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004"OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+0 0 0 0 0"AR6013" "" AC
+RE
+RS
+CR
+SC
diff --git a/gemrb/override/how/bbarrh1.pro b/gemrb/override/how/bbarrh1.pro
new file mode 100644
index 0000000..25d3720
Binary files /dev/null and b/gemrb/override/how/bbarrh1.pro differ
diff --git a/gemrb/override/how/bbarrh2.pro b/gemrb/override/how/bbarrh2.pro
new file mode 100644
index 0000000..361d26c
Binary files /dev/null and b/gemrb/override/how/bbarrh2.pro differ
diff --git a/gemrb/override/how/bbarrier.pro b/gemrb/override/how/bbarrier.pro
new file mode 100644
index 0000000..de3b9c9
Binary files /dev/null and b/gemrb/override/how/bbarrier.pro differ
diff --git a/gemrb/override/how/bdeath.pro b/gemrb/override/how/bdeath.pro
new file mode 100644
index 0000000..514d2b0
Binary files /dev/null and b/gemrb/override/how/bdeath.pro differ
diff --git a/gemrb/override/how/blessh.pro b/gemrb/override/how/blessh.pro
new file mode 100644
index 0000000..dd80c91
Binary files /dev/null and b/gemrb/override/how/blessh.pro differ
diff --git a/gemrb/override/how/bloodclr.2da b/gemrb/override/how/bloodclr.2da
new file mode 100644
index 0000000..1b33a30
--- /dev/null
+++ b/gemrb/override/how/bloodclr.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+0
+ VALUE MIN MAX
+NORMAL 0x2f 0x1000 0xffff
+PURPLE 0x2d 0x4300 0x43ff
+MSKEL 0x25 0x5403 0x5403
+DOOMG 0x22 0x5405 0x5406
+MSKEL 0x25 0x6403 0x6403
+DOOMG 0x22 0x6405 0x6406
+YELLOW 0x32 0x7300 0x73ff
+PURPLE 0x3c 0x7500 0x75ff
+YELLOW 0x32 0x7600 0x76ff
+BLACK 0x66 0x7700 0x77ff
+SLI_GR 7 0x7900 0x7900
+SLI_OL 0x24 0x7901 0x7901
+SLI_MU 0x33 0x7902 0x7902
+SLI_OC 0x25 0x7903 0x7903
+SLIME 0x1b 0x7904 0x7904
+SPIDER 0x33 0x7a00 0x7aff
+ZOMBIE 0x25 0x7c00 0x7cff
+MTRO 0x33 0x7f00 0x7f00
+MMIN 0x3d 0x7f01 0x7f03
+MIGO 0x1a 0x7f04 0x7f04
+MGLC 0x5d 0x7f07 0x7f07
+MTRS 0x32 0x7f0f 0x7f0f
+MBES 0x3d 0x7f15 0x7f15
+MCAR 0x38 0xa100 0xa1ff
+UNDEAD 0x66 0xe300 0xe3ff
+MWWE 0x38 0xef00 0xefff
diff --git a/gemrb/override/how/bolt.pro b/gemrb/override/how/bolt.pro
new file mode 100644
index 0000000..1cfd4f5
Binary files /dev/null and b/gemrb/override/how/bolt.pro differ
diff --git a/gemrb/override/how/boltex.pro b/gemrb/override/how/boltex.pro
new file mode 100644
index 0000000..e963523
Binary files /dev/null and b/gemrb/override/how/boltex.pro differ
diff --git a/gemrb/override/how/bscloud.pro b/gemrb/override/how/bscloud.pro
new file mode 100644
index 0000000..aa0f975
Binary files /dev/null and b/gemrb/override/how/bscloud.pro differ
diff --git a/gemrb/override/how/bullet.pro b/gemrb/override/how/bullet.pro
new file mode 100644
index 0000000..cb89cea
Binary files /dev/null and b/gemrb/override/how/bullet.pro differ
diff --git a/gemrb/override/how/bulletex.pro b/gemrb/override/how/bulletex.pro
new file mode 100644
index 0000000..4a039f5
Binary files /dev/null and b/gemrb/override/how/bulletex.pro differ
diff --git a/gemrb/override/how/calllih.pro b/gemrb/override/how/calllih.pro
new file mode 100644
index 0000000..17723b2
Binary files /dev/null and b/gemrb/override/how/calllih.pro differ
diff --git a/gemrb/override/how/ccdamah.pro b/gemrb/override/how/ccdamah.pro
new file mode 100644
index 0000000..c6fe73b
Binary files /dev/null and b/gemrb/override/how/ccdamah.pro differ
diff --git a/gemrb/override/how/ccommah.pro b/gemrb/override/how/ccommah.pro
new file mode 100644
index 0000000..2dd3ed5
Binary files /dev/null and b/gemrb/override/how/ccommah.pro differ
diff --git a/gemrb/override/how/ccwounh.pro b/gemrb/override/how/ccwounh.pro
new file mode 100644
index 0000000..fd621c3
Binary files /dev/null and b/gemrb/override/how/ccwounh.pro differ
diff --git a/gemrb/override/how/cdiseah.pro b/gemrb/override/how/cdiseah.pro
new file mode 100644
index 0000000..4a10c96
Binary files /dev/null and b/gemrb/override/how/cdiseah.pro differ
diff --git a/gemrb/override/how/ceelem1.pro b/gemrb/override/how/ceelem1.pro
new file mode 100644
index 0000000..14116cd
Binary files /dev/null and b/gemrb/override/how/ceelem1.pro differ
diff --git a/gemrb/override/how/ceelemh.pro b/gemrb/override/how/ceelemh.pro
new file mode 100644
index 0000000..59eb71a
Binary files /dev/null and b/gemrb/override/how/ceelemh.pro differ
diff --git a/gemrb/override/how/ceelemx.pro b/gemrb/override/how/ceelemx.pro
new file mode 100644
index 0000000..5e15e45
Binary files /dev/null and b/gemrb/override/how/ceelemx.pro differ
diff --git a/gemrb/override/how/cfelem1.pro b/gemrb/override/how/cfelem1.pro
new file mode 100644
index 0000000..d550f94
Binary files /dev/null and b/gemrb/override/how/cfelem1.pro differ
diff --git a/gemrb/override/how/cfelemh.pro b/gemrb/override/how/cfelemh.pro
new file mode 100644
index 0000000..9aaba24
Binary files /dev/null and b/gemrb/override/how/cfelemh.pro differ
diff --git a/gemrb/override/how/cfelemx.pro b/gemrb/override/how/cfelemx.pro
new file mode 100644
index 0000000..d87881c
Binary files /dev/null and b/gemrb/override/how/cfelemx.pro differ
diff --git a/gemrb/override/how/cfog.pro b/gemrb/override/how/cfog.pro
new file mode 100644
index 0000000..702da41
Binary files /dev/null and b/gemrb/override/how/cfog.pro differ
diff --git a/gemrb/override/how/cgraceh.pro b/gemrb/override/how/cgraceh.pro
new file mode 100644
index 0000000..93ff65b
Binary files /dev/null and b/gemrb/override/how/cgraceh.pro differ
diff --git a/gemrb/override/how/cgtable.2da b/gemrb/override/how/cgtable.2da
new file mode 100644
index 0000000..36e784d
--- /dev/null
+++ b/gemrb/override/how/cgtable.2da
@@ -0,0 +1,20 @@
+2DA V1.0
+*
+ RESREF
+UNUSED0 *
+UNUSED1 *
+UNUSED2 *
+UNUSED3 *
+UNUSED4 *
+UNUSED5 *
+UNUSED6 *
+UNUSED7 *
+UNUSED8 *
+NECROMANCY NecroCG
+ALTERATION AlterCG
+ENCHANTMENT EnchaCG
+ABJURATION AbjurCG
+ILLUSION IllusCG
+CONJURATION ConjuCG
+INVOCATION InvocCG
+DIVINATION DivinCG
diff --git a/gemrb/override/how/chant.pro b/gemrb/override/how/chant.pro
new file mode 100644
index 0000000..740be7b
Binary files /dev/null and b/gemrb/override/how/chant.pro differ
diff --git a/gemrb/override/how/chromorb.pro b/gemrb/override/how/chromorb.pro
new file mode 100644
index 0000000..c39d719
Binary files /dev/null and b/gemrb/override/how/chromorb.pro differ
diff --git a/gemrb/override/how/classes.2da b/gemrb/override/how/classes.2da
new file mode 100644
index 0000000..8f7f386
--- /dev/null
+++ b/gemrb/override/how/classes.2da
@@ -0,0 +1,21 @@
+2DA V1.0
+*
+ NAME_REF DESC_REF CAP_REF SAVE MULTI ID HP USABILITY MC_WAS_ID HUMAN ELF HALF_ELF DWARF HALFLING GNOME
+FIGHTER 10174 9556 10174 SAVEWAR 0 2 HPWAR 0x800 0x0008 1 1 1 1 1 1
+RANGER 10173 9557 10173 SAVEWAR 0 12 HPWAR 0x200000 0x0100 1 1 1 0 0 0
+PALADIN 10194 9558 10194 SAVEWAR 0 6 HPWAR 0x100000 -1 1 0 0 0 0 0
+CLERIC 10177 9559 10177 SAVEPRS 0 3 HPPRS 128 0x0020 1 1 1 1 1 1
+DRUID 10186 9560 10186 SAVEPRS 0 11 HPPRS 0x40000000 0x0080 1 1 1 0 0 0
+MAGE 10176 9563 10176 SAVEWIZ 0 1 HPWIZ 0x40000 0x0010 1 1 1 0 0 2
+THIEF 10175 9561 10175 SAVEROG 0 4 HPROG 0x400000 0x0040 1 1 1 1 1 1
+BARD 10179 9562 10179 SAVEROG 0 5 HPROG 64 -1 1 0 1 0 0 0
+FIGHTER_THIEF 10178 9572 10178 * 10 9 0 0x20000 -1 0 1 1 1 1 1
+FIGHTER_CLERIC 10187 9573 10187 * 6 8 0 0x4000 -1 0 0 1 1 0 1
+FIGHTER_MAGE 10189 9574 10189 * 3 7 0 0x2000 -1 0 1 1 0 0 2
+MAGE_THIEF 10193 9575 10193 * 9 13 0 0x80000 -1 0 1 1 0 0 2
+CLERIC_MAGE 10180 9577 10180 * 5 14 0 256 -1 0 0 1 0 0 2
+CLERIC_THIEF 10184 9578 10184 * 12 15 0 512 -1 0 0 0 0 0 1
+FIGHTER_DRUID 10188 9579 10188 * 1026 16 0 0x1000 -1 0 0 1 0 0 0
+CLERIC_RANGER 10182 9580 10182 * 2052 18 0 1024 -1 0 0 1 0 0 0
+FIGHTER_MAGE_THIEF 10191 9576 10191 * 11 10 0 0x10000 -1 0 1 1 0 0 0
+FIGHTER_MAGE_CLERIC 10190 9581 10190 * 7 17 0 0x8000 -1 0 0 1 0 0 0
diff --git a/gemrb/override/how/cldamah.pro b/gemrb/override/how/cldamah.pro
new file mode 100644
index 0000000..aec3d15
Binary files /dev/null and b/gemrb/override/how/cldamah.pro differ
diff --git a/gemrb/override/how/cloud.pro b/gemrb/override/how/cloud.pro
new file mode 100644
index 0000000..3fd8e26
Binary files /dev/null and b/gemrb/override/how/cloud.pro differ
diff --git a/gemrb/override/how/cloudb.pro b/gemrb/override/how/cloudb.pro
new file mode 100644
index 0000000..aa2fdde
Binary files /dev/null and b/gemrb/override/how/cloudb.pro differ
diff --git a/gemrb/override/how/cloudbh.pro b/gemrb/override/how/cloudbh.pro
new file mode 100644
index 0000000..c3aa212
Binary files /dev/null and b/gemrb/override/how/cloudbh.pro differ
diff --git a/gemrb/override/how/cloudkil.pro b/gemrb/override/how/cloudkil.pro
new file mode 100644
index 0000000..2dd544a
Binary files /dev/null and b/gemrb/override/how/cloudkil.pro differ
diff --git a/gemrb/override/how/cloudks.pro b/gemrb/override/how/cloudks.pro
new file mode 100644
index 0000000..27a06e4
Binary files /dev/null and b/gemrb/override/how/cloudks.pro differ
diff --git a/gemrb/override/how/clowncol.2da b/gemrb/override/how/clowncol.2da
new file mode 100644
index 0000000..59103ae
--- /dev/null
+++ b/gemrb/override/how/clowncol.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+*
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
+HAIR 0 1 2 3 4 5 6 7 79 80 81 82 110 111 * * * * * * * * * * * * * * * * * * * *
+SKIN 8 9 10 11 12 13 14 15 16 17 18 19 20 83 84 85 86 87 88 89 90 105 106 107 108 109 112 113 114 * * * * *
+MAJOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 21 22 23
+MINOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 21 22 23
diff --git a/gemrb/override/how/clskills.2da b/gemrb/override/how/clskills.2da
new file mode 100644
index 0000000..593bfd8
--- /dev/null
+++ b/gemrb/override/how/clskills.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+*
+ HATERACE CLERICSPELL MAGESPELL STARTXP BARDSKILL THIEFSKILL LAYHANDS TURNLEVEL BOOKTYPE
+UNUSED * * * * * * * * *
+MAGE * * MXSPLWIZ 0 * * * *
+FIGHTER * * * 0 * * * *
+CLERIC * MXSPLPRS * 0 * * * 1
+THIEF * * * 0 * SKILLS * *
+BARD * * MXSPLBRD 0 SKILLBRD * * *
+PALADIN * MXSPLPAL * 0 * * PALADIN 5
+FIGHTER_MAGE * * MXSPLWIZ 0 * * * *
+FIGHTER_CLERIC * MXSPLPRS * 0 * * * *
+FIGHTER_THIEF * * * 0 * SKILLS * *
+FIGHTER_MAGE_THIEF * * MXSPLWIZ 0 * SKILLS * *
+DRUID * MXSPLDRU * 0 * * * *
+RANGER HATERACE MXSPLRAN * 0 * * * *
+MAGE_THIEF * * MXSPLWIZ 0 * SKILLS * *
+CLERIC_MAGE * MXSPLPRS MXSPLWIZ 0 * * * *
+CLERIC_THIEF * MXSPLPRS * 0 * SKILLS * *
+FIGHTER_DRUID * MXSPLDRU * 0 * * * *
+FIGHTER_MAGE_CLERIC * MXSPLPRS MXSPLWIZ 0 * * * *
+CLERIC_RANGER HATERACE MXSPLPRS * 0 * * * *
diff --git a/gemrb/override/how/clssplab.2da b/gemrb/override/how/clssplab.2da
new file mode 100644
index 0000000..cc94c44
--- /dev/null
+++ b/gemrb/override/how/clssplab.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+6
+ DEX STR
+UNUSED 6 8
+MAGE 6 4
+FIGHTER 6 8
+CLERIC 4 6
+THIEF 8 6
+BARD 8 6
+PALADIN 6 8
+FIGHTER_MAGE 6 8
+FIGHTER_CLERIC 6 8
+FIGHTER_THIEF 8 8
+FIGHTER_MAGE_THIEF 8 8
+DRUID 4 6
+RANGER 6 8
+MAGE_THIEF 8 6
+CLERIC_MAGE 6 6
+CLERIC_THIEF 8 6
+FIGHTER_DRUID 6 8
+FIGHTER_MAGE_CLERIC 6 8
+CLERIC_RANGER 6 8
diff --git a/gemrb/override/how/clwounh.pro b/gemrb/override/how/clwounh.pro
new file mode 100644
index 0000000..10efa84
Binary files /dev/null and b/gemrb/override/how/clwounh.pro differ
diff --git a/gemrb/override/how/cmdamah.pro b/gemrb/override/how/cmdamah.pro
new file mode 100644
index 0000000..d408401
Binary files /dev/null and b/gemrb/override/how/cmdamah.pro differ
diff --git a/gemrb/override/how/cmwounh.pro b/gemrb/override/how/cmwounh.pro
new file mode 100644
index 0000000..e7e644d
Binary files /dev/null and b/gemrb/override/how/cmwounh.pro differ
diff --git a/gemrb/override/how/cobones.pro b/gemrb/override/how/cobones.pro
new file mode 100644
index 0000000..0f2475a
Binary files /dev/null and b/gemrb/override/how/cobones.pro differ
diff --git a/gemrb/override/how/cobonh1.pro b/gemrb/override/how/cobonh1.pro
new file mode 100644
index 0000000..05d1e99
Binary files /dev/null and b/gemrb/override/how/cobonh1.pro differ
diff --git a/gemrb/override/how/cobonh2.pro b/gemrb/override/how/cobonh2.pro
new file mode 100644
index 0000000..d42d11e
Binary files /dev/null and b/gemrb/override/how/cobonh2.pro differ
diff --git a/gemrb/override/how/cocold.pro b/gemrb/override/how/cocold.pro
new file mode 100644
index 0000000..6953993
Binary files /dev/null and b/gemrb/override/how/cocold.pro differ
diff --git a/gemrb/override/how/cocoldh.pro b/gemrb/override/how/cocoldh.pro
new file mode 100644
index 0000000..a785192
Binary files /dev/null and b/gemrb/override/how/cocoldh.pro differ
diff --git a/gemrb/override/how/cofire.pro b/gemrb/override/how/cofire.pro
new file mode 100644
index 0000000..ef23e1b
Binary files /dev/null and b/gemrb/override/how/cofire.pro differ
diff --git a/gemrb/override/how/coldh.pro b/gemrb/override/how/coldh.pro
new file mode 100644
index 0000000..dd5e692
Binary files /dev/null and b/gemrb/override/how/coldh.pro differ
diff --git a/gemrb/override/how/colrspry.pro b/gemrb/override/how/colrspry.pro
new file mode 100644
index 0000000..1fa7f2d
Binary files /dev/null and b/gemrb/override/how/colrspry.pro differ
diff --git a/gemrb/override/how/confush.pro b/gemrb/override/how/confush.pro
new file mode 100644
index 0000000..3027e09
Binary files /dev/null and b/gemrb/override/how/confush.pro differ
diff --git a/gemrb/override/how/confusp.pro b/gemrb/override/how/confusp.pro
new file mode 100644
index 0000000..bb4b8b6
Binary files /dev/null and b/gemrb/override/how/confusp.pro differ
diff --git a/gemrb/override/how/confusw.pro b/gemrb/override/how/confusw.pro
new file mode 100644
index 0000000..ea6e585
Binary files /dev/null and b/gemrb/override/how/confusw.pro differ
diff --git a/gemrb/override/how/conjuh.pro b/gemrb/override/how/conjuh.pro
new file mode 100644
index 0000000..34eefc4
Binary files /dev/null and b/gemrb/override/how/conjuh.pro differ
diff --git a/gemrb/override/how/conjut.pro b/gemrb/override/how/conjut.pro
new file mode 100644
index 0000000..6ef8ba1
Binary files /dev/null and b/gemrb/override/how/conjut.pro differ
diff --git a/gemrb/override/how/copest.pro b/gemrb/override/how/copest.pro
new file mode 100644
index 0000000..6853632
Binary files /dev/null and b/gemrb/override/how/copest.pro differ
diff --git a/gemrb/override/how/cry150.pro b/gemrb/override/how/cry150.pro
new file mode 100644
index 0000000..ac00906
Binary files /dev/null and b/gemrb/override/how/cry150.pro differ
diff --git a/gemrb/override/how/cry200.pro b/gemrb/override/how/cry200.pro
new file mode 100644
index 0000000..2ab9ac5
Binary files /dev/null and b/gemrb/override/how/cry200.pro differ
diff --git a/gemrb/override/how/cry225.pro b/gemrb/override/how/cry225.pro
new file mode 100644
index 0000000..afb7c92
Binary files /dev/null and b/gemrb/override/how/cry225.pro differ
diff --git a/gemrb/override/how/cry250.pro b/gemrb/override/how/cry250.pro
new file mode 100644
index 0000000..31ee26e
Binary files /dev/null and b/gemrb/override/how/cry250.pro differ
diff --git a/gemrb/override/how/cry500np.pro b/gemrb/override/how/cry500np.pro
new file mode 100644
index 0000000..13d718a
Binary files /dev/null and b/gemrb/override/how/cry500np.pro differ
diff --git a/gemrb/override/how/csdamah.pro b/gemrb/override/how/csdamah.pro
new file mode 100644
index 0000000..76eaa24
Binary files /dev/null and b/gemrb/override/how/csdamah.pro differ
diff --git a/gemrb/override/how/cstrenh.pro b/gemrb/override/how/cstrenh.pro
new file mode 100644
index 0000000..eb5136a
Binary files /dev/null and b/gemrb/override/how/cstrenh.pro differ
diff --git a/gemrb/override/how/cswounh.pro b/gemrb/override/how/cswounh.pro
new file mode 100644
index 0000000..195106b
Binary files /dev/null and b/gemrb/override/how/cswounh.pro differ
diff --git a/gemrb/override/how/curseh.pro b/gemrb/override/how/curseh.pro
new file mode 100644
index 0000000..e3c45c4
Binary files /dev/null and b/gemrb/override/how/curseh.pro differ
diff --git a/gemrb/override/how/cwelem1.pro b/gemrb/override/how/cwelem1.pro
new file mode 100644
index 0000000..a0ec19d
Binary files /dev/null and b/gemrb/override/how/cwelem1.pro differ
diff --git a/gemrb/override/how/cwelemh.pro b/gemrb/override/how/cwelemh.pro
new file mode 100644
index 0000000..9f2052d
Binary files /dev/null and b/gemrb/override/how/cwelemh.pro differ
diff --git a/gemrb/override/how/cwelemx.pro b/gemrb/override/how/cwelemx.pro
new file mode 100644
index 0000000..3f0cdb7
Binary files /dev/null and b/gemrb/override/how/cwelemx.pro differ
diff --git a/gemrb/override/how/cynicism.2da b/gemrb/override/how/cynicism.2da
new file mode 100644
index 0000000..508e837
--- /dev/null
+++ b/gemrb/override/how/cynicism.2da
@@ -0,0 +1,14 @@
+2DA V1.0
+*
+ STRREF
+0 0x6155
+1 0x6156
+2 0x6157
+3 0x6158
+4 0x6159
+5 0x615A
+6 0x615B
+7 0x615C
+8 0x615D
+9 0x615E
+10 0x615F
diff --git a/gemrb/override/how/dagger.pro b/gemrb/override/how/dagger.pro
new file mode 100644
index 0000000..284c351
Binary files /dev/null and b/gemrb/override/how/dagger.pro differ
diff --git a/gemrb/override/how/daggerex.pro b/gemrb/override/how/daggerex.pro
new file mode 100644
index 0000000..e33a595
Binary files /dev/null and b/gemrb/override/how/daggerex.pro differ
diff --git a/gemrb/override/how/damage.2da b/gemrb/override/how/damage.2da
new file mode 100644
index 0000000..f2fbf14
--- /dev/null
+++ b/gemrb/override/how/damage.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+*
+ MAIN SPARKS GRADIENT
+CRIT BLOODCR * -1
+SMALL BLOODS * -1
+MEDIUM BLOODM * -1
+LARGE BLOODL * -1
+FIRES FIREH FIREL -1
+FIREM FIREH FIREL -1
+FIREL FIREH FIREL -1
+SPARKS ELECTRH ELECTRL -1
+SPARKM ELECTRH ELECTRL -1
+SPARKL ELECTRH ELECTRL -1
+ICES COLDH COLDL -1
+ICEM COLDH COLDL -1
+ICEL COLDH COLDL -1
diff --git a/gemrb/override/how/dart.pro b/gemrb/override/how/dart.pro
new file mode 100644
index 0000000..a8ec832
Binary files /dev/null and b/gemrb/override/how/dart.pro differ
diff --git a/gemrb/override/how/dartex.pro b/gemrb/override/how/dartex.pro
new file mode 100644
index 0000000..b09ac20
Binary files /dev/null and b/gemrb/override/how/dartex.pro differ
diff --git a/gemrb/override/how/dbreath.pro b/gemrb/override/how/dbreath.pro
new file mode 100644
index 0000000..e64a64f
Binary files /dev/null and b/gemrb/override/how/dbreath.pro differ
diff --git a/gemrb/override/how/ddeath.pro b/gemrb/override/how/ddeath.pro
new file mode 100644
index 0000000..4b587b4
Binary files /dev/null and b/gemrb/override/how/ddeath.pro differ
diff --git a/gemrb/override/how/ddeath2.pro b/gemrb/override/how/ddeath2.pro
new file mode 100644
index 0000000..e1a5407
Binary files /dev/null and b/gemrb/override/how/ddeath2.pro differ
diff --git a/gemrb/override/how/ddoorh.pro b/gemrb/override/how/ddoorh.pro
new file mode 100644
index 0000000..fa09734
Binary files /dev/null and b/gemrb/override/how/ddoorh.pro differ
diff --git a/gemrb/override/how/defsound.2da b/gemrb/override/how/defsound.2da
new file mode 100644
index 0000000..22cfee3
--- /dev/null
+++ b/gemrb/override/how/defsound.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+*
+ RESREF
+OPEN AMB_D03A
+CLOSE AMB_D03B
+HOPEN AMB_D04A
+HCLOSE AMB_D04B
+BUTTON1 GAM_09
+BUTTON2 GAM_03
+BUTTON3 GAM_04
+OPENFAIL *
+CLOSEFAIL *
+ITEM_GONE EFF_M02
+SECRET ACT_09
+11 *
+12 *
+13 *
+14 *
+15 *
+16 *
+17 *
+18 *
+19 *
+LIGHTNING1 AMB_E13A
+LIGHTNING2 AMB_E13B
+LIGHTNING3 AMB_E13F
+RAIN AMB_E11
+SNOW AMB_E02B
+
diff --git a/gemrb/override/how/destruh.pro b/gemrb/override/how/destruh.pro
new file mode 100644
index 0000000..87dfeaa
Binary files /dev/null and b/gemrb/override/how/destruh.pro differ
diff --git a/gemrb/override/how/dfog.pro b/gemrb/override/how/dfog.pro
new file mode 100644
index 0000000..702da41
Binary files /dev/null and b/gemrb/override/how/dfog.pro differ
diff --git a/gemrb/override/how/disint.pro b/gemrb/override/how/disint.pro
new file mode 100644
index 0000000..5d5f5d7
Binary files /dev/null and b/gemrb/override/how/disint.pro differ
diff --git a/gemrb/override/how/disinth.pro b/gemrb/override/how/disinth.pro
new file mode 100644
index 0000000..2b4b80f
Binary files /dev/null and b/gemrb/override/how/disinth.pro differ
diff --git a/gemrb/override/how/dispel.pro b/gemrb/override/how/dispel.pro
new file mode 100644
index 0000000..3984714
Binary files /dev/null and b/gemrb/override/how/dispel.pro differ
diff --git a/gemrb/override/how/divinh.pro b/gemrb/override/how/divinh.pro
new file mode 100644
index 0000000..475830a
Binary files /dev/null and b/gemrb/override/how/divinh.pro differ
diff --git a/gemrb/override/how/divint.pro b/gemrb/override/how/divint.pro
new file mode 100644
index 0000000..e2972f2
Binary files /dev/null and b/gemrb/override/how/divint.pro differ
diff --git a/gemrb/override/how/dspell.pro b/gemrb/override/how/dspell.pro
new file mode 100644
index 0000000..4eb4d1d
Binary files /dev/null and b/gemrb/override/how/dspell.pro differ
diff --git a/gemrb/override/how/dspellh.pro b/gemrb/override/how/dspellh.pro
new file mode 100644
index 0000000..9029a53
Binary files /dev/null and b/gemrb/override/how/dspellh.pro differ
diff --git a/gemrb/override/how/dualclas.2da b/gemrb/override/how/dualclas.2da
new file mode 100644
index 0000000..8d40d90
--- /dev/null
+++ b/gemrb/override/how/dualclas.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+0
+ FIGHTER CLERIC MAGE THIEF DRUID RANGER
+MAGE 1 1 0 1 0 0
+FIGHTER 0 1 1 1 1 0
+CLERIC 1 0 1 1 0 1
+THIEF 1 1 1 0 0 0
+BARD 0 0 0 0 0 0
+PALADIN 0 0 0 0 0 0
+DRUID 1 0 0 0 0 0
+RANGER 0 1 0 0 0 0
+FIGHTER_MAGE 0 0 0 0 0 0
+FIGHTER_CLERIC 0 0 0 0 0 0
+FIGHTER_THIEF 0 0 0 0 0 0
+FIGHTER_MAGE_THIEF 0 0 0 0 0 0
+MAGE_THIEF 0 0 0 0 0 0
+CLERIC_MAGE 0 0 0 0 0 0
+CLERIC_THIEF 0 0 0 0 0 0
+FIGHTER_DRUID 0 0 0 0 0 0
+FIGHTER_MAGE_CLERIC 0 0 0 0 0 0
+CLERIC_RANGER 0 0 0 0 0 0
+ABJURER 1 1 0 1 0 0
+CONJURER 1 1 0 1 0 0
+DIVINER 1 1 0 1 0 0
+ENCHANTER 1 1 0 1 0 0
+ILLUSIONIST 1 1 0 1 0 0
+INVOKER 1 1 0 1 0 0
+NECROMANCER 1 1 0 1 0 0
+TRANSMUTER 1 1 0 1 0 0
diff --git a/gemrb/override/how/effects.ids b/gemrb/override/how/effects.ids
new file mode 100644
index 0000000..be5bcb2
--- /dev/null
+++ b/gemrb/override/how/effects.ids
@@ -0,0 +1,301 @@
+IDS
+0x0 ACVsDamageTypeModifier
+0x1 AttacksPerRoundModifier
+0x2 Cure:Sleep
+0x3 State:Berserk
+0x4 Cure:Berserk
+0x5 State:Charmed
+0x6 CharismaModifier
+0x7 Color:SetPalette
+0x8 Color:SetRGB
+0x9 Color:PulseRGB
+0xa ConstitutionModifier
+0xb Cure:Poison
+0xc Damage
+0xd Death
+0xe Cure:Defrost
+0xf DexterityModifier
+0x10 State:Hasted
+0x11 CurrentHPModifier
+0x12 MaximumHPModifier
+0x13 IntelligenceModifier
+0x14 State:Invisible
+0x15 LoreModifier
+0x16 LuckModifier
+0x17 MoraleModifier
+0x18 State:Panic
+0x19 State:Poisoned
+0x1a RemoveCurse
+0x1b AcidResistanceModifier
+0x1c ColdResistanceModifier
+0x1d ElectricityResistanceModifier
+0x1e FireResistanceModifier
+0x1f MagicDamageResistanceModifier
+0x20 Cure:Death
+0x21 SaveVsDeathModifier
+0x22 SaveVsWandsModifier
+0x23 SaveVsPolyModifier
+0x24 SaveVsBreathModifier
+0x25 SaveVsSpellsModifier
+0x26 State:Silenced
+0x27 State:Helpless
+0x28 State:Slowed
+0x29 Sparkle
+0x2a WizardSpellSlotsModifier
+0x2b Cure:Petrification
+0x2c StrengthModifier
+0x2d State:Stun
+0x2e Cure:Stun
+0x2f Cure:Invisible
+0x30 Cure:Silence
+0x31 WisdomModifier
+0x32 Color:BriefRGB
+0x33 Color:DarkenRGB
+0x34 Color:GlowRGB
+0x35 AnimationIDModifier
+0x36 ToHitModifier
+0x37 KillCreatureType
+0x38 Alignment:Invert
+0x39 Alignment:Change
+0x3a DispelEffects
+0x3b StealthModifier
+0x3c MiscastMagicModifier
+0x3d AlchemyModifier
+0x3e PriestSpellSlotsModifier
+0x3f State:Infravision
+0x40 Cure:Infravision
+0x41 State:Blur
+0x42 TransparencyModifier
+0x43 SummonCreature
+0x44 UnsummonCreature
+0x45 State:NonDetection
+0x46 Cure:NonDetection
+0x47 SexModifier
+0x48 AIIdentifierModifier
+0x49 DamageBonusModifier
+0x4a State:Blind
+0x4b Cure:Blind
+0x4c State:Feeblemind
+0x4d Cure:Feeblemind
+0x4e State:Diseased
+0x4f Cure:Disease
+0x50 State:Deafness
+0x51 Cure:Deafness
+0x52 SetAIScript
+0x53 Protection:Projectile
+0x54 MagicalFireResistanceModifier
+0x55 MagicalColdResistanceModifier
+0x56 SlashingResistanceModifier
+0x57 CrushingResistanceModifier
+0x58 PiercingResistanceModifier
+0x59 MissilesResistanceModifier
+0x5a OpenLocksModifier
+0x5b FindTrapsModifier
+0x5c PickPocketsModifier
+0x5d FatigueModifier
+0x5e IntoxicationModifier
+0x5f TrackingModifier
+0x60 LevelModifier
+0x61 StrengthBonusModifier
+0x62 State:Regenerating
+0x63 SpellDurationModifier
+0x64 Protection:Creature
+0x65 Protection:Opcode
+0x66 Protection:SpellLevel
+0x67 ChangeName
+0x68 ExperienceModifier
+0x69 GoldModifier
+0x6a MoraleBreakModifier
+0x6b PortraitChange
+0x6c ReputationModifier
+0x6d State:Hold3
+0x6e RetreatFrom
+0x6f Item:CreateMagic
+0x70 Item:Remove
+0x71 Item:Equip
+0x72 Dither
+0x73 DetectAlignment
+0x74 Cure:Invisible2
+0x75 Reveal:Area
+0x76 Reveal:Creatures
+0x77 MirrorImage
+0x78 Protection:Weapons
+0x79 VisualAnimationEffect
+0x7a Item:CreateInventory
+0x7b Item:RemoveInventory
+0x7c DimensionDoor
+0x7d Unlock
+0x7e MovementRateModifier
+0x7f MonsterSummoning
+0x80 State:Confused
+0x81 AidNonCumulative
+0x82 BlessNonCumulative
+0x83 ChantNonCumulative
+0x84 HolyNonCumulative
+0x85 LuckNonCumulative
+0x86 State:Petrification
+0x87 Polymorph
+0x88 ForceVisible
+0x89 ChantBadNonCumulative
+0x8a AnimationStateChange
+0x8b DisplayString
+0x8c CastingGlow
+0x8d VisualSpellHit
+0x8e Icon:Display
+0x8f Item:CreateInSlot
+0x90 DisableButton
+0x91 DisableCasting
+0x92 Spell:Cast
+0x93 Spell:Learn
+0x94 Spell:CastPoint
+0x95 Identify
+0x96 FindTraps
+0x97 ReplaceCreature
+0x98 PlayMovie
+0x99 Overlay:Sanctuary
+0x9a Overlay:Entangle
+0x9b Overlay:MinorGlobe
+0x9c Overlay:ShieldGlobe
+0x9d Overlay:Web
+0x9e Overlay:Grease
+0x9f MirrorImageModifier
+0xa0 Cure:Sanctuary
+0xa1 Cure:Panic
+0xa2 Cure:Hold
+0xa3 FreeAction
+0xa4 Cure:Intoxication
+0xa5 PauseTarget
+0xa6 MagicResistanceModifier
+0xa7 MissileHitModifier
+0xa8 RemoveCreature
+0xa9 Icon:Disable
+0xaa DamageAnimation
+0xab Spell:Add
+0xac Spell:Remove
+0xad PoisonResistanceModifier
+0xae PlaySound
+0xaf State:Hold
+0xb0 MovementRateModifier2
+0xb1 ApplyEffect
+0xb2 ToHitVsCreature
+0xb3 DamageVsCreature
+0xb4 CantUseItem
+0xb5 CantUseItemType
+0xb6 ApplyEffectItem
+0xb7 ApplyEffectItemType
+0xb8 DontJumpModifier
+0xb9 State:Hold2
+0xba MoveToArea
+0xbb Variable:StoreLocalVariable
+0xbc AuraCleansingModifier
+0xbd CastingSpeedModifier
+0xbe AttackSpeedModifier
+0xbf CastingLevelModifier
+0xc0 FindFamiliar
+0xc1 InvisibleDetection
+0xc2 IgnoreDialogPause
+0xc3 FamiliarBond
+0xc4 FamiliarMarker
+0xc5 Bounce:Projectile
+0xc6 Bounce:Opcode
+0xc7 Bounce:SpellLevel
+0xc8 Bounce:SpellLevelDec
+0xc9 Protection:SpellLevelDec
+0xca Bounce:School
+0xcb Bounce:SecondaryType
+0xcc Protection:School
+0xcd Protection:SecondaryType
+0xce Protection:Spell2
+0xcf Bounce:Spell
+0xd0 MinimumHPModifier
+0xd1 PowerWordKill
+0xd2 PowerWordStun
+0xd3 State:Imprisonment
+0xd4 Cure:Imprisonment
+0xd5 Maze
+0xd6 CastFromList
+0xd7 PlayVisualEffect
+0xd8 LevelDrainModifier
+0xd9 PowerWordSleep
+0xda StoneskinModifier
+0xdb ACVsCreatureType
+0xdc DispelSchool
+0xdd DispelSecondaryType
+0xde RandomTeleport
+0xdf Protection:SchoolDec
+0xe0 Cure:LevelDrain
+0xe1 Reveal:Magic
+0xe2 Protection:SecondaryTypeDec
+0xe3 Bounce:SchoolDec
+0xe4 Bounce:SecondaryTypeDec
+0xe5 DispelSchoolOne
+0xe6 DispelSecondaryTypeOne
+0xe7 TimeStop
+0xe8 Color:FadeRGB
+0xe9 IWDVisualSpellHit
+0xea ColdDamage
+0xeb CastingGlow2
+0xec ChillTouch
+0xed CrushingDamage
+0xee SaveBonus
+0xef SlowPoison
+0xf0 IWDMonsterSummoning
+0xf1 VampiricTouch
+0xf2 Overlay
+0xf3 AnimateDead
+0xf4 Prayer2
+0xf5 Curse2
+0xf6 SummonMonster2
+0xf7 BurningBlood
+0xf8 SummonShadowMonster
+0xf9 Recitation
+0xfa RecitationBad
+0xfb Hold2
+0xfc BlindingOrb
+0xfd ACVsDamageTypeModifier2
+0xfe RemoveEffects
+0xff SalamanderAura
+0x100 UmberHulkGaze
+0x101 ZombieLordAura
+0x102 Protection:Spell
+0x103 SummonCreature2
+0x104 AvatarRemoval
+0x105 Protection:Opcode2
+0x106 SummonPomab
+0x107 ControlUndead
+0x108 StaticCharge
+0x109 CloakOfFear
+0x10a MovementRateModifier3
+0x10b Cure:Confusion
+0x10c EyeOfTheMind
+0x10d EyeOfTheSword
+0x10e EyeOfTheMage
+0x10f EyeOfVenom
+0x110 EyeOfTheSpirit
+0x111 EyeOfFortitude
+0x112 EyeOfStone
+0x113 RemoveSevenEyes
+0x114 RemoveEffect
+0x115 SoulEater
+0x116 ShroudOfFlame
+0x117 AnimalRage
+0x118 TurnUndead
+0x119 VitriolicSphere
+0x11a SuppressHP
+0x11b FloatText
+0x11c MaceOfDisruption
+0x11d State:Sleep
+0x11e Reveal:Tracks
+0x11f Protection:Backstab
+0x120 State:Set
+0x121 Cutscene
+0x122 Protection:Spell3
+0x123 RodOfSmithing
+0x124 MagicalRest
+0x125 BeholderDispelMagic
+0x126 HarpyWail
+0x127 JackalWereGaze
+0x128 ModifyGlobalVariable
+0x129 HideInShadowsModifier
+0x12a UseMagicDeviceModifier
+399 AlterAnimation
diff --git a/gemrb/override/how/efftext.2da b/gemrb/override/how/efftext.2da
new file mode 100644
index 0000000..78829b0
--- /dev/null
+++ b/gemrb/override/how/efftext.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+-1
+ EFFECT_NAME STRREF
+25 POISON 14017
+13 DEATH 14026
+134 PETRIFICATION 14127
+#12 DAMAGE 14027
+39 SLEEP 20438
+68 UNSUMMON 14065
+
diff --git a/gemrb/override/how/electrh.pro b/gemrb/override/how/electrh.pro
new file mode 100644
index 0000000..2046bda
Binary files /dev/null and b/gemrb/override/how/electrh.pro differ
diff --git a/gemrb/override/how/emotion.pro b/gemrb/override/how/emotion.pro
new file mode 100644
index 0000000..2178dd4
Binary files /dev/null and b/gemrb/override/how/emotion.pro differ
diff --git a/gemrb/override/how/enchah.pro b/gemrb/override/how/enchah.pro
new file mode 100644
index 0000000..bc16f04
Binary files /dev/null and b/gemrb/override/how/enchah.pro differ
diff --git a/gemrb/override/how/enchannp.pro b/gemrb/override/how/enchannp.pro
new file mode 100644
index 0000000..e09813a
Binary files /dev/null and b/gemrb/override/how/enchannp.pro differ
diff --git a/gemrb/override/how/enchat.pro b/gemrb/override/how/enchat.pro
new file mode 100644
index 0000000..0c99116
Binary files /dev/null and b/gemrb/override/how/enchat.pro differ
diff --git a/gemrb/override/how/entangle.pro b/gemrb/override/how/entangle.pro
new file mode 100644
index 0000000..77a9dc4
Binary files /dev/null and b/gemrb/override/how/entangle.pro differ
diff --git a/gemrb/override/how/equake.pro b/gemrb/override/how/equake.pro
new file mode 100644
index 0000000..c8f920c
Binary files /dev/null and b/gemrb/override/how/equake.pro differ
diff --git a/gemrb/override/how/exaltah.pro b/gemrb/override/how/exaltah.pro
new file mode 100644
index 0000000..6a02ac3
Binary files /dev/null and b/gemrb/override/how/exaltah.pro differ
diff --git a/gemrb/override/how/factioh.pro b/gemrb/override/how/factioh.pro
new file mode 100644
index 0000000..60d568b
Binary files /dev/null and b/gemrb/override/how/factioh.pro differ
diff --git a/gemrb/override/how/findtrap.pro b/gemrb/override/how/findtrap.pro
new file mode 100644
index 0000000..768c882
Binary files /dev/null and b/gemrb/override/how/findtrap.pro differ
diff --git a/gemrb/override/how/fireball.pro b/gemrb/override/how/fireball.pro
new file mode 100644
index 0000000..4acc632
Binary files /dev/null and b/gemrb/override/how/fireball.pro differ
diff --git a/gemrb/override/how/fireblic.pro b/gemrb/override/how/fireblic.pro
new file mode 100644
index 0000000..f9b920f
Binary files /dev/null and b/gemrb/override/how/fireblic.pro differ
diff --git a/gemrb/override/how/firebolt.pro b/gemrb/override/how/firebolt.pro
new file mode 100644
index 0000000..a7b3b25
Binary files /dev/null and b/gemrb/override/how/firebolt.pro differ
diff --git a/gemrb/override/how/firebtbl.pro b/gemrb/override/how/firebtbl.pro
new file mode 100644
index 0000000..5599064
Binary files /dev/null and b/gemrb/override/how/firebtbl.pro differ
diff --git a/gemrb/override/how/fireh.pro b/gemrb/override/how/fireh.pro
new file mode 100644
index 0000000..c41f7de
Binary files /dev/null and b/gemrb/override/how/fireh.pro differ
diff --git a/gemrb/override/how/firestor.pro b/gemrb/override/how/firestor.pro
new file mode 100644
index 0000000..f3701e3
Binary files /dev/null and b/gemrb/override/how/firestor.pro differ
diff --git a/gemrb/override/how/fistweap.2da b/gemrb/override/how/fistweap.2da
new file mode 100644
index 0000000..e492835
--- /dev/null
+++ b/gemrb/override/how/fistweap.2da
@@ -0,0 +1,3 @@
+2DA V1.0
+FIST
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
diff --git a/gemrb/override/how/fodeath.pro b/gemrb/override/how/fodeath.pro
new file mode 100644
index 0000000..16cf8c3
Binary files /dev/null and b/gemrb/override/how/fodeath.pro differ
diff --git a/gemrb/override/how/fonts.2da b/gemrb/override/how/fonts.2da
new file mode 100644
index 0000000..fe1ed92
--- /dev/null
+++ b/gemrb/override/how/fonts.2da
@@ -0,0 +1,19 @@
+2DA V1.0
+NORMAL
+ RESREF NEED_PALETTE FIRST_CHAR
+0 NORMAL 1 0
+1 INFOFONT 1 0
+2 NUMBER 0 47
+3 INITIALS 0 0
+4 NUMBER2 0 47
+5 NUMBER3 0 47
+6 NUMFONT 1 0
+7 REALMS 0 0
+8 REALMS2 1 0
+9 STONEBIG 0 0
+10 STONESM2 0 0
+11 STONESM3 0 0
+12 STONESML 0 0
+13 TOOLFONT 1 0
+14 STATES 0 0
+15 STATES2 0 0
diff --git a/gemrb/override/how/formatio.2da b/gemrb/override/how/formatio.2da
new file mode 100644
index 0000000..c369656
--- /dev/null
+++ b/gemrb/override/how/formatio.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+-10
+# generated by make_formation.py, do not edit
+ X1 Y1 X2 Y2 X3 Y3 X4 Y4 X5 Y5 X6 Y6 X7 Y7 X8 Y8 X9 Y9 X10 Y10 X11 Y11 X12 Y12 X13 Y13 X14 Y14 X15 Y15 X16 Y16 X17 Y17 X18 Y18 X19 Y19 X20 Y20
+FOLLOW 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
+T 0 0 48 0 -48 0 0 48 0 84 0 120 0 156 0 192 0 228 0 264 0 300 0 336 0 372 0 408 0 444 0 480 0 516 0 552 0 588 0 624
+GATHER 0 -36 48 -24 -48 -24 48 24 -48 24 0 36 48 48 -48 48 0 72 48 72 -48 72 0 108 48 96 -48 96 0 144 48 120 -48 120 0 180 48 144 -48 144
+4AND2 0 0 64 0 -64 0 128 0 0 48 64 48 -64 48 128 48 0 96 64 96 -64 96 128 96 0 144 64 144 -64 144 128 144 0 192 64 192 -64 192 128 192
+3BY2 0 0 64 0 -64 0 0 48 64 48 -64 48 0 96 64 96 -64 96 0 144 64 144 -64 144 0 192 64 192 -64 192 0 240 64 240 -64 240 0 288 64 288
+PROTECT 0 0 0 -36 -64 0 64 0 -32 48 32 48 0 24 0 48 0 72 0 96 0 120 0 144 0 168 0 192 0 216 0 240 0 264 0 288 0 312 0 336
+2BY3 -24 0 24 0 -24 48 24 48 -24 84 24 84 -24 120 24 120 -24 156 24 156 -24 192 24 192 -24 228 24 228 -24 264 24 264 -24 300 24 300 -24 336 24 336
+RANK -32 0 32 0 -96 0 96 0 -160 0 160 0 -224 0 224 0 -288 0 288 0 -352 0 352 0 -416 0 416 0 -480 0 480 0 -544 0 544 0 -608 0 608 0
+V 0 0 64 0 -15 48 49 48 -30 96 34 96 -45 144 19 144 -60 192 4 192 -75 240 -11 240 -90 288 -26 288 -105 336 -41 336 -120 384 -56 384 -135 432 -71 432
+WEDGE 0 0 64 36 -64 36 -124 72 124 72 0 72 0 144 -124 144 124 144 0 180 -124 180 124 180 0 216 -124 216 124 216 0 252 -124 252 124 252 0 288 -124 288
+S 0 0 64 24 0 48 64 72 0 96 64 120 0 144 64 168 0 192 64 216 0 240 64 264 0 288 64 312 0 336 64 360 0 384 64 408 0 432 64 456
+LINE 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
diff --git a/gemrb/override/how/fseed.pro b/gemrb/override/how/fseed.pro
new file mode 100644
index 0000000..2d0b929
Binary files /dev/null and b/gemrb/override/how/fseed.pro differ
diff --git a/gemrb/override/how/fstrikh.pro b/gemrb/override/how/fstrikh.pro
new file mode 100644
index 0000000..bbd9d62
Binary files /dev/null and b/gemrb/override/how/fstrikh.pro differ
diff --git a/gemrb/override/how/gametime.2da b/gemrb/override/how/gametime.2da
new file mode 100644
index 0000000..2d29d2c
--- /dev/null
+++ b/gemrb/override/how/gametime.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+0
+ DURATION
+ROUND_SECONDS 7
+TURN_SECONDS 70
diff --git a/gemrb/override/how/garmorh.pro b/gemrb/override/how/garmorh.pro
new file mode 100644
index 0000000..f4f1d5a
Binary files /dev/null and b/gemrb/override/how/garmorh.pro differ
diff --git a/gemrb/override/how/gaze.pro b/gemrb/override/how/gaze.pro
new file mode 100644
index 0000000..632263a
Binary files /dev/null and b/gemrb/override/how/gaze.pro differ
diff --git a/gemrb/override/how/gemprjtl.ids b/gemrb/override/how/gemprjtl.ids
new file mode 100644
index 0000000..a5cacea
--- /dev/null
+++ b/gemrb/override/how/gemprjtl.ids
@@ -0,0 +1,465 @@
+IDS V1.0
+1 ARROW
+2 ARROWEX
+3 ARROWFLM
+4 ARROWHVY
+5 ARROW
+6 AXE
+7 AXEEX
+8 AXEFLM
+9 AXE
+10 AXE
+11 BOLT
+12 BOLTEX
+13 ARROWHVY
+14 BOLT
+15 BOLT
+16 BULLET
+17 BULLETEX
+18 <flamingbullet>
+19 BULLET
+20 BULLET
+21 SPBRNHND
+22 CALLLIH
+23 CHROMORB
+24 SPCONECO
+25 SPCONEFI
+26 DAGGER
+27 DAGGEREX
+28 <flamingdagger>
+29 DAGGER
+30 DAGGER
+31 DART
+32 DARTEX
+33 <flamingdart>
+34 DART
+35 DART
+36 MAGICMIS
+37 FIREBALL
+38 <icefragments>
+39 LIGHTB
+40 STONE
+41 SLEEP
+42 <skeleton>
+43 SPSMPUFF
+44 SPSMKJET
+45 SPSMOLD
+46 SPARKLBL
+47 SPARKLGO
+48 SPARKLPU
+49 SPARKLIC
+50 SPARKLST
+51 SPARKLBK
+52 SPARKLCH
+53 SPARKLRE
+54 SPARKLGR
+55 SPEAR
+56 SPEAREX
+57 <flamingspear>
+58 SPEAR
+59 SPEAR
+60 <starsprite>
+61 <stoned>
+62 <webtravel>
+63 WEB
+64 GAZE
+65 HMIGHTH
+66 FLMSTRK
+67 MAGICMIS
+68 SPMAGMIS
+69 SPMAGMIS
+70 SPMAGMIS
+71 SPMAGMIS
+72 SPMAGMIS
+73 SPMAGMIS
+74 SPMAGMIS
+75 SPMAGMIS
+76 SPMAGMIS
+77 SPMAGMIS
+78 INVTRAV
+79 FIREBOLT
+80 SKYBOLT
+81 SKYBOLT2
+82 SKYBOLT2
+83 SKYBOLT2
+84 SKYBOLT2
+85 SKYBOLT2
+86 SKYBOLT2
+87 SKYBOLT2
+88 SKYBOLT2
+89 SKYBOLT2
+90 SKYBOLT2
+91 FIRESTOR
+92 LIGHTSTO
+93 INAREA
+94 CLOUD
+95 TRAPSKUL
+96 COLRSPRY
+97 ICESTORM
+98 SPFIREWL
+99 TRAPGLYP
+100 GREASE
+101 ARROWFLG
+102 ARROWFLB
+103 FIREBLGR
+104 FIREBTBL
+105 <potion>
+106 <potionexplode>
+107 ACIDBLOB
+108 SPSCORCH
+109 SPDIMDR
+110 CGNECROM
+111 CGALTERA
+112 CGENCHAN
+113 CGABJURA
+114 CGILLUSI
+115 CGCONJUR
+116 CGINVOCA
+117 CGDIVINA
+118 SHAIR
+119 SHEARTH
+120 SHWATER
+121 SHAIR1
+122 SHEARTH1
+123 SHWATER1
+124 SHAIR2
+125 SHEARTH2
+126 SHWATER2
+127 SHAIR3
+128 SHEARTH3
+129 SHWATER3
+130 SHAIR4
+131 SHEARTH4
+132 SHWATER4
+133 SHAIR5
+134 SHEARTH5
+135 SHWATER5
+136 SHAIR6
+137 SHEARTH6
+138 SHWATER6
+139 SHAIR7
+140 SHEARTH7
+141 SHWATER7
+142 SPBOOM1
+143 SPBOOM2
+144 SPBOOM3
+145 FLMSTRK
+146 HLYMITE
+147 SHAIR7
+148 SPKLARBL
+149 SPKLARGO
+150 SPKLARPU
+151 SPKLARIC
+152 SPKLARST
+153 SPKLARBK
+154 SPKLARCH
+155 SPKLARRE
+156 SPKLARGR
+157 INAREAPA
+158 INAREANP
+159 SPARBLPA
+160 SPARGOPA
+161 SPARPUPA
+162 SPARICPA
+163 SPARSTPA
+164 SPARBKPA
+165 SPARCHPA
+166 SPARREPA
+167 SPARGRPA
+168 SPARBLNP
+169 SPARGONP
+170 SPARPUNP
+171 SPARICNP
+172 SPARSTNP
+173 SPARBKNP
+174 SPARCHNP
+175 SPARRENP
+176 SPARGRNP
+177 SPARMANP
+178 SPARORNP
+179 SPARMAPA
+180 SPARORPA
+181 SPKLARMA
+182 SPKLAROR
+183 SPARKLMA
+184 SPARKLOR
+185 INAREANS
+186 CLOUDKIL
+187 ARROWFLI
+188 COW
+189 HOLD
+190 SPSCORIC
+191 ACIDBLMU
+192 ACIDBLGR
+193 ACIDBLOC
+194 REDHOLY
+195 SHAREA
+196 SHAREA1
+197 SHAREA
+198 SHAREA2
+199 SHAREA3
+200 SHAREA4
+201 SHAREA5
+202 SHAREA6
+203 FIREBLIC
+204 INAREASM
+205 LIGHTB
+206 LIGHTBNB
+207 SPFDEATH
+208 MRAGE
+209 CLIGHT
+210 ASTORM
+211 DFOG
+212 SSTONE
+213 ICLOUD
+214 PFIRE
+215 IPLAGUE
+216 SSSWARM
+217 MMISSILE
+218 ABJURH
+219 ALTERH
+220 INVOCH
+221 NECROH
+222 CONJUH
+223 ENCHAH
+224 ILLUSH
+225 DIVINH
+226 ABJURT
+227 ALTERT
+228 INVOCT
+229 NECROT
+230 CONJUT
+231 ENCHAT
+232 ILLUST
+233 DIVINT
+234 ENTANGLE
+235 AREA1P
+236 AREA1NP
+237 ABJURAP
+238 AREA2
+239 AREA2NP
+240 AREA4NP
+241 ABJURAP
+242 CHANT
+243 FINDTRAP
+244 ALTERAS
+245 DISPEL
+246 ALTERAP
+247 ALTERANP
+248 ENCHAN
+249 ABJURAP
+250 ICELANCE
+251 ALTERA
+252 PRAYER
+253 CONFUSW
+254 EMOTION
+255 MALISON
+256 HARMONY
+257 PROTEVIL
+258 CLOAK
+259 PRAYER
+260 INVTRAV
+261 SCHARGE
+262 ENCHANNP
+263 CHAOS
+264 SHROUD
+265 ABJURAP
+266 DSPELL
+267 DISINT
+268 OFSPHE
+269 FSEED
+270 OFSPHE
+271 PSPRAY
+272 AREA3P
+273 SUNRAY
+274 CONFUSP
+275 SOPAIN
+276 SOHOPE
+277 PWKILL
+278 DFOG2
+279 RAD100
+280 RAD250
+281 BSCLOUD
+282 ZLAURA
+283 GOLCLOUD
+284 MSPORE
+285 ICLOUDB
+286 ICLOUDA
+287 INAREANP
+288 MSUMM1
+289 ASUMM1
+290 CEELEM
+291 CFELEM
+292 CWELEM
+293 PRTL_OP
+294 SAREANP
+295 WWOLF
+296 PRTL_CL
+297 ALANCE
+298 SEATER
+299 SGROWTH
+300 CLOUDB
+301 SWAVE
+302 TSPRAY
+303 WOMOON
+304 WHIRLW
+305 EQUAKE
+306 MOELDA
+307 COBONES
+308 COPEST
+309 UWARD
+310 BBARRIER
+311 SPWRATH
+312 LODISR
+313 MFMISS
+314 SHOUT
+315 VSPHER
+316 SUFFOC
+317 ADHWIL
+318 GSHOUT
+319 CRY225
+320 CRY250
+321 CRY150
+322 CRY200
+323 MFMISS
+324 MFMISS2
+325 MFMISS2
+326 MFMISS2
+327 MFMISS2
+328 MFMISS2
+329 MFMISS2
+330 MFMISS2
+331 MFMISS2
+332 MFMISS2
+333 MFMISS2
+334 SUNFIRE
+335 PWSTUN
+336 HSMITE
+337 UBLIGHT
+338 ENCHANNP
+339 HWORD
+340 HWORD
+341 CRY500NP
+342 WOWISP
+343 RETRIB1
+344 RETRIB2
+345 ASCORCH
+346 PORTAL
+347 OFSPHE
+348 DBREATH
+349 RNG450
+350 CLOUDKS
+351 INVTRAV
+352 ILLUSH
+353 CCDAMAH
+354 RNG450
+355 RNG450
+0x1001 INVTRAV
+0x1002 ABJURH
+0x1003 ALTERH
+0x1004 INVOCH
+0x1005 NECROH
+0x1006 CONJUH
+0x1007 ENCHAH
+0x1008 ILLUSH
+0x1009 DIVINH
+0x100a ARMORH
+0x100b SARMORH
+0x100c GARMORH
+0x100d STRENGH
+0x100e CONFUSH
+0x100f SOFLAMH
+0x1010 DSPELLH
+0x1011 DISINTH
+0x1012 PWSILEH
+0x1013 PWSTUNH
+0x1014 FODEATH
+0x1015 MSWORDH
+0x1016 MSUMM1H
+0x1017 MSUMM2H
+0x1018 MSUMM3H
+0x1019 MSUMM4H
+0x101a MSUMM5H
+0x101b MSUMM6H
+0x101c MSUMM7H
+0x101d CFELEMH
+0x101e CEELEMH
+0x101f CWELEMH
+0x1020 BLESSH
+0x1021 CURSEH
+0x1022 PRAYERH
+0x1023 RECITAH
+0x1024 CLWOUNH
+0x1025 CMWOUNH
+0x1026 CSWOUNH
+0x1027 CCWOUNH
+0x1028 HEALH
+0x1029 ASUMM1H
+0x102a ASUMM2H
+0x102b ASUMM3H
+0x102c SPOISOH
+0x102d NPOISOH
+0x102e CALLLIH
+0x102f SCHARGH
+0x1030 RPARALH
+0x1031 FACTIOH
+0x1032 MMAGICH
+0x1033 SOONEH
+0x1034 CSTRENH
+0x1035 FSTRIKH
+0x1036 RDEADH
+0x1037 RESURRH
+0x1038 CCOMMAH
+0x1039 RWOTFAH
+0x103a SUNRAYH
+0x103b SSTONE
+0x103c DDOORH
+0x103d DDOORH
+0x103e COCOLDH
+0x103f SSORBH
+0x1040 FIREH
+0x1041 COLDH
+0x1042 ELECTRH
+0x1043 ACIDH
+0x1044 PARALH
+0x1045 MRAGEH
+0x1046 RWOTFAG
+0x1047 BDEATH
+0x1048 PORTALH
+0x1049 SUNSCOH
+0x104a BBARRH1
+0x104b BBARRH2
+0x104c COBONH1
+0x104d COBONH2
+0x104e CLDAMAH
+0x104f CMDAMAH
+0x1050 CSDAMAH
+0x1051 CCDAMAH
+0x1052 CDISEAH
+0x1053 POISONH
+0x1054 SLIVINH
+0x1055 HARMH
+0x1056 DESTRUH
+0x1057 EXALTAH
+0x1058 CLOUDBH
+0x1059 MTOUCHH
+0x105a MTOUCHH
+0x105b CGRACEH
+0x105c SEATERH
+0x105d SWAVEH
+0x105e SUFFOCH
+0x105f ADHWILH
+0x1060 MFMISSH
+0x1061 VSPHERH
+0x1062 WVDEATH
+0x1063 UWARDH
+0x1064 WVHITH
+0x1065 WDEATH1
+0x1066 WDEATH2
+0x1067 DDEATH
+0x1068 DDEATH2
+0x1069 MSUMM1X
+0x106a ASUMM1X
+0x106b CEELEMX
+0x106c CFELEMX
+0x106d CWELEMX
diff --git a/gemrb/override/how/gemrb.ini b/gemrb/override/how/gemrb.ini
new file mode 100644
index 0000000..2bc8091
--- /dev/null
+++ b/gemrb/override/how/gemrb.ini
@@ -0,0 +1,123 @@
+; 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.ini - game type-specific settings for GemRB engine
+
+[resources]
+
+; Bitmap resource for cursors
+CursorBAM = CAROT
+
+; Bitmap resource for scroll cursor arrow
+ScrollCursorBAM = CURSARW
+
+; Bitmap resource for dialog buttons font
+ButtonFont = STONESML
+
+; Font used to display tooltips
+TooltipFont = TOOLFONT
+
+; Font used to display subtitles
+MovieFont = NORMAL
+
+; Sprite displayed behind the tooltip text, if any
+TooltipBack = TOOLTIP
+
+; Tooltip text color (RGBA)
+TooltipColor = #ffffffff
+
+; Space between tooltip text and sides of TooltipBack (x2)
+TooltipMargin = 2
+
+; INI file from the original games
+INIConfig = icewind.ini
+
+; Palette bitmaps in various widths
+Palette16 = MPALETTE
+Palette32 = MPALETTE
+Palette256 = MPAL256
+
+MaximumAbility = 25
+IgnoreButtonFrames = 0
+AllStringsTagged = 1
+HasDPLAYER = 1
+HasPickSound = 0
+HasDescIcon = 0
+HasEXPTABLE = 1
+SoundFolders = 1
+HasSongList = 0
+ReverseToHit = 1
+UpperButtonText = 0
+LowerLabelText = 0
+HasPartyINI = 0
+HasBeastsINI = 0
+ForceStereo = 0
+IWDMapDimensions = 1
+SmallFog = 0
+ReverseDoor = 0
+IWD2ScriptName = 1
+DialogueScrolls = 0
+CharNameIsGabber = 1
+MagicBit = 1
+SpawnIni = 1
+SelectiveMagicRes = 1
+HasHideInShadows = 1
+AreaVisitedVar = 1
+HasSpecificDamageBonus = 1
+BiographyIsRes = 1
+FlexibleWorldmap = 1
+AutoSearchHidden = 1
+CutsceneAreascripts = 0
+PSTStateFlags = 0
+CastingSounds = 1
+ForceAreaScript = 1
+
+[charset]
+CharCount = 31
+Letter1 = 192,224
+Letter2 = 193,225
+Letter3 = 194,226
+Letter4 = 195,227
+Letter5 = 196,228
+Letter6 = 197,229
+Letter7 = 198,230
+Letter8 = 199,231
+Letter9 = 200,232
+Letter10 = 201,233
+Letter11 = 202,234
+Letter12 = 203,235
+Letter13 = 204,236
+Letter14 = 205,237
+Letter15 = 206,238
+Letter16 = 207,239
+Letter17 = 208,240
+Letter18 = 209,241
+Letter19 = 210,242
+Letter20 = 211,243
+Letter21 = 212,244
+Letter22 = 213,245
+Letter23 = 214,246
+Letter24 = 140,156
+Letter25 = 216,248
+Letter26 = 217,249
+Letter27 = 218,250
+Letter28 = 219,251
+Letter29 = 220,252
+Letter30 = 221,253
+Letter31 = 222,254
diff --git a/gemrb/override/how/gender.2da b/gemrb/override/how/gender.2da
new file mode 100644
index 0000000..609cf68
--- /dev/null
+++ b/gemrb/override/how/gender.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+*
+ TYPE MALE FEMALE
+SIRMAAM -1 27473 27475
+GIRLBOY -1 27477 27476
+BROTHERSISTER -1 27478 27479
+LADYLORD -1 27481 27480
+MALEFEMALE -1 27482 27483
+HESHE -1 27484 27485
+HISHER -1 27486 27487
+HIMHER -1 27488 27487
+MANWOMAN -1 27489 27490
+SONDAUGHTER -1 70567 70568
+PROTAGONIST_SIRMAAM 0 27473 27475
+PROTAGONIST_GIRLBOY 0 27477 27476
+PROTAGONIST_BROTHERSISTER 0 27478 27479
+PROTAGONIST_LADYLORD 0 27481 27480
+PROTAGONIST_MALEFEMALE 0 27482 27483
+PROTAGONIST_HESHE 0 27484 27485
+PROTAGONIST_HISHER 0 27486 27487
+PROTAGONIST_HIMHER 0 27488 27487
+PROTAGONIST_MANWOMAN 0 27489 27490
diff --git a/gemrb/override/how/golcloud.pro b/gemrb/override/how/golcloud.pro
new file mode 100644
index 0000000..3c97d09
Binary files /dev/null and b/gemrb/override/how/golcloud.pro differ
diff --git a/gemrb/override/how/grease.pro b/gemrb/override/how/grease.pro
new file mode 100644
index 0000000..bf273b6
Binary files /dev/null and b/gemrb/override/how/grease.pro differ
diff --git a/gemrb/override/how/gshout.pro b/gemrb/override/how/gshout.pro
new file mode 100644
index 0000000..1b362d6
Binary files /dev/null and b/gemrb/override/how/gshout.pro differ
diff --git a/gemrb/override/how/guibtact.2da b/gemrb/override/how/guibtact.2da
new file mode 100644
index 0000000..7b16720
--- /dev/null
+++ b/gemrb/override/how/guibtact.2da
@@ -0,0 +1,37 @@
+2DA V1.0
+-1
+ 1 2 3 4 TOOLTIP RESREF
+Stealth 30 31 32 33 4968 guibtact
+Thieving 26 27 28 29 4971 guibtact
+Cast 12 13 52 53 4688 guibtact
+QSpell1 0 1 24 25 4938 guibtbut
+QSpell2 2 3 26 27 4938 guibtbut
+QSpell3 4 5 28 29 4938 guibtbut
+Turn 8 9 10 11 4974 guibtact
+Talk 4 5 6 7 4933 guibtact
+UseItem 18 19 56 57 4978 guibtact
+QItem1 0 1 24 25 4937 guibtbut
+QItem4 6 7 30 31 4937 guibtbut
+QItem2 2 3 26 27 4937 guibtbut
+QItem3 4 5 28 29 4937 guibtbut
+Innate 38 39 54 55 4954 guibtact
+Defend 0 1 2 3 15925 guibtact
+Attack 14 15 16 17 4666 guibtact
+QWeapon1 0 1 24 25 4950 guibtbut
+QWeapon2 2 3 26 27 4950 guibtbut
+QWeapon3 4 5 28 29 4950 guibtbut
+QWeapon4 6 7 30 31 4950 guibtbut
+BardSong 22 23 24 25 11798 guibtact
+Stop 58 59 60 61 15924 guibtact
+Search 34 35 36 37 4927 guibtact
+23 * * * * * *
+24 * * * * * *
+25 * * * * * *
+26 * * * * * *
+27 * * * * * *
+28 * * * * * *
+29 * * * * * *
+30 * * * * * *
+QItem5 0 1 2 3 4937 guibtbut
+Left 44 45 -1 -1 -1 guibtact
+Right 42 43 -1 -1 -1 guibtact
diff --git a/gemrb/override/how/guils.chu b/gemrb/override/how/guils.chu
new file mode 100644
index 0000000..901a44c
Binary files /dev/null and b/gemrb/override/how/guils.chu differ
diff --git a/gemrb/override/how/harmh.pro b/gemrb/override/how/harmh.pro
new file mode 100644
index 0000000..f8b2ded
Binary files /dev/null and b/gemrb/override/how/harmh.pro differ
diff --git a/gemrb/override/how/harmony.pro b/gemrb/override/how/harmony.pro
new file mode 100644
index 0000000..03a7b54
Binary files /dev/null and b/gemrb/override/how/harmony.pro differ
diff --git a/gemrb/override/how/haterace.2da b/gemrb/override/how/haterace.2da
new file mode 100644
index 0000000..75d0d0b
--- /dev/null
+++ b/gemrb/override/how/haterace.2da
@@ -0,0 +1,15 @@
+2DA V1.0
+-1
+ NAME_REF ID DESC_REF CAP_REF
+CADEVEROUS_UNDEAD 3284 108 3304 59
+GIANTS 3279 153 3291 61
+GOBLINS 3280 155 3292 29518
+LIZARD_MEN 3281 157 3293 3275
+ORCS 3282 160 3294 3271
+SALAMANDERS 3290 161 3295 3272
+SKELETAL_UNDEAD 3283 115 3296 3273
+SPECTRAL_UNDEAD 3285 167 3297 3278
+SPIDERS 3286 116 3298 3274
+TROLLS 3288 165 3299 3276
+UMBER_HULKS 3289 166 3300 3277
+YUAN-TI 30850 168 3301 30849
diff --git a/gemrb/override/how/healh.pro b/gemrb/override/how/healh.pro
new file mode 100644
index 0000000..1213e70
Binary files /dev/null and b/gemrb/override/how/healh.pro differ
diff --git a/gemrb/override/how/hmighth.pro b/gemrb/override/how/hmighth.pro
new file mode 100644
index 0000000..60ba342
Binary files /dev/null and b/gemrb/override/how/hmighth.pro differ
diff --git a/gemrb/override/how/hold.pro b/gemrb/override/how/hold.pro
new file mode 100644
index 0000000..aeecbfd
Binary files /dev/null and b/gemrb/override/how/hold.pro differ
diff --git a/gemrb/override/how/hsmite.pro b/gemrb/override/how/hsmite.pro
new file mode 100644
index 0000000..8b3dcb3
Binary files /dev/null and b/gemrb/override/how/hsmite.pro differ
diff --git a/gemrb/override/how/hword.pro b/gemrb/override/how/hword.pro
new file mode 100644
index 0000000..51295d7
Binary files /dev/null and b/gemrb/override/how/hword.pro differ
diff --git a/gemrb/override/how/icelance.pro b/gemrb/override/how/icelance.pro
new file mode 100644
index 0000000..936854e
Binary files /dev/null and b/gemrb/override/how/icelance.pro differ
diff --git a/gemrb/override/how/icestorm.pro b/gemrb/override/how/icestorm.pro
new file mode 100644
index 0000000..06e4b05
Binary files /dev/null and b/gemrb/override/how/icestorm.pro differ
diff --git a/gemrb/override/how/icloud.pro b/gemrb/override/how/icloud.pro
new file mode 100644
index 0000000..9f85cd1
Binary files /dev/null and b/gemrb/override/how/icloud.pro differ
diff --git a/gemrb/override/how/iclouda.pro b/gemrb/override/how/iclouda.pro
new file mode 100644
index 0000000..845d3f0
Binary files /dev/null and b/gemrb/override/how/iclouda.pro differ
diff --git a/gemrb/override/how/icloudb.pro b/gemrb/override/how/icloudb.pro
new file mode 100644
index 0000000..d5f3f31
Binary files /dev/null and b/gemrb/override/how/icloudb.pro differ
diff --git a/gemrb/override/how/illush.pro b/gemrb/override/how/illush.pro
new file mode 100644
index 0000000..158d82f
Binary files /dev/null and b/gemrb/override/how/illush.pro differ
diff --git a/gemrb/override/how/illust.pro b/gemrb/override/how/illust.pro
new file mode 100644
index 0000000..e2966de
Binary files /dev/null and b/gemrb/override/how/illust.pro differ
diff --git a/gemrb/override/how/inarea.pro b/gemrb/override/how/inarea.pro
new file mode 100644
index 0000000..ec6a50d
Binary files /dev/null and b/gemrb/override/how/inarea.pro differ
diff --git a/gemrb/override/how/inareanp.pro b/gemrb/override/how/inareanp.pro
new file mode 100644
index 0000000..91859f9
Binary files /dev/null and b/gemrb/override/how/inareanp.pro differ
diff --git a/gemrb/override/how/inareasm.pro b/gemrb/override/how/inareasm.pro
new file mode 100644
index 0000000..6545bce
Binary files /dev/null and b/gemrb/override/how/inareasm.pro differ
diff --git a/gemrb/override/how/invoch.pro b/gemrb/override/how/invoch.pro
new file mode 100644
index 0000000..0eccaef
Binary files /dev/null and b/gemrb/override/how/invoch.pro differ
diff --git a/gemrb/override/how/invoct.pro b/gemrb/override/how/invoct.pro
new file mode 100644
index 0000000..3a051b0
Binary files /dev/null and b/gemrb/override/how/invoct.pro differ
diff --git a/gemrb/override/how/iplague.pro b/gemrb/override/how/iplague.pro
new file mode 100644
index 0000000..1b3501e
Binary files /dev/null and b/gemrb/override/how/iplague.pro differ
diff --git a/gemrb/override/how/island00.2da b/gemrb/override/how/island00.2da
new file mode 100644
index 0000000..aa3f8e2
--- /dev/null
+++ b/gemrb/override/how/island00.2da
@@ -0,0 +1,11 @@
+2DA V1.0
+0
+ X Y
+1 0 105
+2 0 590
+3 692 590
+4 692 459
+5 1018 459
+6 1018 301
+7 1027 301
+8 1027 105
diff --git a/gemrb/override/how/itemsnd.2da b/gemrb/override/how/itemsnd.2da
new file mode 100644
index 0000000..9678ee9
--- /dev/null
+++ b/gemrb/override/how/itemsnd.2da
@@ -0,0 +1,80 @@
+2DA V1.0
+*
+ TAKE DROP
+AMULET G_NECK1 G_NECK2
+ARMOR G_CLOAK1 G_CLOAK2
+BELT G_BELT1 G_BELT2
+BOOT G_BOOT1 G_BOOT2
+ARROW G_ARRW1 G_ARRW2
+BRACER G_GLOVE1 G_GLOVE2
+HELMET G_HELM1 G_HELM2
+KEY G_KEY1 G_KEY2
+POTION G_POTN1 G_POTN2
+RING G_RING1 G_RING2
+SCROLL G_SCROL1 G_SCROL2
+SHIELD GAM_21A GAM_21B
+FOOD GAM_21A GAM_21B
+BULLET G_BULLT1 G_BULLT2
+BOW G_BOW1 G_BOW2
+DAGGER G_DAGGR1 G_DAGGR2
+MACE G_MACE1 G_MACE2
+SLING G_SLING1 G_SLING2
+SMSWORD G_SMSWD1 G_SMSWD2
+BGSWORD G_LGSWD1 G_LGSWD2
+HAMMER G_HAMMR1 G_HAMMR2
+MSTAR G_MSTAR1 G_MSTAR2
+FLAIL G_FLAIL1 G_FLAIL2
+DART G_DART1 G_DART2
+AXE G_AXE1 G_AXE2
+STAFF G_STAFF1 G_STAFF2
+XBOW G_CROSB1 G_CROSB2
+FIST GAM_21A GAM_21B
+SPEAR G_SPEAR1 G_SPEAR2
+POLEARM G_HALB1 G_HALB2
+BOLT G_BOLT1 G_BOLT2
+CLOAK G_CLOAK1 G_CLOAK2
+COIN G_GOLD1 G_GOLD2
+GEM G_GEM1 G_GEM2
+WAND G_WAND1 G_WAND2
+BROKEN1 G_B_ARM1 G_B_ARM2
+BROKEN2 G_B_SHD1 G_B_SHD2
+BROKEN3 G_B_WPN1 G_B_WPN2
+UNUSED1 GAM_21A GAM_21B
+UNUSED2 G_BROKN1 G_BROKN2
+BUCKLER G_BUCKR1 G_BUCKR2
+CANDLE G_CAND1 G_CAND2
+CBODY G_CHILD1 G_CHILD2
+CLUB G_CLUB1 G_CLUB2
+FBODY G_FEM1 G_FEM2
+KEY2 G_KEYS1 G_KEYS2
+LSHIELD G_LGSLD1 G_LGSLD2
+MBODY G_MALE1 G_MALE2
+MSHIELD G_MDSLD1 G_MDSLD2
+NOTES G_PAPR1 G_PAPR2
+ROD G_ROD1 G_ROD2
+SKULL G_SKULL1 G_SKULL2
+SSHIELD G_SMSLD1 G_SMSLD2
+SPIDER G_SPIDR1 G_SPIDR2
+TELESCOPE G_TELE1 G_TELE2
+DRINK G_WINE1 G_WINE2
+GTSWORD G_LGSWD1 G_LGSWD2
+BAG GAM_21A GAM_21B
+FUR G_LETHR1 G_LETHR2
+LARMOR G_CLOAK1 G_CLOAK2
+SLARMOR G_CLOAK1 G_CLOAK2
+CHARMOR G_CLOAK1 G_CLOAK2
+SPARMOR G_CLOAK1 G_CLOAK2
+HPARMOR G_CLOAK1 G_CLOAK2
+FPARMOR G_CLOAK1 G_CLOAK2
+HARMOR G_CLOAK1 G_CLOAK2
+ROBE G_CLOAK1 G_CLOAK2
+SCALE G_CLOAK1 G_CLOAK2
+BTSWORD G_LGSWD1 G_LGSWD2
+SCARF G_GLOVE1 G_GLOVE2
+FOOD2 G_SLING1 G_SLING2
+HAT G_GLOVE1 G_GLOVE2
+GAUNTLET G_CAND1 G_CAND2
+DEFAULT GAM_21A GAM_21B
+LEATHER G_LETHR1 G_LETHR2
+CHAIN G_Chain1 G_Chain2
+PLATE G_Plate1 G_Plate2
diff --git a/gemrb/override/how/itemtype.2da b/gemrb/override/how/itemtype.2da
new file mode 100644
index 0000000..b7f7e28
--- /dev/null
+++ b/gemrb/override/how/itemtype.2da
@@ -0,0 +1,77 @@
+2DA V1.0
+0
+ HELMET ARMOR SHIELD GLOVES RING AMULET BELT BOOTS WEAPON QUIVER CLOAK QUICK SCROLL BAG POTION
+MISC 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+AMULET 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+ARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+BELT 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
+BOOT 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
+ARROW 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BRACER 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
+HELMET 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+KEY 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+POTION 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
+RING 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
+SCROLL 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0
+SHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+FOOD 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BULLET 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DAGGER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MACE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SLING 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SMSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BGSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+HAMMER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MSTAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FLAIL 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DART 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+AXE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+STAFF 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+XBOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FIST 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SPEAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+POLEARM 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BOLT 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+CLOAK 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
+COIN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+GEM 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+WAND 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BROKEN1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BROKEN2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BROKEN3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BUCKLER 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+CANDLE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+CLUB 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+UNUSED4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+LSHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+MSHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+NOTES 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+SSHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+TELESCOPE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DRINK 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
+GTSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BAG 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
+FUR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+LARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+SLARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+CHARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+SPARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+HPARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+FPARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+HARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+ROBE 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BTSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SCARF 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+FOOD2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+HAT 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+GAUNTLET 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
diff --git a/gemrb/override/how/itemuse.2da b/gemrb/override/how/itemuse.2da
new file mode 100644
index 0000000..3540165
--- /dev/null
+++ b/gemrb/override/how/itemuse.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+0
+ STAT FILE MCOL VCOL WHICH
+0 ALIGNMENT aligns 3 5 0
+1 RACE races 3 4 0
+2 CLASS classes 5 7 0
diff --git a/gemrb/override/how/kitlist.2da b/gemrb/override/how/kitlist.2da
new file mode 100644
index 0000000..1ce17fb
--- /dev/null
+++ b/gemrb/override/how/kitlist.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+*
+ ROWNAME LOWER MIXED HELP ABILITIES PROFICIENCY UNUSABLE CLASS
+0 RESERVE * * * * * * *
+1 ABJURER 597 502 9564 * 21 0x00000040 1
+2 CONJURER 2179 504 9565 * 22 0x00000080 1
+3 DIVINER 2846 2012 9566 * 23 0x00000100 1
+4 ENCHANTER 2861 2022 9567 * 24 0x00000200 1
+5 ILLUSIONIST 2862 12785 9568 * 25 0x00000400 1
+6 INVOKER 3015 12786 9569 * 26 0x00000800 1
+7 NECROMANCER 12744 12787 9570 * 27 0x00001000 1
+8 TRANSMUTER 12745 12788 9571 * 28 0x00002000 1
diff --git a/gemrb/override/how/lightb.pro b/gemrb/override/how/lightb.pro
new file mode 100644
index 0000000..fa7f84a
Binary files /dev/null and b/gemrb/override/how/lightb.pro differ
diff --git a/gemrb/override/how/lightsto.pro b/gemrb/override/how/lightsto.pro
new file mode 100644
index 0000000..7b502b1
Binary files /dev/null and b/gemrb/override/how/lightsto.pro differ
diff --git a/gemrb/override/how/lodisr.pro b/gemrb/override/how/lodisr.pro
new file mode 100644
index 0000000..d78b250
Binary files /dev/null and b/gemrb/override/how/lodisr.pro differ
diff --git a/gemrb/override/how/magesch.2da b/gemrb/override/how/magesch.2da
new file mode 100644
index 0000000..acd3cef
--- /dev/null
+++ b/gemrb/override/how/magesch.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF KIT
+GENERALIST 18039 9563 9987 0x4000
+ABJURER 597 9564 502 0x40
+CONJURER 2179 9565 504 0x80
+DIVINER 2846 9566 2012 0x100
+ENCHANTER 2861 9567 2022 0x200
+ILLUSIONIST 2862 9568 8421 0x400
+INVOKER 3015 9569 12786 0x800
+NECROMANCER 12744 9570 12787 0x1000
+TRANSMUTER 12745 9571 12788 0x2000
diff --git a/gemrb/override/how/magicmis.pro b/gemrb/override/how/magicmis.pro
new file mode 100644
index 0000000..33ceed1
Binary files /dev/null and b/gemrb/override/how/magicmis.pro differ
diff --git a/gemrb/override/how/malison.pro b/gemrb/override/how/malison.pro
new file mode 100644
index 0000000..1bab8cd
Binary files /dev/null and b/gemrb/override/how/malison.pro differ
diff --git a/gemrb/override/how/mfmiss.pro b/gemrb/override/how/mfmiss.pro
new file mode 100644
index 0000000..c3cd185
Binary files /dev/null and b/gemrb/override/how/mfmiss.pro differ
diff --git a/gemrb/override/how/mfmiss2.pro b/gemrb/override/how/mfmiss2.pro
new file mode 100644
index 0000000..320be1f
Binary files /dev/null and b/gemrb/override/how/mfmiss2.pro differ
diff --git a/gemrb/override/how/mfmissh.pro b/gemrb/override/how/mfmissh.pro
new file mode 100644
index 0000000..171863e
Binary files /dev/null and b/gemrb/override/how/mfmissh.pro differ
diff --git a/gemrb/override/how/mmagich.pro b/gemrb/override/how/mmagich.pro
new file mode 100644
index 0000000..d74d4f2
Binary files /dev/null and b/gemrb/override/how/mmagich.pro differ
diff --git a/gemrb/override/how/mmissile.pro b/gemrb/override/how/mmissile.pro
new file mode 100644
index 0000000..145b3fd
Binary files /dev/null and b/gemrb/override/how/mmissile.pro differ
diff --git a/gemrb/override/how/moelda.pro b/gemrb/override/how/moelda.pro
new file mode 100644
index 0000000..c063a08
Binary files /dev/null and b/gemrb/override/how/moelda.pro differ
diff --git a/gemrb/override/how/mrage.pro b/gemrb/override/how/mrage.pro
new file mode 100644
index 0000000..8b9148b
Binary files /dev/null and b/gemrb/override/how/mrage.pro differ
diff --git a/gemrb/override/how/mrageh.pro b/gemrb/override/how/mrageh.pro
new file mode 100644
index 0000000..4a8b252
Binary files /dev/null and b/gemrb/override/how/mrageh.pro differ
diff --git a/gemrb/override/how/mspore.pro b/gemrb/override/how/mspore.pro
new file mode 100644
index 0000000..370e001
Binary files /dev/null and b/gemrb/override/how/mspore.pro differ
diff --git a/gemrb/override/how/msumm1.pro b/gemrb/override/how/msumm1.pro
new file mode 100644
index 0000000..3f4ad3a
Binary files /dev/null and b/gemrb/override/how/msumm1.pro differ
diff --git a/gemrb/override/how/msumm1h.pro b/gemrb/override/how/msumm1h.pro
new file mode 100644
index 0000000..7b4ad4d
Binary files /dev/null and b/gemrb/override/how/msumm1h.pro differ
diff --git a/gemrb/override/how/msumm1x.pro b/gemrb/override/how/msumm1x.pro
new file mode 100644
index 0000000..8242d6c
Binary files /dev/null and b/gemrb/override/how/msumm1x.pro differ
diff --git a/gemrb/override/how/msumm2h.pro b/gemrb/override/how/msumm2h.pro
new file mode 100644
index 0000000..07b7f94
Binary files /dev/null and b/gemrb/override/how/msumm2h.pro differ
diff --git a/gemrb/override/how/msumm3h.pro b/gemrb/override/how/msumm3h.pro
new file mode 100644
index 0000000..8a775a8
Binary files /dev/null and b/gemrb/override/how/msumm3h.pro differ
diff --git a/gemrb/override/how/msumm4h.pro b/gemrb/override/how/msumm4h.pro
new file mode 100644
index 0000000..4dbaf7b
Binary files /dev/null and b/gemrb/override/how/msumm4h.pro differ
diff --git a/gemrb/override/how/msumm5h.pro b/gemrb/override/how/msumm5h.pro
new file mode 100644
index 0000000..a1e88b0
Binary files /dev/null and b/gemrb/override/how/msumm5h.pro differ
diff --git a/gemrb/override/how/msumm6h.pro b/gemrb/override/how/msumm6h.pro
new file mode 100644
index 0000000..600dc17
Binary files /dev/null and b/gemrb/override/how/msumm6h.pro differ
diff --git a/gemrb/override/how/msumm7h.pro b/gemrb/override/how/msumm7h.pro
new file mode 100644
index 0000000..2f3f5b3
Binary files /dev/null and b/gemrb/override/how/msumm7h.pro differ
diff --git a/gemrb/override/how/mswordh.pro b/gemrb/override/how/mswordh.pro
new file mode 100644
index 0000000..6cf138d
Binary files /dev/null and b/gemrb/override/how/mswordh.pro differ
diff --git a/gemrb/override/how/mtouchh.pro b/gemrb/override/how/mtouchh.pro
new file mode 100644
index 0000000..a9f7c06
Binary files /dev/null and b/gemrb/override/how/mtouchh.pro differ
diff --git a/gemrb/override/how/necroh.pro b/gemrb/override/how/necroh.pro
new file mode 100644
index 0000000..ad3c190
Binary files /dev/null and b/gemrb/override/how/necroh.pro differ
diff --git a/gemrb/override/how/necrot.pro b/gemrb/override/how/necrot.pro
new file mode 100644
index 0000000..975d707
Binary files /dev/null and b/gemrb/override/how/necrot.pro differ
diff --git a/gemrb/override/how/npoisoh.pro b/gemrb/override/how/npoisoh.pro
new file mode 100644
index 0000000..9c741f3
Binary files /dev/null and b/gemrb/override/how/npoisoh.pro differ
diff --git a/gemrb/override/how/ofsphe.pro b/gemrb/override/how/ofsphe.pro
new file mode 100644
index 0000000..e07fc9a
Binary files /dev/null and b/gemrb/override/how/ofsphe.pro differ
diff --git a/gemrb/override/how/overlay.2da b/gemrb/override/how/overlay.2da
new file mode 100644
index 0000000..229ff10
--- /dev/null
+++ b/gemrb/override/how/overlay.2da
@@ -0,0 +1,35 @@
+2DA V1.0
+*
+ VVC UNDER FLAGS
+SANCTUARY SANCTUC 0 1
+ENTANGLE ENTANGC 0 0
+WISP WISP 0 0
+SHIELDGLOBE SHIELDC 0 1
+GREASE GREASEC 1 0
+WEB WEBC 1 0
+MINORGLOBE MGOINVC 0 1
+GLOBE GOINVUC 0 1
+FLAMESHROUD SOFLAMC 0 0
+ANTIMAGIC AMSHELC 0 0
+RESILIENT ORSPHEC 0 0
+PROTFROMMISS PFNMISC 0 0
+CLOAKOFFEAR COFEARC 0 0
+ENTROPY ESHIELC 0 0
+FIREAURA FIAURAC 0 0
+FROSTAURA FRAURAC 0 0
+INSECT IPLAGUC 0 0
+STORMSHELL SSHELLC 0 0
+LATHANDER1 SOLATC1 0 0
+LATHANDER2 SOLATC2 1 0
+GLATHANDER1 GSOLAC1 0 0
+GLATHANDER2 GSOLAC2 1 0
+SEVENEYES1 SEYESC1 0 0
+SEVENEYES2 SEYESC2 1 0
+BOUNCE SPTURNI2 1 0
+BOUNCE2 SPTURNI 1 0
+FIRESHIELD1 FSHIRC1 0 0
+FIRESHIELD2 FSHIRC2 1 0
+ICESHIELD1 FSHIBC1 0 0
+ICESHIELD2 FSHIBC2 1 0
+TORTOISE TSHELLC 0 0
+DEATHARMOR DARMORC 0 0
diff --git a/gemrb/override/how/paralh.pro b/gemrb/override/how/paralh.pro
new file mode 100644
index 0000000..b8d8ed6
Binary files /dev/null and b/gemrb/override/how/paralh.pro differ
diff --git a/gemrb/override/how/pathfind.2da b/gemrb/override/how/pathfind.2da
new file mode 100644
index 0000000..78f511e
--- /dev/null
+++ b/gemrb/override/how/pathfind.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+*
+ 0 1 2 3 4 5 6 7 8 9 a b c d e f
+PASSABLE 8 1 1 1 1 1 1 1 0 1 8 1 0 8 3 1
+COST 10 4
+SOUND * FS_Dirt FS_WdSn FS_WOOD FS_Tomb * * FS_Ston * FS_WOOD * FS_Snow * * * FS_Gras
\ No newline at end of file
diff --git a/gemrb/override/how/pdolls.2da b/gemrb/override/how/pdolls.2da
new file mode 100644
index 0000000..11593f9
--- /dev/null
+++ b/gemrb/override/how/pdolls.2da
@@ -0,0 +1,75 @@
+2DA V1.0
+*
+ LEVEL1 LEVEL2 LEVEL3 LEVEL4 SIZE
+0x5000 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x5001 CEMC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x5002 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x5003 CIMC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x5010 CHFC1INV CHMC2INV CHMC3INV CHMC4INV N
+0x5011 CEFC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x5012 CDFC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x5013 CIFC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x5100 CHMF1INV CHMF2INV CHMF3INV CHMC4INV L
+0x5101 CEMF1INV CEMF2INV CEMF3INV CEMC4INV M
+0x5102 CDMF1INV CDMF2INV CDMF3INV CDMC4INV S
+0x5103 CIMF1INV CIMF2INV CIMF3INV CIMC4INV S
+0x5110 CHFF1INV CHFF2INV CHMF3INV CHMC4INV N
+0x5111 CEFF1INV CEFF2INV CEMF3INV CEMC4INV M
+0x5112 CDFF1INV CDFF2INV CDMF3INV CDMC4INV S
+0x5113 CIFF1INV CIFF2INV CIMF3INV CIMC4INV S
+0x5200 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x5201 CEMW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x5202 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5203 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5210 CHFW1INV CHMW2INV CHMW3INV CHMW4INV N
+0x5211 CEFW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x5212 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5213 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5300 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x5301 CEMT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x5302 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x5303 CIMT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x5310 CHFT1INV CHMT2INV CHMF3INV CHMF4INV N
+0x5311 CEFT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x5312 CDFT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x5313 CIFT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x6000 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x6001 CEMC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x6002 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x6003 CIMC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x6004 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x6010 CHFC1INV CHFC2INV CHFC3INV CHFC4INV M
+0x6011 CEFC1INV CEFC2INV CEFC3INV CEFC4INV M
+0x6012 CDFC1INV CDFC2INV CDFC3INV CDFC4INV S
+0x6013 CIFC1INV CIFC2INV CIFC3INV CIFC4INV S
+0x6014 CIFC1INV CIFC2INV CIFC3INV CIFC4INV S
+0x6100 CHMF1INV CHMF2INV CHMF3INV CHMF4INV L
+0x6101 CEMF1INV CEMF2INV CEMF3INV CEMF4INV M
+0x6102 CDMF1INV CDMF2INV CDMF3INV CDMF4INV S
+0x6103 CIMF1INV CIMF2INV CIMF3INV CIMF4INV S
+0x6104 CDMF1INV CDMF2INV CDMF3INV CDMF4INV S
+0x6110 CHFF1INV CHFF2INV CHFF3INV CHFF4INV N
+0x6111 CEFF1INV CEFF2INV CEFF3INV CEFF4INV M
+0x6112 CDFF1INV CDFF2INV CDFF3INV CDFF4INV S
+0x6113 CIFF1INV CIFF2INV CIFF3INV CIFF4INV S
+0x6114 CIFF1INV CIFF2INV CIFF3INV CIFF4INV S
+0x6200 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x6201 CEMW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x6202 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6203 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6204 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6210 CHFW1INV CHFW2INV CHFW3INV CHFW4INV N
+0x6211 CEFW1INV CEFW2INV CEFW3INV CHFW4INV M
+0x6212 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6213 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6214 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6300 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x6301 CEMT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x6302 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x6303 CIMT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x6304 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x6310 CHFT1INV CHFT2INV CHFF3INV CHFF4INV N
+0x6311 CEFT1INV CEFT2INV CEFF3INV CEFF4INV M
+0x6312 CDFT1INV CDFT2INV CDFF3INV CDFF4INV S
+0x6313 CIFT1INV CIFT2INV CIFF3INV CIFF4INV S
+0x6314 CIFT1INV CIFT2INV CIFF3INV CIFF4INV S
diff --git a/gemrb/override/how/pfire.pro b/gemrb/override/how/pfire.pro
new file mode 100644
index 0000000..8dd8653
Binary files /dev/null and b/gemrb/override/how/pfire.pro differ
diff --git a/gemrb/override/how/pictures.2da b/gemrb/override/how/pictures.2da
new file mode 100644
index 0000000..cf07712
--- /dev/null
+++ b/gemrb/override/how/pictures.2da
@@ -0,0 +1,36 @@
+2DA V1.0
+-1
+ GENDER
+DFF_ 2
+DMC_ 1
+DMF_ 1
+DMT_ 1
+EFC_ 2
+EFW_ 2
+EMF_ 1
+EMF2_ 1
+EMT_ 1
+EMW_ 1
+GFW_ 2
+GMT_ 1
+GMW_ 1
+HFF_ 2
+HFT_ 2
+HFW_ 2
+HMB_ 1
+HMC_ 1
+HMF2_ 1
+HMF_ 1
+HMW_ 1
+HaFF_ 2
+HaFT_ 2
+HaMF_ 1
+HaMT_ 1
+HeFB_ 2
+HeFC_ 2
+HeFF_ 2
+HeFW_ 2
+HeMF_ 1
+HeMT_ 1
+HeMW_ 1
+HFW2_ 2
diff --git a/gemrb/override/how/poisonh.pro b/gemrb/override/how/poisonh.pro
new file mode 100644
index 0000000..bd97ddc
Binary files /dev/null and b/gemrb/override/how/poisonh.pro differ
diff --git a/gemrb/override/how/pomab.2da b/gemrb/override/how/pomab.2da
new file mode 100644
index 0000000..da66836
--- /dev/null
+++ b/gemrb/override/how/pomab.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+*
+ X Y
+RESREF eepomab pomimg
+1 0x1e8 0x20a
+2 0x1c6 0x1f6
+3 0x1d0 0x20a
+4 0x1b5 0x204
+5 0x1c2 0x216
+6 0x1dd 0x217
diff --git a/gemrb/override/how/portal.pro b/gemrb/override/how/portal.pro
new file mode 100644
index 0000000..3bf4dd3
Binary files /dev/null and b/gemrb/override/how/portal.pro differ
diff --git a/gemrb/override/how/prayer.pro b/gemrb/override/how/prayer.pro
new file mode 100644
index 0000000..454ca43
Binary files /dev/null and b/gemrb/override/how/prayer.pro differ
diff --git a/gemrb/override/how/prayerh.pro b/gemrb/override/how/prayerh.pro
new file mode 100644
index 0000000..3e6c533
Binary files /dev/null and b/gemrb/override/how/prayerh.pro differ
diff --git a/gemrb/override/how/protevil.pro b/gemrb/override/how/protevil.pro
new file mode 100644
index 0000000..44972ee
Binary files /dev/null and b/gemrb/override/how/protevil.pro differ
diff --git a/gemrb/override/how/prtl_cl.pro b/gemrb/override/how/prtl_cl.pro
new file mode 100644
index 0000000..6a5bfaa
Binary files /dev/null and b/gemrb/override/how/prtl_cl.pro differ
diff --git a/gemrb/override/how/prtl_cl.spl b/gemrb/override/how/prtl_cl.spl
new file mode 100644
index 0000000..326f930
Binary files /dev/null and b/gemrb/override/how/prtl_cl.spl differ
diff --git a/gemrb/override/how/prtl_op.pro b/gemrb/override/how/prtl_op.pro
new file mode 100644
index 0000000..6f689f1
Binary files /dev/null and b/gemrb/override/how/prtl_op.pro differ
diff --git a/gemrb/override/how/prtl_op.spl b/gemrb/override/how/prtl_op.spl
new file mode 100644
index 0000000..326f930
Binary files /dev/null and b/gemrb/override/how/prtl_op.spl differ
diff --git a/gemrb/override/how/pspray.pro b/gemrb/override/how/pspray.pro
new file mode 100644
index 0000000..66a5326
Binary files /dev/null and b/gemrb/override/how/pspray.pro differ
diff --git a/gemrb/override/how/pwkill.pro b/gemrb/override/how/pwkill.pro
new file mode 100644
index 0000000..8e60ca0
Binary files /dev/null and b/gemrb/override/how/pwkill.pro differ
diff --git a/gemrb/override/how/pwsileh.pro b/gemrb/override/how/pwsileh.pro
new file mode 100644
index 0000000..46b144d
Binary files /dev/null and b/gemrb/override/how/pwsileh.pro differ
diff --git a/gemrb/override/how/pwstun.pro b/gemrb/override/how/pwstun.pro
new file mode 100644
index 0000000..a4ea2be
Binary files /dev/null and b/gemrb/override/how/pwstun.pro differ
diff --git a/gemrb/override/how/pwstunh.pro b/gemrb/override/how/pwstunh.pro
new file mode 100644
index 0000000..5afd33d
Binary files /dev/null and b/gemrb/override/how/pwstunh.pro differ
diff --git a/gemrb/override/how/qslots.2da b/gemrb/override/how/qslots.2da
new file mode 100644
index 0000000..c9fc3d2
--- /dev/null
+++ b/gemrb/override/how/qslots.2da
@@ -0,0 +1,24 @@
+2DA V1.0
+100
+ SLOT1 SLOT2 SLOT3 SLOT4 SLOT5 SLOT6 SLOT7 SLOT8 SLOT9
+DEFAULT 18 19 14 100 8 9 11 12 13
+MAGE 3 4 5 2 8 9 11 12 13
+FIGHTER 18 19 14 100 8 9 11 12 13
+CLERIC 6 3 4 2 8 9 11 12 13
+THIEF 22 0 1 100 8 9 11 12 13
+BARD 20 1 3 2 8 9 11 12 13
+PALADIN 18 14 6 2 8 9 11 12 13
+FIGHTER_MAGE 3 4 5 2 8 9 11 12 13
+FIGHTER_CLERIC 6 3 4 2 8 9 11 12 13
+FIGHTER_THIEF 18 22 0 1 8 9 11 12 13
+FIGHTER_MAGE_THIEF 22 0 1 2 8 9 11 12 13
+DRUID 3 4 5 2 8 9 11 12 13
+RANGER 18 14 0 2 8 9 11 12 13
+MAGE_THIEF 22 0 1 2 8 9 11 12 13
+CLERIC_MAGE 6 3 4 2 8 9 11 12 13
+CLERIC_THIEF 22 0 1 2 8 9 11 12 13
+FIGHTER_DRUID 3 4 5 2 8 9 11 12 13
+FIGHTER_MAGE_CLERIC 6 3 4 2 8 9 11 12 13
+CLERIC_RANGER 6 3 4 2 8 9 11 12 13
+SORCERER 3 4 5 2 8 9 11 12 13
+MONK 18 14 22 0 8 9 11 12 13
diff --git a/gemrb/override/how/races.2da b/gemrb/override/how/races.2da
new file mode 100644
index 0000000..b912e12
--- /dev/null
+++ b/gemrb/override/how/races.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF ID USABILITY SAVE
+HUMAN 7193 9550 1096 1 0x08000000 *
+ELF 7194 9552 1097 2 0x00800000 *
+HALF_ELF 7197 9555 1098 3 0x02000000 *
+GNOME 7196 9553 1099 6 0x10000000 SAVECNG
+HALFLING 7195 9554 1101 5 0x04000000 SAVECNDH
+DWARF 7182 9551 1100 4 0x01000000 SAVECNDH
diff --git a/gemrb/override/how/rad100.pro b/gemrb/override/how/rad100.pro
new file mode 100644
index 0000000..1a8e9a0
Binary files /dev/null and b/gemrb/override/how/rad100.pro differ
diff --git a/gemrb/override/how/rad250.pro b/gemrb/override/how/rad250.pro
new file mode 100644
index 0000000..31ee26e
Binary files /dev/null and b/gemrb/override/how/rad250.pro differ
diff --git a/gemrb/override/how/randitem.2da b/gemrb/override/how/randitem.2da
new file mode 100644
index 0000000..f612585
--- /dev/null
+++ b/gemrb/override/how/randitem.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ RESREF FURY_MODE
+GOLD MISC07
+RND RNDTRES RNDTRES
diff --git a/gemrb/override/how/rdeadh.pro b/gemrb/override/how/rdeadh.pro
new file mode 100644
index 0000000..8a79354
Binary files /dev/null and b/gemrb/override/how/rdeadh.pro differ
diff --git a/gemrb/override/how/recitah.pro b/gemrb/override/how/recitah.pro
new file mode 100644
index 0000000..49c7aeb
Binary files /dev/null and b/gemrb/override/how/recitah.pro differ
diff --git a/gemrb/override/how/resurrh.pro b/gemrb/override/how/resurrh.pro
new file mode 100644
index 0000000..0ae7bb9
Binary files /dev/null and b/gemrb/override/how/resurrh.pro differ
diff --git a/gemrb/override/how/rng450.pro b/gemrb/override/how/rng450.pro
new file mode 100644
index 0000000..e991873
Binary files /dev/null and b/gemrb/override/how/rng450.pro differ
diff --git a/gemrb/override/how/rparalh.pro b/gemrb/override/how/rparalh.pro
new file mode 100644
index 0000000..a163b89
Binary files /dev/null and b/gemrb/override/how/rparalh.pro differ
diff --git a/gemrb/override/how/rwotfag.pro b/gemrb/override/how/rwotfag.pro
new file mode 100644
index 0000000..e8dd52b
Binary files /dev/null and b/gemrb/override/how/rwotfag.pro differ
diff --git a/gemrb/override/how/rwotfah.pro b/gemrb/override/how/rwotfah.pro
new file mode 100644
index 0000000..1a2a98e
Binary files /dev/null and b/gemrb/override/how/rwotfah.pro differ
diff --git a/gemrb/override/how/sarmorh.pro b/gemrb/override/how/sarmorh.pro
new file mode 100644
index 0000000..64fb522
Binary files /dev/null and b/gemrb/override/how/sarmorh.pro differ
diff --git a/gemrb/override/how/savegame.2da b/gemrb/override/how/savegame.2da
new file mode 100644
index 0000000..ff8d5ca
--- /dev/null
+++ b/gemrb/override/how/savegame.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+Auto-Save
+ SLOTNAME QSAVE
+0 Auto-Save 0
+1 Quick-Save 1
+2 Final-Save 0
diff --git a/gemrb/override/how/scharge.pro b/gemrb/override/how/scharge.pro
new file mode 100644
index 0000000..83d1b66
Binary files /dev/null and b/gemrb/override/how/scharge.pro differ
diff --git a/gemrb/override/how/scharge.spl b/gemrb/override/how/scharge.spl
new file mode 100644
index 0000000..ff98fea
Binary files /dev/null and b/gemrb/override/how/scharge.spl differ
diff --git a/gemrb/override/how/schargh.pro b/gemrb/override/how/schargh.pro
new file mode 100644
index 0000000..c7e08d0
Binary files /dev/null and b/gemrb/override/how/schargh.pro differ
diff --git a/gemrb/override/how/script.2da b/gemrb/override/how/script.2da
new file mode 100644
index 0000000..4654377
--- /dev/null
+++ b/gemrb/override/how/script.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+0
+ DATA 1 2 3 4 5 6 7
+OBJECT_IDS_COUNT 7 ea general race class specific gender align
+MAX_OBJECT_NESTING 5
+ADDITIONAL_RECT 1
+EXTRA_PARAMETERS_COUNT 0
+TRIGGER_POINT 0
\ No newline at end of file
diff --git a/gemrb/override/how/seater.pro b/gemrb/override/how/seater.pro
new file mode 100644
index 0000000..5f6d62c
Binary files /dev/null and b/gemrb/override/how/seater.pro differ
diff --git a/gemrb/override/how/seaterh.pro b/gemrb/override/how/seaterh.pro
new file mode 100644
index 0000000..488d807
Binary files /dev/null and b/gemrb/override/how/seaterh.pro differ
diff --git a/gemrb/override/how/sgrowth.pro b/gemrb/override/how/sgrowth.pro
new file mode 100644
index 0000000..06a72f5
Binary files /dev/null and b/gemrb/override/how/sgrowth.pro differ
diff --git a/gemrb/override/how/shout.pro b/gemrb/override/how/shout.pro
new file mode 100644
index 0000000..9e1a6a2
Binary files /dev/null and b/gemrb/override/how/shout.pro differ
diff --git a/gemrb/override/how/shroud.pro b/gemrb/override/how/shroud.pro
new file mode 100644
index 0000000..e31a942
Binary files /dev/null and b/gemrb/override/how/shroud.pro differ
diff --git a/gemrb/override/how/shtable.2da b/gemrb/override/how/shtable.2da
new file mode 100644
index 0000000..ac06d6f
--- /dev/null
+++ b/gemrb/override/how/shtable.2da
@@ -0,0 +1,43 @@
+2DA V1.0
+*
+ RESREF
+0 SHAIR
+1 SHEARTH
+2 SHWATER
+3 *
+4 SHAIR
+5 SHEARTH
+6 SHWATER
+7 *
+8 SHAIR
+9 SHEARTH
+10 SHWATER
+11 *
+12 SHAIR
+13 SHEARTH
+14 SHWATER
+15 *
+16 SHAIR
+17 SHEARTH
+18 SHWATER
+19 *
+20 SHAIR
+21 SHEARTH
+22 SHWATER
+23 *
+24 SHAIR
+25 SHEARTH
+26 SHWATER
+27 *
+28 SHAIR
+29 SHEARTH
+30 SHWATER
+31 *
+32
+33
+34
+35 FLMSTRK
+36 HLYMITE
+37
+38 DDOORH
+39 SPFDEATH
diff --git a/gemrb/override/how/skills.2da b/gemrb/override/how/skills.2da
new file mode 100644
index 0000000..afdf917
--- /dev/null
+++ b/gemrb/override/how/skills.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF ID THIEF MAGE_THIEF FIGHTER_THIEF CLERIC_THIEF FIGHTER_MAGE_THIEF
+FIRST_LEVEL * * * * 40 40 40 40 40
+RATE * * * * 25 25 25 25 25
+STEALTH 11984 9600 9461 27 1 1 1 1 1
+FIND_TRAPS 11985 9599 9462 28 1 1 1 1 1
+PICK_POCKETS 11986 9597 9463 29 1 1 1 1 1
+OPEN_LOCKS 11987 9598 9460 26 1 1 1 1 1
diff --git a/gemrb/override/how/sleep.pro b/gemrb/override/how/sleep.pro
new file mode 100644
index 0000000..06568c9
Binary files /dev/null and b/gemrb/override/how/sleep.pro differ
diff --git a/gemrb/override/how/slivinh.pro b/gemrb/override/how/slivinh.pro
new file mode 100644
index 0000000..1e74c1a
Binary files /dev/null and b/gemrb/override/how/slivinh.pro differ
diff --git a/gemrb/override/how/slottype.2da b/gemrb/override/how/slottype.2da
new file mode 100644
index 0000000..8a14119
--- /dev/null
+++ b/gemrb/override/how/slottype.2da
@@ -0,0 +1,42 @@
+2DA V1.0
+*
+ BITS SCRIPT ICON STRREF EFFECT FLAGS
+10 0 0 * 0 2 0
+6 1 13 STONHELM 11999 7 0
+1 2 11 STONARM 11997 1 0
+9 4 26 STONSHIL 12006 6 0
+5 8 12 STONGLET 11998 1 0
+7 16 22 STONRING 12002 1 1
+8 16 23 STONRING 12003 1 1
+0 32 14 STONAMUL 12000 1 0
+2 64 21 STONBELT 12001 1 0
+3 128 25 STONBOOT 12005 1 0
+35 256 1 STONWEAP 12010 4 1
+36 256 2 STONWEAP 12010 4 1
+37 256 3 STONWEAP 12010 4 1
+38 256 4 STONWEAP 12010 4 1
+11 512 15 STONQUIV 12009 5 1
+12 512 16 STONQUIV 12009 5 1
+13 512 17 STONQUIV 12009 5 1
+14 512 0 STONQUIV 12009 5 1
+4 1024 24 STONCLOK 12004 1 0
+15 2048 5 STONITEM 12012 0 1
+16 2048 6 STONITEM 12012 0 1
+17 2048 7 STONITEM 12012 0 1
+18 -1 30 * 12013 0 1
+19 -1 31 * 12013 0 1
+20 -1 32 * 12013 0 1
+21 -1 33 * 12013 0 1
+22 -1 34 * 12013 0 1
+23 -1 35 * 12013 0 1
+24 -1 36 * 12013 0 1
+25 -1 37 * 12013 0 1
+26 -1 38 * 12013 0 1
+27 -1 39 * 12013 0 1
+28 -1 40 * 12013 0 1
+29 -1 41 * 12013 0 1
+30 -1 42 * 12013 0 1
+31 -1 43 * 12013 0 1
+32 -1 44 * 12013 0 1
+33 -1 45 * 12013 0 1
+34 0 0 * 0 3 0
diff --git a/gemrb/override/how/soflamh.pro b/gemrb/override/how/soflamh.pro
new file mode 100644
index 0000000..334e211
Binary files /dev/null and b/gemrb/override/how/soflamh.pro differ
diff --git a/gemrb/override/how/sohope.pro b/gemrb/override/how/sohope.pro
new file mode 100644
index 0000000..f70ba7d
Binary files /dev/null and b/gemrb/override/how/sohope.pro differ
diff --git a/gemrb/override/how/sooneh.pro b/gemrb/override/how/sooneh.pro
new file mode 100644
index 0000000..3672511
Binary files /dev/null and b/gemrb/override/how/sooneh.pro differ
diff --git a/gemrb/override/how/sopain.pro b/gemrb/override/how/sopain.pro
new file mode 100644
index 0000000..0cd795a
Binary files /dev/null and b/gemrb/override/how/sopain.pro differ
diff --git a/gemrb/override/how/sparbknp.pro b/gemrb/override/how/sparbknp.pro
new file mode 100644
index 0000000..aba7673
Binary files /dev/null and b/gemrb/override/how/sparbknp.pro differ
diff --git a/gemrb/override/how/sparbkpa.pro b/gemrb/override/how/sparbkpa.pro
new file mode 100644
index 0000000..7a51eed
Binary files /dev/null and b/gemrb/override/how/sparbkpa.pro differ
diff --git a/gemrb/override/how/sparblnp.pro b/gemrb/override/how/sparblnp.pro
new file mode 100644
index 0000000..f858276
Binary files /dev/null and b/gemrb/override/how/sparblnp.pro differ
diff --git a/gemrb/override/how/sparblpa.pro b/gemrb/override/how/sparblpa.pro
new file mode 100644
index 0000000..4124a9d
Binary files /dev/null and b/gemrb/override/how/sparblpa.pro differ
diff --git a/gemrb/override/how/sparchnp.pro b/gemrb/override/how/sparchnp.pro
new file mode 100644
index 0000000..7dd540e
Binary files /dev/null and b/gemrb/override/how/sparchnp.pro differ
diff --git a/gemrb/override/how/sparchpa.pro b/gemrb/override/how/sparchpa.pro
new file mode 100644
index 0000000..101f9a2
Binary files /dev/null and b/gemrb/override/how/sparchpa.pro differ
diff --git a/gemrb/override/how/spargonp.pro b/gemrb/override/how/spargonp.pro
new file mode 100644
index 0000000..234aca6
Binary files /dev/null and b/gemrb/override/how/spargonp.pro differ
diff --git a/gemrb/override/how/spargopa.pro b/gemrb/override/how/spargopa.pro
new file mode 100644
index 0000000..2665825
Binary files /dev/null and b/gemrb/override/how/spargopa.pro differ
diff --git a/gemrb/override/how/spargrnp.pro b/gemrb/override/how/spargrnp.pro
new file mode 100644
index 0000000..a1d4dbb
Binary files /dev/null and b/gemrb/override/how/spargrnp.pro differ
diff --git a/gemrb/override/how/spargrpa.pro b/gemrb/override/how/spargrpa.pro
new file mode 100644
index 0000000..55a741b
Binary files /dev/null and b/gemrb/override/how/spargrpa.pro differ
diff --git a/gemrb/override/how/sparicnp.pro b/gemrb/override/how/sparicnp.pro
new file mode 100644
index 0000000..77ff12b
Binary files /dev/null and b/gemrb/override/how/sparicnp.pro differ
diff --git a/gemrb/override/how/sparicpa.pro b/gemrb/override/how/sparicpa.pro
new file mode 100644
index 0000000..2d8b9d7
Binary files /dev/null and b/gemrb/override/how/sparicpa.pro differ
diff --git a/gemrb/override/how/sparklbk.pro b/gemrb/override/how/sparklbk.pro
new file mode 100644
index 0000000..147bd43
Binary files /dev/null and b/gemrb/override/how/sparklbk.pro differ
diff --git a/gemrb/override/how/sparklbl.pro b/gemrb/override/how/sparklbl.pro
new file mode 100644
index 0000000..4928b0c
Binary files /dev/null and b/gemrb/override/how/sparklbl.pro differ
diff --git a/gemrb/override/how/sparklch.pro b/gemrb/override/how/sparklch.pro
new file mode 100644
index 0000000..c6ad4c8
Binary files /dev/null and b/gemrb/override/how/sparklch.pro differ
diff --git a/gemrb/override/how/sparklgo.pro b/gemrb/override/how/sparklgo.pro
new file mode 100644
index 0000000..748d91d
Binary files /dev/null and b/gemrb/override/how/sparklgo.pro differ
diff --git a/gemrb/override/how/sparklgr.pro b/gemrb/override/how/sparklgr.pro
new file mode 100644
index 0000000..ac152c1
Binary files /dev/null and b/gemrb/override/how/sparklgr.pro differ
diff --git a/gemrb/override/how/sparklic.pro b/gemrb/override/how/sparklic.pro
new file mode 100644
index 0000000..e833132
Binary files /dev/null and b/gemrb/override/how/sparklic.pro differ
diff --git a/gemrb/override/how/sparklma.pro b/gemrb/override/how/sparklma.pro
new file mode 100644
index 0000000..50d7be0
Binary files /dev/null and b/gemrb/override/how/sparklma.pro differ
diff --git a/gemrb/override/how/sparklor.pro b/gemrb/override/how/sparklor.pro
new file mode 100644
index 0000000..dc02305
Binary files /dev/null and b/gemrb/override/how/sparklor.pro differ
diff --git a/gemrb/override/how/sparklpu.pro b/gemrb/override/how/sparklpu.pro
new file mode 100644
index 0000000..ef0f1fd
Binary files /dev/null and b/gemrb/override/how/sparklpu.pro differ
diff --git a/gemrb/override/how/sparklre.pro b/gemrb/override/how/sparklre.pro
new file mode 100644
index 0000000..1a15bfd
Binary files /dev/null and b/gemrb/override/how/sparklre.pro differ
diff --git a/gemrb/override/how/sparklst.pro b/gemrb/override/how/sparklst.pro
new file mode 100644
index 0000000..543ea91
Binary files /dev/null and b/gemrb/override/how/sparklst.pro differ
diff --git a/gemrb/override/how/sparmanp.pro b/gemrb/override/how/sparmanp.pro
new file mode 100644
index 0000000..c76d137
Binary files /dev/null and b/gemrb/override/how/sparmanp.pro differ
diff --git a/gemrb/override/how/sparmapa.pro b/gemrb/override/how/sparmapa.pro
new file mode 100644
index 0000000..6a21bd1
Binary files /dev/null and b/gemrb/override/how/sparmapa.pro differ
diff --git a/gemrb/override/how/sparornp.pro b/gemrb/override/how/sparornp.pro
new file mode 100644
index 0000000..abf33b8
Binary files /dev/null and b/gemrb/override/how/sparornp.pro differ
diff --git a/gemrb/override/how/sparorpa.pro b/gemrb/override/how/sparorpa.pro
new file mode 100644
index 0000000..686dd90
Binary files /dev/null and b/gemrb/override/how/sparorpa.pro differ
diff --git a/gemrb/override/how/sparpunp.pro b/gemrb/override/how/sparpunp.pro
new file mode 100644
index 0000000..23f98f2
Binary files /dev/null and b/gemrb/override/how/sparpunp.pro differ
diff --git a/gemrb/override/how/sparpupa.pro b/gemrb/override/how/sparpupa.pro
new file mode 100644
index 0000000..322a995
Binary files /dev/null and b/gemrb/override/how/sparpupa.pro differ
diff --git a/gemrb/override/how/sparrenp.pro b/gemrb/override/how/sparrenp.pro
new file mode 100644
index 0000000..7718874
Binary files /dev/null and b/gemrb/override/how/sparrenp.pro differ
diff --git a/gemrb/override/how/sparrepa.pro b/gemrb/override/how/sparrepa.pro
new file mode 100644
index 0000000..a5edc18
Binary files /dev/null and b/gemrb/override/how/sparrepa.pro differ
diff --git a/gemrb/override/how/sparstnp.pro b/gemrb/override/how/sparstnp.pro
new file mode 100644
index 0000000..7d4b4c3
Binary files /dev/null and b/gemrb/override/how/sparstnp.pro differ
diff --git a/gemrb/override/how/sparstpa.pro b/gemrb/override/how/sparstpa.pro
new file mode 100644
index 0000000..4d55459
Binary files /dev/null and b/gemrb/override/how/sparstpa.pro differ
diff --git a/gemrb/override/how/spear.pro b/gemrb/override/how/spear.pro
new file mode 100644
index 0000000..bc9288c
Binary files /dev/null and b/gemrb/override/how/spear.pro differ
diff --git a/gemrb/override/how/spearex.pro b/gemrb/override/how/spearex.pro
new file mode 100644
index 0000000..4bb5786
Binary files /dev/null and b/gemrb/override/how/spearex.pro differ
diff --git a/gemrb/override/how/spfirebl.pro b/gemrb/override/how/spfirebl.pro
new file mode 100644
index 0000000..3b44f50
Binary files /dev/null and b/gemrb/override/how/spfirebl.pro differ
diff --git a/gemrb/override/how/spklarbk.pro b/gemrb/override/how/spklarbk.pro
new file mode 100644
index 0000000..f8f12c0
Binary files /dev/null and b/gemrb/override/how/spklarbk.pro differ
diff --git a/gemrb/override/how/spklarbl.pro b/gemrb/override/how/spklarbl.pro
new file mode 100644
index 0000000..eddae9d
Binary files /dev/null and b/gemrb/override/how/spklarbl.pro differ
diff --git a/gemrb/override/how/spklarch.pro b/gemrb/override/how/spklarch.pro
new file mode 100644
index 0000000..82766f4
Binary files /dev/null and b/gemrb/override/how/spklarch.pro differ
diff --git a/gemrb/override/how/spklargo.pro b/gemrb/override/how/spklargo.pro
new file mode 100644
index 0000000..e4323ac
Binary files /dev/null and b/gemrb/override/how/spklargo.pro differ
diff --git a/gemrb/override/how/spklargr.pro b/gemrb/override/how/spklargr.pro
new file mode 100644
index 0000000..38fc137
Binary files /dev/null and b/gemrb/override/how/spklargr.pro differ
diff --git a/gemrb/override/how/spklaric.pro b/gemrb/override/how/spklaric.pro
new file mode 100644
index 0000000..4fd7d7e
Binary files /dev/null and b/gemrb/override/how/spklaric.pro differ
diff --git a/gemrb/override/how/spklarma.pro b/gemrb/override/how/spklarma.pro
new file mode 100644
index 0000000..d8f18c2
Binary files /dev/null and b/gemrb/override/how/spklarma.pro differ
diff --git a/gemrb/override/how/spklaror.pro b/gemrb/override/how/spklaror.pro
new file mode 100644
index 0000000..7e395f4
Binary files /dev/null and b/gemrb/override/how/spklaror.pro differ
diff --git a/gemrb/override/how/spklarpu.pro b/gemrb/override/how/spklarpu.pro
new file mode 100644
index 0000000..e70bb75
Binary files /dev/null and b/gemrb/override/how/spklarpu.pro differ
diff --git a/gemrb/override/how/spklarre.pro b/gemrb/override/how/spklarre.pro
new file mode 100644
index 0000000..29d636c
Binary files /dev/null and b/gemrb/override/how/spklarre.pro differ
diff --git a/gemrb/override/how/spklarst.pro b/gemrb/override/how/spklarst.pro
new file mode 100644
index 0000000..ba5633b
Binary files /dev/null and b/gemrb/override/how/spklarst.pro differ
diff --git a/gemrb/override/how/splprot.2da b/gemrb/override/how/splprot.2da
new file mode 100644
index 0000000..b3603c1
--- /dev/null
+++ b/gemrb/override/how/splprot.2da
@@ -0,0 +1,105 @@
+2DA V1.0
+0
+ STAT VALUE RELATION COMMENT
+0 EA 0 4 anything
+1 GENERAL 4 1 general/undead
+2 GENERAL 4 5 general/not_undead
+3 RESISTFIRE 100 4 fire_resistance>=100
+4 RESISTFIRE 100 2 fire_resistance<100
+5 GENERAL 1 1 general/humanoid
+6 GENERAL 1 5 general/not_humanoid
+7 GENERAL 2 1 general/animal
+8 GENERAL 2 5 general/not_animal
+9 RACE 152 1 race/elemental
+10 RACE 152 5 race/not_elemental
+11 RACE 159 1 race/fungus
+12 RACE 159 5 race/not_fungus
+13 0x102 3 0 huge_creature
+14 0x102 3 3 not_huge_creature
+15 RACE 2 1 race/elf
+16 RACE 2 5 race/not_elf
+17 RACE 166 1 race/umberhulk
+18 RACE 166 5 race/not_umberhulk
+19 RACE 3 1 race/halfelf
+20 RACE 3 5 race/not_halfelf
+21 0x103 5 7 humanoid_or_animal
+22 0x104 5 7 not_humanoid_or_animal
+23 STATE 0x40000 9 state/blind
+24 STATE 0x40000 8 state/not_blind
+25 RESISTCOLD 100 4 cold_resistance>=100
+26 RESISTCOLD 100 2 cold_resistance<100
+27 RACE 156 1 race/golem
+28 RACE 156 5 race/not_golem
+29 RACE 179 1 race/minotaur
+30 RACE 179 5 race/not_minotaur
+31 0x103 1 11 undead_or_fungus
+32 0x104 1 11 not_undead_or_fungus
+33 ALIGNMENT 2 9 alignment/good
+34 ALIGNMENT 2 8 alignment/not_good
+35 ALIGNMENT 1 9 alignment/neutral
+36 ALIGNMENT 1 8 alignment/not_neutral
+37 ALIGNMENT 3 7 alignment/evil
+38 ALIGNMENT 3 11 alignment/not_evil
+39 CLASS 7 1 class/paladin
+40 CLASS 7 5 class/not_paladin
+41 0x105 217 1 moral_alignment_same
+42 0x105 217 5 moral_alignment_not_same
+43 0x100 * * source
+44 0x101 * * not_source
+45 ? * * water_using-FIXME
+46 ? * * not_water_using-FIXME
+47 0x104 32 27 breathes/not_undead_fungus_golem
+48 0x103 32 27 not_breathes/undead_fungus_or_golem
+49 EA 30 0 allies
+50 EA 30 3 not_allies
+51 EA 200 4 enemies
+52 EA 200 2 not_enemies
+53 0x103 3 25 race/fire_or_cold_using
+54 0x104 3 25 race/not_fire_or_cold_using
+55 ? * * unnatural
+56 ? * * not_unnatural
+57 SEX 1 1 gender/male
+58 SEX 1 5 gender/not_male
+59 ALIGNMENT 0x10 1 alignment/lawful
+60 ALIGNMENT 0x10 5 alignment/not_lawful
+61 ALIGNMENT 0x30 1 alignment/chaotic
+62 ALIGNMENT 0x30 5 alignment/not_chaotic
+63 0x109 * * evasion_check
+64 RACE 160 1 race/orc
+65 RACE 160 5 race/not_orc
+66 EXTSTATE ? 9 extstate/deaf
+67 EXTSTATE ? 8 extstate/not_deaf
+68 SEX 6 1 gender/summoned
+69 SEX 6 5 gender/not_summoned
+70 RACE 184 1 race/mindflayer
+71 RACE 184 5 race/not_mindflayer
+72 STATE 0x1000 9 state/silenced
+73 STATE 0x1000 8 state/not_silenced
+74 INT -1 4 int/less
+75 INT -1 0 int/greater
+76 INT -1 3 int/less_or_equal
+77 INT -1 2 int/greater_or_equal
+78 CLASS 2 1 class/bard
+79 CLASS 2 5 class/not_bard
+80 0x108 1 * near_enemies
+81 0x108 0 * not_near_enemies
+82 SUBRACE 0x20001 1 subrace/drow
+83 SUBRACE 0x20001 5 subrace/not_drow
+84 SUBRACE 0x40002 1 subrace/gray_dwarf
+85 SUBRACE 0x40002 5 subrace/not_gray_dwarf
+86 0x107 1 * daytime
+87 0x107 0 * not_daytime
+88 0x106 1 9 outdoor
+89 0x106 1 8 not_outdoor
+90 RACE 190 1 race/keg
+91 RACE 190 5 race/not_keg
+92 RACE * * outsider
+93 RACE * * not_outsider
+94 RACE 215 1 race/plate
+95 RACE 215 5 race/not_plate
+96 0x103 90 94 race/keg_or_plate
+97 0x104 90 94 race/not_keg_or_plate
+98 RACE 161 1 race/salamander
+99 RACE 161 5 race/not_salamander
+100 RACE 164 1 race/tanari
+101 RACE 164 5 race/not_tanari
diff --git a/gemrb/override/how/splspec.2da b/gemrb/override/how/splspec.2da
new file mode 100644
index 0000000..de337f7
--- /dev/null
+++ b/gemrb/override/how/splspec.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ IDENTIFY
+SPWI110 1
diff --git a/gemrb/override/how/spoisoh.pro b/gemrb/override/how/spoisoh.pro
new file mode 100644
index 0000000..6aa9302
Binary files /dev/null and b/gemrb/override/how/spoisoh.pro differ
diff --git a/gemrb/override/how/spscorch.pro b/gemrb/override/how/spscorch.pro
new file mode 100644
index 0000000..3ad6c32
Binary files /dev/null and b/gemrb/override/how/spscorch.pro differ
diff --git a/gemrb/override/how/spscoric.pro b/gemrb/override/how/spscoric.pro
new file mode 100644
index 0000000..3d7e0b7
Binary files /dev/null and b/gemrb/override/how/spscoric.pro differ
diff --git a/gemrb/override/how/spwrath.pro b/gemrb/override/how/spwrath.pro
new file mode 100644
index 0000000..d66271c
Binary files /dev/null and b/gemrb/override/how/spwrath.pro differ
diff --git a/gemrb/override/how/ssorbh.pro b/gemrb/override/how/ssorbh.pro
new file mode 100644
index 0000000..2d9489e
Binary files /dev/null and b/gemrb/override/how/ssorbh.pro differ
diff --git a/gemrb/override/how/ssswarm.pro b/gemrb/override/how/ssswarm.pro
new file mode 100644
index 0000000..efd382f
Binary files /dev/null and b/gemrb/override/how/ssswarm.pro differ
diff --git a/gemrb/override/how/sstone.pro b/gemrb/override/how/sstone.pro
new file mode 100644
index 0000000..f1a87f5
Binary files /dev/null and b/gemrb/override/how/sstone.pro differ
diff --git a/gemrb/override/how/sstoneh.pro b/gemrb/override/how/sstoneh.pro
new file mode 100644
index 0000000..b3af8da
Binary files /dev/null and b/gemrb/override/how/sstoneh.pro differ
diff --git a/gemrb/override/how/start.2da b/gemrb/override/how/start.2da
new file mode 100644
index 0000000..17d460b
--- /dev/null
+++ b/gemrb/override/how/start.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ XPOS YPOS AREA ROT
+NORMAL START_XPOS START_YPOS START_AREA *
+EXPANSION START_XPOS_EXPANSION START_YPOS_EXPANSION START_AREA_EXPANSION *
diff --git a/gemrb/override/how/stone.pro b/gemrb/override/how/stone.pro
new file mode 100644
index 0000000..13ce3b0
Binary files /dev/null and b/gemrb/override/how/stone.pro differ
diff --git a/gemrb/override/how/strengh.pro b/gemrb/override/how/strengh.pro
new file mode 100644
index 0000000..53d8a67
Binary files /dev/null and b/gemrb/override/how/strengh.pro differ
diff --git a/gemrb/override/how/strings.2da b/gemrb/override/how/strings.2da
new file mode 100644
index 0000000..aaa9aee
--- /dev/null
+++ b/gemrb/override/how/strings.2da
@@ -0,0 +1,170 @@
+2DA V1.0
+-1
+ STRREF
+SCATTERED 16457
+WHOLEPARTY 16484
+DOORLOCKED 16485
+MAGICTRAP 16486
+NORMALTRAP 16487
+TRAP 16488
+CANNOTGO 16489
+TRAPREMOVED 16490
+OVERSTOCKED 16491
+SLEEP 16492
+AMBUSH 16493
+CONTLOCKED 16494
+NOMONEY 16495
+CURSED 16496
+SPELLDISRUPT 16497
+DIED 16498
+MAYNOTREST 16499
+CANTRESTMONS 16500
+CANTSAVEMONS 16501
+CANTSAVE 16502
+NODIALOG 10945
+CANTSAVEDIALOG 19253
+CANTSAVEDIALOG2 19254
+CANTSAVEMOVIE 19255
+TARGETBUSY -1
+CANTTALKTRANS -1
+GOTGOLD 17572
+LOSTGOLD 17573
+GOTXP 17574
+LOSTXP 17575
+GOTITEM 17576
+LOSTITEM 17577
+GOTREP 19686
+LOSTREP 19687
+GOTABILITY 10514
+GOTSPELL 10514
+GOTSONG 26320
+NOTHINGTOSAY -1
+JOURNALCHANGE 11359
+WORLDMAPCHANGE 11360
+PAUSED 16321
+UNPAUSED 16322
+SCRIPTPAUSED 7666
+AP_UNUSABLE 17113
+AP_ATTACKED 17114
+AP_HIT 17115
+AP_WOUNDED 17116
+AP_DEAD 17117
+AP_NOTARGET 17118
+AP_ENDROUND 10014
+AP_ENEMY 23511
+AP_TRAP -1
+AP_SPELLCAST -1
+AP_RESERVED1 -1
+AP_RESERVED2 -1
+AP_RESERVED3 -1
+AP_RESERVED4 -1
+CHARMED 14672
+DIRECHARMED 14780
+CONTROLLED -1
+EVIL 16505
+GNE_NEUTRAL 16504
+GOOD 16503
+STR_LAWFUL -1
+LNC_NEUTRAL -1
+CHAOTIC -1
+ACTION_CAST 16464
+ACTION_ATTACK 16465
+ACTION_TURN 16466
+ACTION_SONG 16467
+ACTION_FINDTRAP 16468
+MAGICWEAPON 10141
+OFFHAND_USED 9380
+TWOHANDED_USED 9381
+CANNOT_USE_ITEM 9382
+CANT_DROP_ITEM 25697
+NOT_IN_OFFHAND 26342
+ITEM_IS_CURSED 16304
+NO_CRITICAL 20696
+TRACKING -1
+TRACKINGFAILED 19534
+DOOR_NOPICK 23169
+CONT_NOPICK 23169
+CANTSAVECOMBAT -1
+CANTSAVENOCTRL -1
+LOCKPICK_DONE 16517
+LOCKPICK_FAILED 16518
+STATIC_DISSIPATE 26518
+LIGHTNING_DISSIPATE -1
+HAS_NO_ABILITY 17317
+NEEDS_IDENTIFY 17316
+WRONG_ITEMTYPE 9375
+HAS_ITEMEXCL 20685
+PICKPOCKET_DONE 10072
+PICKPOCKET_NONE 18297
+PICKPOCKET_FAIL 10069
+PICKPOCKET_EVIL 10068
+PICKPOCKET_ARMOR 10067
+USING_FEAT -1
+STOPPED_FEAT -1
+DISARM_DONE 16520
+DISARM_FAIL 1608
+DOORBASH_DONE 9915
+DOORBASH_FAIL 9913
+CONTBASH_DONE 9916
+CONTBASH_FAIL 9914
+MAYNOTSETTRAP -1
+SNAREFAILED -1
+SNARESUCCEED -1
+NOMORETRAP -1
+DISABLEDMAGE -1
+SAVESUCCEED 1682
+QSAVESUCCEED 10237
+UNINJURED 2943
+INJURED1 2944
+INJURED2 2945
+INJURED3 2946
+INJURED4 2947
+HOURS 10700
+HOUR 10701
+DAYS 10697
+DAY 10698
+REST 10690
+JOURNEY 10689
+PST_REST -1
+PST_HOUR -1
+PST_HOURS -1
+DAMAGE_IMMUNITY 25038
+DAMAGE_STR1 25028
+DAMAGE_STR2 25017
+DAMAGE_STR3 26223
+DMG_POISON 25018
+DMG_MAGIC 25019
+DMG_MISSILE 25020
+DMG_SLASHING 25021
+DMG_PIERCING 25022
+DMG_CRUSHING 25023
+DMG_FIRE 25024
+DMG_ELECTRIC 25025
+DMG_COLD 25026
+DMG_ACID 25027
+DMG_OTHER 0
+GOTQUESTXP -1
+LEVELUP 17119
+INVFULL_ITEMDROP 32879
+CONT_DUP 32876
+CONT_TRIG -1
+CONT_FAIL -1
+SEQ_DUP -1
+CRITICAL_HIT 16462
+CRITICAL_MISS 16463
+DEATH 14026
+BACKSTAB 12128
+BACKSTAB_BAD 10013
+BACKSTAB_FAIL -1
+CASTER_LVL_INC -1
+CASTER_LVL_DEC -1
+CHARS_EXPORTED 26827
+PALADIN_FALL 19620
+RANGER_FALL 19621
+RES_RESISTED 26818
+DEADMAGIC_FAIL -1
+MISCASTMAGIC -1
+WILDSURGE -1
+FAMBLOCK 8537
+FAMPROTAGONIST 8538
+
diff --git a/gemrb/override/how/suffoc.pro b/gemrb/override/how/suffoc.pro
new file mode 100644
index 0000000..c4e0c4c
Binary files /dev/null and b/gemrb/override/how/suffoc.pro differ
diff --git a/gemrb/override/how/suffoch.pro b/gemrb/override/how/suffoch.pro
new file mode 100644
index 0000000..ef84c8c
Binary files /dev/null and b/gemrb/override/how/suffoch.pro differ
diff --git a/gemrb/override/how/sunfire.pro b/gemrb/override/how/sunfire.pro
new file mode 100644
index 0000000..fcaeafd
Binary files /dev/null and b/gemrb/override/how/sunfire.pro differ
diff --git a/gemrb/override/how/sunray.pro b/gemrb/override/how/sunray.pro
new file mode 100644
index 0000000..9525a7a
Binary files /dev/null and b/gemrb/override/how/sunray.pro differ
diff --git a/gemrb/override/how/sunscoh.pro b/gemrb/override/how/sunscoh.pro
new file mode 100644
index 0000000..be43f0e
Binary files /dev/null and b/gemrb/override/how/sunscoh.pro differ
diff --git a/gemrb/override/how/swave.pro b/gemrb/override/how/swave.pro
new file mode 100644
index 0000000..1987698
Binary files /dev/null and b/gemrb/override/how/swave.pro differ
diff --git a/gemrb/override/how/swaveh.pro b/gemrb/override/how/swaveh.pro
new file mode 100644
index 0000000..c417b2b
Binary files /dev/null and b/gemrb/override/how/swaveh.pro differ
diff --git a/gemrb/override/how/trapglyp.pro b/gemrb/override/how/trapglyp.pro
new file mode 100644
index 0000000..5542681
Binary files /dev/null and b/gemrb/override/how/trapglyp.pro differ
diff --git a/gemrb/override/how/trapskul.pro b/gemrb/override/how/trapskul.pro
new file mode 100644
index 0000000..5706fec
Binary files /dev/null and b/gemrb/override/how/trapskul.pro differ
diff --git a/gemrb/override/how/tspray.pro b/gemrb/override/how/tspray.pro
new file mode 100644
index 0000000..2b297ca
Binary files /dev/null and b/gemrb/override/how/tspray.pro differ
diff --git a/gemrb/override/how/turn.spl b/gemrb/override/how/turn.spl
new file mode 100644
index 0000000..f4b14d6
Binary files /dev/null and b/gemrb/override/how/turn.spl differ
diff --git a/gemrb/override/how/ublight.pro b/gemrb/override/how/ublight.pro
new file mode 100644
index 0000000..6f6a33d
Binary files /dev/null and b/gemrb/override/how/ublight.pro differ
diff --git a/gemrb/override/how/uward.pro b/gemrb/override/how/uward.pro
new file mode 100644
index 0000000..6cf5b94
Binary files /dev/null and b/gemrb/override/how/uward.pro differ
diff --git a/gemrb/override/how/uwardh.pro b/gemrb/override/how/uwardh.pro
new file mode 100644
index 0000000..b385f90
Binary files /dev/null and b/gemrb/override/how/uwardh.pro differ
diff --git a/gemrb/override/how/vcremap.2da b/gemrb/override/how/vcremap.2da
new file mode 100644
index 0000000..b23c7aa
--- /dev/null
+++ b/gemrb/override/how/vcremap.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ ORIGINAL NEW
+BIO 74 63
diff --git a/gemrb/override/how/vspherh.pro b/gemrb/override/how/vspherh.pro
new file mode 100644
index 0000000..717b5a8
Binary files /dev/null and b/gemrb/override/how/vspherh.pro differ
diff --git a/gemrb/override/how/wdeath1.pro b/gemrb/override/how/wdeath1.pro
new file mode 100644
index 0000000..c3e2021
Binary files /dev/null and b/gemrb/override/how/wdeath1.pro differ
diff --git a/gemrb/override/how/wdeath2.pro b/gemrb/override/how/wdeath2.pro
new file mode 100644
index 0000000..764ee67
Binary files /dev/null and b/gemrb/override/how/wdeath2.pro differ
diff --git a/gemrb/override/how/weapprof.2da b/gemrb/override/how/weapprof.2da
new file mode 100644
index 0000000..cee9e63
--- /dev/null
+++ b/gemrb/override/how/weapprof.2da
@@ -0,0 +1,18 @@
+2DA V1.0
+-1
+ ID NAME_REF DESC_REF CAP_REF
+BOW 91 11965 9591 8733
+CROSSBOW 103 16034 18323 16033
+MISSILE 94 11971 9596 9403
+AXE 93 11969 9595 9402
+CLUB 101 16030 18324 16029
+DAGGER 96 16014 18325 16013
+FLAIL 99 16021 18326 16020
+HALBERD 97 16016 18327 16015
+HAMMER 100 16027 18328 16022
+MACE 98 16018 18329 16017
+SPEAR 92 11972 9592 8734
+QUARTER_STAFF 102 16032 18330 16031
+GREAT_SWORD 95 16012 18331 16011
+LARGE_SWORD 89 11968 9589 8668
+SMALL_SWORD 90 11967 9590 8732
diff --git a/gemrb/override/how/web.pro b/gemrb/override/how/web.pro
new file mode 100644
index 0000000..120c2a0
Binary files /dev/null and b/gemrb/override/how/web.pro differ
diff --git a/gemrb/override/how/whirlw.pro b/gemrb/override/how/whirlw.pro
new file mode 100644
index 0000000..50d9199
Binary files /dev/null and b/gemrb/override/how/whirlw.pro differ
diff --git a/gemrb/override/how/womoon.pro b/gemrb/override/how/womoon.pro
new file mode 100644
index 0000000..35543b7
Binary files /dev/null and b/gemrb/override/how/womoon.pro differ
diff --git a/gemrb/override/how/wowisp.pro b/gemrb/override/how/wowisp.pro
new file mode 100644
index 0000000..6e82545
Binary files /dev/null and b/gemrb/override/how/wowisp.pro differ
diff --git a/gemrb/override/how/wvdeath.pro b/gemrb/override/how/wvdeath.pro
new file mode 100644
index 0000000..b3d956a
Binary files /dev/null and b/gemrb/override/how/wvdeath.pro differ
diff --git a/gemrb/override/how/wvhith.pro b/gemrb/override/how/wvhith.pro
new file mode 100644
index 0000000..587b2af
Binary files /dev/null and b/gemrb/override/how/wvhith.pro differ
diff --git a/gemrb/override/how/wwolf.pro b/gemrb/override/how/wwolf.pro
new file mode 100644
index 0000000..4df3732
Binary files /dev/null and b/gemrb/override/how/wwolf.pro differ
diff --git a/gemrb/override/how/zlaura.pro b/gemrb/override/how/zlaura.pro
new file mode 100644
index 0000000..31ee26e
Binary files /dev/null and b/gemrb/override/how/zlaura.pro differ
diff --git a/gemrb/override/iwd/CMakeLists.txt b/gemrb/override/iwd/CMakeLists.txt
new file mode 100644
index 0000000..a662b98
--- /dev/null
+++ b/gemrb/override/iwd/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_OVERRIDE (iwd)
\ No newline at end of file
diff --git a/gemrb/override/iwd/Makefile.am b/gemrb/override/iwd/Makefile.am
new file mode 100644
index 0000000..9db0196
--- /dev/null
+++ b/gemrb/override/iwd/Makefile.am
@@ -0,0 +1,3 @@
+iwdoverride_DATA = *.2da *.bmp *.ini *.chu *.ids *.bcs
+iwdoverridedir = $(moddir)/override/iwd/
+EXTRA_DIST = *.2da *.bmp *.ini *.chu *.ids *.bcs
diff --git a/gemrb/override/iwd/ability.2da b/gemrb/override/iwd/ability.2da
new file mode 100644
index 0000000..2fa0989
--- /dev/null
+++ b/gemrb/override/iwd/ability.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF STAT_ID
+STRENGTH 11975 9582 1145 36
+DEXTERITY 11977 9584 1151 40
+CONSTITUTION 11978 9583 1178 41
+INTELLIGENCE 11979 9585 1179 38
+WISDOM 11980 9586 1180 39
+CHARISMA 11981 9587 1181 42
diff --git a/gemrb/override/iwd/abjurap.pro b/gemrb/override/iwd/abjurap.pro
new file mode 100644
index 0000000..9f72db3
Binary files /dev/null and b/gemrb/override/iwd/abjurap.pro differ
diff --git a/gemrb/override/iwd/abjurh.pro b/gemrb/override/iwd/abjurh.pro
new file mode 100644
index 0000000..4927bf6
Binary files /dev/null and b/gemrb/override/iwd/abjurh.pro differ
diff --git a/gemrb/override/iwd/abjurt.pro b/gemrb/override/iwd/abjurt.pro
new file mode 100644
index 0000000..092eeb8
Binary files /dev/null and b/gemrb/override/iwd/abjurt.pro differ
diff --git a/gemrb/override/iwd/acidblgr.pro b/gemrb/override/iwd/acidblgr.pro
new file mode 100644
index 0000000..fe37e1c
Binary files /dev/null and b/gemrb/override/iwd/acidblgr.pro differ
diff --git a/gemrb/override/iwd/acidblmu.pro b/gemrb/override/iwd/acidblmu.pro
new file mode 100644
index 0000000..aa9e8df
Binary files /dev/null and b/gemrb/override/iwd/acidblmu.pro differ
diff --git a/gemrb/override/iwd/acidblob.pro b/gemrb/override/iwd/acidblob.pro
new file mode 100644
index 0000000..16ef4f3
Binary files /dev/null and b/gemrb/override/iwd/acidblob.pro differ
diff --git a/gemrb/override/iwd/acidbloc.pro b/gemrb/override/iwd/acidbloc.pro
new file mode 100644
index 0000000..a2e6822
Binary files /dev/null and b/gemrb/override/iwd/acidbloc.pro differ
diff --git a/gemrb/override/iwd/acidh.pro b/gemrb/override/iwd/acidh.pro
new file mode 100644
index 0000000..de3952a
Binary files /dev/null and b/gemrb/override/iwd/acidh.pro differ
diff --git a/gemrb/override/iwd/adhwil.pro b/gemrb/override/iwd/adhwil.pro
new file mode 100644
index 0000000..07e10dd
Binary files /dev/null and b/gemrb/override/iwd/adhwil.pro differ
diff --git a/gemrb/override/iwd/adhwilh.pro b/gemrb/override/iwd/adhwilh.pro
new file mode 100644
index 0000000..387a674
Binary files /dev/null and b/gemrb/override/iwd/adhwilh.pro differ
diff --git a/gemrb/override/iwd/alance.pro b/gemrb/override/iwd/alance.pro
new file mode 100644
index 0000000..9336ca7
Binary files /dev/null and b/gemrb/override/iwd/alance.pro differ
diff --git a/gemrb/override/iwd/aligns.2da b/gemrb/override/iwd/aligns.2da
new file mode 100644
index 0000000..1047ea6
--- /dev/null
+++ b/gemrb/override/iwd/aligns.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF VALUE COLNAME USABILITY
+LAWFUL_GOOD 7186 9603 1102 0x11 L_G 0x14
+NEUTRAL_GOOD 7183 9606 1105 0x21 N_G 0x24
+CHAOTIC_GOOD 7189 9609 1108 0x31 C_G 0x5
+LAWFUL_NEUTRAL 7188 9604 1104 0x12 L_N 0x18
+TRUE_NEUTRAL 7185 9608 1106 0x22 N_N 0x28
+CHAOTIC_NEUTRAL 7191 9610 1109 0x32 C_N 0x9
+LAWFUL_EVIL 7187 9605 1103 0x13 L_E 0x12
+NEUTRAL_EVIL 7184 9607 1107 0x23 N_E 0x22
+CHAOTIC_EVIL 7190 9611 1110 0x33 C_E 0x3
diff --git a/gemrb/override/iwd/altera.pro b/gemrb/override/iwd/altera.pro
new file mode 100644
index 0000000..cb466b3
Binary files /dev/null and b/gemrb/override/iwd/altera.pro differ
diff --git a/gemrb/override/iwd/alteranp.pro b/gemrb/override/iwd/alteranp.pro
new file mode 100644
index 0000000..952660b
Binary files /dev/null and b/gemrb/override/iwd/alteranp.pro differ
diff --git a/gemrb/override/iwd/alterap.pro b/gemrb/override/iwd/alterap.pro
new file mode 100644
index 0000000..2196be2
Binary files /dev/null and b/gemrb/override/iwd/alterap.pro differ
diff --git a/gemrb/override/iwd/alteras.pro b/gemrb/override/iwd/alteras.pro
new file mode 100644
index 0000000..81ad729
Binary files /dev/null and b/gemrb/override/iwd/alteras.pro differ
diff --git a/gemrb/override/iwd/alterh.pro b/gemrb/override/iwd/alterh.pro
new file mode 100644
index 0000000..4ef79d3
Binary files /dev/null and b/gemrb/override/iwd/alterh.pro differ
diff --git a/gemrb/override/iwd/altert.pro b/gemrb/override/iwd/altert.pro
new file mode 100644
index 0000000..2a5f332
Binary files /dev/null and b/gemrb/override/iwd/altert.pro differ
diff --git a/gemrb/override/iwd/area1np.pro b/gemrb/override/iwd/area1np.pro
new file mode 100644
index 0000000..b617ed5
Binary files /dev/null and b/gemrb/override/iwd/area1np.pro differ
diff --git a/gemrb/override/iwd/area1p.pro b/gemrb/override/iwd/area1p.pro
new file mode 100644
index 0000000..e4b1d13
Binary files /dev/null and b/gemrb/override/iwd/area1p.pro differ
diff --git a/gemrb/override/iwd/area2.pro b/gemrb/override/iwd/area2.pro
new file mode 100644
index 0000000..a9eea19
Binary files /dev/null and b/gemrb/override/iwd/area2.pro differ
diff --git a/gemrb/override/iwd/area2np.pro b/gemrb/override/iwd/area2np.pro
new file mode 100644
index 0000000..6739976
Binary files /dev/null and b/gemrb/override/iwd/area2np.pro differ
diff --git a/gemrb/override/iwd/area3p.pro b/gemrb/override/iwd/area3p.pro
new file mode 100644
index 0000000..4356697
Binary files /dev/null and b/gemrb/override/iwd/area3p.pro differ
diff --git a/gemrb/override/iwd/area4np.pro b/gemrb/override/iwd/area4np.pro
new file mode 100644
index 0000000..d042aeb
Binary files /dev/null and b/gemrb/override/iwd/area4np.pro differ
diff --git a/gemrb/override/iwd/armorh.pro b/gemrb/override/iwd/armorh.pro
new file mode 100644
index 0000000..cceb813
Binary files /dev/null and b/gemrb/override/iwd/armorh.pro differ
diff --git a/gemrb/override/iwd/arrow.pro b/gemrb/override/iwd/arrow.pro
new file mode 100644
index 0000000..293014f
Binary files /dev/null and b/gemrb/override/iwd/arrow.pro differ
diff --git a/gemrb/override/iwd/arrowex.pro b/gemrb/override/iwd/arrowex.pro
new file mode 100644
index 0000000..70b7773
Binary files /dev/null and b/gemrb/override/iwd/arrowex.pro differ
diff --git a/gemrb/override/iwd/arrowflb.pro b/gemrb/override/iwd/arrowflb.pro
new file mode 100644
index 0000000..12b5b91
Binary files /dev/null and b/gemrb/override/iwd/arrowflb.pro differ
diff --git a/gemrb/override/iwd/arrowflg.pro b/gemrb/override/iwd/arrowflg.pro
new file mode 100644
index 0000000..45a7bab
Binary files /dev/null and b/gemrb/override/iwd/arrowflg.pro differ
diff --git a/gemrb/override/iwd/arrowfli.pro b/gemrb/override/iwd/arrowfli.pro
new file mode 100644
index 0000000..202c16c
Binary files /dev/null and b/gemrb/override/iwd/arrowfli.pro differ
diff --git a/gemrb/override/iwd/arrowflm.pro b/gemrb/override/iwd/arrowflm.pro
new file mode 100644
index 0000000..ae84563
Binary files /dev/null and b/gemrb/override/iwd/arrowflm.pro differ
diff --git a/gemrb/override/iwd/arrowhvy.pro b/gemrb/override/iwd/arrowhvy.pro
new file mode 100644
index 0000000..1471c2e
Binary files /dev/null and b/gemrb/override/iwd/arrowhvy.pro differ
diff --git a/gemrb/override/iwd/ascorch.pro b/gemrb/override/iwd/ascorch.pro
new file mode 100644
index 0000000..9ec8567
Binary files /dev/null and b/gemrb/override/iwd/ascorch.pro differ
diff --git a/gemrb/override/iwd/astorm.pro b/gemrb/override/iwd/astorm.pro
new file mode 100644
index 0000000..d6ec20f
Binary files /dev/null and b/gemrb/override/iwd/astorm.pro differ
diff --git a/gemrb/override/iwd/asumm1.pro b/gemrb/override/iwd/asumm1.pro
new file mode 100644
index 0000000..0671b04
Binary files /dev/null and b/gemrb/override/iwd/asumm1.pro differ
diff --git a/gemrb/override/iwd/asumm1h.pro b/gemrb/override/iwd/asumm1h.pro
new file mode 100644
index 0000000..42cf78c
Binary files /dev/null and b/gemrb/override/iwd/asumm1h.pro differ
diff --git a/gemrb/override/iwd/asumm1x.pro b/gemrb/override/iwd/asumm1x.pro
new file mode 100644
index 0000000..e3e49c2
Binary files /dev/null and b/gemrb/override/iwd/asumm1x.pro differ
diff --git a/gemrb/override/iwd/asumm2h.pro b/gemrb/override/iwd/asumm2h.pro
new file mode 100644
index 0000000..86ab54a
Binary files /dev/null and b/gemrb/override/iwd/asumm2h.pro differ
diff --git a/gemrb/override/iwd/asumm3h.pro b/gemrb/override/iwd/asumm3h.pro
new file mode 100644
index 0000000..4bf942b
Binary files /dev/null and b/gemrb/override/iwd/asumm3h.pro differ
diff --git a/gemrb/override/iwd/avatars.2da b/gemrb/override/iwd/avatars.2da
new file mode 100644
index 0000000..1db34c5
--- /dev/null
+++ b/gemrb/override/iwd/avatars.2da
@@ -0,0 +1,414 @@
+2DA V1.0
+*
+ AT_1 AT_2 AT_3 AT_4 TYPE SPACE PALETTE SIZE
+0x0100 SPCHUNKS SPCHUNKS SPCHUNKS SPCHUNKS 13 0 0 *
+0x0300 SPSMPUFF SPSMPUFF SPSMPUFF SPSMPUFF 13 0 0 *
+0x0400 SKLH SKLH SKLH SKLH 13 0 1 *
+0x0410 GLPHWRDH GLPHWRDH GLPHWRDH GLPHWRDH 13 0 1 *
+0x1000 MWYV MWYV MWYV MWYV 11 5 1 *
+0x1001 MWYV MWYV MWYV MWYV 11 5 1 *
+0x1100 MTAN MTAN MTAN MTAN 11 5 1 *
+0x1200 MDR1 MDR1 MDR1 MDR1 12 5 1 *
+0x1201 MDR2 MDR2 MDR2 MDR2 12 5 1 *
+0x1202 MDR3 MDR3 MDR3 MDR3 12 5 1 *
+0x1203 MDR1 MDR1 MDR1 MDR1 12 7 GR *
+0x1204 MDR1 MDR1 MDR1 MDR1 12 7 AQ *
+0x1205 MDR1 MDR1 MDR1 MDR1 12 7 BL *
+0x1206 MDR1 MDR1 MDR1 MDR1 12 7 BR *
+0x1207 MDR1 MDR1 MDR1 MDR1 12 7 MC *
+0x1208 MDR1 MDR1 MDR1 MDR1 12 7 PU *
+0x2000 MSIR MSIR MSIR MSIR 2 2 0 *
+0x2100 UVOL UVOL UVOL UVOL 2 2 1 *
+0x2200 MOGM MOGM MOGM MOGM 2 2 0 S
+0x2300 MDKN MDKN MDKN MDKN 2 2 1 *
+0x3000 MAKH MAKH MAKH MAKH 2 3 1 *
+0x4000 SNOMC SNOMC SNOMC SNOMC 1 2 0 *
+0x4002 SNOMM SNOMM SNOMM SNOMM 1 2 0 *
+0x4010 SNOWC SNOWC SNOWC SNOWC 1 2 0 *
+0x4012 SNOWM SNOWM SNOWM SNOWM 1 2 0 *
+0x4100 SSIMC SSIMC SSIMC SSIMC 1 2 0 *
+0x4101 SSIMS SSIMS SSIMS SSIMS 1 2 0 *
+0x4102 SSIMM SSIMM SSIMM SSIMM 1 2 0 *
+0x4110 SSIWC SSIWC SSIWC SSIWC 1 2 0 *
+0x4112 SSIWM SSIWM SSIWM SSIWM 1 2 0 *
+0x4200 SHMCM SHMCM SHMCM SHMCM 1 2 0 *
+0x4300 MSPLG1 MSPLG1 MSPLG1 MSPLG1 1 2 1 *
+0x4400 LHMC LHMC LHMC LHMC 1 2 0 *
+0x4410 LHFC LHFC LHFC LHFC 1 2 0 *
+0x4500 LFAM LFAM LFAM LFAM 1 2 0 *
+0x4600 LDMF LDMF LDMF LDMF 1 2 0 *
+0x4700 LEMF LEMF LEMF LEMF 1 2 0 *
+0x4710 LEFF LEFF LEFF LEFF 1 2 0 *
+0x4800 LIMC LIMC LIMC LIMC 1 2 0 *
+0x5000 CHMC1 CHMC2 CHMC3 CHMC4 6 2 0 L
+0x5001 CEMC1 CEMC2 CEMC3 CEMC4 6 2 0 M
+0x5002 CDMC1 CDMC2 CDMC3 CDMC4 6 2 0 S
+0x5003 CIMC1 CIMC2 CIMC3 CIMC4 6 2 0 S
+0x5010 CHFC1 CHFC2 CHFC3 CHFC4 6 2 0 M
+0x5011 CEFC1 CEFC2 CEFC3 CEFC4 6 2 0 M
+0x5012 CDMC1 CDMC2 CDMC3 CDMC4 6 2 0 S
+0x5013 CIFC1 CIFC2 CIFC3 CIFC4 6 2 0 S
+0x5100 CHMF1 CHMF2 CHMF3 CHMF4 6 2 0 L
+0x5101 CEMF1 CEMF2 CEMF3 CHMF4 6 2 0 M
+0x5102 CDMF1 CDMF2 CDMF3 CDMF4 6 2 0 S
+0x5103 CIMF1 CIMF2 CIMF3 CIMF4 6 2 0 S
+0x5110 CHFF1 CHFF2 CHFF3 CHFF4 6 2 0 M
+0x5111 CEFF1 CEFF2 CEFF3 CEFF4 6 2 0 M
+0x5112 CDMF1 CDMF2 CDMF3 CDMF4 6 2 0 S
+0x5113 CIFF1 CIFF2 CIFF3 CIFF4 6 2 0 S
+0x5200 CHMW1 CHMW2 CHMW3 CHMW4 6 2 0 L
+0x5201 CEMW1 CEMW2 CEMW3 CEMW4 6 2 0 M
+0x5202 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x5203 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x5210 CHFW1 CHFW2 CHFW3 CHFW4 6 2 0 M
+0x5211 CEFW1 CEFW2 CEFW3 CEFW4 6 2 0 M
+0x5212 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x5213 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x5300 CHMT1 CHMT2 CHMT2 CHMT2 6 2 0 L
+0x5301 CEMT1 CEMT2 CEMT2 CEMT2 6 2 0 M
+0x5302 CDMT1 CDMT2 CDMT2 CDMT2 6 2 0 S
+0x5303 CIMT1 CIMT2 CIMT2 CIMT2 6 2 0 S
+0x5310 CHFT1 CHFT2 CHFT2 CHFT2 6 2 0 M
+0x5311 CEFT1 CEFT2 CEFT2 CEFT2 6 2 0 M
+0x5312 CDMT1 CDMT2 CDMT2 CDMT2 6 2 0 S
+0x5313 CIFT1 CIFT2 CIFT2 CIFT2 6 2 0 S
+0x6000 CHMC1 CHMC2 CHMC3 CHMC4 6 2 0 L
+0x6001 CEMC1 CEMC2 CEMC3 CEMC4 6 2 0 M
+0x6002 CDMC1 CDMC2 CDMC3 CDMC4 6 2 0 S
+0x6003 CIMC1 CIMC2 CIMC3 CIMC4 6 2 0 S
+0x6010 CHFC1 CHFC2 CHFC3 CHFC4 6 2 0 M
+0x6011 CEFC1 CEFC2 CEFC3 CEFC4 6 2 0 M
+0x6012 CDMC1 CDMC2 CDMC3 CDMC4 6 2 0 S
+0x6013 CIFC1 CIFC2 CIFC3 CIFC4 6 2 0 S
+0x6100 CHMF1 CHMF2 CHMF3 CHMF4 6 2 0 L
+0x6101 CEMF1 CEMF2 CEMF3 CEMF4 6 2 0 M
+0x6102 CDMF1 CDMF2 CDMF3 CDMF4 6 2 0 S
+0x6103 CIMF1 CIMF2 CIMF3 CIMF4 6 2 0 S
+0x6110 CHFF1 CHFF2 CHFF3 CHFF4 6 2 0 M
+0x6111 CEFF1 CEFF2 CEFF3 CEFF4 6 2 0 M
+0x6112 CDMF1 CDMF2 CDMF3 CDMF4 6 2 0 S
+0x6113 CIFF1 CIFF2 CIFF3 CIFF4 6 2 0 S
+0x6200 CHMW1 CHMW2 CHMW3 CHMW4 6 2 0 L
+0x6201 CEMW1 CEMW2 CEMW3 CEMW4 6 2 0 M
+0x6202 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x6203 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x6210 CHFW1 CHFW2 CHFW3 CHFW4 6 2 0 M
+0x6211 CEFW1 CEFW2 CEFW3 CEFW4 6 2 0 M
+0x6212 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x6213 CDMW1 CDMW2 CDMW3 CDMW4 6 2 0 S
+0x6300 CHMT1 CHMT2 CHMT2 CHMT2 6 2 0 L
+0x6301 CEMT1 CEMT2 CEMT2 CEMT2 6 2 0 M
+0x6302 CDMT1 CDMT2 CDMT2 CDMT2 6 2 0 S
+0x6303 CIMT1 CIMT2 CIMT2 CIMT2 6 2 0 S
+0x6310 CHFT1 CHFT2 CHFT2 CHFT2 6 2 0 M
+0x6311 CEFT1 CEFT2 CEFT2 CEFT2 6 2 0 M
+0x6312 CDMT1 CDMT2 CDMT2 CDMT2 6 2 0 S
+0x6313 CIFT1 CIFT2 CIFT2 CIFT2 6 2 0 S
+0x6400 UDRZ1 UDRZ1 UDRZ1 UDRZ1 6 2 0 *
+0x6401 UELM1 UELM1 UELM1 UELM1 6 2 1 *
+0x6402 CMNK1 CMNK1 CMNK1 CMNK1 6 2 0 *
+0x6403 MSKL1 MSKL1 MSKL1 MSKL1 6 2 0 M
+0x6404 USAR1 USAR1 USAR1 USAR1 6 2 1 *
+0x6405 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 M
+0x6406 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 L
+0x6500 CHMM1 CHMB2 CHMB3 CHMC4 6 2 0 *
+0x6510 CHFM1 CHFB2 CHFB3 CHFC4 6 2 0 *
+0x7000 MOGH MOGH MOGH MOGH 2 2 0 *
+0x7001 MOGN MOGN MOGN MOGN 2 2 0 *
+0x7100 MBAS MBAS MBAS MBAS 2 2 0 *
+0x7101 MBAS MBAS MBAS MBAS 2 2 GR *
+0x7200 MBER MBER MBER MBER 3 2 BL *
+0x7201 MBER MBER MBER MBER 3 2 1 *
+0x7202 MBER MBER MBER MBER 3 2 CA *
+0x7203 MBER MBER MBER MBER 3 2 PO *
+0x7300 MEAE MEAE MEAE MEAE 4 2 1 *
+0x7301 MEAS MEAS MEAS MEAS 4 2 1 *
+0x7310 MFIE MFIE MFIE MFIE 4 2 1 *
+0x7311 MFIS MFIS MFIS MFIS 4 2 1 *
+0x7320 MAIR MAIR MAIR MAIR 4 2 1 *
+0x7321 MAIS MAIS MAIS MAIS 4 2 1 *
+0x7400 MDOG MDOG MDOG MDOG 2 2 WI *
+0x7401 MDOG MDOG MDOG MDOG 2 2 WA *
+0x7402 MDOG MDOG MDOG MDOG 2 2 MO *
+0x7500 MDOP MDOP MDOP MDOP 3 2 1 *
+0x7501 MDOP MDOP MDOP MDOP 3 2 GR *
+0x7600 METT METT METT METT 2 2 1 *
+0x7701 MGHL MGHL MGHL MGHL 2 2 1 *
+0x7702 MGHL MGHL MGHL MGHL 2 2 RE *
+0x7703 MGHL MGHL MGHL MGHL 2 2 GA *
+0x7704 MSHD MSHD MSHD MSHD 4 2 1 *
+0x7800 MGIB MGIB MGIB MGIB 2 2 1 *
+0x7900 MSLI MSLI MSLI MSLI 3 3 GR *
+0x7901 MSLI MSLI MSLI MSLI 3 3 OL *
+0x7902 MSLI MSLI MSLI MSLI 3 3 MU *
+0x7903 MSLI MSLI MSLI MSLI 3 3 OC *
+0x7904 MSLI MSLI MSLI MSLI 3 3 1 *
+0x7A00 MSPI MSPI MSPI MSPI 3 2 GI *
+0x7A01 MSPI MSPI MSPI MSPI 3 2 HU *
+0x7A02 MSPI MSPI MSPI MSPI 3 2 PH *
+0x7A03 MSPI MSPI MSPI MSPI 3 2 SW *
+0x7A04 MSPI MSPI MSPI MSPI 3 2 WR *
+0x7B00 MWLF MWLF MWLF MWLF 2 2 1 *
+0x7B01 MWLF MWLF MWLF MWLF 2 2 WO *
+0x7B02 MWLF MWLF MWLF MWLF 2 2 DI *
+0x7B03 MWLF MWLF MWLF MWLF 2 2 WI *
+0x7B04 MWLF MWLF MWLF MWLF 2 2 VA *
+0x7B05 MWLF MWLF MWLF MWLF 2 2 DR *
+0x7B06 MWLS MWLS MWLS MWLS 2 2 1 *
+0x7C00 MXVT MXVT MXVT MXVT 2 2 0 *
+0x7C01 MTAS MTAS MTAS MTAS 2 2 0 *
+0x7D00 MZOM MZOM MZOM MZOM 2 2 0 *
+0x7E00 MWER MWER MWER MWER 2 2 1 *
+0x7E01 MGWE MGWE MGWE MGWE 2 2 1 *
+0x7F00 MTRO MTRO MTRO MTRO 4 2 1 *
+0x7F01 MMIN MMIN MMIN MMIN 4 2 1 *
+0x7F02 MBEH MBEH MBEH MBEH 4 3 1 *
+0x7F03 MIMP MIMP MIMP MIMP 4 2 1 *
+0x7F04 MIGO MIGO MIGO MIGO 4 2 1 *
+0x7F05 MDJI MDJI MDJI MDJI 4 2 1 *
+0x7F06 MDJL MDJL MDJL MDJL 4 2 1 *
+0x7F07 MGLC MGLC MGLC MGLC 4 3 1 *
+0x7F08 MOTY MOTY MOTY MOTY 4 4 1 *
+0x7F09 MSAH MSAH MSAH MSAH 4 2 1 *
+0x7F0A MGCP MGCP MGCP MGCP 4 2 1 *
+0x7F0B MGCL MGCL MGCL MGCL 4 2 1 *
+0x7F0C MKUO MKUO MKUO MKUO 4 2 1 *
+0x7F0D MLIC MLIC MLIC MLIC 4 2 1 *
+0x7F0E MDLI MDLI MDLI MDLI 4 2 1 *
+0x7F0F MTRS MTRS MTRS MTRS 4 2 1 *
+0x7F10 MRAK MRAK MRAK MRAK 4 2 1 *
+0x7F11 MUMB MUMB MUMB MUMB 4 2 1 *
+0x7F12 MVAM MVAM MVAM MVAM 4 2 1 *
+0x7F13 MSNK MSNK MSNK MSNK 4 2 1 *
+0x7F14 MGIT MGIT MGIT MGIT 4 2 1 *
+0x7F15 MBES MBES MBES MBES 4 2 1 *
+0x7F16 AMOO AMOO AMOO AMOO 4 3 1 *
+0x7F17 ARAB ARAB ARAB ARAB 4 1 1 *
+0x7F18 ADER ADER ADER ADER 4 2 1 *
+0x7F19 MDSW MDSW MDSW MDSW 4 2 1 *
+0x7F20 AGRO AGRO AGRO AGRO 4 2 1 *
+0x7F21 APHE APHE APHE APHE 4 2 1 *
+0x7F22 MVAF MVAF MVAF MVAF 4 2 1 *
+0x7F23 MSAT MSAT MSAT MSAT 4 3 1 *
+0x7F24 NPIR NPIR NPIR NPIR 4 2 1 *
+0x7F27 MDRO MDRO MDRO MDRO 4 2 1 *
+0x7F28 MKUL MKUL MKUL MKUL 4 3 1 *
+0x7F29 MFDR MFDR MFDR MFDR 4 2 1 *
+0x7F2A NSAI NSAI NSAI NSAI 4 2 1 *
+0x7F2C NSOL NSOL NSOL NSOL 4 2 1 *
+0x7F2D MWFM MWFM MWFM MWFM 4 2 1 *
+0x7F2E MRAV MRAV MRAV MRAV 4 3 1 *
+0x7F2F MSPS MSPS MSPS MSPS 4 2 1 *
+0x7F30 NBOH NBOH NBOH NBOH 4 2 1 *
+0x7F31 NELL NELL NELL NELL 4 2 1 *
+0x7F32 MSLY MSLY MSLY MSLY 4 2 1 *
+0x7F35 MMIS MMIS MMIS MMIS 4 3 1 *
+0x7F36 NSHD NSHD NSHD NSHD 4 2 1 *
+0x7F37 NIRE NIRE NIRE NIRE 4 2 1 *
+0x8000 MGNL MGNL MGNL MGNL 2 2 0 *
+0x8100 MHOB MHOB MHOB MHOB 2 2 0 *
+0x8200 MKOB MKOB MKOB MKOB 2 2 0 *
+0x9000 MOGR MOGR MOGR MOGR 5 2 0 *
+0xA000 MWYV MWYV MWYV MWYV 8 3 1 *
+0xA100 MCAR MCAR MCAR MCAR 8 3 1 *
+0xB000 ACOW ACOW ACOW ACOW 10 3 1 *
+0xB100 AHRS AHRS AHRS AHRS 10 3 1 *
+0xB200 NBEGL NBEGL NBEGL NBEGL 2 2 0 *
+0xB210 NPROL NPROL NPROL NPROL 2 2 0 *
+0xB300 NBOYL NBOYL NBOYL NBOYL 3 2 0 *
+0xB310 NGRLL NGRLL NGRLL NGRLL 3 2 0 *
+0xB400 NFAML NFAML NFAML NFAML 2 2 0 *
+0xB410 NFAWL NFAWL NFAWL NFAWL 2 2 0 *
+0xB500 NSIML NSIML NSIML NSIML 2 2 0 *
+0xB510 NSIWL NSIWL NSIWL NSIWL 2 2 0 *
+0xB600 NNOML NNOML NNOML NNOML 2 2 0 *
+0xB610 NNOWL NNOWL NNOWL NNOWL 2 2 0 *
+0xB700 NSVLL NSVLL NSIWL NSIWL 2 2 0 *
+0xC000 ABAT ABAT ABAT ABAT 2 1 1 *
+0xC100 ACAT ACAT ACAT ACAT 2 1 1 *
+0xC200 ACHK ACHK ACHK ACHK 2 1 1 *
+0xC300 ARAT ARAT ARAT ARAT 2 1 1 *
+0xC400 ASQU ASQU ASQU ASQU 2 1 1 *
+0xC500 ABAT ABAT ABAT ABAT 2 1 1 *
+0xC600 NBEGH NBEGH NBEGH NBEGH 2 2 0 *
+0xC610 NPROH NPROH NPROH NPROH 2 2 0 *
+0xC700 NBOYH NBOYH NBOYH NBOYH 3 2 0 *
+0xC710 NGRLH NGRLH NGRLH NGRLH 3 2 0 *
+0xC800 NFAMH NFAMH NFAMH NFAMH 2 2 0 *
+0xC810 NFAWH NFAWH NFAWH NFAWH 2 2 0 *
+0xC900 NSIMH NSIMH NSIMH NSIMH 2 2 0 *
+0xC910 NSIWH NSIWH NSIWH NSIWH 2 2 0 *
+0xCA00 NNOMH NNOMH NNOMH NNOMH 2 2 0 *
+0xCA10 NNOWH NNOWH NNOWH NNOWH 2 2 0 *
+0xCB00 NSLVH NSLVH NSLVH NSLVH 2 2 0 *
+0xD000 AEAGG1 AEAGG1 AEAGG1 AEAGG1 7 0 1 *
+0xD100 AGULG1 AGULG1 AGULG1 AGULG1 7 0 1 *
+0xD200 AVULG1 AVULG1 AVULG1 AVULG1 7 0 1 *
+0xD300 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xD400 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xE000 MBET MBET MBET MBET 9 2 1 *
+0xE012 MBBM MBBM MBBM MBBM 16 2 1 *
+0xE020 MTAN MTAN MTAN MTAN 9 4 1 *
+0xE022 MBBR MBBR MBBR MBBR 9 2 1 *
+0xE038 MBFI MBFI MBFI MBFI 9 2 1 *
+0xE048 MBRH MBRH MBRH MBRH 9 5 1 *
+0xE050 MREM MREM MREM MREM 9 4 1 *
+0xE060 MLIC MLIC MLIC MLIC 9 2 1 *
+0xE068 MHOH MHOH MHOH MHOH 9 2 1 *
+0xE070 MMIN MMIN MMIN MMIN 9 2 1 *
+0xE090 MMER MMER MMER MMER 9 2 1 *
+0xE0A0 MTIC MTIC MTIC MTIC 9 2 1 *
+0xE0B0 MTRO MTRO MTRO MTRO 9 2 1 *
+0xE0C0 MTSN MTSN MTSN MTSN 9 2 1 *
+0xE0D0 MUMB MUMB MUMB MUMB 9 2 1 *
+0xE0E0 MCOR MCOR MCOR MCOR 9 3 1 *
+0xE0F0 MGIC MGIC MGIC MGIC 9 2 1 *
+0xE0F1 MGLA MGLA MGLA MGLA 9 2 1 *
+0xE108 MCYC MCYC MCYC MCYC 9 4 1 *
+0xE118 METN METN METN METN 9 4 1 *
+0xE138 MGFR MGFR MGFR MGFR 9 4 1 *
+0xE148 MGVE MGVE MGVE MGVE 9 4 1 *
+0xE15A MGFO MGFO MGFO MGFO 9 4 1 *
+0xE218 MELE MELE MELE MELE 9 4 1 *
+0xE228 MELF MELF MELF MELF 9 4 1 *
+0xE238 MELW MELW MELW MELW 9 4 1 *
+0xE249 MHAR MHAR MHAR MHAR 9 2 1 *
+0xE252 MWWE MWWE MWWE MWWE 9 2 1 *
+0xE269 MFEY MFEY MFEY MFEY 9 2 1 *
+0xE278 MDTR MDTR MDTR MDTR 9 3 1 *
+0xE289 MFE2 MFE2 MFE2 MFE2 9 3 1 *
+0xE298 MEW2 MEW2 MEW2 MEW2 9 2 1 *
+0xE300 MGHO MGHO MGHO MGHO 9 2 1 *
+0xE308 MGH2 MGH2 MGH2 MGH2 9 2 1 *
+0xE318 MGH3 MGH3 MGH3 MGH3 9 2 1 *
+0xE329 MWIG MWIG MWIG MWIG 9 2 1 *
+0xE338 MZO2 MZO2 MZO2 MZO2 9 2 1 *
+0xE348 MZO3 MZO3 MZO3 MZO3 16 2 1 *
+0xE359 MWI2 MWI2 MWI2 MWI2 9 2 1 *
+0xE369 MWI3 MWI3 MWI3 MWI3 9 2 1 *
+0xE388 MMUM MMUM MMUM MMUM 9 2 1 *
+0xE389 MMUM MMUM MMUM MMUM 9 2 1 *
+0xE39C MHIS MHIS MHIS MHIS 9 2 1 *
+0xE3A8 MDRD MDRD MDRD MDRD 9 2 1 *
+0xE3BB MWAV MWAV MWAV MWAV 9 2 1 *
+0xE408 MGO1 MGO1 MGO1 MGO1 9 2 1 *
+0xE412 MGO2 MGO2 MGO2 MGO2 16 2 1 *
+0xE428 MGO3 MGO3 MGO3 MGO3 9 2 1 *
+0xE432 MGO4 MGO4 MGO4 MGO4 16 2 1 *
+0xE449 MSVI MSVI MSVI MSVI 9 2 1 *
+0xE459 MSV2 MSV2 MSV2 MSV2 9 2 1 *
+0xE468 MGWO MGWO MGWO MGWO 9 2 1 *
+0xE479 MGOC MGOC MGOC MGOC 9 2 1 *
+0xE488 MGW2 MGW2 MGW2 MGW2 9 2 1 *
+0xE498 MGO5 MGO5 MGO5 MGO5 9 2 1 *
+0xE500 MLIZ MLIZ MLIZ MLIZ 9 2 1 *
+0xE510 MLI2 MLI2 MLI2 MLI2 9 2 1 *
+0xE51D MGIR MGIR MGIR MGIR 9 3 1 *
+0xE520 MLI3 MLI3 MLI3 MLI3 9 2 1 *
+0xE528 MGIC MGIC MGIC MGIC 9 2 1 *
+0xE600 MMYC MMYC MMYC MMYC 9 2 1 *
+0xE610 MMY2 MMY2 MMY2 MMY2 9 2 1 *
+0xE62C MSKB MSKB MSKB MSKB 9 2 1 *
+0xE700 MNO1 MNO1 MNO1 MNO1 9 2 1 *
+0xE708 MMIN MMIN MMIN MMIN 9 2 1 *
+0xE710 MNO2 MNO2 MNO2 MNO2 9 2 1 *
+0xE718 MTRO MTRO MTRO MTRO 9 2 1 *
+0xE720 MNO3 MNO3 MNO3 MNO3 9 2 1 *
+0xE728 MTIC MTIC MTIC MTIC 9 2 1 *
+0xE738 MTSN MTSN MTSN MTSN 9 2 1 *
+0xE759 MUMB MUMB MUMB MUMB 9 2 1 *
+0xE76C MYET MYET MYET MYET 9 2 1 *
+0xE771 MBA4 MBA4 MBA4 MBA4 9 2 1 *
+0xE781 MBA5 MBA5 MBA5 MBA5 9 2 1 *
+0xE791 MBA6 MBA6 MBA6 MBA6 9 2 1 *
+0xE7AC MBAI MBAI MBAI MBAI 9 2 1 *
+0xE7B8 MBOA MBOA MBOA MBOA 9 2 1 *
+0xE7C9 MABW MABW MABW MABW 9 2 1 *
+0xE7D9 MMAL MMAL MMAL MMAL 9 2 1 *
+0xE7E8 MSCR MSCR MSCR MSCR 9 2 1 *
+0xE7F9 MUM2 MUM2 MUM2 MUM2 9 2 1 *
+0xE800 MOR6 MOR6 MOR6 MOR6 9 2 1 *
+0xE810 MOR1 MOR1 MOR1 MOR1 9 2 1 *
+0xE820 MOR2 MOR2 MOR2 MOR2 16 2 1 *
+0xE830 MOR3 MOR3 MOR3 MOR3 9 2 1 *
+0xE840 MOR4 MOR4 MOR4 MOR4 16 2 1 *
+0xE850 MOR5 MOR5 MOR5 MOR5 9 2 1 *
+0xE860 MNO1 MNO1 MNO1 MNO1 9 2 1 *
+0xE870 MNO2 MNO2 MNO2 MNO2 9 2 1 *
+0xE880 MNO3 MNO3 MNO3 MNO3 9 2 1 *
+0xE890 MLI3 MLI3 MLI3 MLI3 9 2 1 *
+0xE8A0 MYU3 MYU3 MYU3 MYU3 9 2 1 *
+0xE8B0 MYUH MYUH MYUH MYUH 9 2 1 *
+0xE8C0 MBUG MBUG MBUG MBUG 9 2 1 *
+0xE8D0 MNOS MNOS MNOS MNOS 9 2 1 *
+0xE8E0 MBU2 MBU2 MBU2 MBU2 9 2 1 *
+0xE8F0 MOR7 MOR7 MOR7 MOR7 9 2 1 *
+0xE900 MSAL MSAL MSAL MSAL 16 2 1 *
+0xE908 MSH1 MSH1 MSH1 MSH1 16 2 1 *
+0xE910 MSA2 MSA2 MSA2 MSA2 16 2 1 *
+0xE918 MSH2 MSH2 MSH2 MSH2 16 2 1 *
+0xE928 MGHO MGHO MGHO MGHO 9 2 1 *
+0xEA00 MSHR MSHR MSHR MSHR 9 2 1 *
+0xEA10 MSH1 MSH1 MSH1 MSH1 16 2 1 *
+0xEA20 MCRD MCRD MCRD MCRD 9 2 1 *
+0xEB00 MSKT MSKT MSKT MSKT 9 2 1 *
+0xEB08 MANI MANI MANI MANI 9 3 1 *
+0xEB10 MSKA MSKA MSKA MSKA 9 2 1 *
+0xEB18 MANI MANI MANI MANI 9 2 1 *
+0xEB28 MAN2 MAN2 MAN2 MAN2 9 2 1 *
+0xEB39 MBE1 MBE1 MBE1 MBE1 9 2 1 *
+0xEB49 MBE2 MBE2 MBE2 MBE2 9 2 1 *
+0xEB51 MSEE MSEE MSEE MSEE 9 2 1 *
+0xEB52 MFIR MFIR MFIR MFIR 9 2 1 *
+0xEB69 MLIC MLIC MLIC MLIC 9 2 1 *
+0xEB79 MLER MLER MLER MLER 9 2 1 *
+0xEB89 MMAN MMAN MMAN MMAN 9 2 1 *
+0xEB99 MMYC MMYC MMYC MMYC 9 2 1 *
+0xEBA9 MMY2 MMY2 MMY2 MMY2 9 2 1 *
+0xEBB1 MSHR MSHR MSHR MSHR 9 2 1 *
+0xEBCD MTAN MTAN MTAN MTAN 9 4 1 *
+0xEBD8 MSAL MSAL MSAL MSAL 16 2 1 *
+0xEBE8 MSA2 MSA2 MSA2 MSA2 16 2 1 *
+0xEBF1 MARU MARU MARU MARU 9 2 1 *
+0xEC0B MWDR MWDR MWDR MWDR 9 2 1 *
+0xEC1D MCHY MCHY MCHY MCHY 9 2 1 *
+0xEC2B MSHE MSHE MSHE MSHE 9 2 1 *
+0xEC33 MCHI MCHI MCHI MCHI 9 2 1 *
+0xEC4B MDH1 MDH1 MDH1 MDH1 9 2 1 *
+0xEC5B MDH2 MDH2 MDH2 MDH2 9 2 1 *
+0xED00 MYU1 MYU1 MYU1 MYU1 9 2 1 *
+0xED09 MCOR MCOR MCOR MCOR 9 3 1 *
+0xED10 MYU2 MYU2 MYU2 MYU2 9 2 1 *
+0xED19 MGLA MGLA MGLA MGLA 9 2 1 *
+0xED20 MYU3 MYU3 MYU3 MYU3 9 2 1 *
+0xED28 MLEM MLEM MLEM MLEM 9 2 1 *
+0xEE08 MWEB MWEB MWEB MWEB 9 2 1 *
+0xEE18 MWRA MWRA MWRA MWRA 9 2 1 *
+0xEF0D MISA MISA MISA MISA 9 2 1 *
+0xEF1D MMAD MMAD MMAD MMAD 9 2 1 *
+0xEF28 MWOR MWOR MWOR MWOR 9 2 1 *
+0xEF50 MKG1 MKG1 MKG1 MKG1 9 2 1 *
+0xEF60 MKG2 MKG2 MKG2 MKG2 9 2 1 *
+0xEF70 MKG3 MKG3 MKG3 MKG3 9 2 1 *
+0xEF92 MWIL MWIL MWIL MWIL 9 2 1 *
+0xEFA3 MGEN MGEN MGEN MGEN 9 2 1 *
+0xEFB3 MGEN MGEN MGEN MGEN 9 3 1 *
+0xEFC3 MGEN MGEN MGEN MGEN 9 4 1 *
+0xEFD3 MGEN MGEN MGEN MGEN 9 5 1 *
+0xEFE3 MGEN MGEN MGEN MGEN 9 1 1 *
+0xEFF3 MGEN MGEN MGEN MGEN 9 6 1 *
+0xF008 MSKA MSKA MSKA MSKA 9 2 1 *
+0xF019 MSKT MSKT MSKT MSKT 9 2 1 *
+0xF029 MWI4 MWI4 MWI4 MWI4 9 2 1 *
+0xF10E MYU1 MYU1 MYU1 MYU1 9 2 1 *
+0xF11E MYU2 MYU2 MYU2 MYU2 9 2 1 *
+0xF209 MLIZ MLIZ MLIZ MLIZ 9 2 1 *
+0xF218 MLI2 MLI2 MLI2 MLI2 9 2 1 *
+0xF308 MGFI MGFI MGFI MGFI 9 4 1 *
+0xF40B MSAH MSAH MSAH MSAH 9 2 1 *
+0xF41B MSAT MSAT MSAT MSAT 9 2 1 *
+0xF50B MDRM MDRM MDRM MDRM 9 4 1 *
+0xF51B MDRF MDRF MDRF MDRF 9 4 1 *
+0xF77A MBA1 MBA1 MBA1 MBA1 9 2 1 *
+0xF78A MBA2 MBA2 MBA2 MBA2 9 2 1 *
+0xF798 MBA3 MBA3 MBA3 MBA3 9 2 1 *
diff --git a/gemrb/override/iwd/avprefc.2da b/gemrb/override/iwd/avprefc.2da
new file mode 100644
index 0000000..37d6684
--- /dev/null
+++ b/gemrb/override/iwd/avprefc.2da
@@ -0,0 +1,25 @@
+2DA V1.0
+*
+ PREFIX
+TYPE 232
+FIGHTER 0x100
+RANGER 0x100
+PALADIN 0x100
+CLERIC 0
+DRUID 0
+MAGE 0x200
+THIEF 0x300
+BARD 0x300
+FIGHTER_MAGE 0x100
+FIGHTER_CLERIC 0x100
+FIGHTER_THIEF 0x100
+FIGHTER_MAGE_THIEF 0x100
+MAGE_THIEF 0x300
+CLERIC_MAGE 0
+CLERIC_THIEF 0
+FIGHTER_DRUID 0x100
+FIGHTER_MAGE_CLERIC 0x100
+CLERIC_RANGER 0
+SORCERER 0x200
+MONK 0x500
+BARBARIAN 0x100
diff --git a/gemrb/override/iwd/avprefr.2da b/gemrb/override/iwd/avprefr.2da
new file mode 100644
index 0000000..bd50876
--- /dev/null
+++ b/gemrb/override/iwd/avprefr.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+*
+ RACE
+TYPE 231
+HUMAN 0
+ELF 1
+HALF_ELF 1
+GNOME 3
+HALFLING 3
+DWARF 2
diff --git a/gemrb/override/iwd/axe.pro b/gemrb/override/iwd/axe.pro
new file mode 100644
index 0000000..2220a75
Binary files /dev/null and b/gemrb/override/iwd/axe.pro differ
diff --git a/gemrb/override/iwd/axeex.pro b/gemrb/override/iwd/axeex.pro
new file mode 100644
index 0000000..475b824
Binary files /dev/null and b/gemrb/override/iwd/axeex.pro differ
diff --git a/gemrb/override/iwd/baldur.bcs b/gemrb/override/iwd/baldur.bcs
new file mode 100644
index 0000000..2a77629
--- /dev/null
+++ b/gemrb/override/iwd/baldur.bcs
@@ -0,0 +1,74 @@
+SC
+CR
+CO
+TR
+16399 1 0 0 0 "GLOBALFORGE_ON" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+TR
+TR
+16397 0 0 0 0 "" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004a"OB
+TR
+TR
+16397 0 0 0 0 "" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004b"OB
+TR
+TR
+16397 0 0 0 0 "" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004c"OB
+TR
+CO
+RS
+RE
+100AC
+398OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004a"OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+0 0 0 0 0"AR6013" "" AC
+AC
+398OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004b"OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+0 0 0 0 0"AR6013" "" AC
+AC
+398OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004c"OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+0 0 0 0 0"AR6013" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 1 0 0 0 "GLOBALFORGE_ON" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+TR
+TR
+16397 0 0 0 0 "" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004"OB
+TR
+CO
+RS
+RE
+100AC
+398OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 "To6004"OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 ""OB
+0 0 0 0 0"AR6013" "" AC
+RE
+RS
+CR
+SC
diff --git a/gemrb/override/iwd/bbarrh1.pro b/gemrb/override/iwd/bbarrh1.pro
new file mode 100644
index 0000000..25d3720
Binary files /dev/null and b/gemrb/override/iwd/bbarrh1.pro differ
diff --git a/gemrb/override/iwd/bbarrh2.pro b/gemrb/override/iwd/bbarrh2.pro
new file mode 100644
index 0000000..361d26c
Binary files /dev/null and b/gemrb/override/iwd/bbarrh2.pro differ
diff --git a/gemrb/override/iwd/bbarrier.pro b/gemrb/override/iwd/bbarrier.pro
new file mode 100644
index 0000000..de3b9c9
Binary files /dev/null and b/gemrb/override/iwd/bbarrier.pro differ
diff --git a/gemrb/override/iwd/bdeath.pro b/gemrb/override/iwd/bdeath.pro
new file mode 100644
index 0000000..514d2b0
Binary files /dev/null and b/gemrb/override/iwd/bdeath.pro differ
diff --git a/gemrb/override/iwd/blessh.pro b/gemrb/override/iwd/blessh.pro
new file mode 100644
index 0000000..dd80c91
Binary files /dev/null and b/gemrb/override/iwd/blessh.pro differ
diff --git a/gemrb/override/iwd/bloodclr.2da b/gemrb/override/iwd/bloodclr.2da
new file mode 100644
index 0000000..1b33a30
--- /dev/null
+++ b/gemrb/override/iwd/bloodclr.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+0
+ VALUE MIN MAX
+NORMAL 0x2f 0x1000 0xffff
+PURPLE 0x2d 0x4300 0x43ff
+MSKEL 0x25 0x5403 0x5403
+DOOMG 0x22 0x5405 0x5406
+MSKEL 0x25 0x6403 0x6403
+DOOMG 0x22 0x6405 0x6406
+YELLOW 0x32 0x7300 0x73ff
+PURPLE 0x3c 0x7500 0x75ff
+YELLOW 0x32 0x7600 0x76ff
+BLACK 0x66 0x7700 0x77ff
+SLI_GR 7 0x7900 0x7900
+SLI_OL 0x24 0x7901 0x7901
+SLI_MU 0x33 0x7902 0x7902
+SLI_OC 0x25 0x7903 0x7903
+SLIME 0x1b 0x7904 0x7904
+SPIDER 0x33 0x7a00 0x7aff
+ZOMBIE 0x25 0x7c00 0x7cff
+MTRO 0x33 0x7f00 0x7f00
+MMIN 0x3d 0x7f01 0x7f03
+MIGO 0x1a 0x7f04 0x7f04
+MGLC 0x5d 0x7f07 0x7f07
+MTRS 0x32 0x7f0f 0x7f0f
+MBES 0x3d 0x7f15 0x7f15
+MCAR 0x38 0xa100 0xa1ff
+UNDEAD 0x66 0xe300 0xe3ff
+MWWE 0x38 0xef00 0xefff
diff --git a/gemrb/override/iwd/bolt.pro b/gemrb/override/iwd/bolt.pro
new file mode 100644
index 0000000..1cfd4f5
Binary files /dev/null and b/gemrb/override/iwd/bolt.pro differ
diff --git a/gemrb/override/iwd/boltex.pro b/gemrb/override/iwd/boltex.pro
new file mode 100644
index 0000000..e963523
Binary files /dev/null and b/gemrb/override/iwd/boltex.pro differ
diff --git a/gemrb/override/iwd/bscloud.pro b/gemrb/override/iwd/bscloud.pro
new file mode 100644
index 0000000..aa0f975
Binary files /dev/null and b/gemrb/override/iwd/bscloud.pro differ
diff --git a/gemrb/override/iwd/bullet.pro b/gemrb/override/iwd/bullet.pro
new file mode 100644
index 0000000..cb89cea
Binary files /dev/null and b/gemrb/override/iwd/bullet.pro differ
diff --git a/gemrb/override/iwd/bulletex.pro b/gemrb/override/iwd/bulletex.pro
new file mode 100644
index 0000000..4a039f5
Binary files /dev/null and b/gemrb/override/iwd/bulletex.pro differ
diff --git a/gemrb/override/iwd/calllih.pro b/gemrb/override/iwd/calllih.pro
new file mode 100644
index 0000000..17723b2
Binary files /dev/null and b/gemrb/override/iwd/calllih.pro differ
diff --git a/gemrb/override/iwd/ccdamah.pro b/gemrb/override/iwd/ccdamah.pro
new file mode 100644
index 0000000..c6fe73b
Binary files /dev/null and b/gemrb/override/iwd/ccdamah.pro differ
diff --git a/gemrb/override/iwd/ccommah.pro b/gemrb/override/iwd/ccommah.pro
new file mode 100644
index 0000000..2dd3ed5
Binary files /dev/null and b/gemrb/override/iwd/ccommah.pro differ
diff --git a/gemrb/override/iwd/ccwounh.pro b/gemrb/override/iwd/ccwounh.pro
new file mode 100644
index 0000000..fd621c3
Binary files /dev/null and b/gemrb/override/iwd/ccwounh.pro differ
diff --git a/gemrb/override/iwd/cdiseah.pro b/gemrb/override/iwd/cdiseah.pro
new file mode 100644
index 0000000..4a10c96
Binary files /dev/null and b/gemrb/override/iwd/cdiseah.pro differ
diff --git a/gemrb/override/iwd/ceelem1.pro b/gemrb/override/iwd/ceelem1.pro
new file mode 100644
index 0000000..14116cd
Binary files /dev/null and b/gemrb/override/iwd/ceelem1.pro differ
diff --git a/gemrb/override/iwd/ceelemh.pro b/gemrb/override/iwd/ceelemh.pro
new file mode 100644
index 0000000..59eb71a
Binary files /dev/null and b/gemrb/override/iwd/ceelemh.pro differ
diff --git a/gemrb/override/iwd/ceelemx.pro b/gemrb/override/iwd/ceelemx.pro
new file mode 100644
index 0000000..5e15e45
Binary files /dev/null and b/gemrb/override/iwd/ceelemx.pro differ
diff --git a/gemrb/override/iwd/cfelem1.pro b/gemrb/override/iwd/cfelem1.pro
new file mode 100644
index 0000000..d550f94
Binary files /dev/null and b/gemrb/override/iwd/cfelem1.pro differ
diff --git a/gemrb/override/iwd/cfelemh.pro b/gemrb/override/iwd/cfelemh.pro
new file mode 100644
index 0000000..9aaba24
Binary files /dev/null and b/gemrb/override/iwd/cfelemh.pro differ
diff --git a/gemrb/override/iwd/cfelemx.pro b/gemrb/override/iwd/cfelemx.pro
new file mode 100644
index 0000000..d87881c
Binary files /dev/null and b/gemrb/override/iwd/cfelemx.pro differ
diff --git a/gemrb/override/iwd/cfog.pro b/gemrb/override/iwd/cfog.pro
new file mode 100644
index 0000000..702da41
Binary files /dev/null and b/gemrb/override/iwd/cfog.pro differ
diff --git a/gemrb/override/iwd/cgraceh.pro b/gemrb/override/iwd/cgraceh.pro
new file mode 100644
index 0000000..93ff65b
Binary files /dev/null and b/gemrb/override/iwd/cgraceh.pro differ
diff --git a/gemrb/override/iwd/cgtable.2da b/gemrb/override/iwd/cgtable.2da
new file mode 100644
index 0000000..36e784d
--- /dev/null
+++ b/gemrb/override/iwd/cgtable.2da
@@ -0,0 +1,20 @@
+2DA V1.0
+*
+ RESREF
+UNUSED0 *
+UNUSED1 *
+UNUSED2 *
+UNUSED3 *
+UNUSED4 *
+UNUSED5 *
+UNUSED6 *
+UNUSED7 *
+UNUSED8 *
+NECROMANCY NecroCG
+ALTERATION AlterCG
+ENCHANTMENT EnchaCG
+ABJURATION AbjurCG
+ILLUSION IllusCG
+CONJURATION ConjuCG
+INVOCATION InvocCG
+DIVINATION DivinCG
diff --git a/gemrb/override/iwd/chant.pro b/gemrb/override/iwd/chant.pro
new file mode 100644
index 0000000..740be7b
Binary files /dev/null and b/gemrb/override/iwd/chant.pro differ
diff --git a/gemrb/override/iwd/chromorb.pro b/gemrb/override/iwd/chromorb.pro
new file mode 100644
index 0000000..c39d719
Binary files /dev/null and b/gemrb/override/iwd/chromorb.pro differ
diff --git a/gemrb/override/iwd/classes.2da b/gemrb/override/iwd/classes.2da
new file mode 100644
index 0000000..8f7f386
--- /dev/null
+++ b/gemrb/override/iwd/classes.2da
@@ -0,0 +1,21 @@
+2DA V1.0
+*
+ NAME_REF DESC_REF CAP_REF SAVE MULTI ID HP USABILITY MC_WAS_ID HUMAN ELF HALF_ELF DWARF HALFLING GNOME
+FIGHTER 10174 9556 10174 SAVEWAR 0 2 HPWAR 0x800 0x0008 1 1 1 1 1 1
+RANGER 10173 9557 10173 SAVEWAR 0 12 HPWAR 0x200000 0x0100 1 1 1 0 0 0
+PALADIN 10194 9558 10194 SAVEWAR 0 6 HPWAR 0x100000 -1 1 0 0 0 0 0
+CLERIC 10177 9559 10177 SAVEPRS 0 3 HPPRS 128 0x0020 1 1 1 1 1 1
+DRUID 10186 9560 10186 SAVEPRS 0 11 HPPRS 0x40000000 0x0080 1 1 1 0 0 0
+MAGE 10176 9563 10176 SAVEWIZ 0 1 HPWIZ 0x40000 0x0010 1 1 1 0 0 2
+THIEF 10175 9561 10175 SAVEROG 0 4 HPROG 0x400000 0x0040 1 1 1 1 1 1
+BARD 10179 9562 10179 SAVEROG 0 5 HPROG 64 -1 1 0 1 0 0 0
+FIGHTER_THIEF 10178 9572 10178 * 10 9 0 0x20000 -1 0 1 1 1 1 1
+FIGHTER_CLERIC 10187 9573 10187 * 6 8 0 0x4000 -1 0 0 1 1 0 1
+FIGHTER_MAGE 10189 9574 10189 * 3 7 0 0x2000 -1 0 1 1 0 0 2
+MAGE_THIEF 10193 9575 10193 * 9 13 0 0x80000 -1 0 1 1 0 0 2
+CLERIC_MAGE 10180 9577 10180 * 5 14 0 256 -1 0 0 1 0 0 2
+CLERIC_THIEF 10184 9578 10184 * 12 15 0 512 -1 0 0 0 0 0 1
+FIGHTER_DRUID 10188 9579 10188 * 1026 16 0 0x1000 -1 0 0 1 0 0 0
+CLERIC_RANGER 10182 9580 10182 * 2052 18 0 1024 -1 0 0 1 0 0 0
+FIGHTER_MAGE_THIEF 10191 9576 10191 * 11 10 0 0x10000 -1 0 1 1 0 0 0
+FIGHTER_MAGE_CLERIC 10190 9581 10190 * 7 17 0 0x8000 -1 0 0 1 0 0 0
diff --git a/gemrb/override/iwd/cldamah.pro b/gemrb/override/iwd/cldamah.pro
new file mode 100644
index 0000000..aec3d15
Binary files /dev/null and b/gemrb/override/iwd/cldamah.pro differ
diff --git a/gemrb/override/iwd/cloud.pro b/gemrb/override/iwd/cloud.pro
new file mode 100644
index 0000000..3fd8e26
Binary files /dev/null and b/gemrb/override/iwd/cloud.pro differ
diff --git a/gemrb/override/iwd/cloudb.pro b/gemrb/override/iwd/cloudb.pro
new file mode 100644
index 0000000..aa2fdde
Binary files /dev/null and b/gemrb/override/iwd/cloudb.pro differ
diff --git a/gemrb/override/iwd/cloudbh.pro b/gemrb/override/iwd/cloudbh.pro
new file mode 100644
index 0000000..c3aa212
Binary files /dev/null and b/gemrb/override/iwd/cloudbh.pro differ
diff --git a/gemrb/override/iwd/cloudkil.pro b/gemrb/override/iwd/cloudkil.pro
new file mode 100644
index 0000000..2dd544a
Binary files /dev/null and b/gemrb/override/iwd/cloudkil.pro differ
diff --git a/gemrb/override/iwd/cloudks.pro b/gemrb/override/iwd/cloudks.pro
new file mode 100644
index 0000000..27a06e4
Binary files /dev/null and b/gemrb/override/iwd/cloudks.pro differ
diff --git a/gemrb/override/iwd/clowncol.2da b/gemrb/override/iwd/clowncol.2da
new file mode 100644
index 0000000..59103ae
--- /dev/null
+++ b/gemrb/override/iwd/clowncol.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+*
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
+HAIR 0 1 2 3 4 5 6 7 79 80 81 82 110 111 * * * * * * * * * * * * * * * * * * * *
+SKIN 8 9 10 11 12 13 14 15 16 17 18 19 20 83 84 85 86 87 88 89 90 105 106 107 108 109 112 113 114 * * * * *
+MAJOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 21 22 23
+MINOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 21 22 23
diff --git a/gemrb/override/iwd/clskills.2da b/gemrb/override/iwd/clskills.2da
new file mode 100644
index 0000000..0bd6f93
--- /dev/null
+++ b/gemrb/override/iwd/clskills.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+*
+ HATERACE CLERICSPELL MAGESPELL STARTXP BARDSKILL THIEFSKILL LAYHANDS TURNLEVEL BOOKTYPE
+UNUSED * * * * * * * * * 0
+MAGE * * MXSPLWIZ 0 * * * * * 1
+FIGHTER * * * 0 * * * * * 0
+CLERIC * MXSPLPRS * 0 * * * 1
+THIEF * * * 0 * SKILLS * *
+BARD * * MXSPLBRD 0 SKILLBRD * * *
+PALADIN * MXSPLPAL * 0 * * PALADIN 5
+FIGHTER_MAGE * * MXSPLWIZ 0 * * * *
+FIGHTER_CLERIC * MXSPLPRS * 0 * * * *
+FIGHTER_THIEF * * * 0 * SKILLS * *
+FIGHTER_MAGE_THIEF * * MXSPLWIZ 0 * SKILLS * *
+DRUID * MXSPLDRU * 0 * * * *
+RANGER HATERACE MXSPLRAN * 0 * * * *
+MAGE_THIEF * * MXSPLWIZ 0 * SKILLS * *
+CLERIC_MAGE * MXSPLPRS MXSPLWIZ 0 * * * *
+CLERIC_THIEF * MXSPLPRS * 0 * SKILLS * *
+FIGHTER_DRUID * MXSPLDRU * 0 * * * *
+FIGHTER_MAGE_CLERIC * MXSPLPRS MXSPLWIZ 0 * * * *
+CLERIC_RANGER HATERACE MXSPLPRS * 0 * * * *
diff --git a/gemrb/override/iwd/clssplab.2da b/gemrb/override/iwd/clssplab.2da
new file mode 100644
index 0000000..cc94c44
--- /dev/null
+++ b/gemrb/override/iwd/clssplab.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+6
+ DEX STR
+UNUSED 6 8
+MAGE 6 4
+FIGHTER 6 8
+CLERIC 4 6
+THIEF 8 6
+BARD 8 6
+PALADIN 6 8
+FIGHTER_MAGE 6 8
+FIGHTER_CLERIC 6 8
+FIGHTER_THIEF 8 8
+FIGHTER_MAGE_THIEF 8 8
+DRUID 4 6
+RANGER 6 8
+MAGE_THIEF 8 6
+CLERIC_MAGE 6 6
+CLERIC_THIEF 8 6
+FIGHTER_DRUID 6 8
+FIGHTER_MAGE_CLERIC 6 8
+CLERIC_RANGER 6 8
diff --git a/gemrb/override/iwd/clwounh.pro b/gemrb/override/iwd/clwounh.pro
new file mode 100644
index 0000000..10efa84
Binary files /dev/null and b/gemrb/override/iwd/clwounh.pro differ
diff --git a/gemrb/override/iwd/cmdamah.pro b/gemrb/override/iwd/cmdamah.pro
new file mode 100644
index 0000000..d408401
Binary files /dev/null and b/gemrb/override/iwd/cmdamah.pro differ
diff --git a/gemrb/override/iwd/cmwounh.pro b/gemrb/override/iwd/cmwounh.pro
new file mode 100644
index 0000000..e7e644d
Binary files /dev/null and b/gemrb/override/iwd/cmwounh.pro differ
diff --git a/gemrb/override/iwd/cobones.pro b/gemrb/override/iwd/cobones.pro
new file mode 100644
index 0000000..0f2475a
Binary files /dev/null and b/gemrb/override/iwd/cobones.pro differ
diff --git a/gemrb/override/iwd/cobonh1.pro b/gemrb/override/iwd/cobonh1.pro
new file mode 100644
index 0000000..05d1e99
Binary files /dev/null and b/gemrb/override/iwd/cobonh1.pro differ
diff --git a/gemrb/override/iwd/cobonh2.pro b/gemrb/override/iwd/cobonh2.pro
new file mode 100644
index 0000000..d42d11e
Binary files /dev/null and b/gemrb/override/iwd/cobonh2.pro differ
diff --git a/gemrb/override/iwd/cocold.pro b/gemrb/override/iwd/cocold.pro
new file mode 100644
index 0000000..6953993
Binary files /dev/null and b/gemrb/override/iwd/cocold.pro differ
diff --git a/gemrb/override/iwd/cocoldh.pro b/gemrb/override/iwd/cocoldh.pro
new file mode 100644
index 0000000..a785192
Binary files /dev/null and b/gemrb/override/iwd/cocoldh.pro differ
diff --git a/gemrb/override/iwd/cofire.pro b/gemrb/override/iwd/cofire.pro
new file mode 100644
index 0000000..ef23e1b
Binary files /dev/null and b/gemrb/override/iwd/cofire.pro differ
diff --git a/gemrb/override/iwd/coldh.pro b/gemrb/override/iwd/coldh.pro
new file mode 100644
index 0000000..dd5e692
Binary files /dev/null and b/gemrb/override/iwd/coldh.pro differ
diff --git a/gemrb/override/iwd/colrspry.pro b/gemrb/override/iwd/colrspry.pro
new file mode 100644
index 0000000..1fa7f2d
Binary files /dev/null and b/gemrb/override/iwd/colrspry.pro differ
diff --git a/gemrb/override/iwd/confush.pro b/gemrb/override/iwd/confush.pro
new file mode 100644
index 0000000..3027e09
Binary files /dev/null and b/gemrb/override/iwd/confush.pro differ
diff --git a/gemrb/override/iwd/confusp.pro b/gemrb/override/iwd/confusp.pro
new file mode 100644
index 0000000..bb4b8b6
Binary files /dev/null and b/gemrb/override/iwd/confusp.pro differ
diff --git a/gemrb/override/iwd/confusw.pro b/gemrb/override/iwd/confusw.pro
new file mode 100644
index 0000000..ea6e585
Binary files /dev/null and b/gemrb/override/iwd/confusw.pro differ
diff --git a/gemrb/override/iwd/conjuh.pro b/gemrb/override/iwd/conjuh.pro
new file mode 100644
index 0000000..34eefc4
Binary files /dev/null and b/gemrb/override/iwd/conjuh.pro differ
diff --git a/gemrb/override/iwd/conjut.pro b/gemrb/override/iwd/conjut.pro
new file mode 100644
index 0000000..6ef8ba1
Binary files /dev/null and b/gemrb/override/iwd/conjut.pro differ
diff --git a/gemrb/override/iwd/copest.pro b/gemrb/override/iwd/copest.pro
new file mode 100644
index 0000000..6853632
Binary files /dev/null and b/gemrb/override/iwd/copest.pro differ
diff --git a/gemrb/override/iwd/cry150.pro b/gemrb/override/iwd/cry150.pro
new file mode 100644
index 0000000..ac00906
Binary files /dev/null and b/gemrb/override/iwd/cry150.pro differ
diff --git a/gemrb/override/iwd/cry200.pro b/gemrb/override/iwd/cry200.pro
new file mode 100644
index 0000000..2ab9ac5
Binary files /dev/null and b/gemrb/override/iwd/cry200.pro differ
diff --git a/gemrb/override/iwd/cry225.pro b/gemrb/override/iwd/cry225.pro
new file mode 100644
index 0000000..afb7c92
Binary files /dev/null and b/gemrb/override/iwd/cry225.pro differ
diff --git a/gemrb/override/iwd/cry250.pro b/gemrb/override/iwd/cry250.pro
new file mode 100644
index 0000000..31ee26e
Binary files /dev/null and b/gemrb/override/iwd/cry250.pro differ
diff --git a/gemrb/override/iwd/cry500np.pro b/gemrb/override/iwd/cry500np.pro
new file mode 100644
index 0000000..13d718a
Binary files /dev/null and b/gemrb/override/iwd/cry500np.pro differ
diff --git a/gemrb/override/iwd/csdamah.pro b/gemrb/override/iwd/csdamah.pro
new file mode 100644
index 0000000..76eaa24
Binary files /dev/null and b/gemrb/override/iwd/csdamah.pro differ
diff --git a/gemrb/override/iwd/cstrenh.pro b/gemrb/override/iwd/cstrenh.pro
new file mode 100644
index 0000000..eb5136a
Binary files /dev/null and b/gemrb/override/iwd/cstrenh.pro differ
diff --git a/gemrb/override/iwd/cswounh.pro b/gemrb/override/iwd/cswounh.pro
new file mode 100644
index 0000000..195106b
Binary files /dev/null and b/gemrb/override/iwd/cswounh.pro differ
diff --git a/gemrb/override/iwd/curseh.pro b/gemrb/override/iwd/curseh.pro
new file mode 100644
index 0000000..e3c45c4
Binary files /dev/null and b/gemrb/override/iwd/curseh.pro differ
diff --git a/gemrb/override/iwd/cwelem1.pro b/gemrb/override/iwd/cwelem1.pro
new file mode 100644
index 0000000..a0ec19d
Binary files /dev/null and b/gemrb/override/iwd/cwelem1.pro differ
diff --git a/gemrb/override/iwd/cwelemh.pro b/gemrb/override/iwd/cwelemh.pro
new file mode 100644
index 0000000..9f2052d
Binary files /dev/null and b/gemrb/override/iwd/cwelemh.pro differ
diff --git a/gemrb/override/iwd/cwelemx.pro b/gemrb/override/iwd/cwelemx.pro
new file mode 100644
index 0000000..3f0cdb7
Binary files /dev/null and b/gemrb/override/iwd/cwelemx.pro differ
diff --git a/gemrb/override/iwd/cynicism.2da b/gemrb/override/iwd/cynicism.2da
new file mode 100644
index 0000000..508e837
--- /dev/null
+++ b/gemrb/override/iwd/cynicism.2da
@@ -0,0 +1,14 @@
+2DA V1.0
+*
+ STRREF
+0 0x6155
+1 0x6156
+2 0x6157
+3 0x6158
+4 0x6159
+5 0x615A
+6 0x615B
+7 0x615C
+8 0x615D
+9 0x615E
+10 0x615F
diff --git a/gemrb/override/iwd/dagger.pro b/gemrb/override/iwd/dagger.pro
new file mode 100644
index 0000000..284c351
Binary files /dev/null and b/gemrb/override/iwd/dagger.pro differ
diff --git a/gemrb/override/iwd/daggerex.pro b/gemrb/override/iwd/daggerex.pro
new file mode 100644
index 0000000..e33a595
Binary files /dev/null and b/gemrb/override/iwd/daggerex.pro differ
diff --git a/gemrb/override/iwd/damage.2da b/gemrb/override/iwd/damage.2da
new file mode 100644
index 0000000..2749aa3
--- /dev/null
+++ b/gemrb/override/iwd/damage.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+*
+ MAIN SPARKS GRADIENT
+CRIT BLOODCR * 47
+SMALL BLOODS * 47
+MEDIUM BLOODM * 47
+LARGE BLOODL * 47
+FIRES FIREH FIREL -1
+FIREM FIREH FIREL -1
+FIREL FIREH FIREL -1
+SPARKS ELECTRH ELECTRL -1
+SPARKM ELECTRH ELECTRL -1
+SPARKL ELECTRH ELECTRL -1
+ICES COLDH COLDL -1
+ICEM COLDH COLDL -1
+ICEL COLDH COLDL -1
diff --git a/gemrb/override/iwd/dart.pro b/gemrb/override/iwd/dart.pro
new file mode 100644
index 0000000..a8ec832
Binary files /dev/null and b/gemrb/override/iwd/dart.pro differ
diff --git a/gemrb/override/iwd/dartex.pro b/gemrb/override/iwd/dartex.pro
new file mode 100644
index 0000000..b09ac20
Binary files /dev/null and b/gemrb/override/iwd/dartex.pro differ
diff --git a/gemrb/override/iwd/dbreath.pro b/gemrb/override/iwd/dbreath.pro
new file mode 100644
index 0000000..e64a64f
Binary files /dev/null and b/gemrb/override/iwd/dbreath.pro differ
diff --git a/gemrb/override/iwd/ddeath.pro b/gemrb/override/iwd/ddeath.pro
new file mode 100644
index 0000000..4b587b4
Binary files /dev/null and b/gemrb/override/iwd/ddeath.pro differ
diff --git a/gemrb/override/iwd/ddeath2.pro b/gemrb/override/iwd/ddeath2.pro
new file mode 100644
index 0000000..e1a5407
Binary files /dev/null and b/gemrb/override/iwd/ddeath2.pro differ
diff --git a/gemrb/override/iwd/ddoorh.pro b/gemrb/override/iwd/ddoorh.pro
new file mode 100644
index 0000000..fa09734
Binary files /dev/null and b/gemrb/override/iwd/ddoorh.pro differ
diff --git a/gemrb/override/iwd/defsound.2da b/gemrb/override/iwd/defsound.2da
new file mode 100644
index 0000000..22cfee3
--- /dev/null
+++ b/gemrb/override/iwd/defsound.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+*
+ RESREF
+OPEN AMB_D03A
+CLOSE AMB_D03B
+HOPEN AMB_D04A
+HCLOSE AMB_D04B
+BUTTON1 GAM_09
+BUTTON2 GAM_03
+BUTTON3 GAM_04
+OPENFAIL *
+CLOSEFAIL *
+ITEM_GONE EFF_M02
+SECRET ACT_09
+11 *
+12 *
+13 *
+14 *
+15 *
+16 *
+17 *
+18 *
+19 *
+LIGHTNING1 AMB_E13A
+LIGHTNING2 AMB_E13B
+LIGHTNING3 AMB_E13F
+RAIN AMB_E11
+SNOW AMB_E02B
+
diff --git a/gemrb/override/iwd/destruh.pro b/gemrb/override/iwd/destruh.pro
new file mode 100644
index 0000000..87dfeaa
Binary files /dev/null and b/gemrb/override/iwd/destruh.pro differ
diff --git a/gemrb/override/iwd/dfog.pro b/gemrb/override/iwd/dfog.pro
new file mode 100644
index 0000000..702da41
Binary files /dev/null and b/gemrb/override/iwd/dfog.pro differ
diff --git a/gemrb/override/iwd/disint.pro b/gemrb/override/iwd/disint.pro
new file mode 100644
index 0000000..5d5f5d7
Binary files /dev/null and b/gemrb/override/iwd/disint.pro differ
diff --git a/gemrb/override/iwd/disinth.pro b/gemrb/override/iwd/disinth.pro
new file mode 100644
index 0000000..2b4b80f
Binary files /dev/null and b/gemrb/override/iwd/disinth.pro differ
diff --git a/gemrb/override/iwd/dispel.pro b/gemrb/override/iwd/dispel.pro
new file mode 100644
index 0000000..3984714
Binary files /dev/null and b/gemrb/override/iwd/dispel.pro differ
diff --git a/gemrb/override/iwd/divinh.pro b/gemrb/override/iwd/divinh.pro
new file mode 100644
index 0000000..475830a
Binary files /dev/null and b/gemrb/override/iwd/divinh.pro differ
diff --git a/gemrb/override/iwd/divint.pro b/gemrb/override/iwd/divint.pro
new file mode 100644
index 0000000..e2972f2
Binary files /dev/null and b/gemrb/override/iwd/divint.pro differ
diff --git a/gemrb/override/iwd/dspell.pro b/gemrb/override/iwd/dspell.pro
new file mode 100644
index 0000000..4eb4d1d
Binary files /dev/null and b/gemrb/override/iwd/dspell.pro differ
diff --git a/gemrb/override/iwd/dspellh.pro b/gemrb/override/iwd/dspellh.pro
new file mode 100644
index 0000000..9029a53
Binary files /dev/null and b/gemrb/override/iwd/dspellh.pro differ
diff --git a/gemrb/override/iwd/dualclas.2da b/gemrb/override/iwd/dualclas.2da
new file mode 100644
index 0000000..8d40d90
--- /dev/null
+++ b/gemrb/override/iwd/dualclas.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+0
+ FIGHTER CLERIC MAGE THIEF DRUID RANGER
+MAGE 1 1 0 1 0 0
+FIGHTER 0 1 1 1 1 0
+CLERIC 1 0 1 1 0 1
+THIEF 1 1 1 0 0 0
+BARD 0 0 0 0 0 0
+PALADIN 0 0 0 0 0 0
+DRUID 1 0 0 0 0 0
+RANGER 0 1 0 0 0 0
+FIGHTER_MAGE 0 0 0 0 0 0
+FIGHTER_CLERIC 0 0 0 0 0 0
+FIGHTER_THIEF 0 0 0 0 0 0
+FIGHTER_MAGE_THIEF 0 0 0 0 0 0
+MAGE_THIEF 0 0 0 0 0 0
+CLERIC_MAGE 0 0 0 0 0 0
+CLERIC_THIEF 0 0 0 0 0 0
+FIGHTER_DRUID 0 0 0 0 0 0
+FIGHTER_MAGE_CLERIC 0 0 0 0 0 0
+CLERIC_RANGER 0 0 0 0 0 0
+ABJURER 1 1 0 1 0 0
+CONJURER 1 1 0 1 0 0
+DIVINER 1 1 0 1 0 0
+ENCHANTER 1 1 0 1 0 0
+ILLUSIONIST 1 1 0 1 0 0
+INVOKER 1 1 0 1 0 0
+NECROMANCER 1 1 0 1 0 0
+TRANSMUTER 1 1 0 1 0 0
diff --git a/gemrb/override/iwd/effects.ids b/gemrb/override/iwd/effects.ids
new file mode 100644
index 0000000..8407a93
--- /dev/null
+++ b/gemrb/override/iwd/effects.ids
@@ -0,0 +1,300 @@
+IDS
+0x0 ACVsDamageTypeModifier
+0x1 AttacksPerRoundModifier
+0x2 Cure:Sleep
+0x3 State:Berserk
+0x4 Cure:Berserk
+0x5 State:Charmed
+0x6 CharismaModifier
+0x7 Color:SetPalette
+0x8 Color:SetRGB
+0x9 Color:PulseRGB
+0xa ConstitutionModifier
+0xb Cure:Poison
+0xc Damage
+0xd Death
+0xe Cure:Defrost
+0xf DexterityModifier
+0x10 State:Hasted
+0x11 CurrentHPModifier
+0x12 MaximumHPModifier
+0x13 IntelligenceModifier
+0x14 State:Invisible
+0x15 LoreModifier
+0x16 LuckModifier
+0x17 MoraleModifier
+0x18 State:Panic
+0x19 State:Poisoned
+0x1a RemoveCurse
+0x1b AcidResistanceModifier
+0x1c ColdResistanceModifier
+0x1d ElectricityResistanceModifier
+0x1e FireResistanceModifier
+0x1f MagicDamageResistanceModifier
+0x20 Cure:Death
+0x21 SaveVsDeathModifier
+0x22 SaveVsWandsModifier
+0x23 SaveVsPolyModifier
+0x24 SaveVsBreathModifier
+0x25 SaveVsSpellsModifier
+0x26 State:Silenced
+0x27 State:Helpless
+0x28 State:Slowed
+0x29 Sparkle
+0x2a WizardSpellSlotsModifier
+0x2b Cure:Petrification
+0x2c StrengthModifier
+0x2d State:Stun
+0x2e Cure:Stun
+0x2f Cure:Invisible
+0x30 Cure:Silence
+0x31 WisdomModifier
+0x32 Color:BriefRGB
+0x33 Color:DarkenRGB
+0x34 Color:GlowRGB
+0x35 AnimationIDModifier
+0x36 ToHitModifier
+0x37 KillCreatureType
+0x38 Alignment:Invert
+0x39 Alignment:Change
+0x3a DispelEffects
+0x3b StealthModifier
+0x3c MiscastMagicModifier
+0x3d AlchemyModifier
+0x3e PriestSpellSlotsModifier
+0x3f State:Infravision
+0x40 Cure:Infravision
+0x41 State:Blur
+0x42 TransparencyModifier
+0x43 SummonCreature
+0x44 UnsummonCreature
+0x45 State:NonDetection
+0x46 Cure:NonDetection
+0x47 SexModifier
+0x48 AIIdentifierModifier
+0x49 DamageBonusModifier
+0x4a State:Blind
+0x4b Cure:Blind
+0x4c State:Feeblemind
+0x4d Cure:Feeblemind
+0x4e State:Diseased
+0x4f Cure:Disease
+0x50 State:Deafness
+0x51 Cure:Deafness
+0x52 SetAIScript
+0x53 Protection:Projectile
+0x54 MagicalFireResistanceModifier
+0x55 MagicalColdResistanceModifier
+0x56 SlashingResistanceModifier
+0x57 CrushingResistanceModifier
+0x58 PiercingResistanceModifier
+0x59 MissilesResistanceModifier
+0x5a OpenLocksModifier
+0x5b FindTrapsModifier
+0x5c PickPocketsModifier
+0x5d FatigueModifier
+0x5e IntoxicationModifier
+0x5f TrackingModifier
+0x60 LevelModifier
+0x61 StrengthBonusModifier
+0x62 State:Regenerating
+0x63 SpellDurationModifier
+0x64 Protection:Creature
+0x65 Protection:Opcode
+0x66 Protection:SpellLevel
+0x67 ChangeName
+0x68 ExperienceModifier
+0x69 GoldModifier
+0x6a MoraleBreakModifier
+0x6b PortraitChange
+0x6c ReputationModifier
+0x6d State:HoldNoIcon
+0x6e RetreatFrom
+0x6f Item:CreateMagic
+0x70 Item:Remove
+0x71 Item:Equip
+0x72 Dither
+0x73 DetectAlignment
+0x74 Cure:Invisible2
+0x75 Reveal:Area
+0x76 Reveal:Creatures
+0x77 MirrorImage
+0x78 Protection:Weapons
+0x79 VisualAnimationEffect
+0x7a Item:CreateInventory
+0x7b Item:RemoveInventory
+0x7c DimensionDoor
+0x7d Unlock
+0x7e MovementRateModifier
+0x7f MonsterSummoning
+0x80 State:Confused
+0x81 AidNonCumulative
+0x82 BlessNonCumulative
+0x83 ChantNonCumulative
+0x84 HolyNonCumulative
+0x85 LuckNonCumulative
+0x86 State:Petrification
+0x87 Polymorph
+0x88 ForceVisible
+0x89 ChantBadNonCumulative
+0x8a AnimationStateChange
+0x8b DisplayString
+0x8c CastingGlow
+0x8d VisualSpellHit
+0x8e Icon:Display
+0x8f Item:CreateInSlot
+0x90 DisableButton
+0x91 DisableCasting
+0x92 Spell:Cast
+0x93 Spell:Learn
+0x94 Spell:CastPoint
+0x95 Identify
+0x96 FindTraps
+0x97 ReplaceCreature
+0x98 PlayMovie
+0x99 Overlay:Sanctuary
+0x9a Overlay:Entangle
+0x9b Overlay:MinorGlobe
+0x9c Overlay:ShieldGlobe
+0x9d Overlay:Web
+0x9e Overlay:Grease
+0x9f MirrorImageModifier
+0xa0 Cure:Sanctuary
+0xa1 Cure:Panic
+0xa2 Cure:Hold
+0xa3 FreeAction
+0xa4 Cure:Intoxication
+0xa5 PauseTarget
+0xa6 MagicResistanceModifier
+0xa7 MissileHitModifier
+0xa8 RemoveCreature
+0xa9 Icon:Disable
+0xaa DamageAnimation
+0xab Spell:Add
+0xac Spell:Remove
+0xad PoisonResistanceModifier
+0xae PlaySound
+0xaf State:Hold
+0xb0 MovementRateModifier2
+0xb1 ApplyEffect
+0xb2 ToHitVsCreature
+0xb3 DamageVsCreature
+0xb4 CantUseItem
+0xb5 CantUseItemType
+0xb6 ApplyEffectItem
+0xb7 ApplyEffectItemType
+0xb8 DontJumpModifier
+0xb9 State:Hold2
+0xba MoveToArea
+0xbb Variable:StoreLocalVariable
+0xbc AuraCleansingModifier
+0xbd CastingSpeedModifier
+0xbe AttackSpeedModifier
+0xbf CastingLevelModifier
+0xc0 FindFamiliar
+0xc1 InvisibleDetection
+0xc2 IgnoreDialogPause
+0xc3 FamiliarBond
+0xc4 FamiliarMarker
+0xc5 Bounce:Projectile
+0xc6 Bounce:Opcode
+0xc7 Bounce:SpellLevel
+0xc8 Bounce:SpellLevelDec
+0xc9 Protection:SpellLevelDec
+0xca Bounce:School
+0xcb Bounce:SecondaryType
+0xcc Protection:School
+0xcd Protection:SecondaryType
+0xce Protection:Spell2
+0xcf Bounce:Spell
+0xd0 MinimumHPModifier
+0xd1 PowerWordKill
+0xd2 PowerWordStun
+0xd3 State:Imprisonment
+0xd4 Cure:Imprisonment
+0xd5 Maze
+0xd6 CastFromList
+0xd7 PlayVisualEffect
+0xd8 LevelDrainModifier
+0xd9 PowerWordSleep
+0xda StoneskinModifier
+0xdb ACVsCreatureType
+0xdc DispelSchool
+0xdd DispelSecondaryType
+0xde RandomTeleport
+0xdf Protection:SchoolDec
+0xe0 Cure:LevelDrain
+0xe1 Reveal:Magic
+0xe2 Protection:SecondaryTypeDec
+0xe3 Bounce:SchoolDec
+0xe4 Bounce:SecondaryTypeDec
+0xe5 DispelSchoolOne
+0xe6 DispelSecondaryTypeOne
+0xe7 TimeStop
+0xe8 Color:FadeRGB
+0xe9 IWDVisualSpellHit
+0xea ColdDamage
+0xeb CastingGlow2
+0xec ChillTouch
+0xed CrushingDamage
+0xee SaveBonus
+0xef SlowPoison
+0xf0 IWDMonsterSummoning
+0xf1 VampiricTouch
+0xf2 Overlay
+0xf3 AnimateDead
+0xf4 Prayer2
+0xf5 Curse2
+0xf6 SummonMonster2
+0xf7 BurningBlood
+0xf8 SummonShadowMonster
+0xf9 Recitation
+0xfa RecitationBad
+0xfb Hold2
+0xfc BlindingOrb
+0xfd ACVsDamageTypeModifier2
+0xfe RemoveEffects
+0xff SalamanderAura
+0x100 UmberHulkGaze
+0x101 ZombieLordAura
+0x102 Protection:Spell
+0x103 SummonCreature2
+0x104 AvatarRemoval
+0x105 Protection:Opcode2
+0x106 SummonPomab
+0x107 ControlCreature
+0x108 StaticCharge
+0x109 CloakOfFear
+0x10a MovementRateModifier3
+0x10b Cure:Confusion
+0x10c EyeOfTheMind
+0x10d EyeOfTheSword
+0x10e EyeOfTheMage
+0x10f EyeOfVenom
+0x110 EyeOfTheSpirit
+0x111 EyeOfFortitude
+0x112 EyeOfStone
+0x113 RemoveSevenEyes
+0x114 RemoveEffect
+0x115 SoulEater
+0x116 ShroudOfFlame
+0x117 AnimalRage
+0x118 TurnUndead
+0x119 VitriolicSphere
+0x11a SuppressHP
+0x11b FloatText
+0x11c MaceOfDisruption
+0x11d State:Sleep
+0x11e Reveal:Tracks
+0x11f Protection:Backstab
+0x120 State:Set
+0x121 Cutscene
+0x122 Protection:Spell3
+0x123 RodOfSmithing
+0x124 MagicalRest
+0x125 BeholderDispelMagic
+0x126 HarpyWail
+0x127 JackalWereGaze
+0x128 ModifyGlobalVariable
+0x129 HideInShadowsModifier
+0x12a UseMagicDeviceModifier
diff --git a/gemrb/override/iwd/efftext.2da b/gemrb/override/iwd/efftext.2da
new file mode 100644
index 0000000..78829b0
--- /dev/null
+++ b/gemrb/override/iwd/efftext.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+-1
+ EFFECT_NAME STRREF
+25 POISON 14017
+13 DEATH 14026
+134 PETRIFICATION 14127
+#12 DAMAGE 14027
+39 SLEEP 20438
+68 UNSUMMON 14065
+
diff --git a/gemrb/override/iwd/electrh.pro b/gemrb/override/iwd/electrh.pro
new file mode 100644
index 0000000..2046bda
Binary files /dev/null and b/gemrb/override/iwd/electrh.pro differ
diff --git a/gemrb/override/iwd/emotion.pro b/gemrb/override/iwd/emotion.pro
new file mode 100644
index 0000000..2178dd4
Binary files /dev/null and b/gemrb/override/iwd/emotion.pro differ
diff --git a/gemrb/override/iwd/enchah.pro b/gemrb/override/iwd/enchah.pro
new file mode 100644
index 0000000..bc16f04
Binary files /dev/null and b/gemrb/override/iwd/enchah.pro differ
diff --git a/gemrb/override/iwd/enchannp.pro b/gemrb/override/iwd/enchannp.pro
new file mode 100644
index 0000000..e09813a
Binary files /dev/null and b/gemrb/override/iwd/enchannp.pro differ
diff --git a/gemrb/override/iwd/enchat.pro b/gemrb/override/iwd/enchat.pro
new file mode 100644
index 0000000..0c99116
Binary files /dev/null and b/gemrb/override/iwd/enchat.pro differ
diff --git a/gemrb/override/iwd/entangle.pro b/gemrb/override/iwd/entangle.pro
new file mode 100644
index 0000000..77a9dc4
Binary files /dev/null and b/gemrb/override/iwd/entangle.pro differ
diff --git a/gemrb/override/iwd/equake.pro b/gemrb/override/iwd/equake.pro
new file mode 100644
index 0000000..c8f920c
Binary files /dev/null and b/gemrb/override/iwd/equake.pro differ
diff --git a/gemrb/override/iwd/exaltah.pro b/gemrb/override/iwd/exaltah.pro
new file mode 100644
index 0000000..6a02ac3
Binary files /dev/null and b/gemrb/override/iwd/exaltah.pro differ
diff --git a/gemrb/override/iwd/factioh.pro b/gemrb/override/iwd/factioh.pro
new file mode 100644
index 0000000..60d568b
Binary files /dev/null and b/gemrb/override/iwd/factioh.pro differ
diff --git a/gemrb/override/iwd/findtrap.pro b/gemrb/override/iwd/findtrap.pro
new file mode 100644
index 0000000..768c882
Binary files /dev/null and b/gemrb/override/iwd/findtrap.pro differ
diff --git a/gemrb/override/iwd/fireball.pro b/gemrb/override/iwd/fireball.pro
new file mode 100644
index 0000000..4acc632
Binary files /dev/null and b/gemrb/override/iwd/fireball.pro differ
diff --git a/gemrb/override/iwd/fireblic.pro b/gemrb/override/iwd/fireblic.pro
new file mode 100644
index 0000000..f9b920f
Binary files /dev/null and b/gemrb/override/iwd/fireblic.pro differ
diff --git a/gemrb/override/iwd/firebolt.pro b/gemrb/override/iwd/firebolt.pro
new file mode 100644
index 0000000..a7b3b25
Binary files /dev/null and b/gemrb/override/iwd/firebolt.pro differ
diff --git a/gemrb/override/iwd/firebtbl.pro b/gemrb/override/iwd/firebtbl.pro
new file mode 100644
index 0000000..5599064
Binary files /dev/null and b/gemrb/override/iwd/firebtbl.pro differ
diff --git a/gemrb/override/iwd/fireh.pro b/gemrb/override/iwd/fireh.pro
new file mode 100644
index 0000000..c41f7de
Binary files /dev/null and b/gemrb/override/iwd/fireh.pro differ
diff --git a/gemrb/override/iwd/firestor.pro b/gemrb/override/iwd/firestor.pro
new file mode 100644
index 0000000..f3701e3
Binary files /dev/null and b/gemrb/override/iwd/firestor.pro differ
diff --git a/gemrb/override/iwd/fistweap.2da b/gemrb/override/iwd/fistweap.2da
new file mode 100644
index 0000000..e492835
--- /dev/null
+++ b/gemrb/override/iwd/fistweap.2da
@@ -0,0 +1,3 @@
+2DA V1.0
+FIST
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
diff --git a/gemrb/override/iwd/fodeath.pro b/gemrb/override/iwd/fodeath.pro
new file mode 100644
index 0000000..16cf8c3
Binary files /dev/null and b/gemrb/override/iwd/fodeath.pro differ
diff --git a/gemrb/override/iwd/fonts.2da b/gemrb/override/iwd/fonts.2da
new file mode 100644
index 0000000..fe1ed92
--- /dev/null
+++ b/gemrb/override/iwd/fonts.2da
@@ -0,0 +1,19 @@
+2DA V1.0
+NORMAL
+ RESREF NEED_PALETTE FIRST_CHAR
+0 NORMAL 1 0
+1 INFOFONT 1 0
+2 NUMBER 0 47
+3 INITIALS 0 0
+4 NUMBER2 0 47
+5 NUMBER3 0 47
+6 NUMFONT 1 0
+7 REALMS 0 0
+8 REALMS2 1 0
+9 STONEBIG 0 0
+10 STONESM2 0 0
+11 STONESM3 0 0
+12 STONESML 0 0
+13 TOOLFONT 1 0
+14 STATES 0 0
+15 STATES2 0 0
diff --git a/gemrb/override/iwd/formatio.2da b/gemrb/override/iwd/formatio.2da
new file mode 100644
index 0000000..c369656
--- /dev/null
+++ b/gemrb/override/iwd/formatio.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+-10
+# generated by make_formation.py, do not edit
+ X1 Y1 X2 Y2 X3 Y3 X4 Y4 X5 Y5 X6 Y6 X7 Y7 X8 Y8 X9 Y9 X10 Y10 X11 Y11 X12 Y12 X13 Y13 X14 Y14 X15 Y15 X16 Y16 X17 Y17 X18 Y18 X19 Y19 X20 Y20
+FOLLOW 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
+T 0 0 48 0 -48 0 0 48 0 84 0 120 0 156 0 192 0 228 0 264 0 300 0 336 0 372 0 408 0 444 0 480 0 516 0 552 0 588 0 624
+GATHER 0 -36 48 -24 -48 -24 48 24 -48 24 0 36 48 48 -48 48 0 72 48 72 -48 72 0 108 48 96 -48 96 0 144 48 120 -48 120 0 180 48 144 -48 144
+4AND2 0 0 64 0 -64 0 128 0 0 48 64 48 -64 48 128 48 0 96 64 96 -64 96 128 96 0 144 64 144 -64 144 128 144 0 192 64 192 -64 192 128 192
+3BY2 0 0 64 0 -64 0 0 48 64 48 -64 48 0 96 64 96 -64 96 0 144 64 144 -64 144 0 192 64 192 -64 192 0 240 64 240 -64 240 0 288 64 288
+PROTECT 0 0 0 -36 -64 0 64 0 -32 48 32 48 0 24 0 48 0 72 0 96 0 120 0 144 0 168 0 192 0 216 0 240 0 264 0 288 0 312 0 336
+2BY3 -24 0 24 0 -24 48 24 48 -24 84 24 84 -24 120 24 120 -24 156 24 156 -24 192 24 192 -24 228 24 228 -24 264 24 264 -24 300 24 300 -24 336 24 336
+RANK -32 0 32 0 -96 0 96 0 -160 0 160 0 -224 0 224 0 -288 0 288 0 -352 0 352 0 -416 0 416 0 -480 0 480 0 -544 0 544 0 -608 0 608 0
+V 0 0 64 0 -15 48 49 48 -30 96 34 96 -45 144 19 144 -60 192 4 192 -75 240 -11 240 -90 288 -26 288 -105 336 -41 336 -120 384 -56 384 -135 432 -71 432
+WEDGE 0 0 64 36 -64 36 -124 72 124 72 0 72 0 144 -124 144 124 144 0 180 -124 180 124 180 0 216 -124 216 124 216 0 252 -124 252 124 252 0 288 -124 288
+S 0 0 64 24 0 48 64 72 0 96 64 120 0 144 64 168 0 192 64 216 0 240 64 264 0 288 64 312 0 336 64 360 0 384 64 408 0 432 64 456
+LINE 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
diff --git a/gemrb/override/iwd/fseed.pro b/gemrb/override/iwd/fseed.pro
new file mode 100644
index 0000000..2d0b929
Binary files /dev/null and b/gemrb/override/iwd/fseed.pro differ
diff --git a/gemrb/override/iwd/fstrikh.pro b/gemrb/override/iwd/fstrikh.pro
new file mode 100644
index 0000000..bbd9d62
Binary files /dev/null and b/gemrb/override/iwd/fstrikh.pro differ
diff --git a/gemrb/override/iwd/gametime.2da b/gemrb/override/iwd/gametime.2da
new file mode 100644
index 0000000..2d29d2c
--- /dev/null
+++ b/gemrb/override/iwd/gametime.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+0
+ DURATION
+ROUND_SECONDS 7
+TURN_SECONDS 70
diff --git a/gemrb/override/iwd/garmorh.pro b/gemrb/override/iwd/garmorh.pro
new file mode 100644
index 0000000..f4f1d5a
Binary files /dev/null and b/gemrb/override/iwd/garmorh.pro differ
diff --git a/gemrb/override/iwd/gaze.pro b/gemrb/override/iwd/gaze.pro
new file mode 100644
index 0000000..632263a
Binary files /dev/null and b/gemrb/override/iwd/gaze.pro differ
diff --git a/gemrb/override/iwd/gemprjtl.ids b/gemrb/override/iwd/gemprjtl.ids
new file mode 100644
index 0000000..a5cacea
--- /dev/null
+++ b/gemrb/override/iwd/gemprjtl.ids
@@ -0,0 +1,465 @@
+IDS V1.0
+1 ARROW
+2 ARROWEX
+3 ARROWFLM
+4 ARROWHVY
+5 ARROW
+6 AXE
+7 AXEEX
+8 AXEFLM
+9 AXE
+10 AXE
+11 BOLT
+12 BOLTEX
+13 ARROWHVY
+14 BOLT
+15 BOLT
+16 BULLET
+17 BULLETEX
+18 <flamingbullet>
+19 BULLET
+20 BULLET
+21 SPBRNHND
+22 CALLLIH
+23 CHROMORB
+24 SPCONECO
+25 SPCONEFI
+26 DAGGER
+27 DAGGEREX
+28 <flamingdagger>
+29 DAGGER
+30 DAGGER
+31 DART
+32 DARTEX
+33 <flamingdart>
+34 DART
+35 DART
+36 MAGICMIS
+37 FIREBALL
+38 <icefragments>
+39 LIGHTB
+40 STONE
+41 SLEEP
+42 <skeleton>
+43 SPSMPUFF
+44 SPSMKJET
+45 SPSMOLD
+46 SPARKLBL
+47 SPARKLGO
+48 SPARKLPU
+49 SPARKLIC
+50 SPARKLST
+51 SPARKLBK
+52 SPARKLCH
+53 SPARKLRE
+54 SPARKLGR
+55 SPEAR
+56 SPEAREX
+57 <flamingspear>
+58 SPEAR
+59 SPEAR
+60 <starsprite>
+61 <stoned>
+62 <webtravel>
+63 WEB
+64 GAZE
+65 HMIGHTH
+66 FLMSTRK
+67 MAGICMIS
+68 SPMAGMIS
+69 SPMAGMIS
+70 SPMAGMIS
+71 SPMAGMIS
+72 SPMAGMIS
+73 SPMAGMIS
+74 SPMAGMIS
+75 SPMAGMIS
+76 SPMAGMIS
+77 SPMAGMIS
+78 INVTRAV
+79 FIREBOLT
+80 SKYBOLT
+81 SKYBOLT2
+82 SKYBOLT2
+83 SKYBOLT2
+84 SKYBOLT2
+85 SKYBOLT2
+86 SKYBOLT2
+87 SKYBOLT2
+88 SKYBOLT2
+89 SKYBOLT2
+90 SKYBOLT2
+91 FIRESTOR
+92 LIGHTSTO
+93 INAREA
+94 CLOUD
+95 TRAPSKUL
+96 COLRSPRY
+97 ICESTORM
+98 SPFIREWL
+99 TRAPGLYP
+100 GREASE
+101 ARROWFLG
+102 ARROWFLB
+103 FIREBLGR
+104 FIREBTBL
+105 <potion>
+106 <potionexplode>
+107 ACIDBLOB
+108 SPSCORCH
+109 SPDIMDR
+110 CGNECROM
+111 CGALTERA
+112 CGENCHAN
+113 CGABJURA
+114 CGILLUSI
+115 CGCONJUR
+116 CGINVOCA
+117 CGDIVINA
+118 SHAIR
+119 SHEARTH
+120 SHWATER
+121 SHAIR1
+122 SHEARTH1
+123 SHWATER1
+124 SHAIR2
+125 SHEARTH2
+126 SHWATER2
+127 SHAIR3
+128 SHEARTH3
+129 SHWATER3
+130 SHAIR4
+131 SHEARTH4
+132 SHWATER4
+133 SHAIR5
+134 SHEARTH5
+135 SHWATER5
+136 SHAIR6
+137 SHEARTH6
+138 SHWATER6
+139 SHAIR7
+140 SHEARTH7
+141 SHWATER7
+142 SPBOOM1
+143 SPBOOM2
+144 SPBOOM3
+145 FLMSTRK
+146 HLYMITE
+147 SHAIR7
+148 SPKLARBL
+149 SPKLARGO
+150 SPKLARPU
+151 SPKLARIC
+152 SPKLARST
+153 SPKLARBK
+154 SPKLARCH
+155 SPKLARRE
+156 SPKLARGR
+157 INAREAPA
+158 INAREANP
+159 SPARBLPA
+160 SPARGOPA
+161 SPARPUPA
+162 SPARICPA
+163 SPARSTPA
+164 SPARBKPA
+165 SPARCHPA
+166 SPARREPA
+167 SPARGRPA
+168 SPARBLNP
+169 SPARGONP
+170 SPARPUNP
+171 SPARICNP
+172 SPARSTNP
+173 SPARBKNP
+174 SPARCHNP
+175 SPARRENP
+176 SPARGRNP
+177 SPARMANP
+178 SPARORNP
+179 SPARMAPA
+180 SPARORPA
+181 SPKLARMA
+182 SPKLAROR
+183 SPARKLMA
+184 SPARKLOR
+185 INAREANS
+186 CLOUDKIL
+187 ARROWFLI
+188 COW
+189 HOLD
+190 SPSCORIC
+191 ACIDBLMU
+192 ACIDBLGR
+193 ACIDBLOC
+194 REDHOLY
+195 SHAREA
+196 SHAREA1
+197 SHAREA
+198 SHAREA2
+199 SHAREA3
+200 SHAREA4
+201 SHAREA5
+202 SHAREA6
+203 FIREBLIC
+204 INAREASM
+205 LIGHTB
+206 LIGHTBNB
+207 SPFDEATH
+208 MRAGE
+209 CLIGHT
+210 ASTORM
+211 DFOG
+212 SSTONE
+213 ICLOUD
+214 PFIRE
+215 IPLAGUE
+216 SSSWARM
+217 MMISSILE
+218 ABJURH
+219 ALTERH
+220 INVOCH
+221 NECROH
+222 CONJUH
+223 ENCHAH
+224 ILLUSH
+225 DIVINH
+226 ABJURT
+227 ALTERT
+228 INVOCT
+229 NECROT
+230 CONJUT
+231 ENCHAT
+232 ILLUST
+233 DIVINT
+234 ENTANGLE
+235 AREA1P
+236 AREA1NP
+237 ABJURAP
+238 AREA2
+239 AREA2NP
+240 AREA4NP
+241 ABJURAP
+242 CHANT
+243 FINDTRAP
+244 ALTERAS
+245 DISPEL
+246 ALTERAP
+247 ALTERANP
+248 ENCHAN
+249 ABJURAP
+250 ICELANCE
+251 ALTERA
+252 PRAYER
+253 CONFUSW
+254 EMOTION
+255 MALISON
+256 HARMONY
+257 PROTEVIL
+258 CLOAK
+259 PRAYER
+260 INVTRAV
+261 SCHARGE
+262 ENCHANNP
+263 CHAOS
+264 SHROUD
+265 ABJURAP
+266 DSPELL
+267 DISINT
+268 OFSPHE
+269 FSEED
+270 OFSPHE
+271 PSPRAY
+272 AREA3P
+273 SUNRAY
+274 CONFUSP
+275 SOPAIN
+276 SOHOPE
+277 PWKILL
+278 DFOG2
+279 RAD100
+280 RAD250
+281 BSCLOUD
+282 ZLAURA
+283 GOLCLOUD
+284 MSPORE
+285 ICLOUDB
+286 ICLOUDA
+287 INAREANP
+288 MSUMM1
+289 ASUMM1
+290 CEELEM
+291 CFELEM
+292 CWELEM
+293 PRTL_OP
+294 SAREANP
+295 WWOLF
+296 PRTL_CL
+297 ALANCE
+298 SEATER
+299 SGROWTH
+300 CLOUDB
+301 SWAVE
+302 TSPRAY
+303 WOMOON
+304 WHIRLW
+305 EQUAKE
+306 MOELDA
+307 COBONES
+308 COPEST
+309 UWARD
+310 BBARRIER
+311 SPWRATH
+312 LODISR
+313 MFMISS
+314 SHOUT
+315 VSPHER
+316 SUFFOC
+317 ADHWIL
+318 GSHOUT
+319 CRY225
+320 CRY250
+321 CRY150
+322 CRY200
+323 MFMISS
+324 MFMISS2
+325 MFMISS2
+326 MFMISS2
+327 MFMISS2
+328 MFMISS2
+329 MFMISS2
+330 MFMISS2
+331 MFMISS2
+332 MFMISS2
+333 MFMISS2
+334 SUNFIRE
+335 PWSTUN
+336 HSMITE
+337 UBLIGHT
+338 ENCHANNP
+339 HWORD
+340 HWORD
+341 CRY500NP
+342 WOWISP
+343 RETRIB1
+344 RETRIB2
+345 ASCORCH
+346 PORTAL
+347 OFSPHE
+348 DBREATH
+349 RNG450
+350 CLOUDKS
+351 INVTRAV
+352 ILLUSH
+353 CCDAMAH
+354 RNG450
+355 RNG450
+0x1001 INVTRAV
+0x1002 ABJURH
+0x1003 ALTERH
+0x1004 INVOCH
+0x1005 NECROH
+0x1006 CONJUH
+0x1007 ENCHAH
+0x1008 ILLUSH
+0x1009 DIVINH
+0x100a ARMORH
+0x100b SARMORH
+0x100c GARMORH
+0x100d STRENGH
+0x100e CONFUSH
+0x100f SOFLAMH
+0x1010 DSPELLH
+0x1011 DISINTH
+0x1012 PWSILEH
+0x1013 PWSTUNH
+0x1014 FODEATH
+0x1015 MSWORDH
+0x1016 MSUMM1H
+0x1017 MSUMM2H
+0x1018 MSUMM3H
+0x1019 MSUMM4H
+0x101a MSUMM5H
+0x101b MSUMM6H
+0x101c MSUMM7H
+0x101d CFELEMH
+0x101e CEELEMH
+0x101f CWELEMH
+0x1020 BLESSH
+0x1021 CURSEH
+0x1022 PRAYERH
+0x1023 RECITAH
+0x1024 CLWOUNH
+0x1025 CMWOUNH
+0x1026 CSWOUNH
+0x1027 CCWOUNH
+0x1028 HEALH
+0x1029 ASUMM1H
+0x102a ASUMM2H
+0x102b ASUMM3H
+0x102c SPOISOH
+0x102d NPOISOH
+0x102e CALLLIH
+0x102f SCHARGH
+0x1030 RPARALH
+0x1031 FACTIOH
+0x1032 MMAGICH
+0x1033 SOONEH
+0x1034 CSTRENH
+0x1035 FSTRIKH
+0x1036 RDEADH
+0x1037 RESURRH
+0x1038 CCOMMAH
+0x1039 RWOTFAH
+0x103a SUNRAYH
+0x103b SSTONE
+0x103c DDOORH
+0x103d DDOORH
+0x103e COCOLDH
+0x103f SSORBH
+0x1040 FIREH
+0x1041 COLDH
+0x1042 ELECTRH
+0x1043 ACIDH
+0x1044 PARALH
+0x1045 MRAGEH
+0x1046 RWOTFAG
+0x1047 BDEATH
+0x1048 PORTALH
+0x1049 SUNSCOH
+0x104a BBARRH1
+0x104b BBARRH2
+0x104c COBONH1
+0x104d COBONH2
+0x104e CLDAMAH
+0x104f CMDAMAH
+0x1050 CSDAMAH
+0x1051 CCDAMAH
+0x1052 CDISEAH
+0x1053 POISONH
+0x1054 SLIVINH
+0x1055 HARMH
+0x1056 DESTRUH
+0x1057 EXALTAH
+0x1058 CLOUDBH
+0x1059 MTOUCHH
+0x105a MTOUCHH
+0x105b CGRACEH
+0x105c SEATERH
+0x105d SWAVEH
+0x105e SUFFOCH
+0x105f ADHWILH
+0x1060 MFMISSH
+0x1061 VSPHERH
+0x1062 WVDEATH
+0x1063 UWARDH
+0x1064 WVHITH
+0x1065 WDEATH1
+0x1066 WDEATH2
+0x1067 DDEATH
+0x1068 DDEATH2
+0x1069 MSUMM1X
+0x106a ASUMM1X
+0x106b CEELEMX
+0x106c CFELEMX
+0x106d CWELEMX
diff --git a/gemrb/override/iwd/gemrb.ini b/gemrb/override/iwd/gemrb.ini
new file mode 100644
index 0000000..40f4286
--- /dev/null
+++ b/gemrb/override/iwd/gemrb.ini
@@ -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.
+;
+
+
+; gemrb.ini - game type-specific settings for GemRB engine
+
+[resources]
+
+; Bitmap resource for cursors
+CursorBAM = CAROT
+
+; Bitmap resource for scroll cursor arrow
+ScrollCursorBAM = CURSARW
+
+; Bitmap resource for dialog buttons font
+ButtonFont = STONESML
+
+; Font used to display tooltips
+TooltipFont = TOOLFONT
+
+; Font used to display subtitles
+MovieFont = REALMS2
+
+; Sprite displayed behind the tooltip text, if any
+TooltipBack = TOOLTIP
+
+; Tooltip text color (RGBA)
+TooltipColor = #ffffffff
+
+; Space between tooltip text and sides of TooltipBack (x2)
+TooltipMargin = 2
+
+; INI file from the original games
+INIConfig = icewind.ini
+
+; Palette bitmaps in various widths
+Palette16 = MPALETTE
+Palette32 = MPALETTE
+Palette256 = MPAL256
+
+MaximumAbility = 25
+IgnoreButtonFrames = 0
+AllStringsTagged = 1
+HasDPLAYER = 1
+HasPickSound = 0
+HasDescIcon = 0
+HasEXPTABLE = 1
+SoundFolders = 1
+HasSongList = 0
+UpperButtonText = 0
+LowerLabelText = 0
+HasPartyINI = 0
+HasBeastsINI = 0
+ForceStereo = 0
+ReverseToHit = 1
+IWDMapDimensions = 1
+SmallFog = 0
+ReverseDoor = 0
+IWD2ScriptName = 1
+DialogueScrolls = 0
+CharNameIsGabber = 1
+SpawnIni = 1
+HasHideInShadows = 1
+AreaVisitedVar = 1
+HasSpecificDamageBonus = 0
+BiographyIsRes = 1
+FlexibleWorldmap = 1
+AutoSearchHidden = 1
+CutsceneAreascripts = 0
+PSTStateFlags = 0
+CastingSounds = 1
+ForceAreaScript = 1
+
+[charset]
+CharCount = 31
+Letter1 = 192,224
+Letter2 = 193,225
+Letter3 = 194,226
+Letter4 = 195,227
+Letter5 = 196,228
+Letter6 = 197,229
+Letter7 = 198,230
+Letter8 = 199,231
+Letter9 = 200,232
+Letter10 = 201,233
+Letter11 = 202,234
+Letter12 = 203,235
+Letter13 = 204,236
+Letter14 = 205,237
+Letter15 = 206,238
+Letter16 = 207,239
+Letter17 = 208,240
+Letter18 = 209,241
+Letter19 = 210,242
+Letter20 = 211,243
+Letter21 = 212,244
+Letter22 = 213,245
+Letter23 = 214,246
+Letter24 = 140,156
+Letter25 = 216,248
+Letter26 = 217,249
+Letter27 = 218,250
+Letter28 = 219,251
+Letter29 = 220,252
+Letter30 = 221,253
+Letter31 = 222,254
diff --git a/gemrb/override/iwd/gender.2da b/gemrb/override/iwd/gender.2da
new file mode 100644
index 0000000..609cf68
--- /dev/null
+++ b/gemrb/override/iwd/gender.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+*
+ TYPE MALE FEMALE
+SIRMAAM -1 27473 27475
+GIRLBOY -1 27477 27476
+BROTHERSISTER -1 27478 27479
+LADYLORD -1 27481 27480
+MALEFEMALE -1 27482 27483
+HESHE -1 27484 27485
+HISHER -1 27486 27487
+HIMHER -1 27488 27487
+MANWOMAN -1 27489 27490
+SONDAUGHTER -1 70567 70568
+PROTAGONIST_SIRMAAM 0 27473 27475
+PROTAGONIST_GIRLBOY 0 27477 27476
+PROTAGONIST_BROTHERSISTER 0 27478 27479
+PROTAGONIST_LADYLORD 0 27481 27480
+PROTAGONIST_MALEFEMALE 0 27482 27483
+PROTAGONIST_HESHE 0 27484 27485
+PROTAGONIST_HISHER 0 27486 27487
+PROTAGONIST_HIMHER 0 27488 27487
+PROTAGONIST_MANWOMAN 0 27489 27490
diff --git a/gemrb/override/iwd/golcloud.pro b/gemrb/override/iwd/golcloud.pro
new file mode 100644
index 0000000..3c97d09
Binary files /dev/null and b/gemrb/override/iwd/golcloud.pro differ
diff --git a/gemrb/override/iwd/grease.pro b/gemrb/override/iwd/grease.pro
new file mode 100644
index 0000000..bf273b6
Binary files /dev/null and b/gemrb/override/iwd/grease.pro differ
diff --git a/gemrb/override/iwd/gshout.pro b/gemrb/override/iwd/gshout.pro
new file mode 100644
index 0000000..1b362d6
Binary files /dev/null and b/gemrb/override/iwd/gshout.pro differ
diff --git a/gemrb/override/iwd/guibtact.2da b/gemrb/override/iwd/guibtact.2da
new file mode 100644
index 0000000..7b16720
--- /dev/null
+++ b/gemrb/override/iwd/guibtact.2da
@@ -0,0 +1,37 @@
+2DA V1.0
+-1
+ 1 2 3 4 TOOLTIP RESREF
+Stealth 30 31 32 33 4968 guibtact
+Thieving 26 27 28 29 4971 guibtact
+Cast 12 13 52 53 4688 guibtact
+QSpell1 0 1 24 25 4938 guibtbut
+QSpell2 2 3 26 27 4938 guibtbut
+QSpell3 4 5 28 29 4938 guibtbut
+Turn 8 9 10 11 4974 guibtact
+Talk 4 5 6 7 4933 guibtact
+UseItem 18 19 56 57 4978 guibtact
+QItem1 0 1 24 25 4937 guibtbut
+QItem4 6 7 30 31 4937 guibtbut
+QItem2 2 3 26 27 4937 guibtbut
+QItem3 4 5 28 29 4937 guibtbut
+Innate 38 39 54 55 4954 guibtact
+Defend 0 1 2 3 15925 guibtact
+Attack 14 15 16 17 4666 guibtact
+QWeapon1 0 1 24 25 4950 guibtbut
+QWeapon2 2 3 26 27 4950 guibtbut
+QWeapon3 4 5 28 29 4950 guibtbut
+QWeapon4 6 7 30 31 4950 guibtbut
+BardSong 22 23 24 25 11798 guibtact
+Stop 58 59 60 61 15924 guibtact
+Search 34 35 36 37 4927 guibtact
+23 * * * * * *
+24 * * * * * *
+25 * * * * * *
+26 * * * * * *
+27 * * * * * *
+28 * * * * * *
+29 * * * * * *
+30 * * * * * *
+QItem5 0 1 2 3 4937 guibtbut
+Left 44 45 -1 -1 -1 guibtact
+Right 42 43 -1 -1 -1 guibtact
diff --git a/gemrb/override/iwd/guils.chu b/gemrb/override/iwd/guils.chu
new file mode 100644
index 0000000..901a44c
Binary files /dev/null and b/gemrb/override/iwd/guils.chu differ
diff --git a/gemrb/override/iwd/harmh.pro b/gemrb/override/iwd/harmh.pro
new file mode 100644
index 0000000..f8b2ded
Binary files /dev/null and b/gemrb/override/iwd/harmh.pro differ
diff --git a/gemrb/override/iwd/harmony.pro b/gemrb/override/iwd/harmony.pro
new file mode 100644
index 0000000..03a7b54
Binary files /dev/null and b/gemrb/override/iwd/harmony.pro differ
diff --git a/gemrb/override/iwd/haterace.2da b/gemrb/override/iwd/haterace.2da
new file mode 100644
index 0000000..75d0d0b
--- /dev/null
+++ b/gemrb/override/iwd/haterace.2da
@@ -0,0 +1,15 @@
+2DA V1.0
+-1
+ NAME_REF ID DESC_REF CAP_REF
+CADEVEROUS_UNDEAD 3284 108 3304 59
+GIANTS 3279 153 3291 61
+GOBLINS 3280 155 3292 29518
+LIZARD_MEN 3281 157 3293 3275
+ORCS 3282 160 3294 3271
+SALAMANDERS 3290 161 3295 3272
+SKELETAL_UNDEAD 3283 115 3296 3273
+SPECTRAL_UNDEAD 3285 167 3297 3278
+SPIDERS 3286 116 3298 3274
+TROLLS 3288 165 3299 3276
+UMBER_HULKS 3289 166 3300 3277
+YUAN-TI 30850 168 3301 30849
diff --git a/gemrb/override/iwd/healh.pro b/gemrb/override/iwd/healh.pro
new file mode 100644
index 0000000..1213e70
Binary files /dev/null and b/gemrb/override/iwd/healh.pro differ
diff --git a/gemrb/override/iwd/hmighth.pro b/gemrb/override/iwd/hmighth.pro
new file mode 100644
index 0000000..60ba342
Binary files /dev/null and b/gemrb/override/iwd/hmighth.pro differ
diff --git a/gemrb/override/iwd/hold.pro b/gemrb/override/iwd/hold.pro
new file mode 100644
index 0000000..aeecbfd
Binary files /dev/null and b/gemrb/override/iwd/hold.pro differ
diff --git a/gemrb/override/iwd/hsmite.pro b/gemrb/override/iwd/hsmite.pro
new file mode 100644
index 0000000..8b3dcb3
Binary files /dev/null and b/gemrb/override/iwd/hsmite.pro differ
diff --git a/gemrb/override/iwd/hword.pro b/gemrb/override/iwd/hword.pro
new file mode 100644
index 0000000..51295d7
Binary files /dev/null and b/gemrb/override/iwd/hword.pro differ
diff --git a/gemrb/override/iwd/icelance.pro b/gemrb/override/iwd/icelance.pro
new file mode 100644
index 0000000..936854e
Binary files /dev/null and b/gemrb/override/iwd/icelance.pro differ
diff --git a/gemrb/override/iwd/icestorm.pro b/gemrb/override/iwd/icestorm.pro
new file mode 100644
index 0000000..06e4b05
Binary files /dev/null and b/gemrb/override/iwd/icestorm.pro differ
diff --git a/gemrb/override/iwd/icloud.pro b/gemrb/override/iwd/icloud.pro
new file mode 100644
index 0000000..9f85cd1
Binary files /dev/null and b/gemrb/override/iwd/icloud.pro differ
diff --git a/gemrb/override/iwd/iclouda.pro b/gemrb/override/iwd/iclouda.pro
new file mode 100644
index 0000000..845d3f0
Binary files /dev/null and b/gemrb/override/iwd/iclouda.pro differ
diff --git a/gemrb/override/iwd/icloudb.pro b/gemrb/override/iwd/icloudb.pro
new file mode 100644
index 0000000..d5f3f31
Binary files /dev/null and b/gemrb/override/iwd/icloudb.pro differ
diff --git a/gemrb/override/iwd/illush.pro b/gemrb/override/iwd/illush.pro
new file mode 100644
index 0000000..158d82f
Binary files /dev/null and b/gemrb/override/iwd/illush.pro differ
diff --git a/gemrb/override/iwd/illust.pro b/gemrb/override/iwd/illust.pro
new file mode 100644
index 0000000..e2966de
Binary files /dev/null and b/gemrb/override/iwd/illust.pro differ
diff --git a/gemrb/override/iwd/inarea.pro b/gemrb/override/iwd/inarea.pro
new file mode 100644
index 0000000..ec6a50d
Binary files /dev/null and b/gemrb/override/iwd/inarea.pro differ
diff --git a/gemrb/override/iwd/inareanp.pro b/gemrb/override/iwd/inareanp.pro
new file mode 100644
index 0000000..91859f9
Binary files /dev/null and b/gemrb/override/iwd/inareanp.pro differ
diff --git a/gemrb/override/iwd/inareasm.pro b/gemrb/override/iwd/inareasm.pro
new file mode 100644
index 0000000..6545bce
Binary files /dev/null and b/gemrb/override/iwd/inareasm.pro differ
diff --git a/gemrb/override/iwd/invoch.pro b/gemrb/override/iwd/invoch.pro
new file mode 100644
index 0000000..0eccaef
Binary files /dev/null and b/gemrb/override/iwd/invoch.pro differ
diff --git a/gemrb/override/iwd/invoct.pro b/gemrb/override/iwd/invoct.pro
new file mode 100644
index 0000000..3a051b0
Binary files /dev/null and b/gemrb/override/iwd/invoct.pro differ
diff --git a/gemrb/override/iwd/iplague.pro b/gemrb/override/iwd/iplague.pro
new file mode 100644
index 0000000..1b3501e
Binary files /dev/null and b/gemrb/override/iwd/iplague.pro differ
diff --git a/gemrb/override/iwd/itemsnd.2da b/gemrb/override/iwd/itemsnd.2da
new file mode 100644
index 0000000..9678ee9
--- /dev/null
+++ b/gemrb/override/iwd/itemsnd.2da
@@ -0,0 +1,80 @@
+2DA V1.0
+*
+ TAKE DROP
+AMULET G_NECK1 G_NECK2
+ARMOR G_CLOAK1 G_CLOAK2
+BELT G_BELT1 G_BELT2
+BOOT G_BOOT1 G_BOOT2
+ARROW G_ARRW1 G_ARRW2
+BRACER G_GLOVE1 G_GLOVE2
+HELMET G_HELM1 G_HELM2
+KEY G_KEY1 G_KEY2
+POTION G_POTN1 G_POTN2
+RING G_RING1 G_RING2
+SCROLL G_SCROL1 G_SCROL2
+SHIELD GAM_21A GAM_21B
+FOOD GAM_21A GAM_21B
+BULLET G_BULLT1 G_BULLT2
+BOW G_BOW1 G_BOW2
+DAGGER G_DAGGR1 G_DAGGR2
+MACE G_MACE1 G_MACE2
+SLING G_SLING1 G_SLING2
+SMSWORD G_SMSWD1 G_SMSWD2
+BGSWORD G_LGSWD1 G_LGSWD2
+HAMMER G_HAMMR1 G_HAMMR2
+MSTAR G_MSTAR1 G_MSTAR2
+FLAIL G_FLAIL1 G_FLAIL2
+DART G_DART1 G_DART2
+AXE G_AXE1 G_AXE2
+STAFF G_STAFF1 G_STAFF2
+XBOW G_CROSB1 G_CROSB2
+FIST GAM_21A GAM_21B
+SPEAR G_SPEAR1 G_SPEAR2
+POLEARM G_HALB1 G_HALB2
+BOLT G_BOLT1 G_BOLT2
+CLOAK G_CLOAK1 G_CLOAK2
+COIN G_GOLD1 G_GOLD2
+GEM G_GEM1 G_GEM2
+WAND G_WAND1 G_WAND2
+BROKEN1 G_B_ARM1 G_B_ARM2
+BROKEN2 G_B_SHD1 G_B_SHD2
+BROKEN3 G_B_WPN1 G_B_WPN2
+UNUSED1 GAM_21A GAM_21B
+UNUSED2 G_BROKN1 G_BROKN2
+BUCKLER G_BUCKR1 G_BUCKR2
+CANDLE G_CAND1 G_CAND2
+CBODY G_CHILD1 G_CHILD2
+CLUB G_CLUB1 G_CLUB2
+FBODY G_FEM1 G_FEM2
+KEY2 G_KEYS1 G_KEYS2
+LSHIELD G_LGSLD1 G_LGSLD2
+MBODY G_MALE1 G_MALE2
+MSHIELD G_MDSLD1 G_MDSLD2
+NOTES G_PAPR1 G_PAPR2
+ROD G_ROD1 G_ROD2
+SKULL G_SKULL1 G_SKULL2
+SSHIELD G_SMSLD1 G_SMSLD2
+SPIDER G_SPIDR1 G_SPIDR2
+TELESCOPE G_TELE1 G_TELE2
+DRINK G_WINE1 G_WINE2
+GTSWORD G_LGSWD1 G_LGSWD2
+BAG GAM_21A GAM_21B
+FUR G_LETHR1 G_LETHR2
+LARMOR G_CLOAK1 G_CLOAK2
+SLARMOR G_CLOAK1 G_CLOAK2
+CHARMOR G_CLOAK1 G_CLOAK2
+SPARMOR G_CLOAK1 G_CLOAK2
+HPARMOR G_CLOAK1 G_CLOAK2
+FPARMOR G_CLOAK1 G_CLOAK2
+HARMOR G_CLOAK1 G_CLOAK2
+ROBE G_CLOAK1 G_CLOAK2
+SCALE G_CLOAK1 G_CLOAK2
+BTSWORD G_LGSWD1 G_LGSWD2
+SCARF G_GLOVE1 G_GLOVE2
+FOOD2 G_SLING1 G_SLING2
+HAT G_GLOVE1 G_GLOVE2
+GAUNTLET G_CAND1 G_CAND2
+DEFAULT GAM_21A GAM_21B
+LEATHER G_LETHR1 G_LETHR2
+CHAIN G_Chain1 G_Chain2
+PLATE G_Plate1 G_Plate2
diff --git a/gemrb/override/iwd/itemtype.2da b/gemrb/override/iwd/itemtype.2da
new file mode 100644
index 0000000..b7f7e28
--- /dev/null
+++ b/gemrb/override/iwd/itemtype.2da
@@ -0,0 +1,77 @@
+2DA V1.0
+0
+ HELMET ARMOR SHIELD GLOVES RING AMULET BELT BOOTS WEAPON QUIVER CLOAK QUICK SCROLL BAG POTION
+MISC 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+AMULET 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+ARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+BELT 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
+BOOT 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
+ARROW 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BRACER 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
+HELMET 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+KEY 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+POTION 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
+RING 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
+SCROLL 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0
+SHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+FOOD 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BULLET 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DAGGER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MACE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SLING 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SMSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BGSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+HAMMER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MSTAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FLAIL 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DART 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+AXE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+STAFF 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+XBOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FIST 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SPEAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+POLEARM 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BOLT 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+CLOAK 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
+COIN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+GEM 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+WAND 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BROKEN1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BROKEN2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BROKEN3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BUCKLER 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+CANDLE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+CLUB 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+UNUSED4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+LSHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+MSHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+NOTES 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+SSHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+TELESCOPE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+DRINK 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
+GTSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BAG 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
+FUR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+LARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+SLARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+CHARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+SPARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+HPARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+FPARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+HARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+ROBE 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BTSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SCARF 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+FOOD2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+HAT 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+GAUNTLET 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
diff --git a/gemrb/override/iwd/itemuse.2da b/gemrb/override/iwd/itemuse.2da
new file mode 100644
index 0000000..3540165
--- /dev/null
+++ b/gemrb/override/iwd/itemuse.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+0
+ STAT FILE MCOL VCOL WHICH
+0 ALIGNMENT aligns 3 5 0
+1 RACE races 3 4 0
+2 CLASS classes 5 7 0
diff --git a/gemrb/override/iwd/kitlist.2da b/gemrb/override/iwd/kitlist.2da
new file mode 100644
index 0000000..1ce17fb
--- /dev/null
+++ b/gemrb/override/iwd/kitlist.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+*
+ ROWNAME LOWER MIXED HELP ABILITIES PROFICIENCY UNUSABLE CLASS
+0 RESERVE * * * * * * *
+1 ABJURER 597 502 9564 * 21 0x00000040 1
+2 CONJURER 2179 504 9565 * 22 0x00000080 1
+3 DIVINER 2846 2012 9566 * 23 0x00000100 1
+4 ENCHANTER 2861 2022 9567 * 24 0x00000200 1
+5 ILLUSIONIST 2862 12785 9568 * 25 0x00000400 1
+6 INVOKER 3015 12786 9569 * 26 0x00000800 1
+7 NECROMANCER 12744 12787 9570 * 27 0x00001000 1
+8 TRANSMUTER 12745 12788 9571 * 28 0x00002000 1
diff --git a/gemrb/override/iwd/lightb.pro b/gemrb/override/iwd/lightb.pro
new file mode 100644
index 0000000..fa7f84a
Binary files /dev/null and b/gemrb/override/iwd/lightb.pro differ
diff --git a/gemrb/override/iwd/lightsto.pro b/gemrb/override/iwd/lightsto.pro
new file mode 100644
index 0000000..7b502b1
Binary files /dev/null and b/gemrb/override/iwd/lightsto.pro differ
diff --git a/gemrb/override/iwd/lodisr.pro b/gemrb/override/iwd/lodisr.pro
new file mode 100644
index 0000000..d78b250
Binary files /dev/null and b/gemrb/override/iwd/lodisr.pro differ
diff --git a/gemrb/override/iwd/magesch.2da b/gemrb/override/iwd/magesch.2da
new file mode 100644
index 0000000..acd3cef
--- /dev/null
+++ b/gemrb/override/iwd/magesch.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF KIT
+GENERALIST 18039 9563 9987 0x4000
+ABJURER 597 9564 502 0x40
+CONJURER 2179 9565 504 0x80
+DIVINER 2846 9566 2012 0x100
+ENCHANTER 2861 9567 2022 0x200
+ILLUSIONIST 2862 9568 8421 0x400
+INVOKER 3015 9569 12786 0x800
+NECROMANCER 12744 9570 12787 0x1000
+TRANSMUTER 12745 9571 12788 0x2000
diff --git a/gemrb/override/iwd/magicmis.pro b/gemrb/override/iwd/magicmis.pro
new file mode 100644
index 0000000..33ceed1
Binary files /dev/null and b/gemrb/override/iwd/magicmis.pro differ
diff --git a/gemrb/override/iwd/malison.pro b/gemrb/override/iwd/malison.pro
new file mode 100644
index 0000000..1bab8cd
Binary files /dev/null and b/gemrb/override/iwd/malison.pro differ
diff --git a/gemrb/override/iwd/mfmiss.pro b/gemrb/override/iwd/mfmiss.pro
new file mode 100644
index 0000000..c3cd185
Binary files /dev/null and b/gemrb/override/iwd/mfmiss.pro differ
diff --git a/gemrb/override/iwd/mfmiss2.pro b/gemrb/override/iwd/mfmiss2.pro
new file mode 100644
index 0000000..320be1f
Binary files /dev/null and b/gemrb/override/iwd/mfmiss2.pro differ
diff --git a/gemrb/override/iwd/mfmissh.pro b/gemrb/override/iwd/mfmissh.pro
new file mode 100644
index 0000000..171863e
Binary files /dev/null and b/gemrb/override/iwd/mfmissh.pro differ
diff --git a/gemrb/override/iwd/mmagich.pro b/gemrb/override/iwd/mmagich.pro
new file mode 100644
index 0000000..d74d4f2
Binary files /dev/null and b/gemrb/override/iwd/mmagich.pro differ
diff --git a/gemrb/override/iwd/mmissile.pro b/gemrb/override/iwd/mmissile.pro
new file mode 100644
index 0000000..145b3fd
Binary files /dev/null and b/gemrb/override/iwd/mmissile.pro differ
diff --git a/gemrb/override/iwd/moelda.pro b/gemrb/override/iwd/moelda.pro
new file mode 100644
index 0000000..c063a08
Binary files /dev/null and b/gemrb/override/iwd/moelda.pro differ
diff --git a/gemrb/override/iwd/mpal256.bmp b/gemrb/override/iwd/mpal256.bmp
new file mode 100644
index 0000000..9eec246
Binary files /dev/null and b/gemrb/override/iwd/mpal256.bmp differ
diff --git a/gemrb/override/iwd/mrage.pro b/gemrb/override/iwd/mrage.pro
new file mode 100644
index 0000000..8b9148b
Binary files /dev/null and b/gemrb/override/iwd/mrage.pro differ
diff --git a/gemrb/override/iwd/mrageh.pro b/gemrb/override/iwd/mrageh.pro
new file mode 100644
index 0000000..4a8b252
Binary files /dev/null and b/gemrb/override/iwd/mrageh.pro differ
diff --git a/gemrb/override/iwd/mspore.pro b/gemrb/override/iwd/mspore.pro
new file mode 100644
index 0000000..370e001
Binary files /dev/null and b/gemrb/override/iwd/mspore.pro differ
diff --git a/gemrb/override/iwd/msumm1.pro b/gemrb/override/iwd/msumm1.pro
new file mode 100644
index 0000000..3f4ad3a
Binary files /dev/null and b/gemrb/override/iwd/msumm1.pro differ
diff --git a/gemrb/override/iwd/msumm1h.pro b/gemrb/override/iwd/msumm1h.pro
new file mode 100644
index 0000000..7b4ad4d
Binary files /dev/null and b/gemrb/override/iwd/msumm1h.pro differ
diff --git a/gemrb/override/iwd/msumm1x.pro b/gemrb/override/iwd/msumm1x.pro
new file mode 100644
index 0000000..8242d6c
Binary files /dev/null and b/gemrb/override/iwd/msumm1x.pro differ
diff --git a/gemrb/override/iwd/msumm2h.pro b/gemrb/override/iwd/msumm2h.pro
new file mode 100644
index 0000000..07b7f94
Binary files /dev/null and b/gemrb/override/iwd/msumm2h.pro differ
diff --git a/gemrb/override/iwd/msumm3h.pro b/gemrb/override/iwd/msumm3h.pro
new file mode 100644
index 0000000..8a775a8
Binary files /dev/null and b/gemrb/override/iwd/msumm3h.pro differ
diff --git a/gemrb/override/iwd/msumm4h.pro b/gemrb/override/iwd/msumm4h.pro
new file mode 100644
index 0000000..4dbaf7b
Binary files /dev/null and b/gemrb/override/iwd/msumm4h.pro differ
diff --git a/gemrb/override/iwd/msumm5h.pro b/gemrb/override/iwd/msumm5h.pro
new file mode 100644
index 0000000..a1e88b0
Binary files /dev/null and b/gemrb/override/iwd/msumm5h.pro differ
diff --git a/gemrb/override/iwd/msumm6h.pro b/gemrb/override/iwd/msumm6h.pro
new file mode 100644
index 0000000..600dc17
Binary files /dev/null and b/gemrb/override/iwd/msumm6h.pro differ
diff --git a/gemrb/override/iwd/msumm7h.pro b/gemrb/override/iwd/msumm7h.pro
new file mode 100644
index 0000000..2f3f5b3
Binary files /dev/null and b/gemrb/override/iwd/msumm7h.pro differ
diff --git a/gemrb/override/iwd/mswordh.pro b/gemrb/override/iwd/mswordh.pro
new file mode 100644
index 0000000..6cf138d
Binary files /dev/null and b/gemrb/override/iwd/mswordh.pro differ
diff --git a/gemrb/override/iwd/mtouchh.pro b/gemrb/override/iwd/mtouchh.pro
new file mode 100644
index 0000000..a9f7c06
Binary files /dev/null and b/gemrb/override/iwd/mtouchh.pro differ
diff --git a/gemrb/override/iwd/necroh.pro b/gemrb/override/iwd/necroh.pro
new file mode 100644
index 0000000..ad3c190
Binary files /dev/null and b/gemrb/override/iwd/necroh.pro differ
diff --git a/gemrb/override/iwd/necrot.pro b/gemrb/override/iwd/necrot.pro
new file mode 100644
index 0000000..975d707
Binary files /dev/null and b/gemrb/override/iwd/necrot.pro differ
diff --git a/gemrb/override/iwd/npoisoh.pro b/gemrb/override/iwd/npoisoh.pro
new file mode 100644
index 0000000..9c741f3
Binary files /dev/null and b/gemrb/override/iwd/npoisoh.pro differ
diff --git a/gemrb/override/iwd/ofsphe.pro b/gemrb/override/iwd/ofsphe.pro
new file mode 100644
index 0000000..e07fc9a
Binary files /dev/null and b/gemrb/override/iwd/ofsphe.pro differ
diff --git a/gemrb/override/iwd/overlay.2da b/gemrb/override/iwd/overlay.2da
new file mode 100644
index 0000000..0c05b4d
--- /dev/null
+++ b/gemrb/override/iwd/overlay.2da
@@ -0,0 +1,35 @@
+2DA V1.0
+*
+ VVC UNDER FLAGS
+SANCTUARY SANCTUC 0 1
+ENTANGLE ENTANGC 0 0
+WISP WISP 0 0
+SHIELDGLOBE SHIELDC 0 1
+GREASE GREASEC 1 0
+WEB WEBC 1 0
+MINORGLOBE MGOINVC 0 1
+GLOBE GOINVUC 0 1
+FLAMESHROUD SOFLAMC 0 0
+ANTIMAGIC AMSHELC 0 0
+RESILIENT ORSPHEC 0 0
+PROTFROMMISS PFNMISC 0 0
+CLOAKOFFEAR COFEARC 0 0
+ENTROPY ESHIELC 0 0
+FIREAURA FIAURAC 0 0
+FROSTAURA FRAURAC 0 0
+INSECT IPLAGUC 0 0
+STORMSHELL SSHELLC 0 0
+LATHANDER1 SOLATC1 0 0
+LATHANDER2 SOLATC2 1 0
+GLATHANDER1 GSOLAC1 0 0
+GLATHANDER2 GSOLAC2 1 0
+SEVENEYES1 SEYESC1 0 0
+SEVENEYES2 SEYESC2 1 0
+BOUNCE SPTURNI2 1 0
+BOUNCE2 SPTURNI 1 0
+FIRESHIELD1 FSHIRC1 0 0
+FIRESHIELD2 FSHIRC2 1 0
+ICESHIELD1 FSHIBC1 0 0
+ICESHIELD2 FSHIBC2 1 0
+TORTOISE TSHELLC 0 0
+DEATHARMOR DARMORC 0 0
diff --git a/gemrb/override/iwd/paralh.pro b/gemrb/override/iwd/paralh.pro
new file mode 100644
index 0000000..b8d8ed6
Binary files /dev/null and b/gemrb/override/iwd/paralh.pro differ
diff --git a/gemrb/override/iwd/pathfind.2da b/gemrb/override/iwd/pathfind.2da
new file mode 100644
index 0000000..d61fcd0
--- /dev/null
+++ b/gemrb/override/iwd/pathfind.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ 0 1 2 3 4 5 6 7 8 9 a b c d e f
+PASSABLE 8 1 1 1 1 1 1 1 0 1 8 1 0 8 3 1
+COST 10 4
diff --git a/gemrb/override/iwd/pdolls.2da b/gemrb/override/iwd/pdolls.2da
new file mode 100644
index 0000000..11593f9
--- /dev/null
+++ b/gemrb/override/iwd/pdolls.2da
@@ -0,0 +1,75 @@
+2DA V1.0
+*
+ LEVEL1 LEVEL2 LEVEL3 LEVEL4 SIZE
+0x5000 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x5001 CEMC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x5002 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x5003 CIMC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x5010 CHFC1INV CHMC2INV CHMC3INV CHMC4INV N
+0x5011 CEFC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x5012 CDFC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x5013 CIFC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x5100 CHMF1INV CHMF2INV CHMF3INV CHMC4INV L
+0x5101 CEMF1INV CEMF2INV CEMF3INV CEMC4INV M
+0x5102 CDMF1INV CDMF2INV CDMF3INV CDMC4INV S
+0x5103 CIMF1INV CIMF2INV CIMF3INV CIMC4INV S
+0x5110 CHFF1INV CHFF2INV CHMF3INV CHMC4INV N
+0x5111 CEFF1INV CEFF2INV CEMF3INV CEMC4INV M
+0x5112 CDFF1INV CDFF2INV CDMF3INV CDMC4INV S
+0x5113 CIFF1INV CIFF2INV CIMF3INV CIMC4INV S
+0x5200 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x5201 CEMW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x5202 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5203 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5210 CHFW1INV CHMW2INV CHMW3INV CHMW4INV N
+0x5211 CEFW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x5212 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5213 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x5300 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x5301 CEMT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x5302 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x5303 CIMT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x5310 CHFT1INV CHMT2INV CHMF3INV CHMF4INV N
+0x5311 CEFT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x5312 CDFT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x5313 CIFT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x6000 CHMC1INV CHMC2INV CHMC3INV CHMC4INV L
+0x6001 CEMC1INV CEMC2INV CEMC3INV CEMC4INV M
+0x6002 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x6003 CIMC1INV CIMC2INV CIMC3INV CIMC4INV S
+0x6004 CDMC1INV CDMC2INV CDMC3INV CDMC4INV S
+0x6010 CHFC1INV CHFC2INV CHFC3INV CHFC4INV M
+0x6011 CEFC1INV CEFC2INV CEFC3INV CEFC4INV M
+0x6012 CDFC1INV CDFC2INV CDFC3INV CDFC4INV S
+0x6013 CIFC1INV CIFC2INV CIFC3INV CIFC4INV S
+0x6014 CIFC1INV CIFC2INV CIFC3INV CIFC4INV S
+0x6100 CHMF1INV CHMF2INV CHMF3INV CHMF4INV L
+0x6101 CEMF1INV CEMF2INV CEMF3INV CEMF4INV M
+0x6102 CDMF1INV CDMF2INV CDMF3INV CDMF4INV S
+0x6103 CIMF1INV CIMF2INV CIMF3INV CIMF4INV S
+0x6104 CDMF1INV CDMF2INV CDMF3INV CDMF4INV S
+0x6110 CHFF1INV CHFF2INV CHFF3INV CHFF4INV N
+0x6111 CEFF1INV CEFF2INV CEFF3INV CEFF4INV M
+0x6112 CDFF1INV CDFF2INV CDFF3INV CDFF4INV S
+0x6113 CIFF1INV CIFF2INV CIFF3INV CIFF4INV S
+0x6114 CIFF1INV CIFF2INV CIFF3INV CIFF4INV S
+0x6200 CHMW1INV CHMW2INV CHMW3INV CHMW4INV L
+0x6201 CEMW1INV CEMW2INV CEMW3INV CEMW4INV M
+0x6202 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6203 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6204 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6210 CHFW1INV CHFW2INV CHFW3INV CHFW4INV N
+0x6211 CEFW1INV CEFW2INV CEFW3INV CHFW4INV M
+0x6212 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6213 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6214 CDMW1INV CDMW2INV CDMW3INV CDMW4INV S
+0x6300 CHMT1INV CHMT2INV CHMF3INV CHMF4INV L
+0x6301 CEMT1INV CEMT2INV CEMF3INV CEMF4INV M
+0x6302 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x6303 CIMT1INV CIMT2INV CIMF3INV CIMF4INV S
+0x6304 CDMT1INV CDMT2INV CDMF3INV CDMF4INV S
+0x6310 CHFT1INV CHFT2INV CHFF3INV CHFF4INV N
+0x6311 CEFT1INV CEFT2INV CEFF3INV CEFF4INV M
+0x6312 CDFT1INV CDFT2INV CDFF3INV CDFF4INV S
+0x6313 CIFT1INV CIFT2INV CIFF3INV CIFF4INV S
+0x6314 CIFT1INV CIFT2INV CIFF3INV CIFF4INV S
diff --git a/gemrb/override/iwd/pfire.pro b/gemrb/override/iwd/pfire.pro
new file mode 100644
index 0000000..8dd8653
Binary files /dev/null and b/gemrb/override/iwd/pfire.pro differ
diff --git a/gemrb/override/iwd/pictures.2da b/gemrb/override/iwd/pictures.2da
new file mode 100644
index 0000000..cf07712
--- /dev/null
+++ b/gemrb/override/iwd/pictures.2da
@@ -0,0 +1,36 @@
+2DA V1.0
+-1
+ GENDER
+DFF_ 2
+DMC_ 1
+DMF_ 1
+DMT_ 1
+EFC_ 2
+EFW_ 2
+EMF_ 1
+EMF2_ 1
+EMT_ 1
+EMW_ 1
+GFW_ 2
+GMT_ 1
+GMW_ 1
+HFF_ 2
+HFT_ 2
+HFW_ 2
+HMB_ 1
+HMC_ 1
+HMF2_ 1
+HMF_ 1
+HMW_ 1
+HaFF_ 2
+HaFT_ 2
+HaMF_ 1
+HaMT_ 1
+HeFB_ 2
+HeFC_ 2
+HeFF_ 2
+HeFW_ 2
+HeMF_ 1
+HeMT_ 1
+HeMW_ 1
+HFW2_ 2
diff --git a/gemrb/override/iwd/poisonh.pro b/gemrb/override/iwd/poisonh.pro
new file mode 100644
index 0000000..bd97ddc
Binary files /dev/null and b/gemrb/override/iwd/poisonh.pro differ
diff --git a/gemrb/override/iwd/pomab.2da b/gemrb/override/iwd/pomab.2da
new file mode 100644
index 0000000..da66836
--- /dev/null
+++ b/gemrb/override/iwd/pomab.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+*
+ X Y
+RESREF eepomab pomimg
+1 0x1e8 0x20a
+2 0x1c6 0x1f6
+3 0x1d0 0x20a
+4 0x1b5 0x204
+5 0x1c2 0x216
+6 0x1dd 0x217
diff --git a/gemrb/override/iwd/portal.pro b/gemrb/override/iwd/portal.pro
new file mode 100644
index 0000000..3bf4dd3
Binary files /dev/null and b/gemrb/override/iwd/portal.pro differ
diff --git a/gemrb/override/iwd/prayer.pro b/gemrb/override/iwd/prayer.pro
new file mode 100644
index 0000000..454ca43
Binary files /dev/null and b/gemrb/override/iwd/prayer.pro differ
diff --git a/gemrb/override/iwd/prayerh.pro b/gemrb/override/iwd/prayerh.pro
new file mode 100644
index 0000000..3e6c533
Binary files /dev/null and b/gemrb/override/iwd/prayerh.pro differ
diff --git a/gemrb/override/iwd/protevil.pro b/gemrb/override/iwd/protevil.pro
new file mode 100644
index 0000000..44972ee
Binary files /dev/null and b/gemrb/override/iwd/protevil.pro differ
diff --git a/gemrb/override/iwd/prtl_cl.pro b/gemrb/override/iwd/prtl_cl.pro
new file mode 100644
index 0000000..6a5bfaa
Binary files /dev/null and b/gemrb/override/iwd/prtl_cl.pro differ
diff --git a/gemrb/override/iwd/prtl_cl.spl b/gemrb/override/iwd/prtl_cl.spl
new file mode 100644
index 0000000..326f930
Binary files /dev/null and b/gemrb/override/iwd/prtl_cl.spl differ
diff --git a/gemrb/override/iwd/prtl_op.pro b/gemrb/override/iwd/prtl_op.pro
new file mode 100644
index 0000000..6f689f1
Binary files /dev/null and b/gemrb/override/iwd/prtl_op.pro differ
diff --git a/gemrb/override/iwd/prtl_op.spl b/gemrb/override/iwd/prtl_op.spl
new file mode 100644
index 0000000..326f930
Binary files /dev/null and b/gemrb/override/iwd/prtl_op.spl differ
diff --git a/gemrb/override/iwd/pspray.pro b/gemrb/override/iwd/pspray.pro
new file mode 100644
index 0000000..66a5326
Binary files /dev/null and b/gemrb/override/iwd/pspray.pro differ
diff --git a/gemrb/override/iwd/pwkill.pro b/gemrb/override/iwd/pwkill.pro
new file mode 100644
index 0000000..8e60ca0
Binary files /dev/null and b/gemrb/override/iwd/pwkill.pro differ
diff --git a/gemrb/override/iwd/pwsileh.pro b/gemrb/override/iwd/pwsileh.pro
new file mode 100644
index 0000000..46b144d
Binary files /dev/null and b/gemrb/override/iwd/pwsileh.pro differ
diff --git a/gemrb/override/iwd/pwstun.pro b/gemrb/override/iwd/pwstun.pro
new file mode 100644
index 0000000..a4ea2be
Binary files /dev/null and b/gemrb/override/iwd/pwstun.pro differ
diff --git a/gemrb/override/iwd/pwstunh.pro b/gemrb/override/iwd/pwstunh.pro
new file mode 100644
index 0000000..5afd33d
Binary files /dev/null and b/gemrb/override/iwd/pwstunh.pro differ
diff --git a/gemrb/override/iwd/qslots.2da b/gemrb/override/iwd/qslots.2da
new file mode 100644
index 0000000..c9fc3d2
--- /dev/null
+++ b/gemrb/override/iwd/qslots.2da
@@ -0,0 +1,24 @@
+2DA V1.0
+100
+ SLOT1 SLOT2 SLOT3 SLOT4 SLOT5 SLOT6 SLOT7 SLOT8 SLOT9
+DEFAULT 18 19 14 100 8 9 11 12 13
+MAGE 3 4 5 2 8 9 11 12 13
+FIGHTER 18 19 14 100 8 9 11 12 13
+CLERIC 6 3 4 2 8 9 11 12 13
+THIEF 22 0 1 100 8 9 11 12 13
+BARD 20 1 3 2 8 9 11 12 13
+PALADIN 18 14 6 2 8 9 11 12 13
+FIGHTER_MAGE 3 4 5 2 8 9 11 12 13
+FIGHTER_CLERIC 6 3 4 2 8 9 11 12 13
+FIGHTER_THIEF 18 22 0 1 8 9 11 12 13
+FIGHTER_MAGE_THIEF 22 0 1 2 8 9 11 12 13
+DRUID 3 4 5 2 8 9 11 12 13
+RANGER 18 14 0 2 8 9 11 12 13
+MAGE_THIEF 22 0 1 2 8 9 11 12 13
+CLERIC_MAGE 6 3 4 2 8 9 11 12 13
+CLERIC_THIEF 22 0 1 2 8 9 11 12 13
+FIGHTER_DRUID 3 4 5 2 8 9 11 12 13
+FIGHTER_MAGE_CLERIC 6 3 4 2 8 9 11 12 13
+CLERIC_RANGER 6 3 4 2 8 9 11 12 13
+SORCERER 3 4 5 2 8 9 11 12 13
+MONK 18 14 22 0 8 9 11 12 13
diff --git a/gemrb/override/iwd/races.2da b/gemrb/override/iwd/races.2da
new file mode 100644
index 0000000..b912e12
--- /dev/null
+++ b/gemrb/override/iwd/races.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF ID USABILITY SAVE
+HUMAN 7193 9550 1096 1 0x08000000 *
+ELF 7194 9552 1097 2 0x00800000 *
+HALF_ELF 7197 9555 1098 3 0x02000000 *
+GNOME 7196 9553 1099 6 0x10000000 SAVECNG
+HALFLING 7195 9554 1101 5 0x04000000 SAVECNDH
+DWARF 7182 9551 1100 4 0x01000000 SAVECNDH
diff --git a/gemrb/override/iwd/rad100.pro b/gemrb/override/iwd/rad100.pro
new file mode 100644
index 0000000..1a8e9a0
Binary files /dev/null and b/gemrb/override/iwd/rad100.pro differ
diff --git a/gemrb/override/iwd/rad250.pro b/gemrb/override/iwd/rad250.pro
new file mode 100644
index 0000000..31ee26e
Binary files /dev/null and b/gemrb/override/iwd/rad250.pro differ
diff --git a/gemrb/override/iwd/randitem.2da b/gemrb/override/iwd/randitem.2da
new file mode 100644
index 0000000..f612585
--- /dev/null
+++ b/gemrb/override/iwd/randitem.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ RESREF FURY_MODE
+GOLD MISC07
+RND RNDTRES RNDTRES
diff --git a/gemrb/override/iwd/rdeadh.pro b/gemrb/override/iwd/rdeadh.pro
new file mode 100644
index 0000000..8a79354
Binary files /dev/null and b/gemrb/override/iwd/rdeadh.pro differ
diff --git a/gemrb/override/iwd/recitah.pro b/gemrb/override/iwd/recitah.pro
new file mode 100644
index 0000000..49c7aeb
Binary files /dev/null and b/gemrb/override/iwd/recitah.pro differ
diff --git a/gemrb/override/iwd/resurrh.pro b/gemrb/override/iwd/resurrh.pro
new file mode 100644
index 0000000..0ae7bb9
Binary files /dev/null and b/gemrb/override/iwd/resurrh.pro differ
diff --git a/gemrb/override/iwd/rng450.pro b/gemrb/override/iwd/rng450.pro
new file mode 100644
index 0000000..e991873
Binary files /dev/null and b/gemrb/override/iwd/rng450.pro differ
diff --git a/gemrb/override/iwd/rparalh.pro b/gemrb/override/iwd/rparalh.pro
new file mode 100644
index 0000000..a163b89
Binary files /dev/null and b/gemrb/override/iwd/rparalh.pro differ
diff --git a/gemrb/override/iwd/rwotfag.pro b/gemrb/override/iwd/rwotfag.pro
new file mode 100644
index 0000000..e8dd52b
Binary files /dev/null and b/gemrb/override/iwd/rwotfag.pro differ
diff --git a/gemrb/override/iwd/rwotfah.pro b/gemrb/override/iwd/rwotfah.pro
new file mode 100644
index 0000000..1a2a98e
Binary files /dev/null and b/gemrb/override/iwd/rwotfah.pro differ
diff --git a/gemrb/override/iwd/sarmorh.pro b/gemrb/override/iwd/sarmorh.pro
new file mode 100644
index 0000000..64fb522
Binary files /dev/null and b/gemrb/override/iwd/sarmorh.pro differ
diff --git a/gemrb/override/iwd/savegame.2da b/gemrb/override/iwd/savegame.2da
new file mode 100644
index 0000000..2b24be1
--- /dev/null
+++ b/gemrb/override/iwd/savegame.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+Auto-Save
+ SLOTNAME QSAVE
+0 Auto-Save 0
+1 Quick-Save 1
+2 Final-Save 0
diff --git a/gemrb/override/iwd/scharge.pro b/gemrb/override/iwd/scharge.pro
new file mode 100644
index 0000000..83d1b66
Binary files /dev/null and b/gemrb/override/iwd/scharge.pro differ
diff --git a/gemrb/override/iwd/scharge.spl b/gemrb/override/iwd/scharge.spl
new file mode 100644
index 0000000..ff98fea
Binary files /dev/null and b/gemrb/override/iwd/scharge.spl differ
diff --git a/gemrb/override/iwd/schargh.pro b/gemrb/override/iwd/schargh.pro
new file mode 100644
index 0000000..c7e08d0
Binary files /dev/null and b/gemrb/override/iwd/schargh.pro differ
diff --git a/gemrb/override/iwd/script.2da b/gemrb/override/iwd/script.2da
new file mode 100644
index 0000000..4654377
--- /dev/null
+++ b/gemrb/override/iwd/script.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+0
+ DATA 1 2 3 4 5 6 7
+OBJECT_IDS_COUNT 7 ea general race class specific gender align
+MAX_OBJECT_NESTING 5
+ADDITIONAL_RECT 1
+EXTRA_PARAMETERS_COUNT 0
+TRIGGER_POINT 0
\ No newline at end of file
diff --git a/gemrb/override/iwd/seater.pro b/gemrb/override/iwd/seater.pro
new file mode 100644
index 0000000..5f6d62c
Binary files /dev/null and b/gemrb/override/iwd/seater.pro differ
diff --git a/gemrb/override/iwd/seaterh.pro b/gemrb/override/iwd/seaterh.pro
new file mode 100644
index 0000000..488d807
Binary files /dev/null and b/gemrb/override/iwd/seaterh.pro differ
diff --git a/gemrb/override/iwd/sgrowth.pro b/gemrb/override/iwd/sgrowth.pro
new file mode 100644
index 0000000..06a72f5
Binary files /dev/null and b/gemrb/override/iwd/sgrowth.pro differ
diff --git a/gemrb/override/iwd/shout.pro b/gemrb/override/iwd/shout.pro
new file mode 100644
index 0000000..9e1a6a2
Binary files /dev/null and b/gemrb/override/iwd/shout.pro differ
diff --git a/gemrb/override/iwd/shroud.pro b/gemrb/override/iwd/shroud.pro
new file mode 100644
index 0000000..e31a942
Binary files /dev/null and b/gemrb/override/iwd/shroud.pro differ
diff --git a/gemrb/override/iwd/shtable.2da b/gemrb/override/iwd/shtable.2da
new file mode 100644
index 0000000..ac06d6f
--- /dev/null
+++ b/gemrb/override/iwd/shtable.2da
@@ -0,0 +1,43 @@
+2DA V1.0
+*
+ RESREF
+0 SHAIR
+1 SHEARTH
+2 SHWATER
+3 *
+4 SHAIR
+5 SHEARTH
+6 SHWATER
+7 *
+8 SHAIR
+9 SHEARTH
+10 SHWATER
+11 *
+12 SHAIR
+13 SHEARTH
+14 SHWATER
+15 *
+16 SHAIR
+17 SHEARTH
+18 SHWATER
+19 *
+20 SHAIR
+21 SHEARTH
+22 SHWATER
+23 *
+24 SHAIR
+25 SHEARTH
+26 SHWATER
+27 *
+28 SHAIR
+29 SHEARTH
+30 SHWATER
+31 *
+32
+33
+34
+35 FLMSTRK
+36 HLYMITE
+37
+38 DDOORH
+39 SPFDEATH
diff --git a/gemrb/override/iwd/skills.2da b/gemrb/override/iwd/skills.2da
new file mode 100644
index 0000000..afdf917
--- /dev/null
+++ b/gemrb/override/iwd/skills.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF ID THIEF MAGE_THIEF FIGHTER_THIEF CLERIC_THIEF FIGHTER_MAGE_THIEF
+FIRST_LEVEL * * * * 40 40 40 40 40
+RATE * * * * 25 25 25 25 25
+STEALTH 11984 9600 9461 27 1 1 1 1 1
+FIND_TRAPS 11985 9599 9462 28 1 1 1 1 1
+PICK_POCKETS 11986 9597 9463 29 1 1 1 1 1
+OPEN_LOCKS 11987 9598 9460 26 1 1 1 1 1
diff --git a/gemrb/override/iwd/sleep.pro b/gemrb/override/iwd/sleep.pro
new file mode 100644
index 0000000..06568c9
Binary files /dev/null and b/gemrb/override/iwd/sleep.pro differ
diff --git a/gemrb/override/iwd/slivinh.pro b/gemrb/override/iwd/slivinh.pro
new file mode 100644
index 0000000..1e74c1a
Binary files /dev/null and b/gemrb/override/iwd/slivinh.pro differ
diff --git a/gemrb/override/iwd/slottype.2da b/gemrb/override/iwd/slottype.2da
new file mode 100644
index 0000000..8a14119
--- /dev/null
+++ b/gemrb/override/iwd/slottype.2da
@@ -0,0 +1,42 @@
+2DA V1.0
+*
+ BITS SCRIPT ICON STRREF EFFECT FLAGS
+10 0 0 * 0 2 0
+6 1 13 STONHELM 11999 7 0
+1 2 11 STONARM 11997 1 0
+9 4 26 STONSHIL 12006 6 0
+5 8 12 STONGLET 11998 1 0
+7 16 22 STONRING 12002 1 1
+8 16 23 STONRING 12003 1 1
+0 32 14 STONAMUL 12000 1 0
+2 64 21 STONBELT 12001 1 0
+3 128 25 STONBOOT 12005 1 0
+35 256 1 STONWEAP 12010 4 1
+36 256 2 STONWEAP 12010 4 1
+37 256 3 STONWEAP 12010 4 1
+38 256 4 STONWEAP 12010 4 1
+11 512 15 STONQUIV 12009 5 1
+12 512 16 STONQUIV 12009 5 1
+13 512 17 STONQUIV 12009 5 1
+14 512 0 STONQUIV 12009 5 1
+4 1024 24 STONCLOK 12004 1 0
+15 2048 5 STONITEM 12012 0 1
+16 2048 6 STONITEM 12012 0 1
+17 2048 7 STONITEM 12012 0 1
+18 -1 30 * 12013 0 1
+19 -1 31 * 12013 0 1
+20 -1 32 * 12013 0 1
+21 -1 33 * 12013 0 1
+22 -1 34 * 12013 0 1
+23 -1 35 * 12013 0 1
+24 -1 36 * 12013 0 1
+25 -1 37 * 12013 0 1
+26 -1 38 * 12013 0 1
+27 -1 39 * 12013 0 1
+28 -1 40 * 12013 0 1
+29 -1 41 * 12013 0 1
+30 -1 42 * 12013 0 1
+31 -1 43 * 12013 0 1
+32 -1 44 * 12013 0 1
+33 -1 45 * 12013 0 1
+34 0 0 * 0 3 0
diff --git a/gemrb/override/iwd/soflamh.pro b/gemrb/override/iwd/soflamh.pro
new file mode 100644
index 0000000..334e211
Binary files /dev/null and b/gemrb/override/iwd/soflamh.pro differ
diff --git a/gemrb/override/iwd/sohope.pro b/gemrb/override/iwd/sohope.pro
new file mode 100644
index 0000000..f70ba7d
Binary files /dev/null and b/gemrb/override/iwd/sohope.pro differ
diff --git a/gemrb/override/iwd/sooneh.pro b/gemrb/override/iwd/sooneh.pro
new file mode 100644
index 0000000..3672511
Binary files /dev/null and b/gemrb/override/iwd/sooneh.pro differ
diff --git a/gemrb/override/iwd/sopain.pro b/gemrb/override/iwd/sopain.pro
new file mode 100644
index 0000000..0cd795a
Binary files /dev/null and b/gemrb/override/iwd/sopain.pro differ
diff --git a/gemrb/override/iwd/sparbknp.pro b/gemrb/override/iwd/sparbknp.pro
new file mode 100644
index 0000000..aba7673
Binary files /dev/null and b/gemrb/override/iwd/sparbknp.pro differ
diff --git a/gemrb/override/iwd/sparbkpa.pro b/gemrb/override/iwd/sparbkpa.pro
new file mode 100644
index 0000000..7a51eed
Binary files /dev/null and b/gemrb/override/iwd/sparbkpa.pro differ
diff --git a/gemrb/override/iwd/sparblnp.pro b/gemrb/override/iwd/sparblnp.pro
new file mode 100644
index 0000000..f858276
Binary files /dev/null and b/gemrb/override/iwd/sparblnp.pro differ
diff --git a/gemrb/override/iwd/sparblpa.pro b/gemrb/override/iwd/sparblpa.pro
new file mode 100644
index 0000000..4124a9d
Binary files /dev/null and b/gemrb/override/iwd/sparblpa.pro differ
diff --git a/gemrb/override/iwd/sparchnp.pro b/gemrb/override/iwd/sparchnp.pro
new file mode 100644
index 0000000..7dd540e
Binary files /dev/null and b/gemrb/override/iwd/sparchnp.pro differ
diff --git a/gemrb/override/iwd/sparchpa.pro b/gemrb/override/iwd/sparchpa.pro
new file mode 100644
index 0000000..101f9a2
Binary files /dev/null and b/gemrb/override/iwd/sparchpa.pro differ
diff --git a/gemrb/override/iwd/spargonp.pro b/gemrb/override/iwd/spargonp.pro
new file mode 100644
index 0000000..234aca6
Binary files /dev/null and b/gemrb/override/iwd/spargonp.pro differ
diff --git a/gemrb/override/iwd/spargopa.pro b/gemrb/override/iwd/spargopa.pro
new file mode 100644
index 0000000..2665825
Binary files /dev/null and b/gemrb/override/iwd/spargopa.pro differ
diff --git a/gemrb/override/iwd/spargrnp.pro b/gemrb/override/iwd/spargrnp.pro
new file mode 100644
index 0000000..a1d4dbb
Binary files /dev/null and b/gemrb/override/iwd/spargrnp.pro differ
diff --git a/gemrb/override/iwd/spargrpa.pro b/gemrb/override/iwd/spargrpa.pro
new file mode 100644
index 0000000..55a741b
Binary files /dev/null and b/gemrb/override/iwd/spargrpa.pro differ
diff --git a/gemrb/override/iwd/sparicnp.pro b/gemrb/override/iwd/sparicnp.pro
new file mode 100644
index 0000000..77ff12b
Binary files /dev/null and b/gemrb/override/iwd/sparicnp.pro differ
diff --git a/gemrb/override/iwd/sparicpa.pro b/gemrb/override/iwd/sparicpa.pro
new file mode 100644
index 0000000..2d8b9d7
Binary files /dev/null and b/gemrb/override/iwd/sparicpa.pro differ
diff --git a/gemrb/override/iwd/sparklbk.pro b/gemrb/override/iwd/sparklbk.pro
new file mode 100644
index 0000000..147bd43
Binary files /dev/null and b/gemrb/override/iwd/sparklbk.pro differ
diff --git a/gemrb/override/iwd/sparklbl.pro b/gemrb/override/iwd/sparklbl.pro
new file mode 100644
index 0000000..4928b0c
Binary files /dev/null and b/gemrb/override/iwd/sparklbl.pro differ
diff --git a/gemrb/override/iwd/sparklch.pro b/gemrb/override/iwd/sparklch.pro
new file mode 100644
index 0000000..c6ad4c8
Binary files /dev/null and b/gemrb/override/iwd/sparklch.pro differ
diff --git a/gemrb/override/iwd/sparklgo.pro b/gemrb/override/iwd/sparklgo.pro
new file mode 100644
index 0000000..748d91d
Binary files /dev/null and b/gemrb/override/iwd/sparklgo.pro differ
diff --git a/gemrb/override/iwd/sparklgr.pro b/gemrb/override/iwd/sparklgr.pro
new file mode 100644
index 0000000..ac152c1
Binary files /dev/null and b/gemrb/override/iwd/sparklgr.pro differ
diff --git a/gemrb/override/iwd/sparklic.pro b/gemrb/override/iwd/sparklic.pro
new file mode 100644
index 0000000..e833132
Binary files /dev/null and b/gemrb/override/iwd/sparklic.pro differ
diff --git a/gemrb/override/iwd/sparklma.pro b/gemrb/override/iwd/sparklma.pro
new file mode 100644
index 0000000..50d7be0
Binary files /dev/null and b/gemrb/override/iwd/sparklma.pro differ
diff --git a/gemrb/override/iwd/sparklor.pro b/gemrb/override/iwd/sparklor.pro
new file mode 100644
index 0000000..dc02305
Binary files /dev/null and b/gemrb/override/iwd/sparklor.pro differ
diff --git a/gemrb/override/iwd/sparklpu.pro b/gemrb/override/iwd/sparklpu.pro
new file mode 100644
index 0000000..ef0f1fd
Binary files /dev/null and b/gemrb/override/iwd/sparklpu.pro differ
diff --git a/gemrb/override/iwd/sparklre.pro b/gemrb/override/iwd/sparklre.pro
new file mode 100644
index 0000000..1a15bfd
Binary files /dev/null and b/gemrb/override/iwd/sparklre.pro differ
diff --git a/gemrb/override/iwd/sparklst.pro b/gemrb/override/iwd/sparklst.pro
new file mode 100644
index 0000000..543ea91
Binary files /dev/null and b/gemrb/override/iwd/sparklst.pro differ
diff --git a/gemrb/override/iwd/sparmanp.pro b/gemrb/override/iwd/sparmanp.pro
new file mode 100644
index 0000000..c76d137
Binary files /dev/null and b/gemrb/override/iwd/sparmanp.pro differ
diff --git a/gemrb/override/iwd/sparmapa.pro b/gemrb/override/iwd/sparmapa.pro
new file mode 100644
index 0000000..6a21bd1
Binary files /dev/null and b/gemrb/override/iwd/sparmapa.pro differ
diff --git a/gemrb/override/iwd/sparornp.pro b/gemrb/override/iwd/sparornp.pro
new file mode 100644
index 0000000..abf33b8
Binary files /dev/null and b/gemrb/override/iwd/sparornp.pro differ
diff --git a/gemrb/override/iwd/sparorpa.pro b/gemrb/override/iwd/sparorpa.pro
new file mode 100644
index 0000000..686dd90
Binary files /dev/null and b/gemrb/override/iwd/sparorpa.pro differ
diff --git a/gemrb/override/iwd/sparpunp.pro b/gemrb/override/iwd/sparpunp.pro
new file mode 100644
index 0000000..23f98f2
Binary files /dev/null and b/gemrb/override/iwd/sparpunp.pro differ
diff --git a/gemrb/override/iwd/sparpupa.pro b/gemrb/override/iwd/sparpupa.pro
new file mode 100644
index 0000000..322a995
Binary files /dev/null and b/gemrb/override/iwd/sparpupa.pro differ
diff --git a/gemrb/override/iwd/sparrenp.pro b/gemrb/override/iwd/sparrenp.pro
new file mode 100644
index 0000000..7718874
Binary files /dev/null and b/gemrb/override/iwd/sparrenp.pro differ
diff --git a/gemrb/override/iwd/sparrepa.pro b/gemrb/override/iwd/sparrepa.pro
new file mode 100644
index 0000000..a5edc18
Binary files /dev/null and b/gemrb/override/iwd/sparrepa.pro differ
diff --git a/gemrb/override/iwd/sparstnp.pro b/gemrb/override/iwd/sparstnp.pro
new file mode 100644
index 0000000..7d4b4c3
Binary files /dev/null and b/gemrb/override/iwd/sparstnp.pro differ
diff --git a/gemrb/override/iwd/sparstpa.pro b/gemrb/override/iwd/sparstpa.pro
new file mode 100644
index 0000000..4d55459
Binary files /dev/null and b/gemrb/override/iwd/sparstpa.pro differ
diff --git a/gemrb/override/iwd/spear.pro b/gemrb/override/iwd/spear.pro
new file mode 100644
index 0000000..bc9288c
Binary files /dev/null and b/gemrb/override/iwd/spear.pro differ
diff --git a/gemrb/override/iwd/spearex.pro b/gemrb/override/iwd/spearex.pro
new file mode 100644
index 0000000..4bb5786
Binary files /dev/null and b/gemrb/override/iwd/spearex.pro differ
diff --git a/gemrb/override/iwd/spfirebl.pro b/gemrb/override/iwd/spfirebl.pro
new file mode 100644
index 0000000..3b44f50
Binary files /dev/null and b/gemrb/override/iwd/spfirebl.pro differ
diff --git a/gemrb/override/iwd/spklarbk.pro b/gemrb/override/iwd/spklarbk.pro
new file mode 100644
index 0000000..f8f12c0
Binary files /dev/null and b/gemrb/override/iwd/spklarbk.pro differ
diff --git a/gemrb/override/iwd/spklarbl.pro b/gemrb/override/iwd/spklarbl.pro
new file mode 100644
index 0000000..eddae9d
Binary files /dev/null and b/gemrb/override/iwd/spklarbl.pro differ
diff --git a/gemrb/override/iwd/spklarch.pro b/gemrb/override/iwd/spklarch.pro
new file mode 100644
index 0000000..82766f4
Binary files /dev/null and b/gemrb/override/iwd/spklarch.pro differ
diff --git a/gemrb/override/iwd/spklargo.pro b/gemrb/override/iwd/spklargo.pro
new file mode 100644
index 0000000..e4323ac
Binary files /dev/null and b/gemrb/override/iwd/spklargo.pro differ
diff --git a/gemrb/override/iwd/spklargr.pro b/gemrb/override/iwd/spklargr.pro
new file mode 100644
index 0000000..38fc137
Binary files /dev/null and b/gemrb/override/iwd/spklargr.pro differ
diff --git a/gemrb/override/iwd/spklaric.pro b/gemrb/override/iwd/spklaric.pro
new file mode 100644
index 0000000..4fd7d7e
Binary files /dev/null and b/gemrb/override/iwd/spklaric.pro differ
diff --git a/gemrb/override/iwd/spklarma.pro b/gemrb/override/iwd/spklarma.pro
new file mode 100644
index 0000000..d8f18c2
Binary files /dev/null and b/gemrb/override/iwd/spklarma.pro differ
diff --git a/gemrb/override/iwd/spklaror.pro b/gemrb/override/iwd/spklaror.pro
new file mode 100644
index 0000000..7e395f4
Binary files /dev/null and b/gemrb/override/iwd/spklaror.pro differ
diff --git a/gemrb/override/iwd/spklarpu.pro b/gemrb/override/iwd/spklarpu.pro
new file mode 100644
index 0000000..e70bb75
Binary files /dev/null and b/gemrb/override/iwd/spklarpu.pro differ
diff --git a/gemrb/override/iwd/spklarre.pro b/gemrb/override/iwd/spklarre.pro
new file mode 100644
index 0000000..29d636c
Binary files /dev/null and b/gemrb/override/iwd/spklarre.pro differ
diff --git a/gemrb/override/iwd/spklarst.pro b/gemrb/override/iwd/spklarst.pro
new file mode 100644
index 0000000..ba5633b
Binary files /dev/null and b/gemrb/override/iwd/spklarst.pro differ
diff --git a/gemrb/override/iwd/splprot.2da b/gemrb/override/iwd/splprot.2da
new file mode 100644
index 0000000..b3603c1
--- /dev/null
+++ b/gemrb/override/iwd/splprot.2da
@@ -0,0 +1,105 @@
+2DA V1.0
+0
+ STAT VALUE RELATION COMMENT
+0 EA 0 4 anything
+1 GENERAL 4 1 general/undead
+2 GENERAL 4 5 general/not_undead
+3 RESISTFIRE 100 4 fire_resistance>=100
+4 RESISTFIRE 100 2 fire_resistance<100
+5 GENERAL 1 1 general/humanoid
+6 GENERAL 1 5 general/not_humanoid
+7 GENERAL 2 1 general/animal
+8 GENERAL 2 5 general/not_animal
+9 RACE 152 1 race/elemental
+10 RACE 152 5 race/not_elemental
+11 RACE 159 1 race/fungus
+12 RACE 159 5 race/not_fungus
+13 0x102 3 0 huge_creature
+14 0x102 3 3 not_huge_creature
+15 RACE 2 1 race/elf
+16 RACE 2 5 race/not_elf
+17 RACE 166 1 race/umberhulk
+18 RACE 166 5 race/not_umberhulk
+19 RACE 3 1 race/halfelf
+20 RACE 3 5 race/not_halfelf
+21 0x103 5 7 humanoid_or_animal
+22 0x104 5 7 not_humanoid_or_animal
+23 STATE 0x40000 9 state/blind
+24 STATE 0x40000 8 state/not_blind
+25 RESISTCOLD 100 4 cold_resistance>=100
+26 RESISTCOLD 100 2 cold_resistance<100
+27 RACE 156 1 race/golem
+28 RACE 156 5 race/not_golem
+29 RACE 179 1 race/minotaur
+30 RACE 179 5 race/not_minotaur
+31 0x103 1 11 undead_or_fungus
+32 0x104 1 11 not_undead_or_fungus
+33 ALIGNMENT 2 9 alignment/good
+34 ALIGNMENT 2 8 alignment/not_good
+35 ALIGNMENT 1 9 alignment/neutral
+36 ALIGNMENT 1 8 alignment/not_neutral
+37 ALIGNMENT 3 7 alignment/evil
+38 ALIGNMENT 3 11 alignment/not_evil
+39 CLASS 7 1 class/paladin
+40 CLASS 7 5 class/not_paladin
+41 0x105 217 1 moral_alignment_same
+42 0x105 217 5 moral_alignment_not_same
+43 0x100 * * source
+44 0x101 * * not_source
+45 ? * * water_using-FIXME
+46 ? * * not_water_using-FIXME
+47 0x104 32 27 breathes/not_undead_fungus_golem
+48 0x103 32 27 not_breathes/undead_fungus_or_golem
+49 EA 30 0 allies
+50 EA 30 3 not_allies
+51 EA 200 4 enemies
+52 EA 200 2 not_enemies
+53 0x103 3 25 race/fire_or_cold_using
+54 0x104 3 25 race/not_fire_or_cold_using
+55 ? * * unnatural
+56 ? * * not_unnatural
+57 SEX 1 1 gender/male
+58 SEX 1 5 gender/not_male
+59 ALIGNMENT 0x10 1 alignment/lawful
+60 ALIGNMENT 0x10 5 alignment/not_lawful
+61 ALIGNMENT 0x30 1 alignment/chaotic
+62 ALIGNMENT 0x30 5 alignment/not_chaotic
+63 0x109 * * evasion_check
+64 RACE 160 1 race/orc
+65 RACE 160 5 race/not_orc
+66 EXTSTATE ? 9 extstate/deaf
+67 EXTSTATE ? 8 extstate/not_deaf
+68 SEX 6 1 gender/summoned
+69 SEX 6 5 gender/not_summoned
+70 RACE 184 1 race/mindflayer
+71 RACE 184 5 race/not_mindflayer
+72 STATE 0x1000 9 state/silenced
+73 STATE 0x1000 8 state/not_silenced
+74 INT -1 4 int/less
+75 INT -1 0 int/greater
+76 INT -1 3 int/less_or_equal
+77 INT -1 2 int/greater_or_equal
+78 CLASS 2 1 class/bard
+79 CLASS 2 5 class/not_bard
+80 0x108 1 * near_enemies
+81 0x108 0 * not_near_enemies
+82 SUBRACE 0x20001 1 subrace/drow
+83 SUBRACE 0x20001 5 subrace/not_drow
+84 SUBRACE 0x40002 1 subrace/gray_dwarf
+85 SUBRACE 0x40002 5 subrace/not_gray_dwarf
+86 0x107 1 * daytime
+87 0x107 0 * not_daytime
+88 0x106 1 9 outdoor
+89 0x106 1 8 not_outdoor
+90 RACE 190 1 race/keg
+91 RACE 190 5 race/not_keg
+92 RACE * * outsider
+93 RACE * * not_outsider
+94 RACE 215 1 race/plate
+95 RACE 215 5 race/not_plate
+96 0x103 90 94 race/keg_or_plate
+97 0x104 90 94 race/not_keg_or_plate
+98 RACE 161 1 race/salamander
+99 RACE 161 5 race/not_salamander
+100 RACE 164 1 race/tanari
+101 RACE 164 5 race/not_tanari
diff --git a/gemrb/override/iwd/splspec.2da b/gemrb/override/iwd/splspec.2da
new file mode 100644
index 0000000..de337f7
--- /dev/null
+++ b/gemrb/override/iwd/splspec.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ IDENTIFY
+SPWI110 1
diff --git a/gemrb/override/iwd/spoisoh.pro b/gemrb/override/iwd/spoisoh.pro
new file mode 100644
index 0000000..6aa9302
Binary files /dev/null and b/gemrb/override/iwd/spoisoh.pro differ
diff --git a/gemrb/override/iwd/spscorch.pro b/gemrb/override/iwd/spscorch.pro
new file mode 100644
index 0000000..3ad6c32
Binary files /dev/null and b/gemrb/override/iwd/spscorch.pro differ
diff --git a/gemrb/override/iwd/spscoric.pro b/gemrb/override/iwd/spscoric.pro
new file mode 100644
index 0000000..3d7e0b7
Binary files /dev/null and b/gemrb/override/iwd/spscoric.pro differ
diff --git a/gemrb/override/iwd/spwrath.pro b/gemrb/override/iwd/spwrath.pro
new file mode 100644
index 0000000..d66271c
Binary files /dev/null and b/gemrb/override/iwd/spwrath.pro differ
diff --git a/gemrb/override/iwd/ssorbh.pro b/gemrb/override/iwd/ssorbh.pro
new file mode 100644
index 0000000..2d9489e
Binary files /dev/null and b/gemrb/override/iwd/ssorbh.pro differ
diff --git a/gemrb/override/iwd/ssswarm.pro b/gemrb/override/iwd/ssswarm.pro
new file mode 100644
index 0000000..efd382f
Binary files /dev/null and b/gemrb/override/iwd/ssswarm.pro differ
diff --git a/gemrb/override/iwd/sstone.pro b/gemrb/override/iwd/sstone.pro
new file mode 100644
index 0000000..f1a87f5
Binary files /dev/null and b/gemrb/override/iwd/sstone.pro differ
diff --git a/gemrb/override/iwd/sstoneh.pro b/gemrb/override/iwd/sstoneh.pro
new file mode 100644
index 0000000..b3af8da
Binary files /dev/null and b/gemrb/override/iwd/sstoneh.pro differ
diff --git a/gemrb/override/iwd/start.2da b/gemrb/override/iwd/start.2da
new file mode 100644
index 0000000..17d460b
--- /dev/null
+++ b/gemrb/override/iwd/start.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ XPOS YPOS AREA ROT
+NORMAL START_XPOS START_YPOS START_AREA *
+EXPANSION START_XPOS_EXPANSION START_YPOS_EXPANSION START_AREA_EXPANSION *
diff --git a/gemrb/override/iwd/stone.pro b/gemrb/override/iwd/stone.pro
new file mode 100644
index 0000000..13ce3b0
Binary files /dev/null and b/gemrb/override/iwd/stone.pro differ
diff --git a/gemrb/override/iwd/strengh.pro b/gemrb/override/iwd/strengh.pro
new file mode 100644
index 0000000..53d8a67
Binary files /dev/null and b/gemrb/override/iwd/strengh.pro differ
diff --git a/gemrb/override/iwd/strings.2da b/gemrb/override/iwd/strings.2da
new file mode 100644
index 0000000..490fbf1
--- /dev/null
+++ b/gemrb/override/iwd/strings.2da
@@ -0,0 +1,170 @@
+2DA V1.0
+-1
+ STRREF
+SCATTERED 16457
+WHOLEPARTY 16484
+DOORLOCKED 16485
+MAGICTRAP 16486
+NORMALTRAP 16487
+TRAP 16488
+CANNOTGO 16489
+TRAPREMOVED 16490
+OVERSTOCKED 16491
+SLEEP 16492
+AMBUSH 16493
+CONTLOCKED 16494
+NOMONEY 16495
+CURSED 16496
+SPELLDISRUPT 16497
+DIED 16498
+MAYNOTREST 16499
+CANTRESTMONS 16500
+CANTSAVEMONS 16501
+CANTSAVE 16502
+NODIALOG 10945
+CANTSAVEDIALOG 19253
+CANTSAVEDIALOG2 19254
+CANTSAVEMOVIE 19255
+TARGETBUSY -1
+CANTTALKTRANS -1
+GOTGOLD 17572
+LOSTGOLD 17573
+GOTXP 17574
+LOSTXP 17575
+GOTITEM 17576
+LOSTITEM 17577
+GOTREP 19686
+LOSTREP 19687
+GOTABILITY 10514
+GOTSPELL 10514
+GOTSONG 26320
+NOTHINGTOSAY -1
+JOURNALCHANGE 11359
+WORLDMAPCHANGE 11360
+PAUSED 16321
+UNPAUSED 16322
+SCRIPTPAUSED 7666
+AP_UNUSABLE 17113
+AP_ATTACKED 17114
+AP_HIT 17115
+AP_WOUNDED 17116
+AP_DEAD 17117
+AP_NOTARGET 17118
+AP_ENDROUND 10014
+AP_ENEMY 23511
+AP_TRAP -1
+AP_SPELLCAST -1
+AP_RESERVED1 -1
+AP_RESERVED2 -1
+AP_RESERVED3 -1
+AP_RESERVED4 -1
+CHARMED 14672
+DIRECHARMED 14780
+CONTROLLED -1
+EVIL 16505
+GNE_NEUTRAL 16504
+GOOD 16503
+STR_LAWFUL -1
+LNC_NEUTRAL -1
+CHAOTIC -1
+ACTION_CAST 16464
+ACTION_ATTACK 16465
+ACTION_TURN 16466
+ACTION_SONG 16467
+ACTION_FINDTRAP 16468
+MAGICWEAPON 10141
+OFFHAND_USED 9380
+TWOHANDED_USED 9381
+CANNOT_USE_ITEM 9382
+CANT_DROP_ITEM 25697
+NOT_IN_OFFHAND 26342
+ITEM_IS_CURSED 16304
+NO_CRITICAL 20696
+TRACKING -1
+TRACKINGFAILED 19534
+DOOR_NOPICK 23169
+CONT_NOPICK 23169
+CANTSAVECOMBAT -1
+CANTSAVENOCTRL -1
+LOCKPICK_DONE 16517
+LOCKPICK_FAILED 16518
+STATIC_DISSIPATE -1
+LIGHTNING_DISSIPATE -1
+HAS_NO_ABILITY 17317
+NEEDS_IDENTIFY 17316
+WRONG_ITEMTYPE 9375
+HAS_ITEMEXCL 20685
+PICKPOCKET_DONE 10072
+PICKPOCKET_NONE 18297
+PICKPOCKET_FAIL 10069
+PICKPOCKET_EVIL 10068
+PICKPOCKET_ARMOR 10067
+USING_FEAT -1
+STOPPED_FEAT -1
+DISARM_DONE 16520
+DISARM_FAIL 1608
+DOORBASH_DONE 9915
+DOORBASH_FAIL 9913
+CONTBASH_DONE 9916
+CONTBASH_FAIL 9914
+MAYNOTSETTRAP -1
+SNAREFAILED -1
+SNARESUCCEED -1
+NOMORETRAP -1
+DISABLEDMAGE -1
+SAVESUCCEED 1682
+QSAVESUCCEED 10237
+UNINJURED 2943
+INJURED1 2944
+INJURED2 2945
+INJURED3 2946
+INJURED4 2947
+HOURS 10700
+HOUR 10701
+DAYS 10697
+DAY 10698
+REST 10690
+JOURNEY 10689
+PST_REST -1
+PST_HOUR -1
+PST_HOURS -1
+DAMAGE_IMMUNITY -1
+DAMAGE_STR1 14027
+DAMAGE_STR2 -1
+DAMAGE_STR3 -1
+DMG_POISON -1
+DMG_MAGIC -1
+DMG_MISSILE -1
+DMG_SLASHING -1
+DMG_PIERCING -1
+DMG_CRUSHING -1
+DMG_FIRE -1
+DMG_ELECTRIC -1
+DMG_COLD -1
+DMG_ACID -1
+DMG_OTHER -1
+GOTQUESTXP -1
+LEVELUP 17119
+INVFULL_ITEMDROP 32879
+CONT_DUP 32876
+CONT_TRIG -1
+CONT_FAIL -1
+SEQ_DUP -1
+CRITICAL_HIT 16462
+CRITICAL_MISS 16463
+DEATH 14026
+BACKSTAB 12128
+BACKSTAB_BAD 10013
+BACKSTAB_FAIL -1
+CASTER_LVL_INC -1
+CASTER_LVL_DEC -1
+CHARS_EXPORTED 26827
+PALADIN_FALL 19620
+RANGER_FALL 19621
+RES_RESISTED 26818
+DEADMAGIC_FAIL -1
+MISCASTMAGIC -1
+WILDSURGE -1
+FAMBLOCK -1
+FAMPROTAGONIST -1
+
diff --git a/gemrb/override/iwd/suffoc.pro b/gemrb/override/iwd/suffoc.pro
new file mode 100644
index 0000000..c4e0c4c
Binary files /dev/null and b/gemrb/override/iwd/suffoc.pro differ
diff --git a/gemrb/override/iwd/suffoch.pro b/gemrb/override/iwd/suffoch.pro
new file mode 100644
index 0000000..ef84c8c
Binary files /dev/null and b/gemrb/override/iwd/suffoch.pro differ
diff --git a/gemrb/override/iwd/sunfire.pro b/gemrb/override/iwd/sunfire.pro
new file mode 100644
index 0000000..fcaeafd
Binary files /dev/null and b/gemrb/override/iwd/sunfire.pro differ
diff --git a/gemrb/override/iwd/sunray.pro b/gemrb/override/iwd/sunray.pro
new file mode 100644
index 0000000..9525a7a
Binary files /dev/null and b/gemrb/override/iwd/sunray.pro differ
diff --git a/gemrb/override/iwd/sunscoh.pro b/gemrb/override/iwd/sunscoh.pro
new file mode 100644
index 0000000..be43f0e
Binary files /dev/null and b/gemrb/override/iwd/sunscoh.pro differ
diff --git a/gemrb/override/iwd/swave.pro b/gemrb/override/iwd/swave.pro
new file mode 100644
index 0000000..1987698
Binary files /dev/null and b/gemrb/override/iwd/swave.pro differ
diff --git a/gemrb/override/iwd/swaveh.pro b/gemrb/override/iwd/swaveh.pro
new file mode 100644
index 0000000..c417b2b
Binary files /dev/null and b/gemrb/override/iwd/swaveh.pro differ
diff --git a/gemrb/override/iwd/trapglyp.pro b/gemrb/override/iwd/trapglyp.pro
new file mode 100644
index 0000000..5542681
Binary files /dev/null and b/gemrb/override/iwd/trapglyp.pro differ
diff --git a/gemrb/override/iwd/trapskul.pro b/gemrb/override/iwd/trapskul.pro
new file mode 100644
index 0000000..5706fec
Binary files /dev/null and b/gemrb/override/iwd/trapskul.pro differ
diff --git a/gemrb/override/iwd/tspray.pro b/gemrb/override/iwd/tspray.pro
new file mode 100644
index 0000000..2b297ca
Binary files /dev/null and b/gemrb/override/iwd/tspray.pro differ
diff --git a/gemrb/override/iwd/ublight.pro b/gemrb/override/iwd/ublight.pro
new file mode 100644
index 0000000..6f6a33d
Binary files /dev/null and b/gemrb/override/iwd/ublight.pro differ
diff --git a/gemrb/override/iwd/uward.pro b/gemrb/override/iwd/uward.pro
new file mode 100644
index 0000000..6cf5b94
Binary files /dev/null and b/gemrb/override/iwd/uward.pro differ
diff --git a/gemrb/override/iwd/uwardh.pro b/gemrb/override/iwd/uwardh.pro
new file mode 100644
index 0000000..b385f90
Binary files /dev/null and b/gemrb/override/iwd/uwardh.pro differ
diff --git a/gemrb/override/iwd/vcremap.2da b/gemrb/override/iwd/vcremap.2da
new file mode 100644
index 0000000..b23c7aa
--- /dev/null
+++ b/gemrb/override/iwd/vcremap.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ ORIGINAL NEW
+BIO 74 63
diff --git a/gemrb/override/iwd/vspherh.pro b/gemrb/override/iwd/vspherh.pro
new file mode 100644
index 0000000..717b5a8
Binary files /dev/null and b/gemrb/override/iwd/vspherh.pro differ
diff --git a/gemrb/override/iwd/wdeath1.pro b/gemrb/override/iwd/wdeath1.pro
new file mode 100644
index 0000000..c3e2021
Binary files /dev/null and b/gemrb/override/iwd/wdeath1.pro differ
diff --git a/gemrb/override/iwd/wdeath2.pro b/gemrb/override/iwd/wdeath2.pro
new file mode 100644
index 0000000..764ee67
Binary files /dev/null and b/gemrb/override/iwd/wdeath2.pro differ
diff --git a/gemrb/override/iwd/weapprof.2da b/gemrb/override/iwd/weapprof.2da
new file mode 100644
index 0000000..cee9e63
--- /dev/null
+++ b/gemrb/override/iwd/weapprof.2da
@@ -0,0 +1,18 @@
+2DA V1.0
+-1
+ ID NAME_REF DESC_REF CAP_REF
+BOW 91 11965 9591 8733
+CROSSBOW 103 16034 18323 16033
+MISSILE 94 11971 9596 9403
+AXE 93 11969 9595 9402
+CLUB 101 16030 18324 16029
+DAGGER 96 16014 18325 16013
+FLAIL 99 16021 18326 16020
+HALBERD 97 16016 18327 16015
+HAMMER 100 16027 18328 16022
+MACE 98 16018 18329 16017
+SPEAR 92 11972 9592 8734
+QUARTER_STAFF 102 16032 18330 16031
+GREAT_SWORD 95 16012 18331 16011
+LARGE_SWORD 89 11968 9589 8668
+SMALL_SWORD 90 11967 9590 8732
diff --git a/gemrb/override/iwd/web.pro b/gemrb/override/iwd/web.pro
new file mode 100644
index 0000000..120c2a0
Binary files /dev/null and b/gemrb/override/iwd/web.pro differ
diff --git a/gemrb/override/iwd/whirlw.pro b/gemrb/override/iwd/whirlw.pro
new file mode 100644
index 0000000..50d9199
Binary files /dev/null and b/gemrb/override/iwd/whirlw.pro differ
diff --git a/gemrb/override/iwd/womoon.pro b/gemrb/override/iwd/womoon.pro
new file mode 100644
index 0000000..35543b7
Binary files /dev/null and b/gemrb/override/iwd/womoon.pro differ
diff --git a/gemrb/override/iwd/wowisp.pro b/gemrb/override/iwd/wowisp.pro
new file mode 100644
index 0000000..6e82545
Binary files /dev/null and b/gemrb/override/iwd/wowisp.pro differ
diff --git a/gemrb/override/iwd/wvdeath.pro b/gemrb/override/iwd/wvdeath.pro
new file mode 100644
index 0000000..b3d956a
Binary files /dev/null and b/gemrb/override/iwd/wvdeath.pro differ
diff --git a/gemrb/override/iwd/wvhith.pro b/gemrb/override/iwd/wvhith.pro
new file mode 100644
index 0000000..587b2af
Binary files /dev/null and b/gemrb/override/iwd/wvhith.pro differ
diff --git a/gemrb/override/iwd/wwolf.pro b/gemrb/override/iwd/wwolf.pro
new file mode 100644
index 0000000..4df3732
Binary files /dev/null and b/gemrb/override/iwd/wwolf.pro differ
diff --git a/gemrb/override/iwd/zlaura.pro b/gemrb/override/iwd/zlaura.pro
new file mode 100644
index 0000000..31ee26e
Binary files /dev/null and b/gemrb/override/iwd/zlaura.pro differ
diff --git a/gemrb/override/iwd2/CMakeLists.txt b/gemrb/override/iwd2/CMakeLists.txt
new file mode 100644
index 0000000..b03ca46
--- /dev/null
+++ b/gemrb/override/iwd2/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_OVERRIDE (iwd2)
\ No newline at end of file
diff --git a/gemrb/override/iwd2/Makefile.am b/gemrb/override/iwd2/Makefile.am
new file mode 100644
index 0000000..08aee09
--- /dev/null
+++ b/gemrb/override/iwd2/Makefile.am
@@ -0,0 +1,3 @@
+iwd2override_DATA = *.2da *.ini *.chu *.ids *.pro
+iwd2overridedir = $(moddir)/override/iwd2/
+EXTRA_DIST = *.2da *.ini *.chu *.ids *.pro
diff --git a/gemrb/override/iwd2/ability.2da b/gemrb/override/iwd2/ability.2da
new file mode 100644
index 0000000..2fa0989
--- /dev/null
+++ b/gemrb/override/iwd2/ability.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF STAT_ID
+STRENGTH 11975 9582 1145 36
+DEXTERITY 11977 9584 1151 40
+CONSTITUTION 11978 9583 1178 41
+INTELLIGENCE 11979 9585 1179 38
+WISDOM 11980 9586 1180 39
+CHARISMA 11981 9587 1181 42
diff --git a/gemrb/override/iwd2/abjura.pro b/gemrb/override/iwd2/abjura.pro
new file mode 100644
index 0000000..a5b830e
Binary files /dev/null and b/gemrb/override/iwd2/abjura.pro differ
diff --git a/gemrb/override/iwd2/abjurap.pro b/gemrb/override/iwd2/abjurap.pro
new file mode 100644
index 0000000..9f72db3
Binary files /dev/null and b/gemrb/override/iwd2/abjurap.pro differ
diff --git a/gemrb/override/iwd2/abjurh.pro b/gemrb/override/iwd2/abjurh.pro
new file mode 100644
index 0000000..4927bf6
Binary files /dev/null and b/gemrb/override/iwd2/abjurh.pro differ
diff --git a/gemrb/override/iwd2/abjurt.pro b/gemrb/override/iwd2/abjurt.pro
new file mode 100644
index 0000000..092eeb8
Binary files /dev/null and b/gemrb/override/iwd2/abjurt.pro differ
diff --git a/gemrb/override/iwd2/abreath.pro b/gemrb/override/iwd2/abreath.pro
new file mode 100644
index 0000000..b0b5f3c
Binary files /dev/null and b/gemrb/override/iwd2/abreath.pro differ
diff --git a/gemrb/override/iwd2/acidblgr.pro b/gemrb/override/iwd2/acidblgr.pro
new file mode 100644
index 0000000..fe37e1c
Binary files /dev/null and b/gemrb/override/iwd2/acidblgr.pro differ
diff --git a/gemrb/override/iwd2/acidblmu.pro b/gemrb/override/iwd2/acidblmu.pro
new file mode 100644
index 0000000..aa9e8df
Binary files /dev/null and b/gemrb/override/iwd2/acidblmu.pro differ
diff --git a/gemrb/override/iwd2/acidblob.pro b/gemrb/override/iwd2/acidblob.pro
new file mode 100644
index 0000000..16ef4f3
Binary files /dev/null and b/gemrb/override/iwd2/acidblob.pro differ
diff --git a/gemrb/override/iwd2/acidbloc.pro b/gemrb/override/iwd2/acidbloc.pro
new file mode 100644
index 0000000..a2e6822
Binary files /dev/null and b/gemrb/override/iwd2/acidbloc.pro differ
diff --git a/gemrb/override/iwd2/acidh.pro b/gemrb/override/iwd2/acidh.pro
new file mode 100644
index 0000000..de3952a
Binary files /dev/null and b/gemrb/override/iwd2/acidh.pro differ
diff --git a/gemrb/override/iwd2/adhwil.pro b/gemrb/override/iwd2/adhwil.pro
new file mode 100644
index 0000000..07e10dd
Binary files /dev/null and b/gemrb/override/iwd2/adhwil.pro differ
diff --git a/gemrb/override/iwd2/adhwilh.pro b/gemrb/override/iwd2/adhwilh.pro
new file mode 100644
index 0000000..387a674
Binary files /dev/null and b/gemrb/override/iwd2/adhwilh.pro differ
diff --git a/gemrb/override/iwd2/alance.pro b/gemrb/override/iwd2/alance.pro
new file mode 100644
index 0000000..9336ca7
Binary files /dev/null and b/gemrb/override/iwd2/alance.pro differ
diff --git a/gemrb/override/iwd2/aligns.2da b/gemrb/override/iwd2/aligns.2da
new file mode 100644
index 0000000..fbe93bd
--- /dev/null
+++ b/gemrb/override/iwd2/aligns.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF VALUE COLNAME USABILITY
+LAWFUL_GOOD 7186 9603 1102 0x11 L_G 0x14000
+NEUTRAL_GOOD 7183 9606 1105 0x21 N_G 0x24000
+CHAOTIC_GOOD 7189 9609 1108 0x31 C_G 0x5000
+LAWFUL_NEUTRAL 7188 9604 1104 0x12 L_N 0x18000
+TRUE_NEUTRAL 7185 9608 1106 0x22 N_N 0x28000
+CHAOTIC_NEUTRAL 7191 9610 1109 0x32 C_N 0x9000
+LAWFUL_EVIL 7187 9605 1103 0x13 L_E 0x12000
+NEUTRAL_EVIL 7184 9607 1107 0x23 N_E 0x22000
+CHAOTIC_EVIL 7190 9611 1110 0x33 C_E 0x3000
diff --git a/gemrb/override/iwd2/altera.pro b/gemrb/override/iwd2/altera.pro
new file mode 100644
index 0000000..cb466b3
Binary files /dev/null and b/gemrb/override/iwd2/altera.pro differ
diff --git a/gemrb/override/iwd2/alteranp.pro b/gemrb/override/iwd2/alteranp.pro
new file mode 100644
index 0000000..952660b
Binary files /dev/null and b/gemrb/override/iwd2/alteranp.pro differ
diff --git a/gemrb/override/iwd2/alterap.pro b/gemrb/override/iwd2/alterap.pro
new file mode 100644
index 0000000..2196be2
Binary files /dev/null and b/gemrb/override/iwd2/alterap.pro differ
diff --git a/gemrb/override/iwd2/alteraps.pro b/gemrb/override/iwd2/alteraps.pro
new file mode 100644
index 0000000..4c3b600
Binary files /dev/null and b/gemrb/override/iwd2/alteraps.pro differ
diff --git a/gemrb/override/iwd2/alteras.pro b/gemrb/override/iwd2/alteras.pro
new file mode 100644
index 0000000..81ad729
Binary files /dev/null and b/gemrb/override/iwd2/alteras.pro differ
diff --git a/gemrb/override/iwd2/alterh.pro b/gemrb/override/iwd2/alterh.pro
new file mode 100644
index 0000000..4ef79d3
Binary files /dev/null and b/gemrb/override/iwd2/alterh.pro differ
diff --git a/gemrb/override/iwd2/altert.pro b/gemrb/override/iwd2/altert.pro
new file mode 100644
index 0000000..2a5f332
Binary files /dev/null and b/gemrb/override/iwd2/altert.pro differ
diff --git a/gemrb/override/iwd2/area1np.pro b/gemrb/override/iwd2/area1np.pro
new file mode 100644
index 0000000..b617ed5
Binary files /dev/null and b/gemrb/override/iwd2/area1np.pro differ
diff --git a/gemrb/override/iwd2/area1npl.pro b/gemrb/override/iwd2/area1npl.pro
new file mode 100644
index 0000000..8b1cfe6
Binary files /dev/null and b/gemrb/override/iwd2/area1npl.pro differ
diff --git a/gemrb/override/iwd2/area1p.pro b/gemrb/override/iwd2/area1p.pro
new file mode 100644
index 0000000..e4b1d13
Binary files /dev/null and b/gemrb/override/iwd2/area1p.pro differ
diff --git a/gemrb/override/iwd2/area1pl.pro b/gemrb/override/iwd2/area1pl.pro
new file mode 100644
index 0000000..fc69e6e
Binary files /dev/null and b/gemrb/override/iwd2/area1pl.pro differ
diff --git a/gemrb/override/iwd2/area1ps.pro b/gemrb/override/iwd2/area1ps.pro
new file mode 100644
index 0000000..11e31da
Binary files /dev/null and b/gemrb/override/iwd2/area1ps.pro differ
diff --git a/gemrb/override/iwd2/area2.pro b/gemrb/override/iwd2/area2.pro
new file mode 100644
index 0000000..a9eea19
Binary files /dev/null and b/gemrb/override/iwd2/area2.pro differ
diff --git a/gemrb/override/iwd2/area2np.pro b/gemrb/override/iwd2/area2np.pro
new file mode 100644
index 0000000..6739976
Binary files /dev/null and b/gemrb/override/iwd2/area2np.pro differ
diff --git a/gemrb/override/iwd2/area3p.pro b/gemrb/override/iwd2/area3p.pro
new file mode 100644
index 0000000..4356697
Binary files /dev/null and b/gemrb/override/iwd2/area3p.pro differ
diff --git a/gemrb/override/iwd2/area4np.pro b/gemrb/override/iwd2/area4np.pro
new file mode 100644
index 0000000..d042aeb
Binary files /dev/null and b/gemrb/override/iwd2/area4np.pro differ
diff --git a/gemrb/override/iwd2/armorh.pro b/gemrb/override/iwd2/armorh.pro
new file mode 100644
index 0000000..cceb813
Binary files /dev/null and b/gemrb/override/iwd2/armorh.pro differ
diff --git a/gemrb/override/iwd2/arrow.pro b/gemrb/override/iwd2/arrow.pro
new file mode 100644
index 0000000..293014f
Binary files /dev/null and b/gemrb/override/iwd2/arrow.pro differ
diff --git a/gemrb/override/iwd2/arrowex.pro b/gemrb/override/iwd2/arrowex.pro
new file mode 100644
index 0000000..70b7773
Binary files /dev/null and b/gemrb/override/iwd2/arrowex.pro differ
diff --git a/gemrb/override/iwd2/arrowflb.pro b/gemrb/override/iwd2/arrowflb.pro
new file mode 100644
index 0000000..12b5b91
Binary files /dev/null and b/gemrb/override/iwd2/arrowflb.pro differ
diff --git a/gemrb/override/iwd2/arrowflg.pro b/gemrb/override/iwd2/arrowflg.pro
new file mode 100644
index 0000000..45a7bab
Binary files /dev/null and b/gemrb/override/iwd2/arrowflg.pro differ
diff --git a/gemrb/override/iwd2/arrowfli.pro b/gemrb/override/iwd2/arrowfli.pro
new file mode 100644
index 0000000..202c16c
Binary files /dev/null and b/gemrb/override/iwd2/arrowfli.pro differ
diff --git a/gemrb/override/iwd2/arrowflm.pro b/gemrb/override/iwd2/arrowflm.pro
new file mode 100644
index 0000000..ae84563
Binary files /dev/null and b/gemrb/override/iwd2/arrowflm.pro differ
diff --git a/gemrb/override/iwd2/arrowhvy.pro b/gemrb/override/iwd2/arrowhvy.pro
new file mode 100644
index 0000000..1471c2e
Binary files /dev/null and b/gemrb/override/iwd2/arrowhvy.pro differ
diff --git a/gemrb/override/iwd2/ascorch.pro b/gemrb/override/iwd2/ascorch.pro
new file mode 100644
index 0000000..9ec8567
Binary files /dev/null and b/gemrb/override/iwd2/ascorch.pro differ
diff --git a/gemrb/override/iwd2/astorm.pro b/gemrb/override/iwd2/astorm.pro
new file mode 100644
index 0000000..d6ec20f
Binary files /dev/null and b/gemrb/override/iwd2/astorm.pro differ
diff --git a/gemrb/override/iwd2/asumm1.pro b/gemrb/override/iwd2/asumm1.pro
new file mode 100644
index 0000000..0671b04
Binary files /dev/null and b/gemrb/override/iwd2/asumm1.pro differ
diff --git a/gemrb/override/iwd2/asumm1h.pro b/gemrb/override/iwd2/asumm1h.pro
new file mode 100644
index 0000000..42cf78c
Binary files /dev/null and b/gemrb/override/iwd2/asumm1h.pro differ
diff --git a/gemrb/override/iwd2/asumm1x.pro b/gemrb/override/iwd2/asumm1x.pro
new file mode 100644
index 0000000..e3e49c2
Binary files /dev/null and b/gemrb/override/iwd2/asumm1x.pro differ
diff --git a/gemrb/override/iwd2/asumm2h.pro b/gemrb/override/iwd2/asumm2h.pro
new file mode 100644
index 0000000..86ab54a
Binary files /dev/null and b/gemrb/override/iwd2/asumm2h.pro differ
diff --git a/gemrb/override/iwd2/asumm3h.pro b/gemrb/override/iwd2/asumm3h.pro
new file mode 100644
index 0000000..4bf942b
Binary files /dev/null and b/gemrb/override/iwd2/asumm3h.pro differ
diff --git a/gemrb/override/iwd2/avatars.2da b/gemrb/override/iwd2/avatars.2da
new file mode 100644
index 0000000..a04e296
--- /dev/null
+++ b/gemrb/override/iwd2/avatars.2da
@@ -0,0 +1,432 @@
+2DA V1.0
+*
+ AT_1 AT_2 AT_3 AT_4 TYPE SPACE PALETTE SIZE
+0x0100 SPCHUNKS SPCHUNKS SPCHUNKS SPCHUNKS 13 0 0 *
+0x0300 SPSMPUFF SPSMPUFF SPSMPUFF SPSMPUFF 13 0 0 *
+0x0400 SKLH SKLH SKLH SKLH 13 0 1 *
+0x0410 GLPHWRDH GLPHWRDH GLPHWRDH GLPHWRDH 13 0 1 *
+0x1001 MWYV MWYV MWYV MWYV 11 5 1 *
+0x1100 MTAN MTAN MTAN MTAN 11 5 1 *
+0x1200 MDR1 MDR1 MDR1 MDR1 12 5 1 *
+0x1201 MDR2 MDR2 MDR2 MDR2 12 5 1 *
+0x1202 MDR3 MDR3 MDR3 MDR3 12 5 1 *
+0x1203 MDR1 MDR1 MDR1 MDR1 12 7 GR *
+0x1204 MDR1 MDR1 MDR1 MDR1 12 7 AQ *
+0x1205 MDR1 MDR1 MDR1 MDR1 12 7 BL *
+0x1206 MDR1 MDR1 MDR1 MDR1 12 7 BR *
+0x1207 MDR1 MDR1 MDR1 MDR1 12 7 MC *
+0x1208 MDR1 MDR1 MDR1 MDR1 12 7 PU *
+0x2000 MSIR MSIR MSIR MSIR 2 2 0 *
+0x2100 UVOL UVOL UVOL UVOL 2 2 1 *
+0x2200 MOGM MOGM MOGM MOGM 2 2 0 S
+0x2300 MDKN MDKN MDKN MDKN 2 2 1 *
+0x3000 MAKH MAKH MAKH MAKH 2 3 1 *
+0x4000 SNOMC SNOMC SNOMC SNOMC 1 2 0 *
+0x4002 SNOMM SNOMM SNOMM SNOMM 1 2 0 *
+0x4010 SNOWC SNOWC SNOWC SNOWC 1 2 0 *
+0x4012 SNOWM SNOWM SNOWM SNOWM 1 2 0 *
+0x4100 SSIMC SSIMC SSIMC SSIMC 1 2 0 *
+0x4101 SSIMS SSIMS SSIMS SSIMS 1 2 0 *
+0x4102 SSIMM SSIMM SSIMM SSIMM 1 2 0 *
+0x4110 SSIWC SSIWC SSIWC SSIWC 1 2 0 *
+0x4112 SSIWM SSIWM SSIWM SSIWM 1 2 0 *
+0x4200 SHMCM SHMCM SHMCM SHMCM 1 2 0 *
+0x4300 MSPLG1 MSPLG1 MSPLG1 MSPLG1 1 2 1 *
+0x4400 LHMC LHMC LHMC LHMC 1 2 0 *
+0x4410 LHFC LHFC LHFC LHFC 1 2 0 *
+0x4500 LFAM LFAM LFAM LFAM 1 2 0 *
+0x4600 LDMF LDMF LDMF LDMF 1 2 0 *
+0x4700 LEMF LEMF LEMF LEMF 1 2 0 *
+0x4710 LEFF LEFF LEFF LEFF 1 2 0 *
+0x4800 LIMC LIMC LIMC LIMC 1 2 0 *
+0x5000 CHMB1 CHMB2 CHMB3 CHMC4 0 2 0 *
+0x5001 CEMB1 CEMB2 CEMB3 CEMC4 0 2 0 *
+0x5002 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 *
+0x5003 CIMB1 CIMB2 CIMB3 CIMC4 0 2 0 *
+0x5010 CHFB1 CHFB2 CHFB3 CHFC4 0 2 0 *
+0x5011 CEFB1 CEFB2 CEFB3 CEFC4 0 2 0 *
+0x5012 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 *
+0x5013 CIFB1 CIFB2 CIFB3 CIFC4 0 2 0 *
+0x5100 CHMB1 CHMB2 CHMB3 CHMF4 0 2 0 *
+0x5101 CEMB1 CEMB2 CEMB3 CHMF4 0 2 0 *
+0x5102 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 *
+0x5103 CIMB1 CIMB2 CIMB3 CIMF4 0 2 0 *
+0x5110 CHFB1 CHFB2 CHFB3 CHFF4 0 2 0 *
+0x5111 CEFB1 CEFB2 CEFB3 CEFF4 0 2 0 *
+0x5112 CDMB1 CDFB2 CDFB3 CDFF4 0 2 0 *
+0x5113 CIFB1 CIFB2 CIFB3 CIFF4 0 2 0 *
+0x5200 CHMW1 CHMW2 CHMW3 CHMW4 0 2 0 *
+0x5201 CEMW1 CEMW2 CEMW3 CEMW4 0 2 0 *
+0x5202 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 *
+0x5203 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 *
+0x5210 CHFW1 CHFW2 CHFW3 CHFW4 0 2 0 *
+0x5211 CEFW1 CEFW2 CEFW3 CEFW4 0 2 0 *
+0x5212 CDMW1 CDFW2 CDFW3 CDFW4 0 2 0 *
+0x5213 CDMW1 CDFW2 CDFW3 CDFW4 0 2 0 *
+0x5300 CHMB1 CHMT2 CHMB3 CHMF4 0 2 0 *
+0x5301 CEMB1 CEMT2 CEMB3 CEMF4 0 2 0 *
+0x5302 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 *
+0x5303 CIMB1 CIMT2 CIMB3 CIMF4 0 2 0 *
+0x5310 CHFB1 CHFT2 CHFB3 CHFF4 0 2 0 *
+0x5311 CEFB1 CEFT2 CEFB3 CEFF4 0 2 0 *
+0x5312 CDMB1 CDFT2 CDFB3 CDFF4 0 2 0 *
+0x5313 CIFB1 CIFT2 CIFB3 CIFF4 0 2 0 *
+0x6000 CHMB1 CHMB2 CHMB3 CHMC4 0 2 0 *
+0x6001 CEMB1 CEMB2 CEMB3 CEMC4 0 2 0 *
+0x6002 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 *
+0x6003 CIMB1 CIMB2 CIMB3 CIMC4 0 2 0 *
+0x6004 CDMB1 CDMB2 CDMB3 CDMC4 0 2 0 *
+0x6005 CHMB1 CHMB2 CHMB3 CHMC4 0 2 0 *
+0x6010 CHFB1 CHFB2 CHFB3 CHFC4 0 2 0 *
+0x6011 CEFB1 CEFB2 CEFB3 CEFC4 0 2 0 *
+0x6012 CDMB1 CDFB2 CDFB3 CDFC4 0 2 0 *
+0x6013 CIFB1 CIFB2 CIFB3 CIFC4 0 2 0 *
+0x6014 CIFB1 CIFB2 CIFB3 CIFC4 0 2 0 *
+0x6015 CHFB1 CHFB2 CHFB3 CHFC4 0 2 0 *
+0x6100 CHMB1 CHMB2 CHMB3 CHMF4 0 2 0 *
+0x6101 CEMB1 CEMB2 CEMB3 CEMF4 0 2 0 *
+0x6102 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 *
+0x6103 CIMB1 CIMB2 CIMB3 CIMF4 0 2 0 *
+0x6104 CDMB1 CDMB2 CDMB3 CDMF4 0 2 0 *
+0x6105 CHMB1 CHMB2 CHMB3 CHMF4 0 2 0 *
+0x6110 CHFB1 CHFB2 CHFB3 CHFF4 0 2 0 *
+0x6111 CEFB1 CEFB2 CEFB3 CEFF4 0 2 0 *
+0x6112 CDMB1 CDFB2 CDFB3 CDFF4 0 2 0 *
+0x6113 CIFB1 CIFB2 CIFB3 CIFF4 0 2 0 *
+0x6114 CIFB1 CIFB2 CIFB3 CIFF4 0 2 0 *
+0x6115 CHFB1 CHFB2 CHFB3 CHFF4 0 2 0 *
+0x6200 CHMW1 CHMW2 CHMW3 CHMW4 0 2 0 *
+0x6201 CEMW1 CEMW2 CEMW3 CEMW4 0 2 0 *
+0x6202 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 *
+0x6203 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 *
+0x6204 CDMW1 CDMW2 CDMW3 CDMW4 0 2 0 *
+0x6205 CHMW1 CHMW2 CHMW3 CHMW4 0 2 0 *
+0x6210 CHFW1 CHFW2 CHFW3 CHFW4 0 2 0 *
+0x6211 CEFW1 CEFW2 CEFW3 CEFW4 0 2 0 *
+0x6212 CDMW1 CDFW2 CDFW3 CDFW4 0 2 0 *
+0x6213 CDMW1 CDFW2 CDFW3 CDFW4 0 2 0 *
+0x6214 CDMW1 CIFW2 CIFW3 CIFW4 0 2 0 *
+0x6215 CHFW1 CHFW2 CHFW3 CHFW4 0 2 0 *
+0x6300 CHMB1 CHMT2 CHMB3 CHMF4 0 2 0 *
+0x6301 CEMB1 CEMT2 CEMB3 CEMF4 0 2 0 *
+0x6302 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 *
+0x6303 CIMB1 CIMT2 CIMB3 CIMF4 0 2 0 *
+0x6304 CDMB1 CDMT2 CDMB3 CDMF4 0 2 0 *
+0x6305 CHMB1 CHMT2 CHMB3 CHMF4 0 2 0 *
+0x6310 CHFB1 CHFT2 CHFB3 CHFF4 0 2 0 *
+0x6311 CEFB1 CEFT2 CEFB3 CEFF4 0 2 0 *
+0x6312 CDMB1 CDFT2 CDFB3 CDFF4 0 2 0 *
+0x6313 CIFB1 CIFT2 CIFB3 CIFF4 0 2 0 *
+0x6314 CIFB1 CIFT2 CIFB3 CIFF4 0 2 0 *
+0x6315 CHFB1 CHFT2 CHFT3 CHFF4 0 2 0 *
+0x6400 UDRZ1 UDRZ1 UDRZ1 UDRZ1 6 2 0 *
+0x6401 UELM1 UELM1 UELM1 UELM1 6 2 1 *
+0x6402 CMNK1 CMNK1 CMNK1 CMNK1 6 2 0 *
+0x6403 MSKL1 MSKL1 MSKL1 MSKL1 6 2 0 M
+0x6404 USAR1 USAR1 USAR1 USAR1 6 2 1 *
+0x6405 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 M
+0x6406 MDGU1 MDGU1 MDGU1 MDGU1 6 2 0 L
+0x6500 CHMM1 CHMB2 CHMB3 CHMC4 0 2 0 *
+0x6510 CHFM1 CHFB2 CHFB3 CHFC4 0 2 0 *
+0x7000 MOGH MOGH MOGH MOGH 2 2 0 *
+0x7001 MOGN MOGN MOGN MOGN 2 2 0 *
+0x7100 MBAS MBAS MBAS MBAS 2 2 1 *
+0x7101 MBAS MBAS MBAS MBAS 2 2 GR *
+0x7200 MBER MBER MBER MBER 3 2 BL *
+0x7201 MBER MBER MBER MBER 3 2 1 *
+0x7202 MBER MBER MBER MBER 3 2 CA *
+0x7203 MBER MBER MBER MBER 3 2 PO *
+0x7300 MEAE MEAE MEAE MEAE 4 2 1 *
+0x7301 MEAS MEAS MEAS MEAS 4 2 1 *
+0x7310 MFIE MFIE MFIE MFIE 4 2 1 *
+0x7311 MFIS MFIS MFIS MFIS 4 2 1 *
+0x7320 MAIR MAIR MAIR MAIR 4 2 1 *
+0x7321 MAIS MAIS MAIS MAIS 4 2 1 *
+0x7400 MDOG MDOG MDOG MDOG 2 2 WI *
+0x7401 MDOG MDOG MDOG MDOG 2 2 WA *
+0x7402 MDOG MDOG MDOG MDOG 2 2 MO *
+0x7500 MDOP MDOP MDOP MDOP 3 2 1 *
+0x7501 MDOP MDOP MDOP MDOP 3 2 GR *
+0x7600 METT METT METT METT 2 2 1 *
+0x7701 MGHL MGHL MGHL MGHL 2 2 1 *
+0x7702 MGHL MGHL MGHL MGHL 2 2 RE *
+0x7703 MGHL MGHL MGHL MGHL 2 2 GA *
+0x7704 MSHD MSHD MSHD MSHD 4 2 1 *
+0x7800 MGIB MGIB MGIB MGIB 2 2 1 *
+0x7900 MSLI MSLI MSLI MSLI 3 3 GR *
+0x7901 MSLI MSLI MSLI MSLI 3 3 OL *
+0x7902 MSLI MSLI MSLI MSLI 3 3 MU *
+0x7903 MSLI MSLI MSLI MSLI 3 3 OC *
+0x7904 MSLI MSLI MSLI MSLI 3 3 1 *
+0x7A00 MSPI MSPI MSPI MSPI 3 2 GI *
+0x7A01 MSPI MSPI MSPI MSPI 3 2 HU *
+0x7A02 MSPI MSPI MSPI MSPI 3 2 PH *
+0x7A03 MSPI MSPI MSPI MSPI 3 2 SW *
+0x7A04 MSPI MSPI MSPI MSPI 3 2 WR *
+0x7B00 MWLF MWLF MWLF MWLF 2 2 1 *
+0x7B01 MWLF MWLF MWLF MWLF 2 2 WO *
+0x7B02 MWLF MWLF MWLF MWLF 2 2 DI *
+0x7B03 MWLF MWLF MWLF MWLF 2 2 WI *
+0x7B04 MWLF MWLF MWLF MWLF 2 2 VA *
+0x7B05 MWLF MWLF MWLF MWLF 2 2 DR *
+0x7B06 MWLS MWLS MWLS MWLS 2 2 1 *
+0x7C00 MXVT MXVT MXVT MXVT 2 2 0 *
+0x7C01 MTAS MTAS MTAS MTAS 2 2 0 *
+0x7D00 MZOM MZOM MZOM MZOM 2 2 0 *
+0x7E00 MWER MWER MWER MWER 2 2 1 *
+0x7E01 MGWE MGWE MGWE MGWE 2 2 1 *
+0x7F00 MTRO MTRO MTRO MTRO 4 2 1 *
+0x7F01 MMIN MMIN MMIN MMIN 4 2 1 *
+0x7F02 MBEH MBEH MBEH MBEH 4 3 1 *
+0x7F03 MIMP MIMP MIMP MIMP 4 2 1 *
+0x7F04 MIGO MIGO MIGO MIGO 4 2 1 *
+0x7F05 MDJI MDJI MDJI MDJI 4 2 1 *
+0x7F06 MDJL MDJL MDJL MDJL 4 2 1 *
+0x7F07 MGLC MGLC MGLC MGLC 4 3 1 *
+0x7F08 MOTY MOTY MOTY MOTY 4 4 1 *
+0x7F09 MSAH MSAH MSAH MSAH 4 2 1 *
+0x7F0A MGCP MGCP MGCP MGCP 4 2 1 *
+0x7F0B MGCL MGCL MGCL MGCL 4 2 1 *
+0x7F0C MKUO MKUO MKUO MKUO 4 2 1 *
+0x7F0D MLIC MLIC MLIC MLIC 4 2 1 *
+0x7F0E MDLI MDLI MDLI MDLI 4 2 1 *
+0x7F0F MTRS MTRS MTRS MTRS 4 2 1 *
+0x7F10 MRAK MRAK MRAK MRAK 4 2 1 *
+0x7F11 MUMB MUMB MUMB MUMB 4 2 1 *
+0x7F12 MVAM MVAM MVAM MVAM 4 2 1 *
+0x7F13 MSNK MSNK MSNK MSNK 4 2 1 *
+0x7F14 MGIT MGIT MGIT MGIT 4 2 1 *
+0x7F15 MBES MBES MBES MBES 4 2 1 *
+0x7F16 AMOO AMOO AMOO AMOO 4 3 1 *
+0x7F17 ARAB ARAB ARAB ARAB 4 1 1 *
+0x7F18 ADER ADER ADER ADER 4 2 1 *
+0x7F19 MDSW MDSW MDSW MDSW 4 2 1 *
+0x7F20 AGRO AGRO AGRO AGRO 4 2 1 *
+0x7F21 APHE APHE APHE APHE 4 2 1 *
+0x7F22 MVAF MVAF MVAF MVAF 4 2 1 *
+0x7F23 MSAT MSAT MSAT MSAT 4 3 1 *
+0x7F24 NPIR NPIR NPIR NPIR 4 2 1 *
+0x7F27 MDRO MDRO MDRO MDRO 4 2 1 *
+0x7F28 MKUL MKUL MKUL MKUL 4 3 1 *
+0x7F29 MFDR MFDR MFDR MFDR 4 2 1 *
+0x7F2A NSAI NSAI NSAI NSAI 4 2 1 *
+0x7F2B MMAX MMAX MMAX MMAX 4 2 1 *
+0x7F2C NSOL NSOL NSOL NSOL 4 2 1 *
+0x7F2D MWFM MWFM MWFM MWFM 4 2 1 *
+0x7F2E MRAV MRAV MRAV MRAV 4 3 1 *
+0x7F2F MSPS MSPS MSPS MSPS 4 2 1 *
+0x7F30 NBOH NBOH NBOH NBOH 4 2 1 *
+0x7F31 NELL NELL NELL NELL 4 2 1 *
+0x7F32 MSLY MSLY MSLY MSLY 4 2 1 *
+0x7F33 MKUR MKUR MKUR MKUR 4 2 1 *
+0x7F34 MDOC MDOC MDOC MDOC 4 2 1 *
+0x7F35 MMIS MMIS MMIS MMIS 4 3 1 *
+0x7F36 NSHD NSHD NSHD NSHD 4 2 1 *
+0x7F37 NIRE NIRE NIRE NIRE 4 2 1 *
+0x8000 MGNL MGNL MGNL MGNL 2 2 0 *
+0x8100 MHOB MHOB MHOB MHOB 2 2 0 *
+0x8200 MKOB MKOB MKOB MKOB 2 2 0 *
+0x9000 MOGR MOGR MOGR MOGR 5 2 0 *
+0xA000 MWYV MWYV MWYV MWYV 8 3 1 *
+0xA100 MCAR MCAR MCAR MCAR 8 3 1 *
+0xB000 ACOW ACOW ACOW ACOW 10 3 1 *
+0xB100 AHRS AHRS AHRS AHRS 10 3 1 *
+0xB200 NBEGL NBEGL NBEGL NBEGL 3 2 0 *
+0xB210 NPROL NPROL NPROL NPROL 3 2 0 *
+0xB300 NBOYL NBOYL NBOYL NBOYL 3 2 0 *
+0xB310 NGRLL NGRLL NGRLL NGRLL 3 2 0 *
+0xB400 NFAML NFAML NFAML NFAML 3 2 0 *
+0xB410 NFAWL NFAWL NFAWL NFAWL 3 2 0 *
+0xB500 NSIML NSIML NSIML NSIML 3 2 0 *
+0xB510 NSIWL NSIWL NSIWL NSIWL 3 2 0 *
+0xB600 NNOML NNOML NNOML NNOML 3 2 0 *
+0xB610 NNOWL NNOWL NNOWL NNOWL 3 2 0 *
+0xB700 NSLVL NSLVL NSLVL NSLVL 3 2 0 *
+0xC000 ABAT ABAT ABAT ABAT 3 1 1 *
+0xC100 ACAT ACAT ACAT ACAT 3 1 1 *
+0xC200 ACHK ACHK ACHK ACHK 3 1 1 *
+0xC300 ARAT ARAT ARAT ARAT 3 1 1 *
+0xC400 ASQU ASQU ASQU ASQU 3 1 1 *
+0xC500 ABAT ABAT ABAT ABAT 3 1 1 *
+0xC600 NBEGH NBEGH NBEGH NBEGH 3 2 0 *
+0xC610 NPROH NPROH NPROH NPROH 3 2 0 *
+0xC700 NBOYH NBOYH NBOYH NBOYH 3 2 0 *
+0xC710 NGRLH NGRLH NGRLH NGRLH 3 2 0 *
+0xC800 NFAMH NFAMH NFAMH NFAMH 3 2 0 *
+0xC810 NFAWH NFAWH NFAWH NFAWH 3 2 0 *
+0xC900 NSIMH NSIMH NSIMH NSIMH 3 2 0 *
+0xC910 NSIWH NSIWH NSIWH NSIWH 3 2 0 *
+0xCA00 NNOMH NNOMH NNOMH NNOMH 3 2 0 *
+0xCA10 NNOWH NNOWH NNOWH NNOWH 3 2 0 *
+0xCB00 NSLVH NSLVH NSLVH NSLVH 3 2 0 *
+0xD000 AEAGG1 AEAGG1 AEAGG1 AEAGG1 7 0 1 *
+0xD100 AGULG1 AGULG1 AGULG1 AGULG1 7 0 1 *
+0xD200 AVULG1 AVULG1 AVULG1 AVULG1 7 0 1 *
+0xD300 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xD400 ABIRG1 ABIRG1 ABIRG1 ABIRG1 7 0 1 *
+0xE000 MBET MBET MBET MBET 9 2 1 *
+0xE012 MBBM MBBM MBBM MBBM 9 2 1 *
+0xE020 MTAN MTAN MTAN MTAN 9 4 1 *
+0xEBCD MTAN MTAN MTAN MTAN 9 4 1 *
+0xE022 MBBR MBBR MBBR MBBR 9 2 1 *
+0xE038 MBFI MBFI MBFI MBFI 9 2 1 *
+0xE048 MBRH MBRH MBRH MBRH 9 5 1 *
+0xE050 MREM MREM MREM MREM 9 4 1 *
+0xE060 MLIC MLIC MLIC MLIC 9 2 1 *
+0xEB69 MLIC MLIC MLIC MLIC 9 2 1 *
+0xE068 MHOH MHOH MHOH MHOH 9 2 1 *
+0xE070 MMIN MMIN MMIN MMIN 9 2 1 *
+0xE090 MMER MMER MMER MMER 9 2 1 *
+0xE0A0 MTIC MTIC MTIC MTIC 9 2 1 *
+0xE728 MTIC MTIC MTIC MTIC 9 2 1 *
+0xE0B0 MTRO MTRO MTRO MTRO 9 2 1 *
+0xE718 MTRO MTRO MTRO MTRO 9 2 1 *
+0xE0C0 MTSN MTSN MTSN MTSN 9 2 1 *
+0xE738 MTSN MTSN MTSN MTSN 9 2 1 *
+0xE0D0 MUMB MUMB MUMB MUMB 9 2 1 *
+0xE759 MUMB MUMB MUMB MUMB 9 2 1 *
+0xE0E0 MCOR MCOR MCOR MCOR 9 3 1 *
+0xED09 MCOR MCOR MCOR MCOR 9 3 1 *
+0xE0F0 MGIC MGIC MGIC MGIC 9 2 1 *
+0xE0F1 MGLA MGLA MGLA MGLA 9 2 1 *
+0xE108 MCYC MCYC MCYC MCYC 9 4 1 *
+0xE118 METN METN METN METN 9 4 1 *
+0xE138 MGFR MGFR MGFR MGFR 9 4 1 *
+0xE148 MGVE MGVE MGVE MGVE 9 4 1 *
+0xE15A MGFO MGFO MGFO MGFO 9 4 1 *
+0xE218 MELE MELE MELE MELE 9 4 1 *
+0xE228 MELF MELF MELF MELF 9 4 1 *
+0xE238 MELW MELW MELW MELW 9 4 1 *
+0xE249 MHAR MHAR MHAR MHAR 9 2 1 *
+0xE252 MWWE MWWE MWWE MWWE 9 2 1 *
+0xE269 MFEY MFEY MFEY MFEY 9 2 1 *
+0xE278 MDTR MDTR MDTR MDTR 9 3 1 *
+0xE289 MFE2 MFE2 MFE2 MFE2 9 3 1 *
+0xE298 MEW2 MEW2 MEW2 MEW2 9 2 1 *
+0xE300 MGHO MGHO MGHO MGHO 9 2 1 *
+0xE928 MGHO MGHO MGHO MGHO 9 2 1 *
+0xE308 MGH2 MGH2 MGH2 MGH2 9 2 1 *
+0xE318 MGH3 MGH3 MGH3 MGH3 9 2 1 *
+0xE329 MWIG MWIG MWIG MWIG 9 2 1 *
+0xE338 MZO2 MZO2 MZO2 MZO2 9 2 1 *
+0xE348 MZO3 MZO3 MZO3 MZO3 9 2 1 *
+0xE359 MWI2 MWI2 MWI2 MWI2 9 2 1 *
+0xE369 MWI3 MWI3 MWI3 MWI3 9 2 1 *
+0xE388 MMUM MMUM MMUM MMUM 9 2 1 *
+0xE39C MHIS MHIS MHIS MHIS 9 2 1 *
+0xE3A8 MDRD MDRD MDRD MDRD 9 2 1 *
+0xE3BB MWAV MWAV MWAV MWAV 9 2 1 *
+0xE408 MGO1 MGO1 MGO1 MGO1 9 2 1 *
+0xE412 MGO2 MGO2 MGO2 MGO2 9 2 1 *
+0xE428 MGO3 MGO3 MGO3 MGO3 9 2 1 *
+0xE432 MGO4 MGO4 MGO4 MGO4 9 2 1 *
+0xE449 MSVI MSVI MSVI MSVI 9 2 1 *
+0xE459 MSV2 MSV2 MSV2 MSV2 9 2 1 *
+0xE468 MGWO MGWO MGWO MGWO 9 2 1 *
+0xE479 MGOC MGOC MGOC MGOC 9 2 1 *
+0xE488 MGW2 MGW2 MGW2 MGW2 9 2 1 *
+0xE498 MGO5 MGO5 MGO5 MGO5 9 2 1 *
+0xE500 MLIZ MLIZ MLIZ MLIZ 9 2 1 *
+0xF209 MLIZ MLIZ MLIZ MLIZ 9 2 1 *
+0xE510 MLI2 MLI2 MLI2 MLI2 9 2 1 *
+0xF218 MLI2 MLI2 MLI2 MLI2 9 2 1 *
+0xE51D MGIR MGIR MGIR MGIR 9 2 1 *
+0xE520 MLI3 MLI3 MLI3 MLI3 9 2 1 *
+0xE899 MLI3 MLI3 MLI3 MLI3 9 2 1 *
+0xE528 MGIC MGIC MGIC MGIC 9 2 1 *
+0xE600 MMYC MMYC MMYC MMYC 9 2 1 *
+0xEB99 MMYC MMYC MMYC MMYC 9 2 1 *
+0xE610 MMY2 MMY2 MMY2 MMY2 9 2 1 *
+0xEBA9 MMY2 MMY2 MMY2 MMY2 9 2 1 *
+0xE62C MSKB MSKB MSKB MSKB 9 2 1 *
+0xE700 MNO1 MNO1 MNO1 MNO1 9 2 1 *
+0xE868 MNO1 MNO1 MNO1 MNO1 9 2 1 *
+0xE710 MNO2 MNO2 MNO2 MNO2 9 2 1 *
+0xE878 MNO2 MNO2 MNO2 MNO2 9 2 1 *
+0xE720 MNO3 MNO3 MNO3 MNO3 9 2 1 *
+0xE888 MNO3 MNO3 MNO3 MNO3 9 2 1 *
+0xE88C MNO3 MNO3 MNO3 MNO3 9 2 1 *
+0xE76C MYET MYET MYET MYET 9 2 1 *
+0xE771 MBA4 MBA4 MBA4 MBA4 9 2 1 *
+0xE781 MBA5 MBA5 MBA5 MBA5 9 2 1 *
+0xE791 MBA6 MBA6 MBA6 MBA6 9 2 1 *
+0xE7AC MBAI MBAI MBAI MBAI 9 2 1 *
+0xE7B8 MBOA MBOA MBOA MBOA 9 2 1 *
+0xE7C9 MABW MABW MABW MABW 9 2 1 *
+0xE7E8 MSCR MSCR MSCR MSCR 9 2 1 *
+0xE7F9 MUM2 MUM2 MUM2 MUM2 9 2 1 *
+0xE800 MOR1 MOR1 MOR1 MOR1 9 2 1 *
+0xE818 MOR1 MOR1 MOR1 MOR1 9 2 1 *
+0xE808 MOR6 MOR6 MOR6 MOR6 9 2 1 *
+0xE810 MOR2 MOR2 MOR2 MOR2 9 2 1 *
+0xE822 MOR2 MOR2 MOR2 MOR2 9 2 1 *
+0xE820 MOR3 MOR3 MOR3 MOR3 9 2 1 *
+0xE838 MOR3 MOR3 MOR3 MOR3 9 2 1 *
+0xE830 MOR4 MOR4 MOR4 MOR4 9 2 1 *
+0xE842 MOR4 MOR4 MOR4 MOR4 9 2 1 *
+0xE840 MOR5 MOR5 MOR5 MOR5 9 2 1 *
+0xE859 MOR5 MOR5 MOR5 MOR5 9 2 1 *
+0xE8C8 MBUG MBUG MBUG MBUG 9 2 1 *
+0xE8D9 MNOS MNOS MNOS MNOS 9 2 1 *
+0xE8E8 MBU2 MBU2 MBU2 MBU2 9 2 1 *
+0xE8F9 MOR7 MOR7 MOR7 MOR7 9 2 1 *
+0xE900 MSAL MSAL MSAL MSAL 9 2 1 *
+0xEBD8 MSAL MSAL MSAL MSAL 9 2 1 *
+0xE910 MSA2 MSA2 MSA2 MSA2 9 2 1 *
+0xEBE8 MSA2 MSA2 MSA2 MSA2 9 2 1 *
+0xE918 MSH2 MSH2 MSH2 MSH2 9 2 1 *
+0xEA00 MSHR MSHR MSHR MSHR 9 2 1 *
+0xEBB1 MSHR MSHR MSHR MSHR 9 2 1 *
+0xEBF1 MARU MARU MARU MARU 9 2 1 *
+0xEA10 MSH1 MSH1 MSH1 MSH1 9 2 1 *
+0xE908 MSH1 MSH1 MSH1 MSH1 9 2 1 *
+0xEA20 MCRD MCRD MCRD MCRD 9 2 1 *
+0xEB00 MSKT MSKT MSKT MSKT 9 2 1 *
+0xF019 MSKT MSKT MSKT MSKT 9 2 1 *
+0xEB10 MSKA MSKA MSKA MSKA 9 2 1 *
+0xEB18 MANI MANI MANI MANI 9 2 1 *
+0xEB28 MAN2 MAN2 MAN2 MAN2 9 2 1 *
+0xEB39 MBE1 MBE1 MBE1 MBE1 9 2 1 *
+0xEB49 MBE2 MBE2 MBE2 MBE2 9 2 1 *
+0xEB79 MLER MLER MLER MLER 9 2 1 *
+0xEC0B MWDR MWDR MWDR MWDR 9 2 1 *
+0xEC2B MSHE MSHE MSHE MSHE 9 2 1 *
+0xEC33 MCHI MCHI MCHI MCHI 9 2 1 *
+0xEC4B MDH1 MDH1 MDH1 MDH1 9 2 1 *
+0xEC5B MDH2 MDH2 MDH2 MDH2 9 2 1 *
+0xF008 MSKA MSKA MSKA MSKA 9 2 1 *
+0xED00 MYU1 MYU1 MYU1 MYU1 9 2 1 *
+0xF10E MYU1 MYU1 MYU1 MYU1 9 2 1 *
+0xED10 MYU2 MYU2 MYU2 MYU2 9 2 1 *
+0xF11E MYU2 MYU2 MYU2 MYU2 9 2 1 *
+0xED19 MGLA MGLA MGLA MGLA 9 2 1 *
+0xED20 MYU3 MYU3 MYU3 MYU3 9 2 1 *
+0xE8A1 MYU3 MYU3 MYU3 MYU3 9 2 1 *
+0xED28 MLEM MLEM MLEM MLEM 9 2 1 *
+0xEE08 MWEB MWEB MWEB MWEB 9 2 1 *
+0xEE18 MWRA MWRA MWRA MWRA 9 2 1 *
+0xEF0D MISA MISA MISA MISA 9 2 1 *
+0xEF28 MWOR MWOR MWOR MWOR 9 2 1 *
+0xEF50 MKG1 MKG1 MKG1 MKG1 9 2 1 *
+0xEF60 MKG2 MKG2 MKG2 MKG2 9 2 1 *
+0xEF70 MKG3 MKG3 MKG3 MKG3 9 2 1 *
+0xEF92 MWIL MWIL MWIL MWIL 9 2 1 *
+0xEFA3 MGEN MGEN MGEN MGEN 9 2 1 *
+0xEFB3 MGEN MGEN MGEN MGEN 9 3 1 *
+0xEFC3 MGEN MGEN MGEN MGEN 9 4 1 *
+0xEFD3 MGEN MGEN MGEN MGEN 9 5 1 *
+0xEFE3 MGEN MGEN MGEN MGEN 9 1 1 *
+0xEFF3 MGEN MGEN MGEN MGEN 9 6 1 *
+0xE708 MMIN MMIN MMIN MMIN 9 2 1 *
+0xE7D9 MMAL MMAL MMAL MMAL 9 2 1 *
+0xE8B9 MYUH MYUH MYUH MYUH 9 2 1 *
+0xEF1D MMAD MMAD MMAD MMAD 9 2 1 *
+0xF029 MWI4 MWI4 MWI4 MWI4 9 2 1 *
+0xF308 MGFI MGFI MGFI MGFI 9 4 1 *
+0xF40B MSAH MSAH MSAH MSAH 9 2 1 *
+0xF41B MSAT MSAT MSAT MSAT 9 2 1 *
+0xF50B MDRM MDRM MDRM MDRM 9 4 1 *
+0xF51B MDRF MDRF MDRF MDRF 9 4 1 *
+0xF77A MBA1 MBA1 MBA1 MBA1 9 2 1 *
+0xF78A MBA2 MBA2 MBA2 MBA2 9 2 1 *
+0xF798 MBA3 MBA3 MBA3 MBA3 9 2 1 *
diff --git a/gemrb/override/iwd2/avprefc.2da b/gemrb/override/iwd2/avprefc.2da
new file mode 100644
index 0000000..01a3d00
--- /dev/null
+++ b/gemrb/override/iwd2/avprefc.2da
@@ -0,0 +1,15 @@
+2DA V1.0
+*
+ PREFIX
+TYPE 232
+BARBARIAN 0x100
+BARD 0x300
+CLERIC 0
+DRUID 0
+FIGHTER 0x100
+MONK 0x500
+PALADIN 0x100
+RANGER 0x100
+ROGUE 0x300
+SORCERER 0x200
+WIZARD 0x200
diff --git a/gemrb/override/iwd2/avprefr.2da b/gemrb/override/iwd2/avprefr.2da
new file mode 100644
index 0000000..53ea155
--- /dev/null
+++ b/gemrb/override/iwd2/avprefr.2da
@@ -0,0 +1,11 @@
+2DA V1.0
+*
+ RACE
+TYPE 231
+HUMAN 0
+ELF 1
+HALF_ELF 1
+DWARF 2
+HALFLING 3
+GNOME 4
+HALFORC 5
diff --git a/gemrb/override/iwd2/axe.pro b/gemrb/override/iwd2/axe.pro
new file mode 100644
index 0000000..2220a75
Binary files /dev/null and b/gemrb/override/iwd2/axe.pro differ
diff --git a/gemrb/override/iwd2/axeex.pro b/gemrb/override/iwd2/axeex.pro
new file mode 100644
index 0000000..475b824
Binary files /dev/null and b/gemrb/override/iwd2/axeex.pro differ
diff --git a/gemrb/override/iwd2/bbarrh1.pro b/gemrb/override/iwd2/bbarrh1.pro
new file mode 100644
index 0000000..25d3720
Binary files /dev/null and b/gemrb/override/iwd2/bbarrh1.pro differ
diff --git a/gemrb/override/iwd2/bbarrh2.pro b/gemrb/override/iwd2/bbarrh2.pro
new file mode 100644
index 0000000..361d26c
Binary files /dev/null and b/gemrb/override/iwd2/bbarrh2.pro differ
diff --git a/gemrb/override/iwd2/bbarrier.pro b/gemrb/override/iwd2/bbarrier.pro
new file mode 100644
index 0000000..de3b9c9
Binary files /dev/null and b/gemrb/override/iwd2/bbarrier.pro differ
diff --git a/gemrb/override/iwd2/bdeath.pro b/gemrb/override/iwd2/bdeath.pro
new file mode 100644
index 0000000..514d2b0
Binary files /dev/null and b/gemrb/override/iwd2/bdeath.pro differ
diff --git a/gemrb/override/iwd2/bigboldr.pro b/gemrb/override/iwd2/bigboldr.pro
new file mode 100644
index 0000000..7908934
Binary files /dev/null and b/gemrb/override/iwd2/bigboldr.pro differ
diff --git a/gemrb/override/iwd2/bios.2da b/gemrb/override/iwd2/bios.2da
new file mode 100644
index 0000000..4a8fcad
--- /dev/null
+++ b/gemrb/override/iwd2/bios.2da
@@ -0,0 +1,14 @@
+2DA V1.0
+*
+ CLASS BIO
+0 BARBARIAN 27862
+1 BARD 19425
+2 CLERIC 19422
+3 DRUID 19421
+4 FIGHTER 19423
+5 MONK 27860
+6 PALADIN 13427
+7 RANGER 19429
+8 ROGUE 19428
+9 SORCEROR 27863
+10 WIZARD 19430
diff --git a/gemrb/override/iwd2/blessh.pro b/gemrb/override/iwd2/blessh.pro
new file mode 100644
index 0000000..dd80c91
Binary files /dev/null and b/gemrb/override/iwd2/blessh.pro differ
diff --git a/gemrb/override/iwd2/bloodclr.2da b/gemrb/override/iwd2/bloodclr.2da
new file mode 100644
index 0000000..1b33a30
--- /dev/null
+++ b/gemrb/override/iwd2/bloodclr.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+0
+ VALUE MIN MAX
+NORMAL 0x2f 0x1000 0xffff
+PURPLE 0x2d 0x4300 0x43ff
+MSKEL 0x25 0x5403 0x5403
+DOOMG 0x22 0x5405 0x5406
+MSKEL 0x25 0x6403 0x6403
+DOOMG 0x22 0x6405 0x6406
+YELLOW 0x32 0x7300 0x73ff
+PURPLE 0x3c 0x7500 0x75ff
+YELLOW 0x32 0x7600 0x76ff
+BLACK 0x66 0x7700 0x77ff
+SLI_GR 7 0x7900 0x7900
+SLI_OL 0x24 0x7901 0x7901
+SLI_MU 0x33 0x7902 0x7902
+SLI_OC 0x25 0x7903 0x7903
+SLIME 0x1b 0x7904 0x7904
+SPIDER 0x33 0x7a00 0x7aff
+ZOMBIE 0x25 0x7c00 0x7cff
+MTRO 0x33 0x7f00 0x7f00
+MMIN 0x3d 0x7f01 0x7f03
+MIGO 0x1a 0x7f04 0x7f04
+MGLC 0x5d 0x7f07 0x7f07
+MTRS 0x32 0x7f0f 0x7f0f
+MBES 0x3d 0x7f15 0x7f15
+MCAR 0x38 0xa100 0xa1ff
+UNDEAD 0x66 0xe300 0xe3ff
+MWWE 0x38 0xef00 0xefff
diff --git a/gemrb/override/iwd2/bolt.pro b/gemrb/override/iwd2/bolt.pro
new file mode 100644
index 0000000..1cfd4f5
Binary files /dev/null and b/gemrb/override/iwd2/bolt.pro differ
diff --git a/gemrb/override/iwd2/boltex.pro b/gemrb/override/iwd2/boltex.pro
new file mode 100644
index 0000000..e963523
Binary files /dev/null and b/gemrb/override/iwd2/boltex.pro differ
diff --git a/gemrb/override/iwd2/boulder.pro b/gemrb/override/iwd2/boulder.pro
new file mode 100644
index 0000000..b3368d9
Binary files /dev/null and b/gemrb/override/iwd2/boulder.pro differ
diff --git a/gemrb/override/iwd2/bscloud.pro b/gemrb/override/iwd2/bscloud.pro
new file mode 100644
index 0000000..aa0f975
Binary files /dev/null and b/gemrb/override/iwd2/bscloud.pro differ
diff --git a/gemrb/override/iwd2/bullet.pro b/gemrb/override/iwd2/bullet.pro
new file mode 100644
index 0000000..cb89cea
Binary files /dev/null and b/gemrb/override/iwd2/bullet.pro differ
diff --git a/gemrb/override/iwd2/bulletex.pro b/gemrb/override/iwd2/bulletex.pro
new file mode 100644
index 0000000..4a039f5
Binary files /dev/null and b/gemrb/override/iwd2/bulletex.pro differ
diff --git a/gemrb/override/iwd2/calllih.pro b/gemrb/override/iwd2/calllih.pro
new file mode 100644
index 0000000..17723b2
Binary files /dev/null and b/gemrb/override/iwd2/calllih.pro differ
diff --git a/gemrb/override/iwd2/ccdamah.pro b/gemrb/override/iwd2/ccdamah.pro
new file mode 100644
index 0000000..c6fe73b
Binary files /dev/null and b/gemrb/override/iwd2/ccdamah.pro differ
diff --git a/gemrb/override/iwd2/ccommah.pro b/gemrb/override/iwd2/ccommah.pro
new file mode 100644
index 0000000..2dd3ed5
Binary files /dev/null and b/gemrb/override/iwd2/ccommah.pro differ
diff --git a/gemrb/override/iwd2/ccwounh.pro b/gemrb/override/iwd2/ccwounh.pro
new file mode 100644
index 0000000..fd621c3
Binary files /dev/null and b/gemrb/override/iwd2/ccwounh.pro differ
diff --git a/gemrb/override/iwd2/cdiseah.pro b/gemrb/override/iwd2/cdiseah.pro
new file mode 100644
index 0000000..4a10c96
Binary files /dev/null and b/gemrb/override/iwd2/cdiseah.pro differ
diff --git a/gemrb/override/iwd2/ceelem1.pro b/gemrb/override/iwd2/ceelem1.pro
new file mode 100644
index 0000000..14116cd
Binary files /dev/null and b/gemrb/override/iwd2/ceelem1.pro differ
diff --git a/gemrb/override/iwd2/ceelemh.pro b/gemrb/override/iwd2/ceelemh.pro
new file mode 100644
index 0000000..59eb71a
Binary files /dev/null and b/gemrb/override/iwd2/ceelemh.pro differ
diff --git a/gemrb/override/iwd2/ceelemx.pro b/gemrb/override/iwd2/ceelemx.pro
new file mode 100644
index 0000000..5e15e45
Binary files /dev/null and b/gemrb/override/iwd2/ceelemx.pro differ
diff --git a/gemrb/override/iwd2/cfelem1.pro b/gemrb/override/iwd2/cfelem1.pro
new file mode 100644
index 0000000..d550f94
Binary files /dev/null and b/gemrb/override/iwd2/cfelem1.pro differ
diff --git a/gemrb/override/iwd2/cfelemh.pro b/gemrb/override/iwd2/cfelemh.pro
new file mode 100644
index 0000000..9aaba24
Binary files /dev/null and b/gemrb/override/iwd2/cfelemh.pro differ
diff --git a/gemrb/override/iwd2/cfelemx.pro b/gemrb/override/iwd2/cfelemx.pro
new file mode 100644
index 0000000..d87881c
Binary files /dev/null and b/gemrb/override/iwd2/cfelemx.pro differ
diff --git a/gemrb/override/iwd2/cfog.pro b/gemrb/override/iwd2/cfog.pro
new file mode 100644
index 0000000..702da41
Binary files /dev/null and b/gemrb/override/iwd2/cfog.pro differ
diff --git a/gemrb/override/iwd2/cgraceh.pro b/gemrb/override/iwd2/cgraceh.pro
new file mode 100644
index 0000000..93ff65b
Binary files /dev/null and b/gemrb/override/iwd2/cgraceh.pro differ
diff --git a/gemrb/override/iwd2/cgtable.2da b/gemrb/override/iwd2/cgtable.2da
new file mode 100644
index 0000000..36e784d
--- /dev/null
+++ b/gemrb/override/iwd2/cgtable.2da
@@ -0,0 +1,20 @@
+2DA V1.0
+*
+ RESREF
+UNUSED0 *
+UNUSED1 *
+UNUSED2 *
+UNUSED3 *
+UNUSED4 *
+UNUSED5 *
+UNUSED6 *
+UNUSED7 *
+UNUSED8 *
+NECROMANCY NecroCG
+ALTERATION AlterCG
+ENCHANTMENT EnchaCG
+ABJURATION AbjurCG
+ILLUSION IllusCG
+CONJURATION ConjuCG
+INVOCATION InvocCG
+DIVINATION DivinCG
diff --git a/gemrb/override/iwd2/chant.pro b/gemrb/override/iwd2/chant.pro
new file mode 100644
index 0000000..740be7b
Binary files /dev/null and b/gemrb/override/iwd2/chant.pro differ
diff --git a/gemrb/override/iwd2/chromorb.pro b/gemrb/override/iwd2/chromorb.pro
new file mode 100644
index 0000000..c39d719
Binary files /dev/null and b/gemrb/override/iwd2/chromorb.pro differ
diff --git a/gemrb/override/iwd2/classes.2da b/gemrb/override/iwd2/classes.2da
new file mode 100644
index 0000000..e0fcf85
--- /dev/null
+++ b/gemrb/override/iwd2/classes.2da
@@ -0,0 +1,38 @@
+2DA V1.0
+*
+ NAME_REF DESC_REF ID CLASS CLAB SAVE HP USABILITY HUMAN ELF HALF_ELF DWARF HALFLING GNOME HALFORC
+BARBARIAN 34 37 1 0 CLABFI05 SAVEWAR HPBARB 1 1 1 1 1 1 1 1
+BARD 1083 9562 2 0 CLABBA01 SAVEBRD HPBRD 2 1 1 1 1 1 1 1
+CLERIC 1079 9559 3 0 CLABPR01 SAVEPRS HPPRS 4 1 1 1 1 1 1 1
+DRUID 1080 9560 4 0 CLABDR01 SAVEPRS HPPRS 8 1 1 1 1 1 1 1
+FIGHTER 10174 9556 5 0 CLABFI01 SAVEWAR HPWAR 16 1 1 1 1 1 1 1
+MONK 33 36 6 0 CLABMO01 SAVEMONK HPMONK 32 1 1 1 1 1 1 1
+PALADIN 1078 9558 7 0 CLABPA01 SAVEWAR HPWAR 64 1 1 1 1 1 1 1
+RANGER 1077 9557 8 0 CLABRN01 SAVEWAR HPWAR 128 1 1 1 1 1 1 1
+ROGUE 1082 9561 9 0 CLABTH01 SAVEROG HPROG 256 1 1 1 1 1 1 1
+SORCERER 32 35 10 0 CLABSR01 SAVEWIZ HPWIZ 512 1 1 1 1 1 1 1
+WIZARD 9987 9563 11 0 CLABMA01 SAVEWIZ HPWIZ 1024 1 1 1 1 1 1 1
+PALADIN_ILMATER 36875 39407 1 7 CLABPA01 SAVEWAR HPWAR 1 1 1 1 1 1 1 1
+PALADIN_HELM 36872 39408 2 7 CLABPA01 SAVEWAR HPWAR 2 1 1 1 1 1 1 1
+PALADIN_MYSTRA 36873 39409 4 7 CLABPA01 SAVEWAR HPWAR 4 1 1 1 1 1 1 1
+MONK_OLD_ORDER 36877 39410 8 6 CLABMO01 SAVEMONK HPMONK 8 1 1 1 1 1 1 1
+MONK_BROKEN_ONES 36878 39411 0x10 6 CLABMO01 SAVEMONK HPMONK 16 1 1 1 1 1 1 1
+MONK_DARK_MOON 36879 39412 0x20 6 CLABMO01 SAVEMONK HPMONK 32 1 1 1 1 1 1 1
+MAGE_GENERALIST 9987 9563 0x4000 11 CLABMA01 SAVEWIZ HPWIZ 0x4000 1 1 1 1 1 1 1
+MAGE_ABJURER 502 9564 0x40 11 CLABMA02 SAVEWIZ HPWIZ 0x40 1 1 1 1 1 1 1
+MAGE_CONJURER 504 9565 0x80 11 CLABMA06 SAVEWIZ HPWIZ 0x80 1 1 1 1 1 1 1
+MAGE_DIVINER 2012 9566 0x100 11 CLABMA05 SAVEWIZ HPWIZ 0x100 1 1 1 1 1 1 1
+MAGE_ENCHANTER 2022 9567 0x200 11 CLABMA09 SAVEWIZ HPWIZ 0x200 1 1 1 1 1 1 1
+MAGE_EVOKER 12786 9569 0x800 11 CLABMA07 SAVEWIZ HPWIZ 0x400 1 1 1 1 1 1 1
+MAGE_ILLUSIONIST 12785 9568 0x400 11 CLABMA08 SAVEWIZ HPWIZ 0x800 1 1 1 1 1 1 1
+MAGE_NECROMANCER 12787 9570 0x1000 11 CLABMA03 SAVEWIZ HPWIZ 0x1000 1 1 1 1 1 1 1
+MAGE_TRANSMUTER 12788 9571 0x2000 11 CLABMA04 SAVEWIZ HPWIZ 0x2000 1 1 1 1 1 1 1
+CLERIC_ILMATER 38097 38118 0x8000 3 CLABCL01 SAVEPRS HPPRS 0x8000 1 1 1 1 1 1 1
+CLERIC_LATHANDER 38098 38119 0x10000 3 CLABCL02 SAVEPRS HPPRS 0x10000 1 1 1 1 1 1 1
+CLERIC_SELUNE 38099 38120 0x20000 3 CLABCL03 SAVEPRS HPPRS 0x20000 1 1 1 1 1 1 1
+CLERIC_HELM 38100 38121 0x40000 3 CLABCL04 SAVEPRS HPPRS 0x40000 1 1 1 1 1 1 1
+CLERIC_OGHMA 38101 38122 0x80000 3 CLABCL05 SAVEPRS HPPRS 0x80000 1 1 1 1 1 1 1
+CLERIC_TEMPUS 38102 38123 0x100000 3 CLABCL06 SAVEPRS HPPRS 0x100000 1 1 1 1 1 1 1
+CLERIC_BANE 38103 38124 0x200000 3 CLABCL07 SAVEPRS HPPRS 0x200000 1 1 1 1 1 1 1
+CLERIC_MASK 38106 38125 0x400000 3 CLABCL08 SAVEPRS HPPRS 0x400000 1 1 1 1 1 1 1
+CLERIC_TALOS 38107 38126 0x800000 3 CLABCL09 SAVEPRS HPPRS 0x800000 1 1 1 1 1 1 1
diff --git a/gemrb/override/iwd2/cldamah.pro b/gemrb/override/iwd2/cldamah.pro
new file mode 100644
index 0000000..aec3d15
Binary files /dev/null and b/gemrb/override/iwd2/cldamah.pro differ
diff --git a/gemrb/override/iwd2/cloud.pro b/gemrb/override/iwd2/cloud.pro
new file mode 100644
index 0000000..3fd8e26
Binary files /dev/null and b/gemrb/override/iwd2/cloud.pro differ
diff --git a/gemrb/override/iwd2/cloudb.pro b/gemrb/override/iwd2/cloudb.pro
new file mode 100644
index 0000000..aa2fdde
Binary files /dev/null and b/gemrb/override/iwd2/cloudb.pro differ
diff --git a/gemrb/override/iwd2/cloudbh.pro b/gemrb/override/iwd2/cloudbh.pro
new file mode 100644
index 0000000..c3aa212
Binary files /dev/null and b/gemrb/override/iwd2/cloudbh.pro differ
diff --git a/gemrb/override/iwd2/cloudkil.pro b/gemrb/override/iwd2/cloudkil.pro
new file mode 100644
index 0000000..2dd544a
Binary files /dev/null and b/gemrb/override/iwd2/cloudkil.pro differ
diff --git a/gemrb/override/iwd2/cloudks.pro b/gemrb/override/iwd2/cloudks.pro
new file mode 100644
index 0000000..27a06e4
Binary files /dev/null and b/gemrb/override/iwd2/cloudks.pro differ
diff --git a/gemrb/override/iwd2/clowncol.2da b/gemrb/override/iwd2/clowncol.2da
new file mode 100644
index 0000000..f6355a7
--- /dev/null
+++ b/gemrb/override/iwd2/clowncol.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+*
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+HAIR 0 1 2 3 4 5 6 7 79 80 81 82 110 111 * * * * * * * * * * * * * * * * *
+SKIN 8 9 10 11 12 13 14 15 16 17 18 19 20 83 84 85 86 87 88 89 90 105 106 107 108 109 112 113 114 * *
+MAJOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
+MINOR 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
diff --git a/gemrb/override/iwd2/clskills.2da b/gemrb/override/iwd2/clskills.2da
new file mode 100644
index 0000000..3c0cf56
--- /dev/null
+++ b/gemrb/override/iwd2/clskills.2da
@@ -0,0 +1,15 @@
+2DA V1.0
+*
+ HATERACE CLERICSPELL MAGESPELL STARTXP BARDSKILL THIEFSKILL LAYHANDS TURNLEVEL BOOKTYPE
+UNUSED * * * * 0 * * * 0 0
+BARBARIAN * * * 0 * SKILLS * 0 0
+BARD * * MXSPLBRD 0 SKILLBRD SKILLS * 0 1
+CLERIC * MXSPLPRS * 0 * SKILLS * 1 1
+DRUID * MXSPLDRU * 0 * SKILLS * 0 1
+FIGHTER * * * 0 * SKILLS * 0 0
+MONK * * * 0 * SKILLS * 0 0
+PALADIN * MXSPLPAL * 0 * SKILLS LAYHANDS 5 1
+RANGER HATERACE MXSPLRAN * 0 * SKILLS * 0 1
+ROGUE * * * 0 * SKILLS * 0 0
+SORCERER * * MXSPLSRC 0 * SKILLS * 0 2
+WIZARD * * MXSPLWIZ 0 * SKILLS * 0 1
diff --git a/gemrb/override/iwd2/clwounh.pro b/gemrb/override/iwd2/clwounh.pro
new file mode 100644
index 0000000..10efa84
Binary files /dev/null and b/gemrb/override/iwd2/clwounh.pro differ
diff --git a/gemrb/override/iwd2/cmdamah.pro b/gemrb/override/iwd2/cmdamah.pro
new file mode 100644
index 0000000..d408401
Binary files /dev/null and b/gemrb/override/iwd2/cmdamah.pro differ
diff --git a/gemrb/override/iwd2/cmwounh.pro b/gemrb/override/iwd2/cmwounh.pro
new file mode 100644
index 0000000..e7e644d
Binary files /dev/null and b/gemrb/override/iwd2/cmwounh.pro differ
diff --git a/gemrb/override/iwd2/cobones.pro b/gemrb/override/iwd2/cobones.pro
new file mode 100644
index 0000000..0f2475a
Binary files /dev/null and b/gemrb/override/iwd2/cobones.pro differ
diff --git a/gemrb/override/iwd2/cobonh1.pro b/gemrb/override/iwd2/cobonh1.pro
new file mode 100644
index 0000000..05d1e99
Binary files /dev/null and b/gemrb/override/iwd2/cobonh1.pro differ
diff --git a/gemrb/override/iwd2/cobonh2.pro b/gemrb/override/iwd2/cobonh2.pro
new file mode 100644
index 0000000..d42d11e
Binary files /dev/null and b/gemrb/override/iwd2/cobonh2.pro differ
diff --git a/gemrb/override/iwd2/cocold.pro b/gemrb/override/iwd2/cocold.pro
new file mode 100644
index 0000000..6953993
Binary files /dev/null and b/gemrb/override/iwd2/cocold.pro differ
diff --git a/gemrb/override/iwd2/cocoldh.pro b/gemrb/override/iwd2/cocoldh.pro
new file mode 100644
index 0000000..a785192
Binary files /dev/null and b/gemrb/override/iwd2/cocoldh.pro differ
diff --git a/gemrb/override/iwd2/cofire.pro b/gemrb/override/iwd2/cofire.pro
new file mode 100644
index 0000000..ef23e1b
Binary files /dev/null and b/gemrb/override/iwd2/cofire.pro differ
diff --git a/gemrb/override/iwd2/coldh.pro b/gemrb/override/iwd2/coldh.pro
new file mode 100644
index 0000000..dd5e692
Binary files /dev/null and b/gemrb/override/iwd2/coldh.pro differ
diff --git a/gemrb/override/iwd2/colrspry.pro b/gemrb/override/iwd2/colrspry.pro
new file mode 100644
index 0000000..1fa7f2d
Binary files /dev/null and b/gemrb/override/iwd2/colrspry.pro differ
diff --git a/gemrb/override/iwd2/confush.pro b/gemrb/override/iwd2/confush.pro
new file mode 100644
index 0000000..3027e09
Binary files /dev/null and b/gemrb/override/iwd2/confush.pro differ
diff --git a/gemrb/override/iwd2/confusp.pro b/gemrb/override/iwd2/confusp.pro
new file mode 100644
index 0000000..bb4b8b6
Binary files /dev/null and b/gemrb/override/iwd2/confusp.pro differ
diff --git a/gemrb/override/iwd2/confusw.pro b/gemrb/override/iwd2/confusw.pro
new file mode 100644
index 0000000..ea6e585
Binary files /dev/null and b/gemrb/override/iwd2/confusw.pro differ
diff --git a/gemrb/override/iwd2/conjuh.pro b/gemrb/override/iwd2/conjuh.pro
new file mode 100644
index 0000000..34eefc4
Binary files /dev/null and b/gemrb/override/iwd2/conjuh.pro differ
diff --git a/gemrb/override/iwd2/conjut.pro b/gemrb/override/iwd2/conjut.pro
new file mode 100644
index 0000000..6ef8ba1
Binary files /dev/null and b/gemrb/override/iwd2/conjut.pro differ
diff --git a/gemrb/override/iwd2/copest.pro b/gemrb/override/iwd2/copest.pro
new file mode 100644
index 0000000..6853632
Binary files /dev/null and b/gemrb/override/iwd2/copest.pro differ
diff --git a/gemrb/override/iwd2/cry150.pro b/gemrb/override/iwd2/cry150.pro
new file mode 100644
index 0000000..ac00906
Binary files /dev/null and b/gemrb/override/iwd2/cry150.pro differ
diff --git a/gemrb/override/iwd2/cry200.pro b/gemrb/override/iwd2/cry200.pro
new file mode 100644
index 0000000..2ab9ac5
Binary files /dev/null and b/gemrb/override/iwd2/cry200.pro differ
diff --git a/gemrb/override/iwd2/cry225.pro b/gemrb/override/iwd2/cry225.pro
new file mode 100644
index 0000000..afb7c92
Binary files /dev/null and b/gemrb/override/iwd2/cry225.pro differ
diff --git a/gemrb/override/iwd2/cry250.pro b/gemrb/override/iwd2/cry250.pro
new file mode 100644
index 0000000..31ee26e
Binary files /dev/null and b/gemrb/override/iwd2/cry250.pro differ
diff --git a/gemrb/override/iwd2/cry500np.pro b/gemrb/override/iwd2/cry500np.pro
new file mode 100644
index 0000000..13d718a
Binary files /dev/null and b/gemrb/override/iwd2/cry500np.pro differ
diff --git a/gemrb/override/iwd2/csdamah.pro b/gemrb/override/iwd2/csdamah.pro
new file mode 100644
index 0000000..76eaa24
Binary files /dev/null and b/gemrb/override/iwd2/csdamah.pro differ
diff --git a/gemrb/override/iwd2/cstrenh.pro b/gemrb/override/iwd2/cstrenh.pro
new file mode 100644
index 0000000..eb5136a
Binary files /dev/null and b/gemrb/override/iwd2/cstrenh.pro differ
diff --git a/gemrb/override/iwd2/cswounh.pro b/gemrb/override/iwd2/cswounh.pro
new file mode 100644
index 0000000..195106b
Binary files /dev/null and b/gemrb/override/iwd2/cswounh.pro differ
diff --git a/gemrb/override/iwd2/curseh.pro b/gemrb/override/iwd2/curseh.pro
new file mode 100644
index 0000000..e3c45c4
Binary files /dev/null and b/gemrb/override/iwd2/curseh.pro differ
diff --git a/gemrb/override/iwd2/cwelem1.pro b/gemrb/override/iwd2/cwelem1.pro
new file mode 100644
index 0000000..a0ec19d
Binary files /dev/null and b/gemrb/override/iwd2/cwelem1.pro differ
diff --git a/gemrb/override/iwd2/cwelemh.pro b/gemrb/override/iwd2/cwelemh.pro
new file mode 100644
index 0000000..9f2052d
Binary files /dev/null and b/gemrb/override/iwd2/cwelemh.pro differ
diff --git a/gemrb/override/iwd2/cwelemx.pro b/gemrb/override/iwd2/cwelemx.pro
new file mode 100644
index 0000000..3f0cdb7
Binary files /dev/null and b/gemrb/override/iwd2/cwelemx.pro differ
diff --git a/gemrb/override/iwd2/dagger.pro b/gemrb/override/iwd2/dagger.pro
new file mode 100644
index 0000000..284c351
Binary files /dev/null and b/gemrb/override/iwd2/dagger.pro differ
diff --git a/gemrb/override/iwd2/daggerex.pro b/gemrb/override/iwd2/daggerex.pro
new file mode 100644
index 0000000..e33a595
Binary files /dev/null and b/gemrb/override/iwd2/daggerex.pro differ
diff --git a/gemrb/override/iwd2/damage.2da b/gemrb/override/iwd2/damage.2da
new file mode 100644
index 0000000..cbb1b63
--- /dev/null
+++ b/gemrb/override/iwd2/damage.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+*
+ MAIN SPARKS GRADIENT
+CRIT BLOODCR * 47
+SMALL BLOODS * 47
+MEDIUM BLOODM * 47
+LARGE BLOODL * 47
+FIRES FIRIMP SPBURN 19
+FIREM FIRIMP SPBURN 19
+FIREL FIRIMP SPBURN 19
+SPARKS SPSHKIMP SPSPARKS -1
+SPARKM SPSHKIMP SPSPARKS -1
+SPARKL SPSHKIMP SPSPARKS -1
+ICES FIRIMP * 71
+ICEM FIRIMP * 71
+ICEL FIRIMP * 71
diff --git a/gemrb/override/iwd2/dart.pro b/gemrb/override/iwd2/dart.pro
new file mode 100644
index 0000000..a8ec832
Binary files /dev/null and b/gemrb/override/iwd2/dart.pro differ
diff --git a/gemrb/override/iwd2/dartex.pro b/gemrb/override/iwd2/dartex.pro
new file mode 100644
index 0000000..b09ac20
Binary files /dev/null and b/gemrb/override/iwd2/dartex.pro differ
diff --git a/gemrb/override/iwd2/dattach.pro b/gemrb/override/iwd2/dattach.pro
new file mode 100644
index 0000000..bb53636
Binary files /dev/null and b/gemrb/override/iwd2/dattach.pro differ
diff --git a/gemrb/override/iwd2/dbreath.pro b/gemrb/override/iwd2/dbreath.pro
new file mode 100644
index 0000000..e64a64f
Binary files /dev/null and b/gemrb/override/iwd2/dbreath.pro differ
diff --git a/gemrb/override/iwd2/ddeath.pro b/gemrb/override/iwd2/ddeath.pro
new file mode 100644
index 0000000..4b587b4
Binary files /dev/null and b/gemrb/override/iwd2/ddeath.pro differ
diff --git a/gemrb/override/iwd2/ddeath2.pro b/gemrb/override/iwd2/ddeath2.pro
new file mode 100644
index 0000000..e1a5407
Binary files /dev/null and b/gemrb/override/iwd2/ddeath2.pro differ
diff --git a/gemrb/override/iwd2/ddoorh.pro b/gemrb/override/iwd2/ddoorh.pro
new file mode 100644
index 0000000..fa09734
Binary files /dev/null and b/gemrb/override/iwd2/ddoorh.pro differ
diff --git a/gemrb/override/iwd2/defsound.2da b/gemrb/override/iwd2/defsound.2da
new file mode 100644
index 0000000..22cfee3
--- /dev/null
+++ b/gemrb/override/iwd2/defsound.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+*
+ RESREF
+OPEN AMB_D03A
+CLOSE AMB_D03B
+HOPEN AMB_D04A
+HCLOSE AMB_D04B
+BUTTON1 GAM_09
+BUTTON2 GAM_03
+BUTTON3 GAM_04
+OPENFAIL *
+CLOSEFAIL *
+ITEM_GONE EFF_M02
+SECRET ACT_09
+11 *
+12 *
+13 *
+14 *
+15 *
+16 *
+17 *
+18 *
+19 *
+LIGHTNING1 AMB_E13A
+LIGHTNING2 AMB_E13B
+LIGHTNING3 AMB_E13F
+RAIN AMB_E11
+SNOW AMB_E02B
+
diff --git a/gemrb/override/iwd2/destruh.pro b/gemrb/override/iwd2/destruh.pro
new file mode 100644
index 0000000..87dfeaa
Binary files /dev/null and b/gemrb/override/iwd2/destruh.pro differ
diff --git a/gemrb/override/iwd2/dfog.pro b/gemrb/override/iwd2/dfog.pro
new file mode 100644
index 0000000..702da41
Binary files /dev/null and b/gemrb/override/iwd2/dfog.pro differ
diff --git a/gemrb/override/iwd2/disint.pro b/gemrb/override/iwd2/disint.pro
new file mode 100644
index 0000000..5d5f5d7
Binary files /dev/null and b/gemrb/override/iwd2/disint.pro differ
diff --git a/gemrb/override/iwd2/disinth.pro b/gemrb/override/iwd2/disinth.pro
new file mode 100644
index 0000000..2b4b80f
Binary files /dev/null and b/gemrb/override/iwd2/disinth.pro differ
diff --git a/gemrb/override/iwd2/dispel.pro b/gemrb/override/iwd2/dispel.pro
new file mode 100644
index 0000000..3984714
Binary files /dev/null and b/gemrb/override/iwd2/dispel.pro differ
diff --git a/gemrb/override/iwd2/divinh.pro b/gemrb/override/iwd2/divinh.pro
new file mode 100644
index 0000000..475830a
Binary files /dev/null and b/gemrb/override/iwd2/divinh.pro differ
diff --git a/gemrb/override/iwd2/divint.pro b/gemrb/override/iwd2/divint.pro
new file mode 100644
index 0000000..e2972f2
Binary files /dev/null and b/gemrb/override/iwd2/divint.pro differ
diff --git a/gemrb/override/iwd2/dspell.pro b/gemrb/override/iwd2/dspell.pro
new file mode 100644
index 0000000..4eb4d1d
Binary files /dev/null and b/gemrb/override/iwd2/dspell.pro differ
diff --git a/gemrb/override/iwd2/dspellh.pro b/gemrb/override/iwd2/dspellh.pro
new file mode 100644
index 0000000..9029a53
Binary files /dev/null and b/gemrb/override/iwd2/dspellh.pro differ
diff --git a/gemrb/override/iwd2/effects.ids b/gemrb/override/iwd2/effects.ids
new file mode 100644
index 0000000..3aeb09f
--- /dev/null
+++ b/gemrb/override/iwd2/effects.ids
@@ -0,0 +1,356 @@
+IDS
+0x0 ACVsDamageTypeModifierIWD2
+0x1 AttacksPerRoundModifier
+0x2 Cure:Sleep
+0x3 State:Berserk
+0x4 Cure:Berserk
+0x5 State:Charmed
+0x6 CharismaModifier
+0x7 Color:SetPalette
+0x8 Color:SetRGB
+0x9 Color:PulseRGB
+0xa ConstitutionModifier
+0xb Cure:Poison
+0xc Damage
+0xd Death
+0xe Cure:Defrost
+0xf DexterityModifier
+0x10 State:Hasted
+0x11 CurrentHPModifier
+0x12 MaximumHPModifier
+0x13 IntelligenceModifier
+0x14 State:Invisible
+0x15 LoreModifier
+0x16 LuckModifier
+0x17 MoraleModifier
+0x18 State:Panic
+0x19 State:Poisoned
+0x1a RemoveCurse
+0x1b AcidResistanceModifier
+0x1c ColdResistanceModifier
+0x1d ElectricityResistanceModifier
+0x1e FireResistanceModifier
+0x1f MagicDamageResistanceModifier
+0x20 Cure:Death
+0x21 SaveVsDeathModifier
+0x22 SaveVsWandsModifier
+0x23 SaveVsPolyModifier
+0x24 SaveVsBreathModifier
+0x25 SaveVsSpellsModifier
+0x26 State:Silenced
+0x27 State:Helpless
+0x28 State:Slowed
+0x29 Sparkle
+0x2a WizardSpellSlotsModifier
+0x2b Cure:Petrification
+0x2c StrengthModifier
+0x2d State:Stun
+0x2e Cure:Stun
+0x2f Cure:Invisible
+0x30 Cure:Silence
+0x31 WisdomModifier
+0x32 Color:BriefRGB
+0x33 Color:DarkenRGB
+0x34 Color:GlowRGB
+0x35 AnimationIDModifier
+0x36 ToHitModifier
+0x37 KillCreatureType
+0x38 Alignment:Invert
+0x39 Alignment:Change
+0x3a DispelEffects
+0x3b StealthModifier
+0x3c MiscastMagicModifier
+0x3d AlchemyModifier
+0x3e PriestSpellSlotsModifier
+0x3f State:Infravision
+0x40 Cure:Infravision
+0x41 State:Blur
+0x42 TransparencyModifier
+0x43 SummonCreature
+0x44 UnsummonCreature
+0x45 State:NonDetection
+0x46 Cure:NonDetection
+0x47 SexModifier
+0x48 AIIdentifierModifier
+0x49 DamageBonusModifier
+0x4a State:Blind
+0x4b Cure:Blind
+0x4c State:Feeblemind
+0x4d Cure:Feeblemind
+0x4e State:Diseased
+0x4f Cure:Disease
+0x50 State:Deafness
+0x51 Cure:Deafness
+0x52 SetAIScript
+0x53 Protection:Projectile
+0x54 MagicalFireResistanceModifier
+0x55 MagicalColdResistanceModifier
+0x56 SlashingResistanceModifier
+0x57 CrushingResistanceModifier
+0x58 PiercingResistanceModifier
+0x59 MissilesResistanceModifier
+0x5a OpenLocksModifier
+0x5b FindTrapsModifier
+0x5c PickPocketsModifier
+0x5d FatigueModifier
+0x5e IntoxicationModifier
+0x5f TrackingModifier
+0x60 LevelModifier
+0x61 StrengthBonusModifier
+0x62 State:Regenerating
+0x63 SpellDurationModifier
+0x64 Protection:Creature
+0x65 Protection:Opcode
+0x66 Protection:SpellLevel
+0x67 ChangeName
+0x68 ExperienceModifier
+0x69 GoldModifier
+0x6a MoraleBreakModifier
+0x6b PortraitChange
+0x6c ReputationModifier
+0x6d State:Hold3
+0x6e RetreatFrom
+0x6f Item:CreateMagic
+0x70 Item:Remove
+0x71 Item:Equip
+0x72 Dither
+0x73 DetectAlignment
+0x74 Cure:Invisible2
+0x75 Reveal:Area
+0x76 Reveal:Creatures
+0x77 MirrorImage
+0x78 Protection:Weapons
+0x79 VisualAnimationEffect
+0x7a Item:CreateInventory
+0x7b Item:RemoveInventory
+0x7c DimensionDoor
+0x7d Unlock
+0x7e MovementRateModifier
+0x7f MonsterSummoning
+0x80 State:Confused
+0x81 AidNonCumulative
+0x82 BlessNonCumulative
+0x83 ChantNonCumulative
+0x84 DrawUponHolyMight
+0x85 LuckCumulative
+0x86 State:Petrification
+0x87 Polymorph
+0x88 ForceVisible
+0x89 ChantBadNonCumulative
+0x8a AnimationStateChange
+0x8b DisplayString
+0x8c CastingGlow
+0x8d VisualSpellHit
+0x8e Icon:Display
+0x8f Item:CreateInSlot
+0x90 DisableButton
+0x91 DisableCasting
+0x92 Spell:Cast
+0x93 Spell:Learn
+0x94 Spell:CastPoint
+0x95 Identify
+0x96 FindTraps
+0x97 ReplaceCreature
+0x98 PlayMovie
+0x99 Overlay:Sanctuary
+0x9a Overlay:Entangle
+0x9b Overlay:MinorGlobe
+0x9c Overlay:ShieldGlobe
+0x9d Overlay:Web
+0x9e Overlay:Grease
+0x9f MirrorImageModifier
+0xa0 Cure:Sanctuary
+0xa1 Cure:Panic
+0xa2 Cure:Hold
+0xa3 FreeAction
+0xa4 Cure:Intoxication
+0xa5 PauseTarget
+0xa6 MagicResistanceModifier
+0xa7 MissileHitModifier
+0xa8 RemoveCreature
+0xa9 Icon:Disable
+0xaa DamageAnimation
+0xab Spell:Add
+0xac Spell:Remove
+0xad PoisonResistanceModifier
+0xae PlaySound
+0xaf State:Hold
+0xb0 MovementRateModifier2
+0xb1 ApplyEffect
+0xb2 ToHitVsCreature
+0xb3 DamageVsCreature
+0xb4 CantUseItem
+0xb5 CantUseItemType
+0xb6 ApplyEffectItem
+0xb7 ApplyEffectItemType
+0xb8 DontJumpModifier
+0xb9 State:Hold2
+0xba MoveToArea
+0xbb Variable:StoreLocalVariable
+0xbc AuraCleansingModifier
+0xbd CastingSpeedModifier
+0xbe AttackSpeedModifier
+0xbf CastingLevelModifier
+0xc0 FindFamiliar
+0xc1 InvisibleDetection
+0xc2 IgnoreDialogPause
+0xc3 FamiliarBond
+0xc4 FamiliarMarker
+0xc5 Bounce:Projectile
+0xc6 Bounce:Opcode
+0xc7 Bounce:SpellLevel
+0xc8 Bounce:SpellLevelDec
+0xc9 Protection:SpellLevelDec
+0xca Bounce:School
+0xcb Bounce:SecondaryType
+0xcc Protection:School
+0xcd Protection:SecondaryType
+0xce Protection:Spell2
+0xcf Bounce:Spell
+0xd0 MinimumHPModifier
+0xd1 PowerWordKill
+0xd2 PowerWordStun
+0xd3 State:Imprisonment
+0xd4 Cure:Imprisonment
+0xd5 Maze
+0xd6 CastFromList
+0xd7 PlayVisualEffect
+0xd8 LevelDrainModifier
+0xd9 PowerWordSleep
+0xda IronSkins
+0xdb ACVsCreatureType
+0xdc DispelSchool
+0xdd DispelSecondaryType
+0xde RandomTeleport
+0xdf Protection:SchoolDec
+0xe0 Cure:LevelDrain
+0xe1 Reveal:Magic
+0xe2 Protection:SecondaryTypeDec
+0xe3 Bounce:SchoolDec
+0xe4 Bounce:SecondaryTypeDec
+0xe5 DispelSchoolOne
+0xe6 DispelSecondaryTypeOne
+0xe7 TimeStop
+0xe8 Color:FadeRGB
+0xe9 IWDVisualSpellHit
+0xea ColdDamage
+0xeb CastingGlow2
+0xec ChillTouchPanic
+0xed CrushingDamage
+0xee SaveBonus
+0xef SlowPoison
+0xf0 IWDMonsterSummoning
+0xf1 VampiricTouch
+0xf2 Overlay
+0xf3 AnimateDead
+0xf4 Prayer2
+0xf5 Curse2
+0xf6 SummonMonster2
+0xf7 BurningBlood
+0xf8 SummonShadowMonster
+0xf9 Recitation
+0xfa RecitationBad
+0xfb State:HoldNoIcon2
+0xfc BlindingOrb
+0xfd ACVsDamageTypeModifier2
+0xfe RemoveEffects
+0xff SalamanderAura
+0x100 UmberHulkGaze
+0x101 ZombieLordAura
+0x102 Protection:Spell
+0x103 SummonCreature2
+0x104 AvatarRemoval
+0x105 Protection:Opcode2
+0x106 SummonPomab
+0x107 ControlUndead
+0x108 StaticCharge
+0x109 CloakOfFear
+0x10a MovementRateModifier3
+0x10b Cure:Confusion
+0x10c EyeOfTheMind
+0x10d EyeOfTheSword
+0x10e EyeOfTheMage
+0x10f EyeOfVenom
+0x110 EyeOfTheSpirit
+0x111 EyeOfFortitude
+0x112 EyeOfStone
+0x113 RemoveSevenEyes
+0x114 RemoveEffect
+0x115 SoulEater
+0x116 ShroudOfFlame
+0x117 AnimalRage
+0x118 TurnUndead
+0x119 VitriolicSphere
+0x11a SuppressHP
+0x11b FloatText
+0x11c MaceOfDisruption
+0x11d State:Sleep
+0x11e Reveal:Tracks
+0x11f Protection:Backstab
+0x120 State:Set
+0x121 Cutscene
+0x122 Protection:Spell3
+0x123 RodOfSmithing
+0x124 MagicalRest
+0x125 BeholderDispelMagic
+0x126 HarpyWail
+0x127 JackalWereGaze
+0x128 ModifyGlobalVariable
+0x129 HideInShadowsModifier
+0x12a UseMagicDeviceModifier
+400 Hopelessness
+401 ProtectionFromEvil
+402 AddEffectsList
+403 ArmorOfFaith
+404 Nausea
+405 Enfeeblement
+406 FireShield
+407 DeathWard
+408 HolyPower
+409 RighteousWrath
+410 SummonAlly
+411 SummonEnemy
+412 Control2
+413 VisualEffectIWD2
+414 ResilientSphere
+415 Barkskin
+416 BleedingWounds
+417 AreaEffect
+418 FreeAction2
+419 Unconsciousness
+420 Death2
+421 EntropyShield
+422 StormShell
+423 ProtectionFromElements
+424 State:HoldNoIcon3
+425 ControlUndead2
+426 Aegis
+427 ExecutionerEyes
+428 Death3
+429 EffectsOnStruck
+430 ProjectileUseEffectList
+431 EnergyDrain
+432 TortoiseShell
+433 Blink
+434 PersistentUseEffectList
+435 DayBlindness
+436 DamageReduction
+437 Disguise
+438 HeroicInspiration
+439 OffscreenAIModifier
+440 BarbarianRage
+441 MovementRateModifier4
+443 MissileDamageReduction
+444 TensersTransformation
+446 SmiteEvil
+447 Restoration
+448 AlicornLance
+449 CallLightning
+450 GlobeInvulnerability
+451 LowerResistance
+452 Bane
+453 PowerAttack
+454 Expertise
+455 ArterialStrike
+456 Hamstring
+457 RapidShot
diff --git a/gemrb/override/iwd2/efftext.2da b/gemrb/override/iwd2/efftext.2da
new file mode 100644
index 0000000..6044889
--- /dev/null
+++ b/gemrb/override/iwd2/efftext.2da
@@ -0,0 +1,25 @@
+2DA V1.0
+-1
+ EFFECT_NAME STRREF
+13 DEATH 14026
+12 DAMAGE 14027
+68 UNSUMMON 14065
+132 DRAWUPONHOLY 14125
+400 HOPELESSNESS 1280
+401 PROTFROMEVIL 14769
+403 ARMOROFFAITH 509
+404 NAUSEA 4390
+405 ENFEEBLEMENT 7924
+406 FIRESHIELD 736
+407 DEATHWARD 1309
+414 RESILIENT 4732
+415 BARKSKIN 14785
+418 FREEACTION 14794
+419 UNCONSCIOUS 20438
+421 ENTROPYSHIELD 8104
+422 STORMSHELL 8830
+423 PROTFROMELEM 8842
+448 ALICORNLANCE 21646
+450 MINORGLOBE 14771
+451 LOWERRESISTANCE 16967
+452 BANE 14406
diff --git a/gemrb/override/iwd2/electrh.pro b/gemrb/override/iwd2/electrh.pro
new file mode 100644
index 0000000..2046bda
Binary files /dev/null and b/gemrb/override/iwd2/electrh.pro differ
diff --git a/gemrb/override/iwd2/emotion.pro b/gemrb/override/iwd2/emotion.pro
new file mode 100644
index 0000000..2178dd4
Binary files /dev/null and b/gemrb/override/iwd2/emotion.pro differ
diff --git a/gemrb/override/iwd2/enchah.pro b/gemrb/override/iwd2/enchah.pro
new file mode 100644
index 0000000..bc16f04
Binary files /dev/null and b/gemrb/override/iwd2/enchah.pro differ
diff --git a/gemrb/override/iwd2/enchan.pro b/gemrb/override/iwd2/enchan.pro
new file mode 100644
index 0000000..5c3feaf
Binary files /dev/null and b/gemrb/override/iwd2/enchan.pro differ
diff --git a/gemrb/override/iwd2/enchannp.pro b/gemrb/override/iwd2/enchannp.pro
new file mode 100644
index 0000000..e09813a
Binary files /dev/null and b/gemrb/override/iwd2/enchannp.pro differ
diff --git a/gemrb/override/iwd2/enchat.pro b/gemrb/override/iwd2/enchat.pro
new file mode 100644
index 0000000..0c99116
Binary files /dev/null and b/gemrb/override/iwd2/enchat.pro differ
diff --git a/gemrb/override/iwd2/entangle.pro b/gemrb/override/iwd2/entangle.pro
new file mode 100644
index 0000000..77a9dc4
Binary files /dev/null and b/gemrb/override/iwd2/entangle.pro differ
diff --git a/gemrb/override/iwd2/entropy.2da b/gemrb/override/iwd2/entropy.2da
new file mode 100644
index 0000000..c65562b
--- /dev/null
+++ b/gemrb/override/iwd2/entropy.2da
@@ -0,0 +1,62 @@
+2DA V1.0
+*
+1 1
+3 3
+4 4
+5 5
+6 6
+8 8
+9 9
+10 10
+11 11
+13 13
+14 14
+15 15
+16 16
+18 18
+19 19
+20 20
+23 23
+1A 0x1a
+1c 0x1c
+1d 0x1d
+1e 0x1e
+1f 0x1f
+21 0x21
+22 0x22
+23 0x23
+24 0x24
+37 0x37
+39 0x39
+3a 0x3a
+3b 0x3b
+4f 0x4f
+65 0x65
+66 0x66
+69 0x69
+6b 0x6b
+bb 0xbb
+bf 0xbf
+c0 0xc0
+c1 0xc1
+d9 0xd9
+e2 0xe2
+e3 0xe3
+e4 0xe4
+e5 0xe5
+e6 0xe6
+e7 0xe7
+e8 0xe8
+e9 0xe9
+fa 0xfa
+10b 0x10b
+10c 0x10c
+10e 0x10e
+11c 0x11c
+129 0x129
+12e 0x12e
+138 0x138
+139 0x139
+13b 0x13b
+159 0x159
+15e 0x15e
diff --git a/gemrb/override/iwd2/equake.pro b/gemrb/override/iwd2/equake.pro
new file mode 100644
index 0000000..c8f920c
Binary files /dev/null and b/gemrb/override/iwd2/equake.pro differ
diff --git a/gemrb/override/iwd2/exaltah.pro b/gemrb/override/iwd2/exaltah.pro
new file mode 100644
index 0000000..6a02ac3
Binary files /dev/null and b/gemrb/override/iwd2/exaltah.pro differ
diff --git a/gemrb/override/iwd2/factioh.pro b/gemrb/override/iwd2/factioh.pro
new file mode 100644
index 0000000..60d568b
Binary files /dev/null and b/gemrb/override/iwd2/factioh.pro differ
diff --git a/gemrb/override/iwd2/fbreath.pro b/gemrb/override/iwd2/fbreath.pro
new file mode 100644
index 0000000..80ac60d
Binary files /dev/null and b/gemrb/override/iwd2/fbreath.pro differ
diff --git a/gemrb/override/iwd2/featreq.2da b/gemrb/override/iwd2/featreq.2da
new file mode 100644
index 0000000..06ebb23
--- /dev/null
+++ b/gemrb/override/iwd2/featreq.2da
@@ -0,0 +1,79 @@
+2DA V1.0
+4
+ MULTIPLE MAX_LEVEL A_STAT A_VALUE B_STAT B_VALUE C_STAT C_VALUE D_STAT D_VALUE A_OP B_OP C_OP D_OP
+AEGIS_OF_RIME 0 1 SPELLCRAFT 10 0 0 CLASSLEVELMAGE 7 CLASSLEVELSORCEROR 7 * * * *
+AMBIDEXTERITY 0 1 DEX 15 0 0 0 0 0 0 * * * *
+AQUA_MORTIS 0 1 SPELLCRAFT 10 0 0 CLASSLEVELMAGE 7 CLASSLEVELSORCEROR 7 * * * *
+ARMOR_PROF FEAT_ARMOUR 3 0 0 0 0 0 0 0 0 * * * *
+ARMORED_ARCANA FEAT_ARMORED_ARCANA 3 CLASSLEVELMAGE 1 CLASSLEVELSORCEROR 1 0 0 0 0 * * * *
+ARTERIAL_STRIKE 0 1 CLASSLEVELTHIEF 1 0 0 HITBONUS 4 0 0 * * * *
+BLIND_FIGHT 0 1 0 0 0 0 0 0 0 0 * * * *
+BULLHEADED 0 1 RACE 1 RACE 4 0 0 0 0 1 1 * *
+CLEAVE FEAT_CLEAVE 2 HITBONUS 4 FEAT_CLEAVE 1 FEATS2 32 0 0 * 0 8 *
+COMBAT_CASTING 0 1 0 0 0 0 0 0 0 0 * * * *
+COURTEOUS_MAGOCRACY 0 1 0 0 0 0 0 0 0 0 * * * *
+CRIPPLING_STRIKE 0 1 CLASSLEVELTHIEF 10 0 0 0 0 0 0 * * * *
+DASH 0 1 0 0 0 0 0 0 0 0 * * * *
+DEFLECT_ARROWS 0 1 DEX 13 0 0 0 0 0 0 * * * *
+DIRTY_FIGHTING 0 1 DEX 13 0 0 HITBONUS 2 0 0 * * * *
+DISCIPLINE 0 1 0 0 0 0 0 0 0 0 * * * *
+DODGE 0 1 DEX 13 0 0 0 0 0 0 * * * *
+ENVENOM_WEAPON 0 1 CLASSLEVELTHIEF 1 0 0 ALCHEMY 8 0 0 * * * *
+EXOTIC_BASTARD FEAT_BASTARDSWORD 3 0 0 0 0 0 0 0 0 * * * *
+EXPERTISE 0 1 INT 13 0 0 0 0 0 0 * * * *
+EXTRA_RAGE FEAT_EXTRA_RAGE 3 CLASSLEVELBARBARIAN 1 0 0 0 0 0 0 * * * *
+EXTRA_SHAPESHIFTING FEAT_EXTRA_SHAPE 3 CLASSLEVELDRUID 3 0 0 0 0 0 0 * * * *
+EXTRA_SMITING FEAT_EXTRA_SMITING 3 CLASSLEVELPALADIN 2 0 0 0 0 0 0 * * * *
+EXTRA_TURNING FEAT_EXTRA_TURNING 3 TURNUNDEADLEVEL 1 0 0 0 0 0 0 * * * *
+FIENDSLAYER 0 1 CLASSLEVELPALADIN 8 0 0 FEAT_LARGE_SWORD 2 FEAT_GREAT_SWORD 2 * * * *
+FORESTER 0 1 0 0 0 0 0 0 0 0 * * * *
+GREAT_FORTITUDE 0 1 0 0 0 0 0 0 0 0 * * * *
+HAMSTRING 0 1 CLASSLEVELTHIEF 3 0 0 0 0 0 0 * * * *
+HERETICS_BANE 0 1 CLASSLEVELCLERIC 1 CLASSLEVELPALADIN 3 HITBONUS 4 0 0 * * * *
+HEROIC_INSPIRATION 0 1 AnyOfThreeGE -1 CLASSLEVELPALADIN 1 CLASSLEVELBARBARIAN 1 CLASSLEVELBARD 1 * * * *
+IMPROVED_CRITICAL 0 1 HITBONUS 8 0 0 0 0 0 0 * * * *
+IMPROVED_EVASION 0 1 CLASSLEVELTHIEF 10 0 0 0 0 0 0 * * * *
+IMPROVED_INITIATIVE 0 1 0 0 0 0 0 0 0 0 * * * *
+IMPROVED_TURNING 0 1 CLASSLEVELCLERIC 1 0 0 CLASSLEVELPALADIN 3 0 0 * * * *
+IRON_WILL 0 1 0 0 0 0 0 0 0 0 * * * *
+LIGHTNING_REFLEXES 0 1 0 0 0 0 0 0 0 0 * * * *
+LINGERING_SONG 0 1 CLASSLEVELBARD 1 0 0 0 0 0 0 * * * *
+LUCK_OF_HEROES 0 1 RACE 1 0 0 0 0 0 0 1 * * *
+MARTIAL_AXE FEAT_AXE 3 CLASSLEVELFIGHTER 4 FEAT_AXE 2 0 0 0 0 * 0 * *
+MARTIAL_BOW FEAT_BOW 3 CLASSLEVELFIGHTER 4 FEAT_BOW 2 0 0 0 0 * 0 * *
+MARTIAL_FLAIL FEAT_FLAIL 3 CLASSLEVELFIGHTER 4 FEAT_FLAIL 2 0 0 0 0 * 0 * *
+MARTIAL_GREATSWORD FEAT_GREAT_SWORD 3 CLASSLEVELFIGHTER 4 FEAT_GREAT_SWORD 2 0 0 0 0 * 0 * *
+MARTIAL_HAMMER FEAT_HAMMER 3 CLASSLEVELFIGHTER 4 FEAT_HAMMER 2 0 0 0 0 * 0 * *
+MARTIAL_LARGESWORD FEAT_LARGE_SWORD 3 CLASSLEVELFIGHTER 4 FEAT_LARGE_SWORD 2 0 0 0 0 * 0 * *
+MARTIAL_POLEARM FEAT_POLEARM 3 CLASSLEVELFIGHTER 4 FEAT_POLEARM 2 0 0 0 0 * 0 * *
+MAXIMIZED_ATTACKS 0 1 MaximizedAttacks -1 CONCENTRATION 4 0 0 0 0 * * * *
+MERCANTILE_BACKGROUND 0 1 AnyOfThree -1 SUBRACE 0x40002 SUBRACE 0x60002 RACE 1 * * * *
+POWER_ATTACK 0 1 STR 13 0 0 0 0 0 0 * * * *
+PRECISE_SHOT 0 1 0 0 0 0 0 0 0 0 * * * *
+RAPID_SHOT 0 1 DEX 13 0 0 0 0 0 0 * * * *
+RESIST_POISON 0 1 CLASSLEVELSUM 1 0 0 SUBRACE 0x40002 RACE 7 1 * 1 1
+SCION_OF_STORMS 0 1 SPELLCRAFT 10 0 0 CLASSLEVELMAGE 7 CLASSLEVELSORCEROR 7 * * * *
+SHIELD_PROF 0 1 0 0 0 0 0 0 0 0 * * * *
+SIMPLE_CROSSBOW FEAT_CROSSBOW 3 CLASSLEVELFIGHTER 4 FEAT_CROSSBOW 2 0 0 0 0 * 0 * *
+SIMPLE_MACE FEAT_MACE 3 CLASSLEVELFIGHTER 4 FEAT_MACE 2 0 0 0 0 * 0 * *
+SIMPLE_MISSILE FEAT_SLING 3 CLASSLEVELFIGHTER 4 FEAT_SLING 2 0 0 0 0 * 0 * *
+SIMPLE_QUARTERSTAFF FEAT_STAFF 3 CLASSLEVELFIGHTER 4 FEAT_STAFF 2 0 0 0 0 * 0 * *
+SIMPLE_SMALLBLADE FEAT_SMALL_SWORD 3 CLASSLEVELFIGHTER 4 FEAT_SMALL_SWORD 2 0 0 0 0 * 0 * *
+SLIPPERY_MIND 0 1 CLASSLEVELTHIEF 10 0 0 0 0 0 0 * * * *
+SNAKE_BLOOD 0 1 RACE 1 0 0 CLASSLEVELSUM 1 0 0 1 * 1 *
+SPELL_FOCUS_ENCHANTMENT FEAT_ENCHANTMENT 2 0 0 0 0 0 0 0 0 * * * *
+SPELL_FOCUS_EVOCATION FEAT_EVOCATION 2 0 0 0 0 0 0 0 0 * * * *
+SPELL_FOCUS_NECROMANCY FEAT_NECROMANCY 2 0 0 0 0 0 0 0 0 * * * *
+SPELL_FOCUS_TRANSMUTE FEAT_TRANSMUTE 2 0 0 0 0 0 0 0 0 * * * *
+SPELL_PENETRATION FEAT_SPELL_PENETRATION 2 0 0 0 0 0 0 0 0 * * * *
+SPIRIT_OF_FLAME 0 1 SPELLCRAFT 10 0 0 CLASSLEVELMAGE 7 CLASSLEVELSORCEROR 7 * * * *
+STRONG_BACK 0 1 0 0 0 0 0 0 0 0 * * * *
+STUNNING_FIST 0 1 AllOfThreeGE -1 DEX 13 WIS 13 HITBONUS 8 * * * *
+SUBVOCAL_CASTING 0 1 IsCaster -1 0 0 0 0 0 0 * * * *
+TOUGHNESS FEAT_TOUGHNESS 5 0 0 0 0 0 0 0 0 * * * *
+TWO_WEAPON_FIGHTING 0 1 0 0 0 0 0 0 0 0 * * * *
+WEAPON_FINESSE 0 1 FEAT_SMALL_SWORD 1 0 0 HITBONUS 1 0 0 * * * *
+WILDSHAPE_BOAR 0 1 CLASSLEVELDRUID 5 0 0 0 0 0 0 * * * *
+WILDSHAPE_PANTHER 0 1 CLASSLEVELDRUID 5 0 0 0 0 0 0 * * * *
+WILDSHAPE_SHAMBLER 0 1 CLASSLEVELDRUID 8 0 0 0 0 0 0 * * * *
+
diff --git a/gemrb/override/iwd2/ffinger.pro b/gemrb/override/iwd2/ffinger.pro
new file mode 100644
index 0000000..f8bf7d4
Binary files /dev/null and b/gemrb/override/iwd2/ffinger.pro differ
diff --git a/gemrb/override/iwd2/findtrap.pro b/gemrb/override/iwd2/findtrap.pro
new file mode 100644
index 0000000..768c882
Binary files /dev/null and b/gemrb/override/iwd2/findtrap.pro differ
diff --git a/gemrb/override/iwd2/fireball.pro b/gemrb/override/iwd2/fireball.pro
new file mode 100644
index 0000000..4acc632
Binary files /dev/null and b/gemrb/override/iwd2/fireball.pro differ
diff --git a/gemrb/override/iwd2/fireblic.pro b/gemrb/override/iwd2/fireblic.pro
new file mode 100644
index 0000000..f9b920f
Binary files /dev/null and b/gemrb/override/iwd2/fireblic.pro differ
diff --git a/gemrb/override/iwd2/firebolt.pro b/gemrb/override/iwd2/firebolt.pro
new file mode 100644
index 0000000..a7b3b25
Binary files /dev/null and b/gemrb/override/iwd2/firebolt.pro differ
diff --git a/gemrb/override/iwd2/firebtbl.pro b/gemrb/override/iwd2/firebtbl.pro
new file mode 100644
index 0000000..5599064
Binary files /dev/null and b/gemrb/override/iwd2/firebtbl.pro differ
diff --git a/gemrb/override/iwd2/fireh.pro b/gemrb/override/iwd2/fireh.pro
new file mode 100644
index 0000000..c41f7de
Binary files /dev/null and b/gemrb/override/iwd2/fireh.pro differ
diff --git a/gemrb/override/iwd2/firestor.pro b/gemrb/override/iwd2/firestor.pro
new file mode 100644
index 0000000..f3701e3
Binary files /dev/null and b/gemrb/override/iwd2/firestor.pro differ
diff --git a/gemrb/override/iwd2/fistweap.2da b/gemrb/override/iwd2/fistweap.2da
new file mode 100644
index 0000000..3fe215a
--- /dev/null
+++ b/gemrb/override/iwd2/fistweap.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+00FIST
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
+MONK 00MFIST1 00MFIST1 00MFIST2 00MFIST2 00MFIST2 00MFIST3 00MFIST3 00MFIST3 00MFIST4 00MFIST4 00MFIST4 00MFIST5 00MFIST5 00MFIST5 00MFIST6 00MFIST6 00MFIST6 00MFIST7 00MFIST7 00MFIST7 00MFIST7 00MFIST7 00MFIST7 00MFIST7 00MFIST8 00MFIST8 00MFIST8 00MFIST8 00MFIST8 00MFIST8 00MFIST9
diff --git a/gemrb/override/iwd2/fodeath.pro b/gemrb/override/iwd2/fodeath.pro
new file mode 100644
index 0000000..16cf8c3
Binary files /dev/null and b/gemrb/override/iwd2/fodeath.pro differ
diff --git a/gemrb/override/iwd2/fonts.2da b/gemrb/override/iwd2/fonts.2da
new file mode 100644
index 0000000..031b409
--- /dev/null
+++ b/gemrb/override/iwd2/fonts.2da
@@ -0,0 +1,19 @@
+2DA V1.0
+NORMAL
+ RESREF NEED_PALETTE FIRST_CHAR
+0 NORMAL 1 0
+1 INFOFONT 1 0
+2 NUMBER 0 47
+3 INITIALS 0 0
+4 NUMBER2 0 47
+5 NUMBER3 0 47
+6 NUMFONT 1 0
+7 REALMS 0 0
+8 REALMS2 1 0
+9 STONEBIG 0 0
+10 STONESM2 0 0
+11 STONESM3 0 0
+12 STONESML 0 0
+13 TOOLFONT 1 0
+14 STATES 0 0
+14 STATES2 0 0
diff --git a/gemrb/override/iwd2/formatio.2da b/gemrb/override/iwd2/formatio.2da
new file mode 100644
index 0000000..c369656
--- /dev/null
+++ b/gemrb/override/iwd2/formatio.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+-10
+# generated by make_formation.py, do not edit
+ X1 Y1 X2 Y2 X3 Y3 X4 Y4 X5 Y5 X6 Y6 X7 Y7 X8 Y8 X9 Y9 X10 Y10 X11 Y11 X12 Y12 X13 Y13 X14 Y14 X15 Y15 X16 Y16 X17 Y17 X18 Y18 X19 Y19 X20 Y20
+FOLLOW 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
+T 0 0 48 0 -48 0 0 48 0 84 0 120 0 156 0 192 0 228 0 264 0 300 0 336 0 372 0 408 0 444 0 480 0 516 0 552 0 588 0 624
+GATHER 0 -36 48 -24 -48 -24 48 24 -48 24 0 36 48 48 -48 48 0 72 48 72 -48 72 0 108 48 96 -48 96 0 144 48 120 -48 120 0 180 48 144 -48 144
+4AND2 0 0 64 0 -64 0 128 0 0 48 64 48 -64 48 128 48 0 96 64 96 -64 96 128 96 0 144 64 144 -64 144 128 144 0 192 64 192 -64 192 128 192
+3BY2 0 0 64 0 -64 0 0 48 64 48 -64 48 0 96 64 96 -64 96 0 144 64 144 -64 144 0 192 64 192 -64 192 0 240 64 240 -64 240 0 288 64 288
+PROTECT 0 0 0 -36 -64 0 64 0 -32 48 32 48 0 24 0 48 0 72 0 96 0 120 0 144 0 168 0 192 0 216 0 240 0 264 0 288 0 312 0 336
+2BY3 -24 0 24 0 -24 48 24 48 -24 84 24 84 -24 120 24 120 -24 156 24 156 -24 192 24 192 -24 228 24 228 -24 264 24 264 -24 300 24 300 -24 336 24 336
+RANK -32 0 32 0 -96 0 96 0 -160 0 160 0 -224 0 224 0 -288 0 288 0 -352 0 352 0 -416 0 416 0 -480 0 480 0 -544 0 544 0 -608 0 608 0
+V 0 0 64 0 -15 48 49 48 -30 96 34 96 -45 144 19 144 -60 192 4 192 -75 240 -11 240 -90 288 -26 288 -105 336 -41 336 -120 384 -56 384 -135 432 -71 432
+WEDGE 0 0 64 36 -64 36 -124 72 124 72 0 72 0 144 -124 144 124 144 0 180 -124 180 124 180 0 216 -124 216 124 216 0 252 -124 252 124 252 0 288 -124 288
+S 0 0 64 24 0 48 64 72 0 96 64 120 0 144 64 168 0 192 64 216 0 240 64 264 0 288 64 312 0 336 64 360 0 384 64 408 0 432 64 456
+LINE 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
diff --git a/gemrb/override/iwd2/fseed.pro b/gemrb/override/iwd2/fseed.pro
new file mode 100644
index 0000000..2d0b929
Binary files /dev/null and b/gemrb/override/iwd2/fseed.pro differ
diff --git a/gemrb/override/iwd2/fstrikh.pro b/gemrb/override/iwd2/fstrikh.pro
new file mode 100644
index 0000000..bbd9d62
Binary files /dev/null and b/gemrb/override/iwd2/fstrikh.pro differ
diff --git a/gemrb/override/iwd2/gabreath.pro b/gemrb/override/iwd2/gabreath.pro
new file mode 100644
index 0000000..c140cae
Binary files /dev/null and b/gemrb/override/iwd2/gabreath.pro differ
diff --git a/gemrb/override/iwd2/gametime.2da b/gemrb/override/iwd2/gametime.2da
new file mode 100644
index 0000000..2d29d2c
--- /dev/null
+++ b/gemrb/override/iwd2/gametime.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+0
+ DURATION
+ROUND_SECONDS 7
+TURN_SECONDS 70
diff --git a/gemrb/override/iwd2/garmorh.pro b/gemrb/override/iwd2/garmorh.pro
new file mode 100644
index 0000000..f4f1d5a
Binary files /dev/null and b/gemrb/override/iwd2/garmorh.pro differ
diff --git a/gemrb/override/iwd2/gate.pro b/gemrb/override/iwd2/gate.pro
new file mode 100644
index 0000000..9e70b8d
Binary files /dev/null and b/gemrb/override/iwd2/gate.pro differ
diff --git a/gemrb/override/iwd2/gaze.pro b/gemrb/override/iwd2/gaze.pro
new file mode 100644
index 0000000..632263a
Binary files /dev/null and b/gemrb/override/iwd2/gaze.pro differ
diff --git a/gemrb/override/iwd2/geloopx.pro b/gemrb/override/iwd2/geloopx.pro
new file mode 100644
index 0000000..0c3b9b6
Binary files /dev/null and b/gemrb/override/iwd2/geloopx.pro differ
diff --git a/gemrb/override/iwd2/gemprjtl.ids b/gemrb/override/iwd2/gemprjtl.ids
new file mode 100644
index 0000000..31f6b6f
--- /dev/null
+++ b/gemrb/override/iwd2/gemprjtl.ids
@@ -0,0 +1,498 @@
+IDS V1.0
+1 ARROW
+2 ARROWEX
+3 ARROWFLM
+4 ARROWHVY
+5 ARROW
+6 AXE
+7 AXEEX
+8 AXEFLM
+9 AXE
+10 AXE
+11 BOLT
+12 BOLTEX
+13 ARROWHVY
+14 BOLT
+15 BOLT
+16 BULLET
+17 BULLETEX
+18 <flamingbullet>
+19 BULLET
+20 BULLET
+21 SPBRNHND
+22 CALLLIH
+23 CHROMORB
+24 SPCONECO
+25 SPCONEFI
+26 DAGGER
+27 DAGGEREX
+28 <flamingdagger>
+29 DAGGER
+30 DAGGER
+31 DART
+32 DARTEX
+33 <flamingdart>
+34 DART
+35 DART
+36 MAGICMIS
+37 FIREBALL
+38 <icefragments>
+39 LIGHTB
+40 STONE
+41 SLEEP
+42 <skeleton>
+43 SPSMPUFF
+44 SPSMKJET
+45 SPSMOLD
+46 SPARKLBL
+47 SPARKLGO
+48 SPARKLPU
+49 SPARKLIC
+50 SPARKLST
+51 SPARKLBK
+52 SPARKLCH
+53 SPARKLRE
+54 SPARKLGR
+55 SPEAR
+56 SPEAREX
+57 <flamingspear>
+58 SPEAR
+59 SPEAR
+60 <starsprite>
+61 <stoned>
+62 <webtravel>
+63 WEB
+64 GAZE
+65 HMIGHTH
+66 FLMSTRK
+67 MAGICMIS
+68 SPMAGMIS
+69 SPMAGMIS
+70 SPMAGMIS
+71 SPMAGMIS
+72 SPMAGMIS
+73 SPMAGMIS
+74 SPMAGMIS
+75 SPMAGMIS
+76 SPMAGMIS
+77 SPMAGMIS
+78 INVTRAV
+79 FIREBOLT
+80 SKYBOLT
+81 SKYBOLT2
+82 SKYBOLT2
+83 SKYBOLT2
+84 SKYBOLT2
+85 SKYBOLT2
+86 SKYBOLT2
+87 SKYBOLT2
+88 SKYBOLT2
+89 SKYBOLT2
+90 SKYBOLT2
+91 FIRESTOR
+92 LIGHTSTO
+93 INAREA
+94 CLOUD
+95 TRAPSKUL
+96 COLRSPRY
+97 ICESTORM
+98 SPFIREWL
+99 TRAPGLYP
+100 GREASE
+101 ARROWFLG
+102 ARROWFLB
+103 FIREBLGR
+104 FIREBTBL
+105 <potion>
+106 <potionexplode>
+107 ACIDBLOB
+108 SPSCORCH
+109 SPDIMDR
+110 CGNECROM
+111 CGALTERA
+112 CGENCHAN
+113 CGABJURA
+114 CGILLUSI
+115 CGCONJUR
+116 CGINVOCA
+117 CGDIVINA
+118 SHAIR
+119 SHEARTH
+120 SHWATER
+121 SHAIR1
+122 SHEARTH1
+123 SHWATER1
+124 SHAIR2
+125 SHEARTH2
+126 SHWATER2
+127 SHAIR3
+128 SHEARTH3
+129 SHWATER3
+130 SHAIR4
+131 SHEARTH4
+132 SHWATER4
+133 SHAIR5
+134 SHEARTH5
+135 SHWATER5
+136 SHAIR6
+137 SHEARTH6
+138 SHWATER6
+139 SHAIR7
+140 SHEARTH7
+141 SHWATER7
+142 SPBOOM1
+143 SPBOOM2
+144 SPBOOM3
+145 FLMSTRK
+146 HLYMITE
+147 SHAIR7
+148 SPKLARBL
+149 SPKLARGO
+150 SPKLARPU
+151 SPKLARIC
+152 SPKLARST
+153 SPKLARBK
+154 SPKLARCH
+155 SPKLARRE
+156 SPKLARGR
+157 INAREAPA
+158 INAREANP
+159 SPARBLPA
+160 SPARGOPA
+161 SPARPUPA
+162 SPARICPA
+163 SPARSTPA
+164 SPARBKPA
+165 SPARCHPA
+166 SPARREPA
+167 SPARGRPA
+168 SPARBLNP
+169 SPARGONP
+170 SPARPUNP
+171 SPARICNP
+172 SPARSTNP
+173 SPARBKNP
+174 SPARCHNP
+175 SPARRENP
+176 SPARGRNP
+177 SPARMANP
+178 SPARORNP
+179 SPARMAPA
+180 SPARORPA
+181 SPKLARMA
+182 SPKLAROR
+183 SPARKLMA
+184 SPARKLOR
+185 INAREANS
+186 CLOUDKIL
+187 ARROWFLI
+188 COW
+189 HOLD
+190 SPSCORIC
+191 ACIDBLMU
+192 ACIDBLGR
+193 ACIDBLOC
+194 REDHOLY
+195 SHAREA
+196 SHAREA1
+197 SHAREA
+198 SHAREA2
+199 SHAREA3
+200 SHAREA4
+201 SHAREA5
+202 SHAREA6
+203 FIREBLIC
+204 INAREASM
+205 LIGHTB
+206 LIGHTBNB
+207 SPFDEATH
+208 MRAGE
+209 CLIGHT
+210 ASTORM
+211 DFOG
+212 SSTONE
+213 ICLOUD
+214 PFIRE
+215 IPLAGUE
+216 SSSWARM
+217 MMISSILE
+218 ABJURH
+219 ALTERH
+220 INVOCH
+221 NECROH
+222 CONJUH
+223 ENCHAH
+224 ILLUSH
+225 DIVINH
+226 ABJURT
+227 ALTERT
+228 INVOCT
+229 NECROT
+230 CONJUT
+231 ENCHAT
+232 ILLUST
+233 DIVINT
+234 ENTANGLE
+235 AREA1P
+236 AREA1NP
+237 ABJURAP
+238 AREA2
+239 AREA2NP
+240 AREA4NP
+241 ABJURAP
+242 CHANT
+243 FINDTRAP
+244 ALTERAS
+245 DISPEL
+246 ALTERAP
+247 ALTERANP
+248 ENCHAN
+249 ABJURAP
+250 ICELANCE
+251 ALTERA
+252 PRAYER
+253 CONFUSW
+254 EMOTION
+255 MALISON
+256 HARMONY
+257 PROTEVIL
+258 CLOAK
+259 PRAYER
+260 INVTRAV
+261 SCHARGE
+262 ENCHANNP
+263 CHAOS
+264 SHROUD
+265 ABJURAP
+266 DSPELL
+267 DISINT
+268 OFSPHE
+269 FSEED
+270 OFSPHE
+271 PSPRAY
+272 AREA3P
+273 SUNRAY
+274 CONFUSP
+275 SOPAIN
+276 SOHOPE
+277 PWKILL
+278 DFOG2
+279 RAD100
+280 RAD250
+281 BSCLOUD
+282 ZLAURA
+283 GOLCLOUD
+284 MSPORE
+285 ICLOUDB
+286 ICLOUDA
+287 INAREANP
+288 MSUMM1
+289 ASUMM1
+290 CEELEM
+291 CFELEM
+292 CWELEM
+293 PRTL_OP
+294 SAREANP
+295 WWOLF
+296 PRTL_CL
+297 ALANCE
+298 SEATER
+299 SGROWTH
+300 CLOUDB
+301 SWAVE
+302 TSPRAY
+303 WOMOON
+304 WHIRLW
+305 EQUAKE
+306 MOELDA
+307 COBONES
+308 COPEST
+309 UWARD
+310 BBARRIER
+311 SPWRATH
+312 LODISR
+313 MFMISS
+314 SHOUT
+315 VSPHER
+316 SUFFOC
+317 ADHWIL
+318 GSHOUT
+319 CRY225
+320 CRY250
+321 CRY150
+322 CRY200
+323 MFMISS
+324 MFMISS2
+325 MFMISS2
+326 MFMISS2
+327 MFMISS2
+328 MFMISS2
+329 MFMISS2
+330 MFMISS2
+331 MFMISS2
+332 MFMISS2
+333 MFMISS2
+334 SUNFIRE
+335 PWSTUN
+336 HSMITE
+337 UBLIGHT
+338 ENCHANNP
+339 HWORD
+340 HWORD
+341 CRY500NP
+342 WOWISP
+343 RETRIB1
+344 RETRIB2
+345 ASCORCH
+346 PORTAL
+347 OFSPHE
+348 DBREATH
+349 RNG450
+350 CLOUDKS
+351 INVTRAV
+352 ILLUSH
+353 CCDAMAH
+354 RNG450
+355 RNG450
+356 UHOLD
+357 AREA1PS
+358 AREA1PL
+359 DBFIREB
+360 GATE
+361 AREA1NPL
+362 SOHOPE
+363 SOHOPE
+364 SOPAIN
+365 MSWARM
+366 ALTERAP
+367 FFINGER
+368 ELOOP
+369 WOFIRE
+370 ALTERAPS
+371 ABJURA
+372 ENCHAN
+373 SHOUT
+374 BOULDER
+375 RAD300
+376 MFOG
+377 ABREATH
+378 FBREATH
+379 LIGHTBNB
+380 GABREATH
+381 GSHOUT
+382 BIGBOLDR
+383 TRAP
+384 TRAP
+385 FBREATH
+0x1001 INVTRAV
+0x1002 ABJURH
+0x1003 ALTERH
+0x1004 INVOCH
+0x1005 NECROH
+0x1006 CONJUH
+0x1007 ENCHAH
+0x1008 ILLUSH
+0x1009 DIVINH
+0x100a ARMORH
+0x100b SARMORH
+0x100c GARMORH
+0x100d STRENGH
+0x100e CONFUSH
+0x100f SOFLAMH
+0x1010 DSPELLH
+0x1011 DISINTH
+0x1012 PWSILEH
+0x1013 PWSTUNH
+0x1014 FODEATH
+0x1015 MSWORDH
+0x1016 MSUMM1H
+0x1017 MSUMM2H
+0x1018 MSUMM3H
+0x1019 MSUMM4H
+0x101a MSUMM5H
+0x101b MSUMM6H
+0x101c MSUMM7H
+0x101d CFELEMH
+0x101e CEELEMH
+0x101f CWELEMH
+0x1020 BLESSH
+0x1021 CURSEH
+0x1022 PRAYERH
+0x1023 RECITAH
+0x1024 CLWOUNH
+0x1025 CMWOUNH
+0x1026 CSWOUNH
+0x1027 CCWOUNH
+0x1028 HEALH
+0x1029 ASUMM1H
+0x102a ASUMM2H
+0x102b ASUMM3H
+0x102c SPOISOH
+0x102d NPOISOH
+0x102e CALLLIH
+0x102f SCHARGH
+0x1030 RPARALH
+0x1031 FACTIOH
+0x1032 MMAGICH
+0x1033 SOONEH
+0x1034 CSTRENH
+0x1035 FSTRIKH
+0x1036 RDEADH
+0x1037 RESURRH
+0x1038 CCOMMAH
+0x1039 RWOTFAH
+0x103a SUNRAYH
+0x103b SSTONE
+0x103c DDOORH
+0x103d DDOORH
+0x103e COCOLDH
+0x103f SSORBH
+0x1040 FIREH
+0x1041 COLDH
+0x1042 ELECTRH
+0x1043 ACIDH
+0x1044 PARALH
+0x1045 MRAGEH
+0x1046 RWOTFAG
+0x1047 BDEATH
+0x1048 PORTALH
+0x1049 SUNSCOH
+0x104a BBARRH1
+0x104b BBARRH2
+0x104c COBONH1
+0x104d COBONH2
+0x104e CLDAMAH
+0x104f CMDAMAH
+0x1050 CSDAMAH
+0x1051 CCDAMAH
+0x1052 CDISEAH
+0x1053 POISONH
+0x1054 SLIVINH
+0x1055 HARMH
+0x1056 DESTRUH
+0x1057 EXALTAH
+0x1058 CLOUDBH
+0x1059 MTOUCHH
+0x105a MTOUCHH
+0x105b CGRACEH
+0x105c SEATERH
+0x105d SWAVEH
+0x105e SUFFOCH
+0x105f ADHWILH
+0x1060 MFMISSH
+0x1061 VSPHERH
+0x1062 WVDEATH
+0x1063 UWARDH
+0x1064 WVHITH
+0x1065 WDEATH1
+0x1066 WDEATH2
+0x1067 DDEATH
+0x1068 DDEATH2
+0x1069 MSUMM1X
+0x106a ASUMM1X
+0x106b CEELEMX
+0x106c CFELEMX
+0x106d CWELEMX
+0x106e GELOOPX
+0x106f DATTACH
+0x1070 WOMOONX
diff --git a/gemrb/override/iwd2/gemrb.ini b/gemrb/override/iwd2/gemrb.ini
new file mode 100644
index 0000000..b619c47
--- /dev/null
+++ b/gemrb/override/iwd2/gemrb.ini
@@ -0,0 +1,98 @@
+; 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.ini - game type-specific settings for GemRB engine
+
+[resources]
+
+; Bitmap resource for cursors
+CursorBAM = CAROT
+
+; Bitmap resource for scroll cursor arrow
+ScrollCursorBAM = CURSARW
+
+; Bitmap resource for dialog buttons font
+ButtonFont = NORMAL
+
+; Font used to display tooltips
+TooltipFont = TOOLFONT
+
+; Sprite displayed behind the tooltip text, if any
+TooltipBack = TOOLTIP
+
+; Tooltip text color (RGBA)
+TooltipColor = #ffffffff
+
+; Space between tooltip text and sides of TooltipBack (x2)
+TooltipMargin = 2
+#TooltipMargin = 20
+
+; INI file from the original games
+INIConfig = icewind2.ini
+
+; Palette bitmaps in various widths
+Palette16 = MPALETTE
+Palette32 = MPALETTE
+Palette256 = MPAL256
+
+MaximumAbility = 40
+IgnoreButtonFrames = 0
+AllStringsTagged = 1
+HasDPLAYER = 1
+HasPickSound = 0
+HasDescIcon = 0
+HasEXPTABLE = 1
+SoundFolders = 1
+HasSongList = 0
+HasSpellList = 1
+UpperButtonText = 0
+LowerLabelText = 0
+HasPartyINI = 1
+HasBeastsINI = 0
+ForceStereo = 0
+IWDMapDimensions = 1
+SmallFog = 0
+IWD2ScriptName = 1
+DialogueScrolls = 0
+KnowWorld = 1
+ReverseToHit = 0
+CharNameIsGabber = 1
+MagicBit = 1
+ChallengeRating = 1
+DeathOnZeroStat = 0
+SpawnIni = 1
+IWD2DeathVarFormat = 1
+OverrideCursorPos = 1
+EnhancedEffects = 1
+BreakableWeapons = 0
+3EdRules = 1
+LevelslotPerClass = 1
+SelectiveMagicRes = 1
+HasHideInShadows = 1
+AreaVisitedVar = 1
+ProperBackstab = 1
+HasSpecificDamageBonus = 1
+StrrefSaveGame = 1
+BiographyIsRes = 1
+CutsceneAreascripts = 0
+FlexibleWorldmap = 1
+AutoSearchHidden = 0
+PSTStateFlags = 0
+CastingSounds = 1
+ForceAreaScript = 1
diff --git a/gemrb/override/iwd2/gender.2da b/gemrb/override/iwd2/gender.2da
new file mode 100644
index 0000000..609cf68
--- /dev/null
+++ b/gemrb/override/iwd2/gender.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+*
+ TYPE MALE FEMALE
+SIRMAAM -1 27473 27475
+GIRLBOY -1 27477 27476
+BROTHERSISTER -1 27478 27479
+LADYLORD -1 27481 27480
+MALEFEMALE -1 27482 27483
+HESHE -1 27484 27485
+HISHER -1 27486 27487
+HIMHER -1 27488 27487
+MANWOMAN -1 27489 27490
+SONDAUGHTER -1 70567 70568
+PROTAGONIST_SIRMAAM 0 27473 27475
+PROTAGONIST_GIRLBOY 0 27477 27476
+PROTAGONIST_BROTHERSISTER 0 27478 27479
+PROTAGONIST_LADYLORD 0 27481 27480
+PROTAGONIST_MALEFEMALE 0 27482 27483
+PROTAGONIST_HESHE 0 27484 27485
+PROTAGONIST_HISHER 0 27486 27487
+PROTAGONIST_HIMHER 0 27488 27487
+PROTAGONIST_MANWOMAN 0 27489 27490
diff --git a/gemrb/override/iwd2/golcloud.pro b/gemrb/override/iwd2/golcloud.pro
new file mode 100644
index 0000000..3c97d09
Binary files /dev/null and b/gemrb/override/iwd2/golcloud.pro differ
diff --git a/gemrb/override/iwd2/grease.pro b/gemrb/override/iwd2/grease.pro
new file mode 100644
index 0000000..bf273b6
Binary files /dev/null and b/gemrb/override/iwd2/grease.pro differ
diff --git a/gemrb/override/iwd2/gshout.pro b/gemrb/override/iwd2/gshout.pro
new file mode 100644
index 0000000..1b362d6
Binary files /dev/null and b/gemrb/override/iwd2/gshout.pro differ
diff --git a/gemrb/override/iwd2/guibtact.2da b/gemrb/override/iwd2/guibtact.2da
new file mode 100644
index 0000000..9749e6c
--- /dev/null
+++ b/gemrb/override/iwd2/guibtact.2da
@@ -0,0 +1,30 @@
+2DA V1.0
+guibtact
+ 1 2 3 4 TOOLTIP RESREF
+Stealth 28 29 30 31 4968 guibtact
+Thieving 24 25 26 27 4971 guibtact
+Cast 8 9 10 11 4688 guibtact
+QSpell1 0 1 24 25 4938 guibtbut
+QSpell2 2 3 26 27 4938 guibtbut
+QSpell3 4 5 28 29 4938 guibtbut
+Turn 0 0 0 0 0 guibtact
+Talk 0 0 0 0 0 guibtact
+UseItem 16 17 18 19 4978 guibtact
+QItem1 0 1 24 25 4937 guibtbut
+QItem4 2 3 26 27 4937 guibtbut
+QItem2 4 5 28 29 4937 guibtbut
+QItem3 6 7 30 31 4937 guibtbut
+Innate 40 41 42 43 4954 guibtact
+Defend 0 1 2 3 15925 guibtact
+Attack 12 13 14 15 4666 guibtact
+QWeapon1 0 1 24 25 4950 guibtbut
+QWeapon2 2 3 26 27 4950 guibtbut
+QWeapon3 4 5 28 29 4950 guibtbut
+QWeapon4 6 7 30 31 4950 guibtbut
+BardSong 20 21 22 23 4918 guibtact
+Stop 44 45 46 47 15924 guibtact
+Search 36 37 38 39 4927 guibtact
+ShapeChange 0 0 0 0 4958 guibtact
+Taming 124 125 126 127 4974 guibtact
+Skills 96 97 98 99 4933 guibtact
+Wilderness 92 93 94 95 32186 guibtact
diff --git a/gemrb/override/iwd2/guils.chu b/gemrb/override/iwd2/guils.chu
new file mode 100644
index 0000000..898d28c
Binary files /dev/null and b/gemrb/override/iwd2/guils.chu differ
diff --git a/gemrb/override/iwd2/harmh.pro b/gemrb/override/iwd2/harmh.pro
new file mode 100644
index 0000000..f8b2ded
Binary files /dev/null and b/gemrb/override/iwd2/harmh.pro differ
diff --git a/gemrb/override/iwd2/harmony.pro b/gemrb/override/iwd2/harmony.pro
new file mode 100644
index 0000000..03a7b54
Binary files /dev/null and b/gemrb/override/iwd2/harmony.pro differ
diff --git a/gemrb/override/iwd2/haterace.2da b/gemrb/override/iwd2/haterace.2da
new file mode 100644
index 0000000..634479b
--- /dev/null
+++ b/gemrb/override/iwd2/haterace.2da
@@ -0,0 +1,19 @@
+2DA V1.0
+4294967296
+ STRREF IDS STRREF_HELP
+BUGBEAR 28367 180 8032
+DRIDER 37607 182 41079
+GIANT 3279 153 3291
+GOBLIN 3280 155 3292
+HARPY 37616 174 37518
+HOOKHORROR 37606 181 37608
+LIZARDMAN 3281 157 3293
+OGRE 15975 113 15998
+ORC 3282 160 3294
+SALAMANDER 3290 161 3295
+SHAPESHIFTER 37605 191 37609
+TROLL 3288 165 3299
+UMBERHULK 3289 166 3300
+UNDEAD 3285 167 3297
+WYVERN 37617 118 37619
+YUANTI 30850 168 3301
diff --git a/gemrb/override/iwd2/healh.pro b/gemrb/override/iwd2/healh.pro
new file mode 100644
index 0000000..1213e70
Binary files /dev/null and b/gemrb/override/iwd2/healh.pro differ
diff --git a/gemrb/override/iwd2/hmighth.pro b/gemrb/override/iwd2/hmighth.pro
new file mode 100644
index 0000000..60ba342
Binary files /dev/null and b/gemrb/override/iwd2/hmighth.pro differ
diff --git a/gemrb/override/iwd2/hold.pro b/gemrb/override/iwd2/hold.pro
new file mode 100644
index 0000000..aeecbfd
Binary files /dev/null and b/gemrb/override/iwd2/hold.pro differ
diff --git a/gemrb/override/iwd2/hsmite.pro b/gemrb/override/iwd2/hsmite.pro
new file mode 100644
index 0000000..8b3dcb3
Binary files /dev/null and b/gemrb/override/iwd2/hsmite.pro differ
diff --git a/gemrb/override/iwd2/hword.pro b/gemrb/override/iwd2/hword.pro
new file mode 100644
index 0000000..51295d7
Binary files /dev/null and b/gemrb/override/iwd2/hword.pro differ
diff --git a/gemrb/override/iwd2/icelance.pro b/gemrb/override/iwd2/icelance.pro
new file mode 100644
index 0000000..936854e
Binary files /dev/null and b/gemrb/override/iwd2/icelance.pro differ
diff --git a/gemrb/override/iwd2/icestorm.pro b/gemrb/override/iwd2/icestorm.pro
new file mode 100644
index 0000000..06e4b05
Binary files /dev/null and b/gemrb/override/iwd2/icestorm.pro differ
diff --git a/gemrb/override/iwd2/icloud.pro b/gemrb/override/iwd2/icloud.pro
new file mode 100644
index 0000000..9f85cd1
Binary files /dev/null and b/gemrb/override/iwd2/icloud.pro differ
diff --git a/gemrb/override/iwd2/iclouda.pro b/gemrb/override/iwd2/iclouda.pro
new file mode 100644
index 0000000..845d3f0
Binary files /dev/null and b/gemrb/override/iwd2/iclouda.pro differ
diff --git a/gemrb/override/iwd2/icloudb.pro b/gemrb/override/iwd2/icloudb.pro
new file mode 100644
index 0000000..d5f3f31
Binary files /dev/null and b/gemrb/override/iwd2/icloudb.pro differ
diff --git a/gemrb/override/iwd2/illush.pro b/gemrb/override/iwd2/illush.pro
new file mode 100644
index 0000000..158d82f
Binary files /dev/null and b/gemrb/override/iwd2/illush.pro differ
diff --git a/gemrb/override/iwd2/illust.pro b/gemrb/override/iwd2/illust.pro
new file mode 100644
index 0000000..e2966de
Binary files /dev/null and b/gemrb/override/iwd2/illust.pro differ
diff --git a/gemrb/override/iwd2/inarea.pro b/gemrb/override/iwd2/inarea.pro
new file mode 100644
index 0000000..ec6a50d
Binary files /dev/null and b/gemrb/override/iwd2/inarea.pro differ
diff --git a/gemrb/override/iwd2/inareanp.pro b/gemrb/override/iwd2/inareanp.pro
new file mode 100644
index 0000000..91859f9
Binary files /dev/null and b/gemrb/override/iwd2/inareanp.pro differ
diff --git a/gemrb/override/iwd2/inareasm.pro b/gemrb/override/iwd2/inareasm.pro
new file mode 100644
index 0000000..6545bce
Binary files /dev/null and b/gemrb/override/iwd2/inareasm.pro differ
diff --git a/gemrb/override/iwd2/invoch.pro b/gemrb/override/iwd2/invoch.pro
new file mode 100644
index 0000000..0eccaef
Binary files /dev/null and b/gemrb/override/iwd2/invoch.pro differ
diff --git a/gemrb/override/iwd2/invoct.pro b/gemrb/override/iwd2/invoct.pro
new file mode 100644
index 0000000..3a051b0
Binary files /dev/null and b/gemrb/override/iwd2/invoct.pro differ
diff --git a/gemrb/override/iwd2/iplague.pro b/gemrb/override/iwd2/iplague.pro
new file mode 100644
index 0000000..1b3501e
Binary files /dev/null and b/gemrb/override/iwd2/iplague.pro differ
diff --git a/gemrb/override/iwd2/island00.2da b/gemrb/override/iwd2/island00.2da
new file mode 100644
index 0000000..aa3f8e2
--- /dev/null
+++ b/gemrb/override/iwd2/island00.2da
@@ -0,0 +1,11 @@
+2DA V1.0
+0
+ X Y
+1 0 105
+2 0 590
+3 692 590
+4 692 459
+5 1018 459
+6 1018 301
+7 1027 301
+8 1027 105
diff --git a/gemrb/override/iwd2/itemsnd.2da b/gemrb/override/iwd2/itemsnd.2da
new file mode 100644
index 0000000..9678ee9
--- /dev/null
+++ b/gemrb/override/iwd2/itemsnd.2da
@@ -0,0 +1,80 @@
+2DA V1.0
+*
+ TAKE DROP
+AMULET G_NECK1 G_NECK2
+ARMOR G_CLOAK1 G_CLOAK2
+BELT G_BELT1 G_BELT2
+BOOT G_BOOT1 G_BOOT2
+ARROW G_ARRW1 G_ARRW2
+BRACER G_GLOVE1 G_GLOVE2
+HELMET G_HELM1 G_HELM2
+KEY G_KEY1 G_KEY2
+POTION G_POTN1 G_POTN2
+RING G_RING1 G_RING2
+SCROLL G_SCROL1 G_SCROL2
+SHIELD GAM_21A GAM_21B
+FOOD GAM_21A GAM_21B
+BULLET G_BULLT1 G_BULLT2
+BOW G_BOW1 G_BOW2
+DAGGER G_DAGGR1 G_DAGGR2
+MACE G_MACE1 G_MACE2
+SLING G_SLING1 G_SLING2
+SMSWORD G_SMSWD1 G_SMSWD2
+BGSWORD G_LGSWD1 G_LGSWD2
+HAMMER G_HAMMR1 G_HAMMR2
+MSTAR G_MSTAR1 G_MSTAR2
+FLAIL G_FLAIL1 G_FLAIL2
+DART G_DART1 G_DART2
+AXE G_AXE1 G_AXE2
+STAFF G_STAFF1 G_STAFF2
+XBOW G_CROSB1 G_CROSB2
+FIST GAM_21A GAM_21B
+SPEAR G_SPEAR1 G_SPEAR2
+POLEARM G_HALB1 G_HALB2
+BOLT G_BOLT1 G_BOLT2
+CLOAK G_CLOAK1 G_CLOAK2
+COIN G_GOLD1 G_GOLD2
+GEM G_GEM1 G_GEM2
+WAND G_WAND1 G_WAND2
+BROKEN1 G_B_ARM1 G_B_ARM2
+BROKEN2 G_B_SHD1 G_B_SHD2
+BROKEN3 G_B_WPN1 G_B_WPN2
+UNUSED1 GAM_21A GAM_21B
+UNUSED2 G_BROKN1 G_BROKN2
+BUCKLER G_BUCKR1 G_BUCKR2
+CANDLE G_CAND1 G_CAND2
+CBODY G_CHILD1 G_CHILD2
+CLUB G_CLUB1 G_CLUB2
+FBODY G_FEM1 G_FEM2
+KEY2 G_KEYS1 G_KEYS2
+LSHIELD G_LGSLD1 G_LGSLD2
+MBODY G_MALE1 G_MALE2
+MSHIELD G_MDSLD1 G_MDSLD2
+NOTES G_PAPR1 G_PAPR2
+ROD G_ROD1 G_ROD2
+SKULL G_SKULL1 G_SKULL2
+SSHIELD G_SMSLD1 G_SMSLD2
+SPIDER G_SPIDR1 G_SPIDR2
+TELESCOPE G_TELE1 G_TELE2
+DRINK G_WINE1 G_WINE2
+GTSWORD G_LGSWD1 G_LGSWD2
+BAG GAM_21A GAM_21B
+FUR G_LETHR1 G_LETHR2
+LARMOR G_CLOAK1 G_CLOAK2
+SLARMOR G_CLOAK1 G_CLOAK2
+CHARMOR G_CLOAK1 G_CLOAK2
+SPARMOR G_CLOAK1 G_CLOAK2
+HPARMOR G_CLOAK1 G_CLOAK2
+FPARMOR G_CLOAK1 G_CLOAK2
+HARMOR G_CLOAK1 G_CLOAK2
+ROBE G_CLOAK1 G_CLOAK2
+SCALE G_CLOAK1 G_CLOAK2
+BTSWORD G_LGSWD1 G_LGSWD2
+SCARF G_GLOVE1 G_GLOVE2
+FOOD2 G_SLING1 G_SLING2
+HAT G_GLOVE1 G_GLOVE2
+GAUNTLET G_CAND1 G_CAND2
+DEFAULT GAM_21A GAM_21B
+LEATHER G_LETHR1 G_LETHR2
+CHAIN G_Chain1 G_Chain2
+PLATE G_Plate1 G_Plate2
diff --git a/gemrb/override/iwd2/itemtype.2da b/gemrb/override/iwd2/itemtype.2da
new file mode 100644
index 0000000..6bb5e72
--- /dev/null
+++ b/gemrb/override/iwd2/itemtype.2da
@@ -0,0 +1,77 @@
+2DA V1.0
+0
+ HELMET ARMOR SHIELD GLOVES RING AMULET BELT BOOTS WEAPON QUIVER CLOAK QUICK SCROLL BAG POTION
+BOOK 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+AMULET 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+ARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+BELT 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
+BOOT 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
+ARROW 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BRACER 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
+HELMET 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+KEY 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+POTION 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
+RING 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
+SCROLL 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0
+SHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+FOOD 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BULLET 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DAGGER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MACE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SLING 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SMSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BGSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+HAMMER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MSTAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FLAIL 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DART 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+AXE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+STAFF 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+XBOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FIST 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SPEAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+POLEARM 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BOLT 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+CLOAK 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
+COIN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+GEM 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+WAND 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BROKEN1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BROKEN2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BROKEN3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+UNUSED2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BUCKLER 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+CANDLE 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+CBODY 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+CLUB 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FBODY 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+KEY2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+LSHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+MBODY 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+MSHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+NOTES 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ROD 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+SKULL 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+SSHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+SPIDER 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+TELESCOPE 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+DRINK 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
+GTSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BAG 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
+FUR 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+LARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+SLARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+CHARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+SPARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+HPARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+FPARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+HARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+ROBE 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+SCALE 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+BTSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SCARF 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+FOOD2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+HAT 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+GAUNTLET 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
diff --git a/gemrb/override/iwd2/itemuse.2da b/gemrb/override/iwd2/itemuse.2da
new file mode 100644
index 0000000..fc92d10
--- /dev/null
+++ b/gemrb/override/iwd2/itemuse.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+0
+ STAT FILE MCOL VCOL WHICH
+0 KIT classes 2 7 1
+1 ALIGNMENT aligns 3 5 0
+2 RACE races 3 4 0
+3 CLASS classes 2 7 0
diff --git a/gemrb/override/iwd2/iwdshtab.2da b/gemrb/override/iwd2/iwdshtab.2da
new file mode 100644
index 0000000..e02ee20
--- /dev/null
+++ b/gemrb/override/iwd2/iwdshtab.2da
@@ -0,0 +1,117 @@
+2DA V1.0
+*
+ VVC SOUND
+NONE * *
+ABJURATION ABJURH *
+ALTERATION ALTERH *
+INVOCATION INVOCH *
+NECROMANCY NECROH *
+CONJURATION CONJUH *
+ENCHANTMENT ENCHAH *
+ILLUSION ILLUSH *
+DIVINATION DIVINH *
+ARMOR ARMORH *
+SPIRIT_ARMOR SARMORH *
+GHOST_ARMOR GARMORH *
+STRENGTH STRENGH *
+CONFUSION CONFUSH *
+SHROUD_OF_FLAME SOFLAMH *
+DEATH_SPELL DSPELLH *
+DISINTEGRATE DISINTH *
+PWORD_SILENCE PWSILEH *
+PWORD_STUN * *
+FINGER_OF_DEATH FODEATH *
+MORD_SWORD MSWORDH *
+M_SUMMONING_I MSUMM1H *
+M_SUMMONING_II MSUMM2H *
+M_SUMMONING_III MSUMM3H *
+M_SUMMONING_IV MSUMM4H *
+M_SUMMONING_V MSUMM5H *
+M_SUMMONING_VI MSUMM6H *
+M_SUMMONING_VII MSUMM7H *
+C_FIRE_ELEMENT CFELEMH *
+C_EARTH_ELEMENT CEELEMH *
+C_WATER_ELEMENT CWELEMH *
+BLESS BLESSH *
+CURSE CURSEH *
+PRAYER PRAYERH *
+RECITATION RECITAH *
+CURE_LIGHT_W CLWOUNH *
+CURE_MODERATE_W CMWOUNH *
+CURE_SERIOUS_W CSWOUNH *
+CURE_CRITICAL_W CCWOUNH *
+HEAL HEALH *
+A_SUMMONING_I ASUMM1H *
+A_SUMMONING_II ASUMM2H *
+A_SUMMONING_III ASUMM3H *
+SLOW_POISON SPOISOH *
+NEUTRALIZE_P NPOISOH *
+CALL_LIGHTNING SCHARGH *
+STATIC_CHARGE SCHARGH *
+REMOVE_PARA RPARALH *
+FREE_ACTION FACTIOH *
+MISCAST_MAGIC MMAGICH *
+STRENGTH_OF_ONE SOONEH *
+CHAMP_STRENGTH CSTRENH *
+FLAME_STRIKE STRIKH *
+RAISE_DEAD RDEADH *
+RESURRECTION RESURRH *
+CHAOTIC_CMD CCOMMAH *
+RIGHTEOUS_WRATH RWOTHFAH *
+SUNRAY SUNRAYX *
+SPIKE_STONES SSTONEA *
+D_DOOR_ORIGIN * *
+D_DOOR_DEST * *
+CONE_OF_COLD COCOLDH *
+SEARING_ORB SSORBH *
+FIRE FIREH *
+COLD COLDH *
+ELECTRICITY ELECTRH *
+ACID ACIDH *
+PARALYSIS PARALH *
+MALAVONS_RAGE MRAGEH *
+R_WRATH_GROUND RWOTFAG *
+BELHIFETS_DEATH BDEATH *
+PORTAL * *
+SUNSCORCH SUNSCOH *
+BLADE_B_FRONT BBARH1 *
+BLADE_B_BACK BBARH2 *
+C_OF_BONE_FRONT COBONH1 *
+C_OF_BONE_BACK COBONH2 *
+CAUSE_LIGHT_W CLDAMAH *
+CAUSE_MOD_W CMDAMAH *
+CAUSE_SERIOUS_W CSDAMAH *
+CAUSE_CRIT_W CCDAMAH *
+CAUSE_DISEASE CDISEAH *
+POISON POISONH *
+SLAY_LIVING SLIVINH *
+HARM HARMH *
+DESTRUCTION DESTRUH *
+EXALTATION EXALTAH *
+CLOUDBURST CLOUDBH *
+MOLD_TOUCH MTOUCHH *
+LOWER_RES * *
+CATS_GRACE CGRACEH *
+SOUL_EATER SEATERH *
+SMASHING_WAVE SWAVEH *
+SUFFOCATE * *
+HORRID_WILTING ADHWILH *
+FORCE_MISSILE MFMISSX *
+VITR_SPHERE VSPHERX *
+W_VIRGIN_DEATH WVDEATH *
+UNDEAD_WARD UWARDX *
+W_VIRGIN_ON_HIT WVHITH *
+WYLFDENE_DEATH1 WDEATH1 *
+WYLFDENE_DEATH2 WDEATH2 *
+DRAGON_DEATH1 DDEATH *
+DRAGONS_DEATH2 DDEATH2 *
+M_SUMM_CIRCLE * *
+A_SUMM_CIRCLE * *
+E_SUMM_CIRCLE * *
+F_SUMM_CIRCLE * *
+W_SUMM_CIRCLE * *
+ELECTRIC_LOOP GELOOPX *
+DARKTREE_ATTACK DATTACH *
+WALL_OF_MOON WOMOONX *
+210_ACID_STORM * *
+
diff --git a/gemrb/override/iwd2/lightb.pro b/gemrb/override/iwd2/lightb.pro
new file mode 100644
index 0000000..fa7f84a
Binary files /dev/null and b/gemrb/override/iwd2/lightb.pro differ
diff --git a/gemrb/override/iwd2/lightsto.pro b/gemrb/override/iwd2/lightsto.pro
new file mode 100644
index 0000000..7b502b1
Binary files /dev/null and b/gemrb/override/iwd2/lightsto.pro differ
diff --git a/gemrb/override/iwd2/lodisr.pro b/gemrb/override/iwd2/lodisr.pro
new file mode 100644
index 0000000..d78b250
Binary files /dev/null and b/gemrb/override/iwd2/lodisr.pro differ
diff --git a/gemrb/override/iwd2/magicmis.pro b/gemrb/override/iwd2/magicmis.pro
new file mode 100644
index 0000000..33ceed1
Binary files /dev/null and b/gemrb/override/iwd2/magicmis.pro differ
diff --git a/gemrb/override/iwd2/malison.pro b/gemrb/override/iwd2/malison.pro
new file mode 100644
index 0000000..1bab8cd
Binary files /dev/null and b/gemrb/override/iwd2/malison.pro differ
diff --git a/gemrb/override/iwd2/mfmiss.pro b/gemrb/override/iwd2/mfmiss.pro
new file mode 100644
index 0000000..c3cd185
Binary files /dev/null and b/gemrb/override/iwd2/mfmiss.pro differ
diff --git a/gemrb/override/iwd2/mfmiss2.pro b/gemrb/override/iwd2/mfmiss2.pro
new file mode 100644
index 0000000..320be1f
Binary files /dev/null and b/gemrb/override/iwd2/mfmiss2.pro differ
diff --git a/gemrb/override/iwd2/mfmissh.pro b/gemrb/override/iwd2/mfmissh.pro
new file mode 100644
index 0000000..171863e
Binary files /dev/null and b/gemrb/override/iwd2/mfmissh.pro differ
diff --git a/gemrb/override/iwd2/mfog.pro b/gemrb/override/iwd2/mfog.pro
new file mode 100644
index 0000000..240ac52
Binary files /dev/null and b/gemrb/override/iwd2/mfog.pro differ
diff --git a/gemrb/override/iwd2/mmagich.pro b/gemrb/override/iwd2/mmagich.pro
new file mode 100644
index 0000000..d74d4f2
Binary files /dev/null and b/gemrb/override/iwd2/mmagich.pro differ
diff --git a/gemrb/override/iwd2/mmissile.pro b/gemrb/override/iwd2/mmissile.pro
new file mode 100644
index 0000000..145b3fd
Binary files /dev/null and b/gemrb/override/iwd2/mmissile.pro differ
diff --git a/gemrb/override/iwd2/modal.2da b/gemrb/override/iwd2/modal.2da
new file mode 100644
index 0000000..bd9ca9c
--- /dev/null
+++ b/gemrb/override/iwd2/modal.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+*
+ SPELL ACTION STR_ON STR_OFF STR_FAIL AOESPELL
+NONE * * 0 0 0 0
+BARDSONG BARDSONG PlayBardSong 25138 25141 -1 1
+DETECTTRAPS FINDTRAP FindTraps 25085 25090 -1 0
+STEALTH SNEAK Hide 19944 4188 17120 0
+TURNUNDEAD TURN Turn 25135 25136 -1 1
diff --git a/gemrb/override/iwd2/moelda.pro b/gemrb/override/iwd2/moelda.pro
new file mode 100644
index 0000000..c063a08
Binary files /dev/null and b/gemrb/override/iwd2/moelda.pro differ
diff --git a/gemrb/override/iwd2/mrage.pro b/gemrb/override/iwd2/mrage.pro
new file mode 100644
index 0000000..8b9148b
Binary files /dev/null and b/gemrb/override/iwd2/mrage.pro differ
diff --git a/gemrb/override/iwd2/mrageh.pro b/gemrb/override/iwd2/mrageh.pro
new file mode 100644
index 0000000..4a8b252
Binary files /dev/null and b/gemrb/override/iwd2/mrageh.pro differ
diff --git a/gemrb/override/iwd2/mspore.pro b/gemrb/override/iwd2/mspore.pro
new file mode 100644
index 0000000..370e001
Binary files /dev/null and b/gemrb/override/iwd2/mspore.pro differ
diff --git a/gemrb/override/iwd2/msumm1.pro b/gemrb/override/iwd2/msumm1.pro
new file mode 100644
index 0000000..3f4ad3a
Binary files /dev/null and b/gemrb/override/iwd2/msumm1.pro differ
diff --git a/gemrb/override/iwd2/msumm1h.pro b/gemrb/override/iwd2/msumm1h.pro
new file mode 100644
index 0000000..7b4ad4d
Binary files /dev/null and b/gemrb/override/iwd2/msumm1h.pro differ
diff --git a/gemrb/override/iwd2/msumm1x.pro b/gemrb/override/iwd2/msumm1x.pro
new file mode 100644
index 0000000..8242d6c
Binary files /dev/null and b/gemrb/override/iwd2/msumm1x.pro differ
diff --git a/gemrb/override/iwd2/msumm2h.pro b/gemrb/override/iwd2/msumm2h.pro
new file mode 100644
index 0000000..07b7f94
Binary files /dev/null and b/gemrb/override/iwd2/msumm2h.pro differ
diff --git a/gemrb/override/iwd2/msumm3h.pro b/gemrb/override/iwd2/msumm3h.pro
new file mode 100644
index 0000000..8a775a8
Binary files /dev/null and b/gemrb/override/iwd2/msumm3h.pro differ
diff --git a/gemrb/override/iwd2/msumm4h.pro b/gemrb/override/iwd2/msumm4h.pro
new file mode 100644
index 0000000..4dbaf7b
Binary files /dev/null and b/gemrb/override/iwd2/msumm4h.pro differ
diff --git a/gemrb/override/iwd2/msumm5h.pro b/gemrb/override/iwd2/msumm5h.pro
new file mode 100644
index 0000000..a1e88b0
Binary files /dev/null and b/gemrb/override/iwd2/msumm5h.pro differ
diff --git a/gemrb/override/iwd2/msumm6h.pro b/gemrb/override/iwd2/msumm6h.pro
new file mode 100644
index 0000000..600dc17
Binary files /dev/null and b/gemrb/override/iwd2/msumm6h.pro differ
diff --git a/gemrb/override/iwd2/msumm7h.pro b/gemrb/override/iwd2/msumm7h.pro
new file mode 100644
index 0000000..2f3f5b3
Binary files /dev/null and b/gemrb/override/iwd2/msumm7h.pro differ
diff --git a/gemrb/override/iwd2/mswordh.pro b/gemrb/override/iwd2/mswordh.pro
new file mode 100644
index 0000000..6cf138d
Binary files /dev/null and b/gemrb/override/iwd2/mswordh.pro differ
diff --git a/gemrb/override/iwd2/mtouchh.pro b/gemrb/override/iwd2/mtouchh.pro
new file mode 100644
index 0000000..a9f7c06
Binary files /dev/null and b/gemrb/override/iwd2/mtouchh.pro differ
diff --git a/gemrb/override/iwd2/necroh.pro b/gemrb/override/iwd2/necroh.pro
new file mode 100644
index 0000000..ad3c190
Binary files /dev/null and b/gemrb/override/iwd2/necroh.pro differ
diff --git a/gemrb/override/iwd2/necrot.pro b/gemrb/override/iwd2/necrot.pro
new file mode 100644
index 0000000..975d707
Binary files /dev/null and b/gemrb/override/iwd2/necrot.pro differ
diff --git a/gemrb/override/iwd2/npoisoh.pro b/gemrb/override/iwd2/npoisoh.pro
new file mode 100644
index 0000000..9c741f3
Binary files /dev/null and b/gemrb/override/iwd2/npoisoh.pro differ
diff --git a/gemrb/override/iwd2/ofsphe.pro b/gemrb/override/iwd2/ofsphe.pro
new file mode 100644
index 0000000..e07fc9a
Binary files /dev/null and b/gemrb/override/iwd2/ofsphe.pro differ
diff --git a/gemrb/override/iwd2/overlay.2da b/gemrb/override/iwd2/overlay.2da
new file mode 100644
index 0000000..c5432e0
--- /dev/null
+++ b/gemrb/override/iwd2/overlay.2da
@@ -0,0 +1,35 @@
+2DA V1.0
+*
+ VVC UNDER FLAGS
+SANCTUARY SANCTUC 0 1
+ENTANGLE ENTANGC 0 0
+WISP WISP 0 0
+SHIELDGLOBE SHIELDC 0 1
+GREASE GREASEC 1 0
+WEB WEBC 1 0
+MINORGLOBE MGOINVC 0 1
+GLOBE GOINVUC 0 1
+FLAMESHROUD SOFLAMC 0 0
+ANTIMAGIC AMSHELC 0 0
+RESILIENT ORSPHEC 0 0
+PROTFROMMISS PFNMISC 0 0
+CLOAKOFFEAR COFEARC 0 0
+ENTROPY ESHIELC 0 0
+FIREAURA FIAURAC 0 0
+FROSTAURA FRAURAC 0 0
+INSECT IPLAGUC 0 0
+STORMSHELL SSHELLC 0 0
+LATHANDER1 SOLATC1 0 0
+LATHANDER2 SOLATC2 1 0
+GLATHANDER1 GSOLAC1 0 0
+GLATHANDER2 GSOLAC2 1 0
+SEVENEYES1 SEYESC1 0 0
+SEVENEYES2 SEYESC2 1 0
+BOUNCE SPTURNI2 1 0
+BOUNCE2 SPTURNI 1 0
+FIRESHIELD1 FSHIRC1 0 0
+FIRESHIELD2 FSHIRC2 1 0
+ICESHIELD1 FSHIBC1 0 0
+ICESHIELD2 FSHIBC2 1 0
+TORTOISE TSHELLC 0 0
+DEATHARMOR DARMORC 0 0
diff --git a/gemrb/override/iwd2/paralh.pro b/gemrb/override/iwd2/paralh.pro
new file mode 100644
index 0000000..b8d8ed6
Binary files /dev/null and b/gemrb/override/iwd2/paralh.pro differ
diff --git a/gemrb/override/iwd2/pathfind.2da b/gemrb/override/iwd2/pathfind.2da
new file mode 100644
index 0000000..d61fcd0
--- /dev/null
+++ b/gemrb/override/iwd2/pathfind.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ 0 1 2 3 4 5 6 7 8 9 a b c d e f
+PASSABLE 8 1 1 1 1 1 1 1 0 1 8 1 0 8 3 1
+COST 10 4
diff --git a/gemrb/override/iwd2/pfire.pro b/gemrb/override/iwd2/pfire.pro
new file mode 100644
index 0000000..8dd8653
Binary files /dev/null and b/gemrb/override/iwd2/pfire.pro differ
diff --git a/gemrb/override/iwd2/pictures.2da b/gemrb/override/iwd2/pictures.2da
new file mode 100644
index 0000000..acd581e
--- /dev/null
+++ b/gemrb/override/iwd2/pictures.2da
@@ -0,0 +1,86 @@
+2DA V1.0
+37
+ GENDER HAIR SKIN MAJOR MINOR
+2MELF1_ 1 13 6 62 66
+2MAAS1_ 1 12 3 50 39
+2MBAR1_ 1 85 0 41 39
+2MDEF1_ 1 0 79 66 54
+2MDEF2_ 1 0 79 66 56
+2MGNM1_ 1 107 79 37 39
+2MHAF1_ 1 85 2 66 38
+2MHUM1_ 1 10 0 39 63
+2MHUM2_ 1 10 6 49 66
+2MHUM3_ 1 85 0 66 49
+2MHUM4_ 1 85 0 41 66
+2MHUM5_ 1 10 2 65 39
+2MHUM6_ 1 85 2 41 39
+2MHUM7_ 1 8 0 39 66
+2MPAL1_ 1 84 2 41 66
+2MORC1_ 1 112 0 66 41
+2MDWF1_ 1 85 0 41 46
+2MDWF2_ 1 85 2 39 56
+DMC_ 1 84 2 39 27
+DMF_ 1 84 3 38 27
+DMF2_ 1 12 3 66 58
+DMT_ 1 84 1 41 49
+EMC_ 1 83 110 49 60
+EMF_ 1 84 2 41 26
+EMF2_ 1 12 0 39 41
+EMT_ 1 12 2 66 37
+EMW_ 1 13 6 51 49
+GMT_ 1 12 0 66 14
+GMW_ 1 84 2 41 46
+HAMF_ 1 84 2 39 24
+HAMT_ 1 84 2 41 56
+HEMF_ 1 84 1 41 98
+HEMT_ 1 85 2 41 54
+HEMW_ 1 12 0 49 41
+HMC_ 1 12 0 49 53
+HMC2_ 1 87 0 63 60
+HMC3_ 1 12 79 94 91
+HMC4_ 1 12 1 39 56
+HMF_ 1 84 0 66 27
+HMF2_ 1 87 0 66 98
+HMF3_ 1 8 0 66 103
+HMF4_ 1 107 0 66 102
+HMF5_ 1 12 3 41 66
+HMF6_ 1 84 0 66 47
+HMF7_ 1 8 0 41 46
+HMW_ 1 12 5 58 48
+HMW2_ 1 12 0 22 41
+HMW3_ 1 84 0 46 46
+HMW4_ 1 13 0 107 66
+HMB_ 1 87 0 41 49
+HMB2_ 1 84 2 39 47
+2FBAR1_ 2 84 3 41 40
+2FDEF1_ 2 0 79 66 41
+2FELF1_ 2 85 0 39 56
+2FELF2_ 2 13 0 66 63
+2FELF3_ 2 8 4 51 53
+2FGNM1_ 2 107 79 50 51
+2FHUM1_ 2 13 0 49 66
+2FHUM2_ 2 85 0 41 40
+2FHUM3_ 2 84 2 46 45
+2FHUM4_ 2 85 0 50 39
+2FTIF1_ 2 14 0 66 41
+2FORC1_ 2 85 4 39 38
+DFF_ 2 12 2 41 65
+EFC_ 2 13 2 47 60
+EFF_ 2 12 0 66 25
+EFW_ 2 10 2 54 66
+EFW2_ 2 83 0 60 60
+GFW_ 2 85 0 40 58
+HAFF_ 2 84 2 39 49
+HEFC_ 2 12 3 39 58
+HAFT_ 2 12 0 39 14
+HEFB_ 2 12 2 39 56
+HEFF_ 2 84 2 41 66
+HEFW_ 2 84 0 41 40
+HFD_ 2 12 2 41 49
+HFF_ 2 84 2 100 98
+HFF2_ 2 12 2 41 37
+HFT_ 2 84 0 39 66
+HFT2_ 2 12 2 39 63
+HFW_ 2 12 0 49 66
+HFW2_ 2 13 0 66 66
+HFW3_ 2 8 0 66 60
diff --git a/gemrb/override/iwd2/poisonh.pro b/gemrb/override/iwd2/poisonh.pro
new file mode 100644
index 0000000..bd97ddc
Binary files /dev/null and b/gemrb/override/iwd2/poisonh.pro differ
diff --git a/gemrb/override/iwd2/polystat.2da b/gemrb/override/iwd2/polystat.2da
new file mode 100644
index 0000000..928fd7b
--- /dev/null
+++ b/gemrb/override/iwd2/polystat.2da
@@ -0,0 +1,25 @@
+2DA V1.0
+-1
+ STATS
+0 ARMORCLASS
+1 ACCRUSHINGMOD
+2 ACMISSILEMOD
+3 ACPIERCINGMOD
+4 ACSLASHINGMOD
+5 NUMBEROFATTACKS
+6 RESISTFIRE
+7 RESISTCOLD
+8 RESISTELECTRICITY
+9 RESISTACID
+10 RESISTMAGIC
+11 RESISTMAGICFIRE
+12 RESISTMAGICCOLD
+13 RESISTSLASHING
+14 RESISTCRUSHING
+15 RESISTPIERCING
+16 RESISTMISSILE
+17 STR
+19 DEX
+20 CON
+21 MAGICDAMAGERESISTANCE
+22 RESISTPOISON
diff --git a/gemrb/override/iwd2/portal.pro b/gemrb/override/iwd2/portal.pro
new file mode 100644
index 0000000..3bf4dd3
Binary files /dev/null and b/gemrb/override/iwd2/portal.pro differ
diff --git a/gemrb/override/iwd2/prayer.pro b/gemrb/override/iwd2/prayer.pro
new file mode 100644
index 0000000..454ca43
Binary files /dev/null and b/gemrb/override/iwd2/prayer.pro differ
diff --git a/gemrb/override/iwd2/prayerh.pro b/gemrb/override/iwd2/prayerh.pro
new file mode 100644
index 0000000..3e6c533
Binary files /dev/null and b/gemrb/override/iwd2/prayerh.pro differ
diff --git a/gemrb/override/iwd2/protevil.pro b/gemrb/override/iwd2/protevil.pro
new file mode 100644
index 0000000..44972ee
Binary files /dev/null and b/gemrb/override/iwd2/protevil.pro differ
diff --git a/gemrb/override/iwd2/prtl_cl.pro b/gemrb/override/iwd2/prtl_cl.pro
new file mode 100644
index 0000000..6a5bfaa
Binary files /dev/null and b/gemrb/override/iwd2/prtl_cl.pro differ
diff --git a/gemrb/override/iwd2/prtl_op.pro b/gemrb/override/iwd2/prtl_op.pro
new file mode 100644
index 0000000..6f689f1
Binary files /dev/null and b/gemrb/override/iwd2/prtl_op.pro differ
diff --git a/gemrb/override/iwd2/pspray.pro b/gemrb/override/iwd2/pspray.pro
new file mode 100644
index 0000000..66a5326
Binary files /dev/null and b/gemrb/override/iwd2/pspray.pro differ
diff --git a/gemrb/override/iwd2/pwkill.pro b/gemrb/override/iwd2/pwkill.pro
new file mode 100644
index 0000000..8e60ca0
Binary files /dev/null and b/gemrb/override/iwd2/pwkill.pro differ
diff --git a/gemrb/override/iwd2/pwsileh.pro b/gemrb/override/iwd2/pwsileh.pro
new file mode 100644
index 0000000..46b144d
Binary files /dev/null and b/gemrb/override/iwd2/pwsileh.pro differ
diff --git a/gemrb/override/iwd2/pwstun.pro b/gemrb/override/iwd2/pwstun.pro
new file mode 100644
index 0000000..a4ea2be
Binary files /dev/null and b/gemrb/override/iwd2/pwstun.pro differ
diff --git a/gemrb/override/iwd2/pwstunh.pro b/gemrb/override/iwd2/pwstunh.pro
new file mode 100644
index 0000000..5afd33d
Binary files /dev/null and b/gemrb/override/iwd2/pwstunh.pro differ
diff --git a/gemrb/override/iwd2/races.2da b/gemrb/override/iwd2/races.2da
new file mode 100644
index 0000000..48c4784
--- /dev/null
+++ b/gemrb/override/iwd2/races.2da
@@ -0,0 +1,19 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF ID USABILITY HAIR SKIN SKILL_COLUMN FAVORED_CLASS FEATBONUS
+HUMAN 7193 9550 1096 1 0x08000000 HAIRNORM SKINNORM 23 -1 1
+DWARF 7182 9551 1100 4 0x01000000 HAIRNORM SKINNORM 29 0x505 0
+ELF 7194 9552 1097 2 0x00800000 HAIRNORM SKINNORM 26 0xb0b 0
+GNOME 7196 9553 1099 6 0x10000000 HAIRNORM SKINNORM 35 0xb0b 0
+HALF_ELF 7197 9555 1098 3 0x02000000 HAIRNORM SKINNORM 37 -1 0
+HALF_ORC 22 25 23 7 0x80000000 HAIRNORM SKINNORM 38 0x101 0
+HALFLING 7195 9554 5374 5 0x04000000 HAIRNORM SKINNORM 32 0x909 0
+HUMAN_AASIMAR 5377 5386 5377 0x10001 0x00000000 HAIRAASM SKINAASM 24 0x707 0
+HUMAN_TIEFLING 5378 5388 5378 0x10002 0x00000000 HAIRTIEF SKINTIEF 25 0x909 0
+ELF_DROW 5379 5394 5379 0x20001 0x00000000 HAIRDARK SKINDARK 27 0x30b 0
+ELF_WILD 5380 5397 5380 0x20002 0x00000000 HAIRWOOD SKINWOOD 28 0xa0a 0
+DWARF_GOLD 5381 5413 5381 0x40001 0x00000000 HAIRGOLD SKINGOLD 30 0x505 0
+DWARF_GRAY 5382 5416 5382 0x40002 0x00000000 HAIRGRAY SKINGRAY 31 0x505 0
+HALFLING_STRONGHEART 5383 5660 5383 0x50001 0x00000000 HAIRNORM SKINNORM 33 0x909 0
+HALFLING_GHOSTWISE 5384 5661 5384 0x50002 0x00000000 HAIRNORM SKINNORM 34 0x101 0
+GNOME_DEEP 5385 5662 5385 0x60001 0x00000000 HAIRNORM SKINNORM 36 0xb0b 0
diff --git a/gemrb/override/iwd2/rad100.pro b/gemrb/override/iwd2/rad100.pro
new file mode 100644
index 0000000..1a8e9a0
Binary files /dev/null and b/gemrb/override/iwd2/rad100.pro differ
diff --git a/gemrb/override/iwd2/rad250.pro b/gemrb/override/iwd2/rad250.pro
new file mode 100644
index 0000000..31ee26e
Binary files /dev/null and b/gemrb/override/iwd2/rad250.pro differ
diff --git a/gemrb/override/iwd2/rad300.pro b/gemrb/override/iwd2/rad300.pro
new file mode 100644
index 0000000..022fecb
Binary files /dev/null and b/gemrb/override/iwd2/rad300.pro differ
diff --git a/gemrb/override/iwd2/randitem.2da b/gemrb/override/iwd2/randitem.2da
new file mode 100644
index 0000000..ad5dc70
--- /dev/null
+++ b/gemrb/override/iwd2/randitem.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ RESREF FURY_MODE
+GOLD MISC07
+RND RT_NORM RT_FURY
diff --git a/gemrb/override/iwd2/rdeadh.pro b/gemrb/override/iwd2/rdeadh.pro
new file mode 100644
index 0000000..8a79354
Binary files /dev/null and b/gemrb/override/iwd2/rdeadh.pro differ
diff --git a/gemrb/override/iwd2/recitah.pro b/gemrb/override/iwd2/recitah.pro
new file mode 100644
index 0000000..49c7aeb
Binary files /dev/null and b/gemrb/override/iwd2/recitah.pro differ
diff --git a/gemrb/override/iwd2/resurrh.pro b/gemrb/override/iwd2/resurrh.pro
new file mode 100644
index 0000000..0ae7bb9
Binary files /dev/null and b/gemrb/override/iwd2/resurrh.pro differ
diff --git a/gemrb/override/iwd2/rng450.pro b/gemrb/override/iwd2/rng450.pro
new file mode 100644
index 0000000..e991873
Binary files /dev/null and b/gemrb/override/iwd2/rng450.pro differ
diff --git a/gemrb/override/iwd2/rparalh.pro b/gemrb/override/iwd2/rparalh.pro
new file mode 100644
index 0000000..a163b89
Binary files /dev/null and b/gemrb/override/iwd2/rparalh.pro differ
diff --git a/gemrb/override/iwd2/rwotfag.pro b/gemrb/override/iwd2/rwotfag.pro
new file mode 100644
index 0000000..e8dd52b
Binary files /dev/null and b/gemrb/override/iwd2/rwotfag.pro differ
diff --git a/gemrb/override/iwd2/rwotfah.pro b/gemrb/override/iwd2/rwotfah.pro
new file mode 100644
index 0000000..1a2a98e
Binary files /dev/null and b/gemrb/override/iwd2/rwotfah.pro differ
diff --git a/gemrb/override/iwd2/sarmorh.pro b/gemrb/override/iwd2/sarmorh.pro
new file mode 100644
index 0000000..64fb522
Binary files /dev/null and b/gemrb/override/iwd2/sarmorh.pro differ
diff --git a/gemrb/override/iwd2/savegame.2da b/gemrb/override/iwd2/savegame.2da
new file mode 100644
index 0000000..9817985
--- /dev/null
+++ b/gemrb/override/iwd2/savegame.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+Auto-Save
+ SLOTNAME QSAVE
+0 Auto-Save 0
+1 Quick-Save 1
+2 Quick-Save-Backup 1
diff --git a/gemrb/override/iwd2/scharge.pro b/gemrb/override/iwd2/scharge.pro
new file mode 100644
index 0000000..83d1b66
Binary files /dev/null and b/gemrb/override/iwd2/scharge.pro differ
diff --git a/gemrb/override/iwd2/schargh.pro b/gemrb/override/iwd2/schargh.pro
new file mode 100644
index 0000000..c7e08d0
Binary files /dev/null and b/gemrb/override/iwd2/schargh.pro differ
diff --git a/gemrb/override/iwd2/script.2da b/gemrb/override/iwd2/script.2da
new file mode 100644
index 0000000..e39f904
--- /dev/null
+++ b/gemrb/override/iwd2/script.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+0
+ DATA 1 2 3 4 5 6 7 8 9 10
+OBJECT_IDS_COUNT 10 ea general race subrace specific gender alignmnt class class20 classmsk
+MAX_OBJECT_NESTING 5
+ADDITIONAL_RECT 1
+EXTRA_PARAMETERS_COUNT 2
+TRIGGER_POINT 0
diff --git a/gemrb/override/iwd2/seater.pro b/gemrb/override/iwd2/seater.pro
new file mode 100644
index 0000000..5f6d62c
Binary files /dev/null and b/gemrb/override/iwd2/seater.pro differ
diff --git a/gemrb/override/iwd2/seaterh.pro b/gemrb/override/iwd2/seaterh.pro
new file mode 100644
index 0000000..488d807
Binary files /dev/null and b/gemrb/override/iwd2/seaterh.pro differ
diff --git a/gemrb/override/iwd2/sgrowth.pro b/gemrb/override/iwd2/sgrowth.pro
new file mode 100644
index 0000000..06a72f5
Binary files /dev/null and b/gemrb/override/iwd2/sgrowth.pro differ
diff --git a/gemrb/override/iwd2/shout.pro b/gemrb/override/iwd2/shout.pro
new file mode 100644
index 0000000..9e1a6a2
Binary files /dev/null and b/gemrb/override/iwd2/shout.pro differ
diff --git a/gemrb/override/iwd2/shroud.pro b/gemrb/override/iwd2/shroud.pro
new file mode 100644
index 0000000..e31a942
Binary files /dev/null and b/gemrb/override/iwd2/shroud.pro differ
diff --git a/gemrb/override/iwd2/shtable.2da b/gemrb/override/iwd2/shtable.2da
new file mode 100644
index 0000000..ac06d6f
--- /dev/null
+++ b/gemrb/override/iwd2/shtable.2da
@@ -0,0 +1,43 @@
+2DA V1.0
+*
+ RESREF
+0 SHAIR
+1 SHEARTH
+2 SHWATER
+3 *
+4 SHAIR
+5 SHEARTH
+6 SHWATER
+7 *
+8 SHAIR
+9 SHEARTH
+10 SHWATER
+11 *
+12 SHAIR
+13 SHEARTH
+14 SHWATER
+15 *
+16 SHAIR
+17 SHEARTH
+18 SHWATER
+19 *
+20 SHAIR
+21 SHEARTH
+22 SHWATER
+23 *
+24 SHAIR
+25 SHEARTH
+26 SHWATER
+27 *
+28 SHAIR
+29 SHEARTH
+30 SHWATER
+31 *
+32
+33
+34
+35 FLMSTRK
+36 HLYMITE
+37
+38 DDOORH
+39 SPFDEATH
diff --git a/gemrb/override/iwd2/skillsta.2da b/gemrb/override/iwd2/skillsta.2da
new file mode 100644
index 0000000..d590440
--- /dev/null
+++ b/gemrb/override/iwd2/skillsta.2da
@@ -0,0 +1,19 @@
+2DA V1.0
+-1
+ STAT ABILITY
+Alchemy ALCHEMY INT
+Animal_Empathy ANIMALS CHR
+Bluff BLUFF CHR
+Concentration CONCENTRATION CON
+Diplomacy DIPLOMACY CHR
+Disable_Device TRAPS INT
+Hide HIDEINSHADOWS DEX
+Intimidate INTIMIDATE CHR
+Knowledge_Arcana LORE INT
+Move_Silently STEALTH DEX
+Open_Lock LOCKPICKING DEX
+Pick_Pocket PICKPOCKET DEX
+Search SEARCH INT
+Spellcraft SPELLCRAFT INT
+Use_Magic_Device MAGICDEVICE CHR
+Wilderness_Lore TRACKING WIS
diff --git a/gemrb/override/iwd2/sleep.pro b/gemrb/override/iwd2/sleep.pro
new file mode 100644
index 0000000..06568c9
Binary files /dev/null and b/gemrb/override/iwd2/sleep.pro differ
diff --git a/gemrb/override/iwd2/slivinh.pro b/gemrb/override/iwd2/slivinh.pro
new file mode 100644
index 0000000..1e74c1a
Binary files /dev/null and b/gemrb/override/iwd2/slivinh.pro differ
diff --git a/gemrb/override/iwd2/slottype.2da b/gemrb/override/iwd2/slottype.2da
new file mode 100644
index 0000000..bb9600a
--- /dev/null
+++ b/gemrb/override/iwd2/slottype.2da
@@ -0,0 +1,54 @@
+2DA V1.0
+*
+ BITS SCRIPT ICON STRREF EFFECT FLAGS
+10 0 0 * 0 2 0
+6 1 13 STONHELM 11999 7 0
+1 2 11 STONARM 11997 1 0
+9 0 0 * 0 0 0
+5 8 12 STONGLET 11998 1 0
+7 16 22 STONRING 12002 1 1
+8 16 23 STONRING 12003 1 1
+0 32 14 STONAMUL 12000 1 0
+2 64 21 STONBELT 12001 1 0
+3 128 25 STONBOOT 12005 1 0
+43 256 101 STONWEAP 12010 4 1
+44 4 102 STONSHIL 12010 6 0
+45 256 103 STONWEAP 12010 4 1
+46 4 104 STONSHIL 12010 6 0
+47 256 105 STONWEAP 12010 4 1
+48 4 106 STONSHIL 12010 6 0
+49 256 107 STONWEAP 12010 4 1
+50 4 108 STONSHIL 12010 6 0
+4 1024 24 STONCLOK 12004 1 0
+11 512 15 STONQUIV 12009 5 1
+12 512 16 STONQUIV 12009 5 1
+13 512 17 STONQUIV 12009 5 1
+14 0 0 * 0 5 0
+15 2048 5 STONITEM 12012 0 1
+16 2048 6 STONITEM 12012 0 1
+17 2048 7 STONITEM 12012 0 1
+18 -1 30 * 12013 0 1
+19 -1 31 * 12013 0 1
+20 -1 32 * 12013 0 1
+21 -1 33 * 12013 0 1
+22 -1 34 * 12013 0 1
+23 -1 35 * 12013 0 1
+24 -1 36 * 12013 0 1
+25 -1 37 * 12013 0 1
+26 -1 38 * 12013 0 1
+27 -1 39 * 12013 0 1
+28 -1 40 * 12013 0 1
+29 -1 41 * 12013 0 1
+30 -1 42 * 12013 0 1
+31 -1 43 * 12013 0 1
+32 -1 44 * 12013 0 1
+33 -1 45 * 12013 0 1
+34 -1 73 * 12013 0 1
+35 -1 74 * 12013 0 1
+36 -1 75 * 12013 0 1
+37 -1 76 * 12013 0 1
+38 -1 77 * 12013 0 1
+39 -1 78 * 12013 0 1
+40 -1 79 * 12013 0 1
+41 -1 80 * 12013 0 1
+42 0 0 * 0 3 0
diff --git a/gemrb/override/iwd2/soflamh.pro b/gemrb/override/iwd2/soflamh.pro
new file mode 100644
index 0000000..334e211
Binary files /dev/null and b/gemrb/override/iwd2/soflamh.pro differ
diff --git a/gemrb/override/iwd2/sohope.pro b/gemrb/override/iwd2/sohope.pro
new file mode 100644
index 0000000..f70ba7d
Binary files /dev/null and b/gemrb/override/iwd2/sohope.pro differ
diff --git a/gemrb/override/iwd2/sooneh.pro b/gemrb/override/iwd2/sooneh.pro
new file mode 100644
index 0000000..3672511
Binary files /dev/null and b/gemrb/override/iwd2/sooneh.pro differ
diff --git a/gemrb/override/iwd2/sopain.pro b/gemrb/override/iwd2/sopain.pro
new file mode 100644
index 0000000..0cd795a
Binary files /dev/null and b/gemrb/override/iwd2/sopain.pro differ
diff --git a/gemrb/override/iwd2/sparbknp.pro b/gemrb/override/iwd2/sparbknp.pro
new file mode 100644
index 0000000..aba7673
Binary files /dev/null and b/gemrb/override/iwd2/sparbknp.pro differ
diff --git a/gemrb/override/iwd2/sparbkpa.pro b/gemrb/override/iwd2/sparbkpa.pro
new file mode 100644
index 0000000..7a51eed
Binary files /dev/null and b/gemrb/override/iwd2/sparbkpa.pro differ
diff --git a/gemrb/override/iwd2/sparblnp.pro b/gemrb/override/iwd2/sparblnp.pro
new file mode 100644
index 0000000..f858276
Binary files /dev/null and b/gemrb/override/iwd2/sparblnp.pro differ
diff --git a/gemrb/override/iwd2/sparblpa.pro b/gemrb/override/iwd2/sparblpa.pro
new file mode 100644
index 0000000..4124a9d
Binary files /dev/null and b/gemrb/override/iwd2/sparblpa.pro differ
diff --git a/gemrb/override/iwd2/sparchnp.pro b/gemrb/override/iwd2/sparchnp.pro
new file mode 100644
index 0000000..7dd540e
Binary files /dev/null and b/gemrb/override/iwd2/sparchnp.pro differ
diff --git a/gemrb/override/iwd2/sparchpa.pro b/gemrb/override/iwd2/sparchpa.pro
new file mode 100644
index 0000000..101f9a2
Binary files /dev/null and b/gemrb/override/iwd2/sparchpa.pro differ
diff --git a/gemrb/override/iwd2/spargonp.pro b/gemrb/override/iwd2/spargonp.pro
new file mode 100644
index 0000000..234aca6
Binary files /dev/null and b/gemrb/override/iwd2/spargonp.pro differ
diff --git a/gemrb/override/iwd2/spargopa.pro b/gemrb/override/iwd2/spargopa.pro
new file mode 100644
index 0000000..2665825
Binary files /dev/null and b/gemrb/override/iwd2/spargopa.pro differ
diff --git a/gemrb/override/iwd2/spargrnp.pro b/gemrb/override/iwd2/spargrnp.pro
new file mode 100644
index 0000000..a1d4dbb
Binary files /dev/null and b/gemrb/override/iwd2/spargrnp.pro differ
diff --git a/gemrb/override/iwd2/spargrpa.pro b/gemrb/override/iwd2/spargrpa.pro
new file mode 100644
index 0000000..55a741b
Binary files /dev/null and b/gemrb/override/iwd2/spargrpa.pro differ
diff --git a/gemrb/override/iwd2/sparicnp.pro b/gemrb/override/iwd2/sparicnp.pro
new file mode 100644
index 0000000..77ff12b
Binary files /dev/null and b/gemrb/override/iwd2/sparicnp.pro differ
diff --git a/gemrb/override/iwd2/sparicpa.pro b/gemrb/override/iwd2/sparicpa.pro
new file mode 100644
index 0000000..2d8b9d7
Binary files /dev/null and b/gemrb/override/iwd2/sparicpa.pro differ
diff --git a/gemrb/override/iwd2/sparklbk.pro b/gemrb/override/iwd2/sparklbk.pro
new file mode 100644
index 0000000..147bd43
Binary files /dev/null and b/gemrb/override/iwd2/sparklbk.pro differ
diff --git a/gemrb/override/iwd2/sparklbl.pro b/gemrb/override/iwd2/sparklbl.pro
new file mode 100644
index 0000000..4928b0c
Binary files /dev/null and b/gemrb/override/iwd2/sparklbl.pro differ
diff --git a/gemrb/override/iwd2/sparklch.pro b/gemrb/override/iwd2/sparklch.pro
new file mode 100644
index 0000000..c6ad4c8
Binary files /dev/null and b/gemrb/override/iwd2/sparklch.pro differ
diff --git a/gemrb/override/iwd2/sparklgo.pro b/gemrb/override/iwd2/sparklgo.pro
new file mode 100644
index 0000000..748d91d
Binary files /dev/null and b/gemrb/override/iwd2/sparklgo.pro differ
diff --git a/gemrb/override/iwd2/sparklgr.pro b/gemrb/override/iwd2/sparklgr.pro
new file mode 100644
index 0000000..ac152c1
Binary files /dev/null and b/gemrb/override/iwd2/sparklgr.pro differ
diff --git a/gemrb/override/iwd2/sparklic.pro b/gemrb/override/iwd2/sparklic.pro
new file mode 100644
index 0000000..e833132
Binary files /dev/null and b/gemrb/override/iwd2/sparklic.pro differ
diff --git a/gemrb/override/iwd2/sparklma.pro b/gemrb/override/iwd2/sparklma.pro
new file mode 100644
index 0000000..50d7be0
Binary files /dev/null and b/gemrb/override/iwd2/sparklma.pro differ
diff --git a/gemrb/override/iwd2/sparklor.pro b/gemrb/override/iwd2/sparklor.pro
new file mode 100644
index 0000000..dc02305
Binary files /dev/null and b/gemrb/override/iwd2/sparklor.pro differ
diff --git a/gemrb/override/iwd2/sparklpu.pro b/gemrb/override/iwd2/sparklpu.pro
new file mode 100644
index 0000000..ef0f1fd
Binary files /dev/null and b/gemrb/override/iwd2/sparklpu.pro differ
diff --git a/gemrb/override/iwd2/sparklre.pro b/gemrb/override/iwd2/sparklre.pro
new file mode 100644
index 0000000..1a15bfd
Binary files /dev/null and b/gemrb/override/iwd2/sparklre.pro differ
diff --git a/gemrb/override/iwd2/sparklst.pro b/gemrb/override/iwd2/sparklst.pro
new file mode 100644
index 0000000..543ea91
Binary files /dev/null and b/gemrb/override/iwd2/sparklst.pro differ
diff --git a/gemrb/override/iwd2/sparmanp.pro b/gemrb/override/iwd2/sparmanp.pro
new file mode 100644
index 0000000..c76d137
Binary files /dev/null and b/gemrb/override/iwd2/sparmanp.pro differ
diff --git a/gemrb/override/iwd2/sparmapa.pro b/gemrb/override/iwd2/sparmapa.pro
new file mode 100644
index 0000000..6a21bd1
Binary files /dev/null and b/gemrb/override/iwd2/sparmapa.pro differ
diff --git a/gemrb/override/iwd2/sparornp.pro b/gemrb/override/iwd2/sparornp.pro
new file mode 100644
index 0000000..abf33b8
Binary files /dev/null and b/gemrb/override/iwd2/sparornp.pro differ
diff --git a/gemrb/override/iwd2/sparorpa.pro b/gemrb/override/iwd2/sparorpa.pro
new file mode 100644
index 0000000..686dd90
Binary files /dev/null and b/gemrb/override/iwd2/sparorpa.pro differ
diff --git a/gemrb/override/iwd2/sparpunp.pro b/gemrb/override/iwd2/sparpunp.pro
new file mode 100644
index 0000000..23f98f2
Binary files /dev/null and b/gemrb/override/iwd2/sparpunp.pro differ
diff --git a/gemrb/override/iwd2/sparpupa.pro b/gemrb/override/iwd2/sparpupa.pro
new file mode 100644
index 0000000..322a995
Binary files /dev/null and b/gemrb/override/iwd2/sparpupa.pro differ
diff --git a/gemrb/override/iwd2/sparrenp.pro b/gemrb/override/iwd2/sparrenp.pro
new file mode 100644
index 0000000..7718874
Binary files /dev/null and b/gemrb/override/iwd2/sparrenp.pro differ
diff --git a/gemrb/override/iwd2/sparrepa.pro b/gemrb/override/iwd2/sparrepa.pro
new file mode 100644
index 0000000..a5edc18
Binary files /dev/null and b/gemrb/override/iwd2/sparrepa.pro differ
diff --git a/gemrb/override/iwd2/sparstnp.pro b/gemrb/override/iwd2/sparstnp.pro
new file mode 100644
index 0000000..7d4b4c3
Binary files /dev/null and b/gemrb/override/iwd2/sparstnp.pro differ
diff --git a/gemrb/override/iwd2/sparstpa.pro b/gemrb/override/iwd2/sparstpa.pro
new file mode 100644
index 0000000..4d55459
Binary files /dev/null and b/gemrb/override/iwd2/sparstpa.pro differ
diff --git a/gemrb/override/iwd2/spbrnhnd.pro b/gemrb/override/iwd2/spbrnhnd.pro
new file mode 100644
index 0000000..96f98a5
Binary files /dev/null and b/gemrb/override/iwd2/spbrnhnd.pro differ
diff --git a/gemrb/override/iwd2/spear.pro b/gemrb/override/iwd2/spear.pro
new file mode 100644
index 0000000..bc9288c
Binary files /dev/null and b/gemrb/override/iwd2/spear.pro differ
diff --git a/gemrb/override/iwd2/spearex.pro b/gemrb/override/iwd2/spearex.pro
new file mode 100644
index 0000000..4bb5786
Binary files /dev/null and b/gemrb/override/iwd2/spearex.pro differ
diff --git a/gemrb/override/iwd2/spfdeath.pro b/gemrb/override/iwd2/spfdeath.pro
new file mode 100644
index 0000000..ae48e54
Binary files /dev/null and b/gemrb/override/iwd2/spfdeath.pro differ
diff --git a/gemrb/override/iwd2/spfirebl.pro b/gemrb/override/iwd2/spfirebl.pro
new file mode 100644
index 0000000..3b44f50
Binary files /dev/null and b/gemrb/override/iwd2/spfirebl.pro differ
diff --git a/gemrb/override/iwd2/spklarbk.pro b/gemrb/override/iwd2/spklarbk.pro
new file mode 100644
index 0000000..f8f12c0
Binary files /dev/null and b/gemrb/override/iwd2/spklarbk.pro differ
diff --git a/gemrb/override/iwd2/spklarbl.pro b/gemrb/override/iwd2/spklarbl.pro
new file mode 100644
index 0000000..eddae9d
Binary files /dev/null and b/gemrb/override/iwd2/spklarbl.pro differ
diff --git a/gemrb/override/iwd2/spklarch.pro b/gemrb/override/iwd2/spklarch.pro
new file mode 100644
index 0000000..82766f4
Binary files /dev/null and b/gemrb/override/iwd2/spklarch.pro differ
diff --git a/gemrb/override/iwd2/spklargo.pro b/gemrb/override/iwd2/spklargo.pro
new file mode 100644
index 0000000..e4323ac
Binary files /dev/null and b/gemrb/override/iwd2/spklargo.pro differ
diff --git a/gemrb/override/iwd2/spklargr.pro b/gemrb/override/iwd2/spklargr.pro
new file mode 100644
index 0000000..38fc137
Binary files /dev/null and b/gemrb/override/iwd2/spklargr.pro differ
diff --git a/gemrb/override/iwd2/spklaric.pro b/gemrb/override/iwd2/spklaric.pro
new file mode 100644
index 0000000..4fd7d7e
Binary files /dev/null and b/gemrb/override/iwd2/spklaric.pro differ
diff --git a/gemrb/override/iwd2/spklarma.pro b/gemrb/override/iwd2/spklarma.pro
new file mode 100644
index 0000000..d8f18c2
Binary files /dev/null and b/gemrb/override/iwd2/spklarma.pro differ
diff --git a/gemrb/override/iwd2/spklaror.pro b/gemrb/override/iwd2/spklaror.pro
new file mode 100644
index 0000000..7e395f4
Binary files /dev/null and b/gemrb/override/iwd2/spklaror.pro differ
diff --git a/gemrb/override/iwd2/spklarpu.pro b/gemrb/override/iwd2/spklarpu.pro
new file mode 100644
index 0000000..e70bb75
Binary files /dev/null and b/gemrb/override/iwd2/spklarpu.pro differ
diff --git a/gemrb/override/iwd2/spklarre.pro b/gemrb/override/iwd2/spklarre.pro
new file mode 100644
index 0000000..29d636c
Binary files /dev/null and b/gemrb/override/iwd2/spklarre.pro differ
diff --git a/gemrb/override/iwd2/spklarst.pro b/gemrb/override/iwd2/spklarst.pro
new file mode 100644
index 0000000..ba5633b
Binary files /dev/null and b/gemrb/override/iwd2/spklarst.pro differ
diff --git a/gemrb/override/iwd2/splprot.2da b/gemrb/override/iwd2/splprot.2da
new file mode 100644
index 0000000..74254a4
--- /dev/null
+++ b/gemrb/override/iwd2/splprot.2da
@@ -0,0 +1,105 @@
+2DA V1.0
+0
+ STAT VALUE RELATION COMMENT
+0 EA 0 4 anything
+1 GENERAL 4 1 general/undead
+2 GENERAL 4 5 general/not_undead
+3 RESISTFIRE 100 4 fire_resistance>=100
+4 RESISTFIRE 100 2 fire_resistance<100
+5 GENERAL 1 1 general/humanoid
+6 GENERAL 1 5 general/not_humanoid
+7 GENERAL 2 1 general/animal
+8 GENERAL 2 5 general/not_animal
+9 RACE 152 1 race/elemental
+10 RACE 152 5 race/not_elemental
+11 RACE 159 1 race/fungus
+12 RACE 159 5 race/not_fungus
+13 0x102 3 0 huge_creature
+14 0x102 3 3 not_huge_creature
+15 RACE 2 1 race/elf
+16 RACE 2 5 race/not_elf
+17 RACE 166 1 race/umberhulk
+18 RACE 166 5 race/not_umberhulk
+19 RACE 3 1 race/halfelf
+20 RACE 3 5 race/not_halfelf
+21 0x103 5 7 humanoid_or_animal
+22 0x104 5 7 not_humanoid_or_animal
+23 STATE 0x40000 9 state/blind
+24 STATE 0x40000 8 state/not_blind
+25 RESISTCOLD 100 4 cold_resistance>=100
+26 RESISTCOLD 100 2 cold_resistance<100
+27 RACE 156 1 race/golem
+28 RACE 156 5 race/not_golem
+29 RACE 179 1 race/minotaur
+30 RACE 179 5 race/not_minotaur
+31 0x103 1 11 undead_or_fungus
+32 0x104 1 11 not_undead_or_fungus
+33 ALIGNMENT 2 9 alignment/good
+34 ALIGNMENT 2 8 alignment/not_good
+35 ALIGNMENT 1 9 alignment/neutral
+36 ALIGNMENT 1 8 alignment/not_neutral
+37 ALIGNMENT 3 7 alignment/evil
+38 ALIGNMENT 3 11 alignment/not_evil
+39 CLASS 7 1 class/paladin
+40 CLASS 7 5 class/not_paladin
+41 0x105 217 1 moral_alignment_same
+42 0x105 217 5 moral_alignment_not_same
+43 0x100 * * source
+44 0x101 * * not_source
+45 ? * * water_using-FIXME
+46 ? * * not_water_using-FIXME
+47 0x104 32 27 breathes/not_undead_fungus_golem
+48 0x103 32 27 not_breathes/undead_fungus_or_golem
+49 EA 30 0 allies
+50 EA 30 3 not_allies
+51 EA 200 4 enemies
+52 EA 200 2 not_enemies
+53 0x103 3 25 race/fire_or_cold_using
+54 0x104 3 25 race/not_fire_or_cold_using
+55 ? * * unnatural
+56 ? * * not_unnatural
+57 SEX 1 1 gender/male
+58 SEX 1 5 gender/not_male
+59 ALIGNMENT 0x10 1 alignment/lawful
+60 ALIGNMENT 0x10 5 alignment/not_lawful
+61 ALIGNMENT 0x30 1 alignment/chaotic
+62 ALIGNMENT 0x30 5 alignment/not_chaotic
+63 ? * * evasion_check
+64 RACE 160 1 race/orc
+65 RACE 160 5 race/not_orc
+66 EXTSTATE ? 9 extstate/deaf
+67 EXTSTATE ? 8 extstate/not_deaf
+68 SEX 6 1 gender/summoned
+69 SEX 6 5 gender/not_summoned
+70 RACE 184 1 race/mindflayer
+71 RACE 184 5 race/not_mindflayer
+72 STATE 0x1000 9 state/silenced
+73 STATE 0x1000 8 state/not_silenced
+74 INT -1 4 int/less
+75 INT -1 0 int/greater
+76 INT -1 3 int/less_or_equal
+77 INT -1 2 int/greater_or_equal
+78 CLASS 2 1 class/bard
+79 CLASS 2 5 class/not_bard
+80 0x108 1 * near_enemies
+81 0x108 0 * not_near_enemies
+82 SUBRACE 0x20001 1 subrace/drow
+83 SUBRACE 0x20001 5 subrace/not_drow
+84 SUBRACE 0x40002 1 subrace/gray_dwarf
+85 SUBRACE 0x40002 5 subrace/not_gray_dwarf
+86 0x107 1 * daytime
+87 0x107 0 * not_daytime
+88 0x106 1 9 outdoor
+89 0x106 1 8 not_outdoor
+90 RACE 190 1 race/keg
+91 RACE 190 5 race/not_keg
+92 RACE * * outsider
+93 RACE * * not_outsider
+94 RACE 215 1 race/plate
+95 RACE 215 5 race/not_plate
+96 0x103 90 94 race/keg_or_plate
+97 0x104 90 94 race/not_keg_or_plate
+98 RACE 161 1 race/salamander
+99 RACE 161 5 race/not_salamander
+100 RACE 164 1 race/tanari
+101 RACE 164 5 race/not_tanari
diff --git a/gemrb/override/iwd2/splspec.2da b/gemrb/override/iwd2/splspec.2da
new file mode 100644
index 0000000..de337f7
--- /dev/null
+++ b/gemrb/override/iwd2/splspec.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ IDENTIFY
+SPWI110 1
diff --git a/gemrb/override/iwd2/spoisoh.pro b/gemrb/override/iwd2/spoisoh.pro
new file mode 100644
index 0000000..6aa9302
Binary files /dev/null and b/gemrb/override/iwd2/spoisoh.pro differ
diff --git a/gemrb/override/iwd2/spscorch.pro b/gemrb/override/iwd2/spscorch.pro
new file mode 100644
index 0000000..3ad6c32
Binary files /dev/null and b/gemrb/override/iwd2/spscorch.pro differ
diff --git a/gemrb/override/iwd2/spscoric.pro b/gemrb/override/iwd2/spscoric.pro
new file mode 100644
index 0000000..3d7e0b7
Binary files /dev/null and b/gemrb/override/iwd2/spscoric.pro differ
diff --git a/gemrb/override/iwd2/spwrath.pro b/gemrb/override/iwd2/spwrath.pro
new file mode 100644
index 0000000..d66271c
Binary files /dev/null and b/gemrb/override/iwd2/spwrath.pro differ
diff --git a/gemrb/override/iwd2/ssorbh.pro b/gemrb/override/iwd2/ssorbh.pro
new file mode 100644
index 0000000..2d9489e
Binary files /dev/null and b/gemrb/override/iwd2/ssorbh.pro differ
diff --git a/gemrb/override/iwd2/ssswarm.pro b/gemrb/override/iwd2/ssswarm.pro
new file mode 100644
index 0000000..efd382f
Binary files /dev/null and b/gemrb/override/iwd2/ssswarm.pro differ
diff --git a/gemrb/override/iwd2/sstone.pro b/gemrb/override/iwd2/sstone.pro
new file mode 100644
index 0000000..f1a87f5
Binary files /dev/null and b/gemrb/override/iwd2/sstone.pro differ
diff --git a/gemrb/override/iwd2/sstoneh.pro b/gemrb/override/iwd2/sstoneh.pro
new file mode 100644
index 0000000..b3af8da
Binary files /dev/null and b/gemrb/override/iwd2/sstoneh.pro differ
diff --git a/gemrb/override/iwd2/start.2da b/gemrb/override/iwd2/start.2da
new file mode 100644
index 0000000..9f09b3b
--- /dev/null
+++ b/gemrb/override/iwd2/start.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ XPOS YPOS AREA ROT
+NORMAL START_XPOS START_YPOS START_AREA START_ROT
+EXPANSION START_XPOS START_YPOS START_AREA START_ROT
diff --git a/gemrb/override/iwd2/stone.pro b/gemrb/override/iwd2/stone.pro
new file mode 100644
index 0000000..13ce3b0
Binary files /dev/null and b/gemrb/override/iwd2/stone.pro differ
diff --git a/gemrb/override/iwd2/strengh.pro b/gemrb/override/iwd2/strengh.pro
new file mode 100644
index 0000000..53d8a67
Binary files /dev/null and b/gemrb/override/iwd2/strengh.pro differ
diff --git a/gemrb/override/iwd2/strings.2da b/gemrb/override/iwd2/strings.2da
new file mode 100644
index 0000000..2707938
--- /dev/null
+++ b/gemrb/override/iwd2/strings.2da
@@ -0,0 +1,170 @@
+2DA V1.0
+-1
+ STRREF
+SCATTERED 16457
+WHOLEPARTY 16484
+DOORLOCKED 16485
+MAGICTRAP 16486
+NORMALTRAP 16487
+TRAP 16488
+CANNOTGO 16489
+TRAPREMOVED 16490
+OVERSTOCKED 16491
+SLEEP 16492
+AMBUSH 16493
+CONTLOCKED 16494
+NOMONEY 16495
+CURSED 16496
+SPELLDISRUPT 16497
+DIED 16498
+MAYNOTREST 16499
+CANTRESTMONS 16500
+CANTSAVEMONS 16501
+CANTSAVE 16502
+NODIALOG 10945
+CANTSAVEDIALOG 19253
+CANTSAVEDIALOG2 19254
+CANTSAVEMOVIE 19255
+TARGETBUSY -1
+CANTTALKTRANS -1
+GOTGOLD 17572
+LOSTGOLD 17573
+GOTXP 17574
+LOSTXP 17575
+GOTITEM 17576
+LOSTITEM 17577
+GOTREP 19686
+LOSTREP 19687
+GOTABILITY 10514
+GOTSPELL 10514
+GOTSONG 26320
+NOTHINGTOSAY -1
+JOURNALCHANGE 11359
+WORLDMAPCHANGE 11360
+PAUSED 16321
+UNPAUSED 16322
+SCRIPTPAUSED 7666
+AP_UNUSABLE 17113
+AP_ATTACKED 17114
+AP_HIT 17115
+AP_WOUNDED 17116
+AP_DEAD 17117
+AP_NOTARGET 17118
+AP_ENDROUND 10014
+AP_ENEMY 23511
+AP_TRAP -1
+AP_SPELLCAST -1
+AP_RESERVED1 -1
+AP_RESERVED2 -1
+AP_RESERVED3 -1
+AP_RESERVED4 -1
+CHARMED 14672
+DIRECHARMED 14780
+CONTROLLED -1
+EVIL 16505
+GNE_NEUTRAL 16504
+GOOD 16503
+STR_LAWFUL -1
+LNC_NEUTRAL -1
+CHAOTIC -1
+ACTION_CAST 16464
+ACTION_ATTACK 16465
+ACTION_TURN 16466
+ACTION_SONG 16467
+ACTION_FINDTRAP 16468
+MAGICWEAPON 10141
+OFFHAND_USED 9380
+TWOHANDED_USED 9381
+CANNOT_USE_ITEM 9382
+CANT_DROP_ITEM 25697
+NOT_IN_OFFHAND 26342
+ITEM_IS_CURSED 16304
+NO_CRITICAL 20696
+TRACKING -1
+TRACKINGFAILED 19534
+DOOR_NOPICK 23169
+CONT_NOPICK 23169
+CANTSAVECOMBAT -1
+CANTSAVENOCTRL -1
+LOCKPICK_DONE 16517
+LOCKPICK_FAILED 16518
+STATIC_DISSIPATE 26518
+LIGHTNING_DISSIPATE 41008
+HAS_NO_ABILITY 17317
+NEEDS_IDENTIFY 17316
+WRONG_ITEMTYPE 9375
+HAS_ITEMEXCL 20685
+PICKPOCKET_DONE 10072
+PICKPOCKET_NONE 18297
+PICKPOCKET_FAIL 10069
+PICKPOCKET_EVIL 10068
+PICKPOCKET_ARMOR 10067
+USING_FEAT -1
+STOPPED_FEAT -1
+DISARM_DONE 16520
+DISARM_FAIL 1608
+DOORBASH_DONE 9915
+DOORBASH_FAIL 9913
+CONTBASH_DONE 9916
+CONTBASH_FAIL 9914
+MAYNOTSETTRAP -1
+SNAREFAILED -1
+SNARESUCCEED -1
+NOMORETRAP -1
+DISABLEDMAGE -1
+SAVESUCCEED 1682
+QSAVESUCCEED 10237
+UNINJURED 2943
+INJURED1 2944
+INJURED2 2945
+INJURED3 2946
+INJURED4 2947
+HOURS 10700
+HOUR 10701
+DAYS 10697
+DAY 10698
+REST 10690
+JOURNEY 10689
+PST_REST -1
+PST_HOUR -1
+PST_HOURS -1
+DAMAGE_IMMUNITY 25038
+DAMAGE_STR1 25028
+DAMAGE_STR2 25017
+DAMAGE_STR3 26223
+DMG_POISON 25018
+DMG_MAGIC 25019
+DMG_MISSILE 25020
+DMG_SLASHING 25021
+DMG_PIERCING 25022
+DMG_CRUSHING 25023
+DMG_FIRE 25024
+DMG_ELECTRIC 25025
+DMG_COLD 25026
+DMG_ACID 25027
+DMG_OTHER 0
+GOTQUESTXP -1
+LEVELUP 17119
+INVFULL_ITEMDROP 32879
+CONT_DUP 32876
+CONT_TRIG -1
+CONT_FAIL -1
+SEQ_DUP -1
+CRITICAL_HIT 16462
+CRITICAL_MISS 16463
+DEATH 14026
+BACKSTAB 12128
+BACKSTAB_BAD 10013
+BACKSTAB_FAIL 121
+CASTER_LVL_INC -1
+CASTER_LVL_DEC -1
+CHARS_EXPORTED 26827
+PALADIN_FALL 19620
+RANGER_FALL 19621
+RES_RESISTED 26818
+DEADMAGIC_FAIL -1
+MISCASTMAGIC -1
+WILDSURGE -1
+FAMBLOCK 8537
+FAMPROTAGONIST 8538
+
diff --git a/gemrb/override/iwd2/suffoc.pro b/gemrb/override/iwd2/suffoc.pro
new file mode 100644
index 0000000..c4e0c4c
Binary files /dev/null and b/gemrb/override/iwd2/suffoc.pro differ
diff --git a/gemrb/override/iwd2/suffoch.pro b/gemrb/override/iwd2/suffoch.pro
new file mode 100644
index 0000000..ef84c8c
Binary files /dev/null and b/gemrb/override/iwd2/suffoch.pro differ
diff --git a/gemrb/override/iwd2/sunfire.pro b/gemrb/override/iwd2/sunfire.pro
new file mode 100644
index 0000000..fcaeafd
Binary files /dev/null and b/gemrb/override/iwd2/sunfire.pro differ
diff --git a/gemrb/override/iwd2/sunray.pro b/gemrb/override/iwd2/sunray.pro
new file mode 100644
index 0000000..9525a7a
Binary files /dev/null and b/gemrb/override/iwd2/sunray.pro differ
diff --git a/gemrb/override/iwd2/sunscoh.pro b/gemrb/override/iwd2/sunscoh.pro
new file mode 100644
index 0000000..be43f0e
Binary files /dev/null and b/gemrb/override/iwd2/sunscoh.pro differ
diff --git a/gemrb/override/iwd2/swave.pro b/gemrb/override/iwd2/swave.pro
new file mode 100644
index 0000000..1987698
Binary files /dev/null and b/gemrb/override/iwd2/swave.pro differ
diff --git a/gemrb/override/iwd2/swaveh.pro b/gemrb/override/iwd2/swaveh.pro
new file mode 100644
index 0000000..c417b2b
Binary files /dev/null and b/gemrb/override/iwd2/swaveh.pro differ
diff --git a/gemrb/override/iwd2/topics.2da b/gemrb/override/iwd2/topics.2da
new file mode 100644
index 0000000..04ce4ed
--- /dev/null
+++ b/gemrb/override/iwd2/topics.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+0
+ CATEGORY RESOURCE TITLECOL DESCCOL FLAG
+ABILITIES 17088 ABILITY 2 1 0
+CLASSES 23998 CLASSES 0 1 -11
+FEATS 36361 FEATS 1 2 0
+ORDERS 41403 CLASSES 0 1 11
+RACES 41393 RACES 2 1 0
+SKILLS 11983 SKILLS 1 2 0
diff --git a/gemrb/override/iwd2/trap.pro b/gemrb/override/iwd2/trap.pro
new file mode 100644
index 0000000..f57a2aa
Binary files /dev/null and b/gemrb/override/iwd2/trap.pro differ
diff --git a/gemrb/override/iwd2/trapglyp.pro b/gemrb/override/iwd2/trapglyp.pro
new file mode 100644
index 0000000..5542681
Binary files /dev/null and b/gemrb/override/iwd2/trapglyp.pro differ
diff --git a/gemrb/override/iwd2/trapskul.pro b/gemrb/override/iwd2/trapskul.pro
new file mode 100644
index 0000000..5706fec
Binary files /dev/null and b/gemrb/override/iwd2/trapskul.pro differ
diff --git a/gemrb/override/iwd2/tspray.pro b/gemrb/override/iwd2/tspray.pro
new file mode 100644
index 0000000..2b297ca
Binary files /dev/null and b/gemrb/override/iwd2/tspray.pro differ
diff --git a/gemrb/override/iwd2/turn.spl b/gemrb/override/iwd2/turn.spl
new file mode 100644
index 0000000..f4b14d6
Binary files /dev/null and b/gemrb/override/iwd2/turn.spl differ
diff --git a/gemrb/override/iwd2/ublight.pro b/gemrb/override/iwd2/ublight.pro
new file mode 100644
index 0000000..6f6a33d
Binary files /dev/null and b/gemrb/override/iwd2/ublight.pro differ
diff --git a/gemrb/override/iwd2/uhold.pro b/gemrb/override/iwd2/uhold.pro
new file mode 100644
index 0000000..e09f231
Binary files /dev/null and b/gemrb/override/iwd2/uhold.pro differ
diff --git a/gemrb/override/iwd2/uward.pro b/gemrb/override/iwd2/uward.pro
new file mode 100644
index 0000000..6cf5b94
Binary files /dev/null and b/gemrb/override/iwd2/uward.pro differ
diff --git a/gemrb/override/iwd2/uwardh.pro b/gemrb/override/iwd2/uwardh.pro
new file mode 100644
index 0000000..b385f90
Binary files /dev/null and b/gemrb/override/iwd2/uwardh.pro differ
diff --git a/gemrb/override/iwd2/vspherh.pro b/gemrb/override/iwd2/vspherh.pro
new file mode 100644
index 0000000..717b5a8
Binary files /dev/null and b/gemrb/override/iwd2/vspherh.pro differ
diff --git a/gemrb/override/iwd2/wdeath1.pro b/gemrb/override/iwd2/wdeath1.pro
new file mode 100644
index 0000000..c3e2021
Binary files /dev/null and b/gemrb/override/iwd2/wdeath1.pro differ
diff --git a/gemrb/override/iwd2/wdeath2.pro b/gemrb/override/iwd2/wdeath2.pro
new file mode 100644
index 0000000..764ee67
Binary files /dev/null and b/gemrb/override/iwd2/wdeath2.pro differ
diff --git a/gemrb/override/iwd2/web.pro b/gemrb/override/iwd2/web.pro
new file mode 100644
index 0000000..120c2a0
Binary files /dev/null and b/gemrb/override/iwd2/web.pro differ
diff --git a/gemrb/override/iwd2/whirlw.pro b/gemrb/override/iwd2/whirlw.pro
new file mode 100644
index 0000000..50d9199
Binary files /dev/null and b/gemrb/override/iwd2/whirlw.pro differ
diff --git a/gemrb/override/iwd2/wofire.pro b/gemrb/override/iwd2/wofire.pro
new file mode 100644
index 0000000..16057d7
Binary files /dev/null and b/gemrb/override/iwd2/wofire.pro differ
diff --git a/gemrb/override/iwd2/womoon.pro b/gemrb/override/iwd2/womoon.pro
new file mode 100644
index 0000000..35543b7
Binary files /dev/null and b/gemrb/override/iwd2/womoon.pro differ
diff --git a/gemrb/override/iwd2/womoonx.pro b/gemrb/override/iwd2/womoonx.pro
new file mode 100644
index 0000000..04f52d8
Binary files /dev/null and b/gemrb/override/iwd2/womoonx.pro differ
diff --git a/gemrb/override/iwd2/wowisp.pro b/gemrb/override/iwd2/wowisp.pro
new file mode 100644
index 0000000..6e82545
Binary files /dev/null and b/gemrb/override/iwd2/wowisp.pro differ
diff --git a/gemrb/override/iwd2/wvdeath.pro b/gemrb/override/iwd2/wvdeath.pro
new file mode 100644
index 0000000..b3d956a
Binary files /dev/null and b/gemrb/override/iwd2/wvdeath.pro differ
diff --git a/gemrb/override/iwd2/wvhith.pro b/gemrb/override/iwd2/wvhith.pro
new file mode 100644
index 0000000..587b2af
Binary files /dev/null and b/gemrb/override/iwd2/wvhith.pro differ
diff --git a/gemrb/override/iwd2/wwolf.pro b/gemrb/override/iwd2/wwolf.pro
new file mode 100644
index 0000000..4df3732
Binary files /dev/null and b/gemrb/override/iwd2/wwolf.pro differ
diff --git a/gemrb/override/iwd2/zlaura.pro b/gemrb/override/iwd2/zlaura.pro
new file mode 100644
index 0000000..31ee26e
Binary files /dev/null and b/gemrb/override/iwd2/zlaura.pro differ
diff --git a/gemrb/override/pst/CMakeLists.txt b/gemrb/override/pst/CMakeLists.txt
new file mode 100644
index 0000000..4a3f3c6
--- /dev/null
+++ b/gemrb/override/pst/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_OVERRIDE (pst)
diff --git a/gemrb/override/pst/MPALETTE.bmp b/gemrb/override/pst/MPALETTE.bmp
new file mode 100644
index 0000000..9d19d81
Binary files /dev/null and b/gemrb/override/pst/MPALETTE.bmp differ
diff --git a/gemrb/override/pst/Makefile.am b/gemrb/override/pst/Makefile.am
new file mode 100644
index 0000000..1f148cc
--- /dev/null
+++ b/gemrb/override/pst/Makefile.am
@@ -0,0 +1,3 @@
+pstoverride_DATA = *.2da *.bmp *.ini *.chu *.ids *.bcs *.pro
+pstoverridedir = $(moddir)/override/pst/
+EXTRA_DIST = *.2da *.bmp *.ini *.chu *.ids *.bcs *.pro
diff --git a/gemrb/override/pst/abcomm.2da b/gemrb/override/pst/abcomm.2da
new file mode 100644
index 0000000..165da18
--- /dev/null
+++ b/gemrb/override/pst/abcomm.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+33954
+ STR INT WIS DEX CON CHR
+0 33954 34255 34258 34250 34253 34262
+1 33954 34255 34258 34250 34253 34262
+2 33954 34255 34258 34250 34253 34262
+3 33954 34255 34258 34250 34253 34262
+4 33955 34256 34259 34251 34254 34263
+5 33955 34256 34259 34251 34254 34263
+6 33956 34257 34260 34252 34261 33464
+7 33956 34257 34260 34252 34261 33464
+8 33079 33444 33452 33456 33460 33465
+9 33079 33444 33452 33456 33460 33465
+10 33079 33444 33452 33456 33460 33465
+11 33079 33444 33452 33456 33460 33465
+12 33079 33444 33452 33456 33460 33465
+13 33440 33445 33453 33457 33461 33466
+14 33440 33445 33453 33457 33461 33466
+15 33440 33446 33454 33458 33462 33467
+16 33441 33446 33454 33458 33462 33467
+17 33442 33447 33455 33459 33463 33468
+18 33442 33447 33455 33459 33463 33468
+19 33443 33942 33939 33936 33933 33930
+20 33945 33942 33939 33936 33933 33930
+21 33945 33942 33939 33936 33933 33930
+22 33946 33943 33940 33937 33934 33931
+23 33946 33943 33940 33937 33934 33931
+24 33947 33944 33941 33938 33935 33932
+25 33947 33944 33941 33938 33935 33932
diff --git a/gemrb/override/pst/acidblgr.pro b/gemrb/override/pst/acidblgr.pro
new file mode 100644
index 0000000..fe37e1c
Binary files /dev/null and b/gemrb/override/pst/acidblgr.pro differ
diff --git a/gemrb/override/pst/acidblmu.pro b/gemrb/override/pst/acidblmu.pro
new file mode 100644
index 0000000..aa9e8df
Binary files /dev/null and b/gemrb/override/pst/acidblmu.pro differ
diff --git a/gemrb/override/pst/acidblob.pro b/gemrb/override/pst/acidblob.pro
new file mode 100644
index 0000000..16ef4f3
Binary files /dev/null and b/gemrb/override/pst/acidblob.pro differ
diff --git a/gemrb/override/pst/acidbloc.pro b/gemrb/override/pst/acidbloc.pro
new file mode 100644
index 0000000..a2e6822
Binary files /dev/null and b/gemrb/override/pst/acidbloc.pro differ
diff --git a/gemrb/override/pst/aligns.2da b/gemrb/override/pst/aligns.2da
new file mode 100644
index 0000000..a751d1f
--- /dev/null
+++ b/gemrb/override/pst/aligns.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+-1
+ NAME_REF DESC_REF CAP_REF VALUE COLNAME USABILITY
+LAWFUL_GOOD 1404 33657 0 0x11 L_G 0x14
+NEUTRAL_GOOD 4184 33927 0 0x21 N_G 0x24
+CHAOTIC_GOOD 4187 33928 0 0x31 C_G 0x5
+LAWFUL_NEUTRAL 1405 33929 0 0x12 L_N 0x18
+NEUTRAL 4185 33948 0 0x22 N_N 0x28
+CHAOTIC_NEUTRAL 4188 33949 0 0x32 C_N 0x9
+LAWFUL_EVIL 1406 33950 0 0x13 L_E 0x12
+NEUTRAL_EVIL 4186 33951 0 0x23 N_E 0x22
+CHAOTIC_EVIL 4189 33952 0 0x33 C_E 0x3
diff --git a/gemrb/override/pst/amiss.pro b/gemrb/override/pst/amiss.pro
new file mode 100644
index 0000000..76b2b99
Binary files /dev/null and b/gemrb/override/pst/amiss.pro differ
diff --git a/gemrb/override/pst/amiss2.pro b/gemrb/override/pst/amiss2.pro
new file mode 100644
index 0000000..6170437
Binary files /dev/null and b/gemrb/override/pst/amiss2.pro differ
diff --git a/gemrb/override/pst/anims.2da b/gemrb/override/pst/anims.2da
new file mode 100644
index 0000000..06415ee
--- /dev/null
+++ b/gemrb/override/pst/anims.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+-1
+ NAMELESS
+AX 0x602f
+CL 0x6030
+DD 0x6031
+* 0x6032
+WH 0x6033
+ZO 0x6052
+DR 0x605d
diff --git a/gemrb/override/pst/aotorm.pro b/gemrb/override/pst/aotorm.pro
new file mode 100644
index 0000000..fdeb30b
Binary files /dev/null and b/gemrb/override/pst/aotorm.pro differ
diff --git a/gemrb/override/pst/areapro.2da b/gemrb/override/pst/areapro.2da
new file mode 100644
index 0000000..665f80c
--- /dev/null
+++ b/gemrb/override/pst/areapro.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+*
+ RESOURCE1 RESOURCE2 RESOURCE3 SOUND1 SOUND2 FLAGS
+FIREBALL SPRING SPBOOM * EFF_M21 * 40
+STINKCLOUD STNKCLDD * * EFF_M18A EFF_M18C 34
+CLOUDKILL STNKCLDD * * EFF_M18A EFF_M18C 34
+ICESTORM STNKCLDD SPBOOM * EFF_M34 * 42
+GREASE GREASEH SPBOOM GREASED EFF_M31B * 42
+WEB WEBENTH SPBOOM WEBENTD EFF_M19 * 42
diff --git a/gemrb/override/pst/arrow.pro b/gemrb/override/pst/arrow.pro
new file mode 100644
index 0000000..293014f
Binary files /dev/null and b/gemrb/override/pst/arrow.pro differ
diff --git a/gemrb/override/pst/arrowex.pro b/gemrb/override/pst/arrowex.pro
new file mode 100644
index 0000000..70b7773
Binary files /dev/null and b/gemrb/override/pst/arrowex.pro differ
diff --git a/gemrb/override/pst/arrowflb.pro b/gemrb/override/pst/arrowflb.pro
new file mode 100644
index 0000000..12b5b91
Binary files /dev/null and b/gemrb/override/pst/arrowflb.pro differ
diff --git a/gemrb/override/pst/arrowflg.pro b/gemrb/override/pst/arrowflg.pro
new file mode 100644
index 0000000..45a7bab
Binary files /dev/null and b/gemrb/override/pst/arrowflg.pro differ
diff --git a/gemrb/override/pst/arrowfli.pro b/gemrb/override/pst/arrowfli.pro
new file mode 100644
index 0000000..202c16c
Binary files /dev/null and b/gemrb/override/pst/arrowfli.pro differ
diff --git a/gemrb/override/pst/arrowflm.pro b/gemrb/override/pst/arrowflm.pro
new file mode 100644
index 0000000..ae84563
Binary files /dev/null and b/gemrb/override/pst/arrowflm.pro differ
diff --git a/gemrb/override/pst/arrowhvy.pro b/gemrb/override/pst/arrowhvy.pro
new file mode 100644
index 0000000..1471c2e
Binary files /dev/null and b/gemrb/override/pst/arrowhvy.pro differ
diff --git a/gemrb/override/pst/astorm.pro b/gemrb/override/pst/astorm.pro
new file mode 100644
index 0000000..7195f1c
Binary files /dev/null and b/gemrb/override/pst/astorm.pro differ
diff --git a/gemrb/override/pst/astorm.spl b/gemrb/override/pst/astorm.spl
new file mode 100644
index 0000000..bf571d7
Binary files /dev/null and b/gemrb/override/pst/astorm.spl differ
diff --git a/gemrb/override/pst/avatars.2da b/gemrb/override/pst/avatars.2da
new file mode 100644
index 0000000..83e2b34
--- /dev/null
+++ b/gemrb/override/pst/avatars.2da
@@ -0,0 +1,105 @@
+2DA V1.0
+*
+ AT_1 AT_2 AT_3 AT_4 TYPE SPACE
+0x00 CABSB CABSB CABSB CABSB 56 2
+0x01 CADVB CADVB CADVB CADVB 56 2
+0x02 DANNB DANNB DANNB DANNB 56 2
+0x03 DBAUB DBAUB DBAUB DBAUB 56 2
+0x04 CCAMB CCAMB CCAMB CCAMB 59 2
+0x05 CCOLB CCOLB CCOLB CCOLB 56 2
+0x06 DCTHB DCTHB DCTHB DCTHB 56 2
+0x07 CCORB CCORB CCORB CCORB 56 2
+0x08 CCRNB CCRNB CCRNB CCRNB 59 1
+0x09 CCGDB CCGDB CCGDB CCGDB 56 3
+0x0A DCTFB DCTFB DCTFB DCTFB 56 2
+0x0B DCTMB DCTMB DCTMB DCTMB 56 2
+0x0C CDABB CDABB CDABB CDABB 56 2
+0x0D DDKKB DDKKB DDKKB DDKKB 56 2
+0x0E CDEIB CDEIB CDEIB CDEIB 60 2
+0x0F CDHLB CDHLB CDHLB CDHLB 60 2
+0x10 DHKMB DHKMB DHKMB DHKMB 56 2
+0x11 CLKMB CLKMB CLKMB CLKMB 56 2
+0x12 CWIZB CWIZB CWIZB CWIZB 56 2
+0x13 DDTFB DDTFB DDTFB DDTFB 56 2
+0x14 DDTMB DDTMB DDTMB DDTMB 56 2
+0x15 CEBBB CEBBB CEBBB CEBBB 56 2
+0x16 CFFGB CFFGB CFFGB CFFGB 56 2
+0x17 CFHJB CFHJB CFHJB CFHJB 56 2
+0x18 CGARB CGARB CGARB CGARB 59 2
+0x19 DGHFB DGHFB DGHFB DGHFB 56 2
+0x1A DGHMB DGHMB DGHMB DGHMB 56 2
+0x1B CGSTB CGSTB CGSTB CGSTB 60 2
+0x1C CACAB CACAB CACAB CACAB 56 2
+0x1D DGYTB DGYTB DGYTB DGYTB 56 2
+0x1E CGLAB CGLAB CGLAB CGLAB 59 2
+0x1F DGODB DGODB DGODB DGODB 56 2
+0x20 CGDGB CGDGB CGDGB CGDGB 56 2
+0x21 CGRIB CGRIB CGRIB CGRIB 59 2
+0x22 CGRKB CGRKB CGRKB CGRKB 59 2
+0x23 CHRSB CHRSB CHRSB CHRSB 59 2
+0x24 CIGYB CIGYB CIGYB CIGYB 56 2
+0x25 CJBRB CJBRB CJBRB CJBRB 60 2
+0x26 CJFTB CJFTB CJFTB CJFTB 59 2
+0x27 CLOPB CLOPB CLOPB CLOPB 56 2
+0x28 CLEMB CLEMB CLEMB CLEMB 59 2
+0x29 CLTHB CLTHB CLTHB CLTHB 56 2
+0x2A DMHTB DMHTB DMHTB DMHTB 56 2
+0x2B CMKLB CMKLB CMKLB CMKLB 56 2
+0x2C DMIDB DMIDB DMIDB DMIDB 56 3
+0x2D CMDRB CMDRB CMDRB CMDRB 56 3
+0x2E CMRTB CMRTB CMRTB CMRTB 60 2
+0x2F DNOAB DNOAB DNOAB DNOAB 56 2
+0x30 DNOCB DNOCB DNOCB DNOCB 56 2
+0x31 DNODB DNODB DNODB DNODB 56 2
+0x32 DNOFB DNOFB DNOFB DNOFB 56 2
+0x33 DNOHB DNOHB DNOHB DNOHB 56 2
+0x34 CNDMB CNDMB CNDMB CNDMB 56 2
+0x35 CNPRB CNPRB CNPRB CNPRB 59 2
+0x36 CPHDB CPHDB CPHDB CPHDB 56 2
+0x37 DPROB DPROB DPROB DPROB 60 2
+0x38 CPUZB CPUZB CPUZB CPUZB 56 2
+0x39 CSM1B CSM1B CSM1B CSM1B 60 2
+0x3A CARMB CARMB CARMB CARMB 56 2
+0x3B DSPTB DSPTB DSPTB DSPTB 56 2
+0x3C DSKWB DSKWB DSKWB DSKWB 56 2
+0x3D DHLFB DHLFB DHLFB DHLFB 56 2
+0x3E DHLMB DHLMB DHLMB DHLMB 56 2
+0x3F DHUFB DHUFB DHUFB DHUFB 56 2
+0x40 DHUMB DHUMB DHUMB DHUMB 56 2
+0x41 CTRZB CTRZB CTRZB CTRZB 60 2
+0x42 CTGTB CTGTB CTGTB CTGTB 59 2
+0x43 CTREB CTREB CTREB CTREB 59 2
+0x44 CTRSB CTRSB CTRSB CTRSB 56 2
+0x45 CTRCB CTRCB CTRCB CTRCB 59 2
+0x46 CVGYB CVGYB CVGYB CVGYB 59 2
+0x47 DVHAB DVHAB DVHAB DVHAB 56 2
+0x48 DWREB DWREB DWREB DWREB 56 2
+0x49 CWRMB CWRMB CWRMB CWRMB 56 2
+0x4A CWURB CWURB CWURB CWURB 59 2
+0x4B DZMFB DZMFB DZMFB DZMFB 60 2
+0x4C DZMMB DZMMB DZMMB DZMMB 60 2
+0x4D CFELB CFELB CFELB CFELB 56 2
+0x4E CSM2B CSM2B CSM2B CSM2B 56 2
+0x4F CSM3B CSM3B CSM3B CSM3B 59 2
+0x50 DTHKB DTHKB DTHKB DTHKB 56 2
+0x51 DNOSB DNOSB DNOSB DNOSB 56 2
+0x52 DNOZB DNOZB DNOZB DNOZB 60 2
+0x53 DTFFB DTFFB DTFFB DTFFB 56 2
+0x54 DTLRB DTLRB DTLRB DTLRB 56 2
+0x55 DTFMB DTFMB DTFMB DTFMB 56 2
+0x56 DBAUB DBAUB DBAUB DBAUB 56 2
+0x57 CHARB CHARB CHARB CHARB 56 2
+0x58 DMIDB DMIDB DMIDB DMIDB 60 2
+0x59 DAAFB DAAFB DAAFB DAAFB 56 2
+0x5A CGABB CGABB CGABB CGABB 56 2
+0x5B CRABB CRABB CRABB CRABB 56 2
+0x5C DTWZB DTWZB DTWZB DTWZB 56 2
+0x5D DNOMB DNOMB DNOMB DNOMB 56 2
+0x5E CUHDB CUHDB CUHDB CUHDB 58 2
+0x5F DQUIB DQUIB DQUIB DQUIB 58 2
+0x60 CTRDB CTRDB CTRDB CTRDB 58 2
+0x61 DNOFB DNOFB DNOFB DNOFB 56 2
+0x62 DVHSTDB * * * 57 2
+0x2000 GEARS1 GEARS2 * * 57 3
+0x3000 IGHEAD IGARM * * 57 3
+0xf000 POSMAIN POSSHAD * * 57 3
diff --git a/gemrb/override/pst/avprefix.2da b/gemrb/override/pst/avprefix.2da
new file mode 100644
index 0000000..d17e92e
--- /dev/null
+++ b/gemrb/override/pst/avprefix.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESOURCE STANCE
+0 0x6032 18
diff --git a/gemrb/override/pst/avslots.2da b/gemrb/override/pst/avslots.2da
new file mode 100644
index 0000000..656de0a
--- /dev/null
+++ b/gemrb/override/pst/avslots.2da
@@ -0,0 +1,13 @@
+2DA V1.0
+*
+ USABILITY SLOTS
+SPECIFIC 0 *
+NORMAL 0 *
+NAMELESS 0x40000000 0,1,2,3,4,5,47,7,8,9,10,11,12,13,14,15,16,17,-1,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,-1
+VHAILOR 0x800000 0,1,-1,3,4,5,6,-1,8,9,10,11,12,13,14,15,16,17,-1,-1,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,-1
+NORDOM 0x4000000 46,1,-1,-1,4,5,-1,-1,-1,48,49,50,51,13,14,15,16,17,-1,-1,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,-1
+IGNUS 0x1000000 -1,1,-1,3,4,5,-1,-1,8,9,10,-1,-1,13,14,15,16,17,-1,-1,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,-1
+GRACE 0x200000 0,1,-1,3,4,5,6,-1,8,9,10,-1,-1,13,14,15,16,17,-1,-1,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,-1
+DAKKON 0x100000 -1,1,2,3,4,5,-1,7,8,9,-1,-1,-1,13,14,15,16,17,-1,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,-1
+ANNAH 0x10000000 0,1,2,3,4,5,6,7,8,9,10,-1,-1,13,14,15,16,17,-1,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,-1
+MORTE 0x2000000 -1,-1,-1,-1,-1,-1,-1,-1,-1,52,53,-1,-1,13,14,15,16,17,-1,-1,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,-1
diff --git a/gemrb/override/pst/axe.pro b/gemrb/override/pst/axe.pro
new file mode 100644
index 0000000..2220a75
Binary files /dev/null and b/gemrb/override/pst/axe.pro differ
diff --git a/gemrb/override/pst/axeex.pro b/gemrb/override/pst/axeex.pro
new file mode 100644
index 0000000..475b824
Binary files /dev/null and b/gemrb/override/pst/axeex.pro differ
diff --git a/gemrb/override/pst/axoft.spl b/gemrb/override/pst/axoft.spl
new file mode 100644
index 0000000..1c7b812
Binary files /dev/null and b/gemrb/override/pst/axoft.spl differ
diff --git a/gemrb/override/pst/baldur.bcs b/gemrb/override/pst/baldur.bcs
new file mode 100644
index 0000000..69daf1e
--- /dev/null
+++ b/gemrb/override/pst/baldur.bcs
@@ -0,0 +1,402 @@
+SC
+CR
+CO
+TR
+16538 5 1 0 0 [0,0] "" "" OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 1 0 0 0 [0,0] "GLOBALJOIN_ANARCHISTS" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+268OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+5 0 0 0 0"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16538 4 1 0 0 [0,0] "" "" OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 1 0 0 0 [0,0] "GLOBALJOIN_SENSATES" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 1 1 0 0 [0,0] "GLOBALJOIN_ANARCHISTS" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+268OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+4 0 0 0 0"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16538 2 1 0 0 [0,0] "" "" OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 1 0 0 0 [0,0] "GLOBALJOIN_DUSTMEN" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 1 1 0 0 [0,0] "GLOBALJOIN_ANARCHISTS" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+268OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+2 0 0 0 0"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16538 3 1 0 0 [0,0] "" "" OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 6 0 0 0 [0,0] "GLOBALJOIN_GODSMEN" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 1 1 0 0 [0,0] "GLOBALJOIN_ANARCHISTS" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+268OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+3 0 0 0 0"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16538 8 1 0 0 [0,0] "" "" OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 1 0 0 0 [0,0] "GLOBALJOIN_CHAOSMEN" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 1 1 0 0 [0,0] "GLOBALJOIN_ANARCHISTS" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+268OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 27 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+8 0 0 0 0"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 1 0 0 0 [0,0] "GLOBALgame_over" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+30OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 0"GLOBALgame_over" "" AC
+AC
+309OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 44963"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 2 0 0 0 [0,0] "GLOBALgame_over" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+30OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 0"GLOBALgame_over" "" AC
+AC
+309OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 48153"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 3 0 0 0 [0,0] "GLOBALgame_over" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+30OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 0"GLOBALgame_over" "" AC
+AC
+309OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 48154"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 4 0 0 0 [0,0] "GLOBALgame_over" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+30OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 0"GLOBALgame_over" "" AC
+AC
+309OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 48155"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 5 0 0 0 [0,0] "GLOBALgame_over" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+30OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 0"GLOBALgame_over" "" AC
+AC
+309OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 41656"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 6 0 0 0 [0,0] "GLOBALgame_over" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+30OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 0"GLOBALgame_over" "" AC
+AC
+309OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 67295"" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 1 0 0 0 [0,0] "GLOBALAR0200_Visited" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 0 0 0 0 [0,0] "GLOBALPlayed_Shadow_Movie" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+30OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+1 0 0 0 0"GLOBALPlayed_Shadow_Movie" "" AC
+AC
+167OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 0"SS_MSLAB" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 1 0 0 0 [0,0] "GLOBALAR0206_Visited" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 0 0 0 0 [0,0] "GLOBALPlayed_Shadow_Movie" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+30OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+1 0 0 0 0"GLOBALPlayed_Shadow_Movie" "" AC
+AC
+167OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 0"SS_MSLAB" "" AC
+RE
+RS
+CR
+CR
+CO
+TR
+16399 1 0 0 0 [0,0] "GLOBALAR0200_Visited" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+TR
+16399 0 0 0 0 [0,0] "GLOBALPlayed_Sigil_Movie" "" OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+TR
+CO
+RS
+RE
+100AC
+30OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+1 0 0 0 0"GLOBALPlayed_Sigil_Movie" "" AC
+AC
+167OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+OB
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 [-1.-1.-1.-1] ""OB
+0 0 0 0 0"SIGIL" "" AC
+RE
+RS
+CR
+SC
diff --git a/gemrb/override/pst/bbridge.pro b/gemrb/override/pst/bbridge.pro
new file mode 100644
index 0000000..a318b34
Binary files /dev/null and b/gemrb/override/pst/bbridge.pro differ
diff --git a/gemrb/override/pst/bbridge.spl b/gemrb/override/pst/bbridge.spl
new file mode 100644
index 0000000..12ef351
Binary files /dev/null and b/gemrb/override/pst/bbridge.spl differ
diff --git a/gemrb/override/pst/bios.2da b/gemrb/override/pst/bios.2da
new file mode 100644
index 0000000..7fcdda0
--- /dev/null
+++ b/gemrb/override/pst/bios.2da
@@ -0,0 +1,13 @@
+2DA V1.0
+*
+ PC BIO
+0 * -1
+1 * -1
+2 NAMELESS 38787
+3 VHAILOR 39429
+4 NORDOM 39428
+5 IGNUS 39427
+6 GRACE 39426
+7 DAKKON 39425
+8 ANNAH 39424
+9 MORTE 39423
diff --git a/gemrb/override/pst/blessed.pro b/gemrb/override/pst/blessed.pro
new file mode 100644
index 0000000..a7c41ea
Binary files /dev/null and b/gemrb/override/pst/blessed.pro differ
diff --git a/gemrb/override/pst/bluetint.spl b/gemrb/override/pst/bluetint.spl
new file mode 100644
index 0000000..cd20a6a
Binary files /dev/null and b/gemrb/override/pst/bluetint.spl differ
diff --git a/gemrb/override/pst/bolt.pro b/gemrb/override/pst/bolt.pro
new file mode 100644
index 0000000..1cfd4f5
Binary files /dev/null and b/gemrb/override/pst/bolt.pro differ
diff --git a/gemrb/override/pst/boltex.pro b/gemrb/override/pst/boltex.pro
new file mode 100644
index 0000000..e963523
Binary files /dev/null and b/gemrb/override/pst/boltex.pro differ
diff --git a/gemrb/override/pst/bstorm.pro b/gemrb/override/pst/bstorm.pro
new file mode 100644
index 0000000..f536d06
Binary files /dev/null and b/gemrb/override/pst/bstorm.pro differ
diff --git a/gemrb/override/pst/bstorm.spl b/gemrb/override/pst/bstorm.spl
new file mode 100644
index 0000000..d3a8a3a
Binary files /dev/null and b/gemrb/override/pst/bstorm.spl differ
diff --git a/gemrb/override/pst/bstorm2.pro b/gemrb/override/pst/bstorm2.pro
new file mode 100644
index 0000000..446e7bb
Binary files /dev/null and b/gemrb/override/pst/bstorm2.pro differ
diff --git a/gemrb/override/pst/bstorm2.spl b/gemrb/override/pst/bstorm2.spl
new file mode 100644
index 0000000..f439014
Binary files /dev/null and b/gemrb/override/pst/bstorm2.spl differ
diff --git a/gemrb/override/pst/bullet.pro b/gemrb/override/pst/bullet.pro
new file mode 100644
index 0000000..cb89cea
Binary files /dev/null and b/gemrb/override/pst/bullet.pro differ
diff --git a/gemrb/override/pst/bulletex.pro b/gemrb/override/pst/bulletex.pro
new file mode 100644
index 0000000..4a039f5
Binary files /dev/null and b/gemrb/override/pst/bulletex.pro differ
diff --git a/gemrb/override/pst/callih.pro b/gemrb/override/pst/callih.pro
new file mode 100644
index 0000000..e494209
Binary files /dev/null and b/gemrb/override/pst/callih.pro differ
diff --git a/gemrb/override/pst/chost.2da b/gemrb/override/pst/chost.2da
new file mode 100644
index 0000000..b5445fa
--- /dev/null
+++ b/gemrb/override/pst/chost.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+-1
+ XPOS YPOS RESREF DELAY DURATION
+Row0 0 -200 S091GATE 2 *
+Row1 0 -200 S091DEVA 3 *
+Row2 0 -200 S091DVAT 9 *
+Row4 -30 -30 S091RAIL 11 *
+Row5 -20 -20 S091RAIL 12 *
+Row6 -10 -10 S091RAIL 13 *
+Row3 0 0 S091SMHT 14 *
+Row7 0 0 S091RAIL 14 *
+Row8 10 10 S091RAIL 15 *
+Row9 20 20 S091RAIL 15 *
+Row10 30 30 S091RAIL 16 *
+Row11 40 40 S091RAIL 16 *
+Row12 0 -200 S091HTCH 18 *
+Row13 0 0 S091PHNX 19 *
+Row15 0 -200 S091ASUN 21 *
+Row14 0 0 S091ARRS 22 *
+Row16 0 0 S091APPR 23 *
+Row17 0 0 S091GRLY 24 *
+Row18 0 0 S091BLST 25 *
diff --git a/gemrb/override/pst/chost.spl b/gemrb/override/pst/chost.spl
new file mode 100644
index 0000000..0167ad3
Binary files /dev/null and b/gemrb/override/pst/chost.spl differ
diff --git a/gemrb/override/pst/chromorb.pro b/gemrb/override/pst/chromorb.pro
new file mode 100644
index 0000000..b853298
Binary files /dev/null and b/gemrb/override/pst/chromorb.pro differ
diff --git a/gemrb/override/pst/classes.2da b/gemrb/override/pst/classes.2da
new file mode 100644
index 0000000..af5e917
--- /dev/null
+++ b/gemrb/override/pst/classes.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+*
+ NAME_REF DESC_REF CAP_REF SAVE MULTI ID HP USABILITY HUMAN
+MAGE 19762 * 19750 SAVEWIZ 0 1 HPWIZ 0x40000 1
+FIGHTER 19748 * 19747 SAVEWAR 0 2 HPWAR 0x800 1
+CLERIC 19764 * 19752 SAVEPRS 0 3 HPPRS 0x80 0
+THIEF 19758 * 19749 SAVEROG 0 4 HPROG 0x400000 1
+BARD * * * SAVEROG 0 5 HPROG 0 0
+PALADIN * * * SAVEWAR 0 6 HPWAR 0 0
+FIGHTER_MAGE 19767 * 19756 * 3 7 * 0x2000 0
+FIGHTER_CLERIC * * * * 6 8 * 0x4000 0
+FIGHTER_THIEF 19766 * 19754 * 10 9 * 0x20000 0
diff --git a/gemrb/override/pst/cloud.pro b/gemrb/override/pst/cloud.pro
new file mode 100644
index 0000000..444db21
Binary files /dev/null and b/gemrb/override/pst/cloud.pro differ
diff --git a/gemrb/override/pst/cloudkil.pro b/gemrb/override/pst/cloudkil.pro
new file mode 100644
index 0000000..eee63d6
Binary files /dev/null and b/gemrb/override/pst/cloudkil.pro differ
diff --git a/gemrb/override/pst/clskills.2da b/gemrb/override/pst/clskills.2da
new file mode 100644
index 0000000..f9d6c1f
--- /dev/null
+++ b/gemrb/override/pst/clskills.2da
@@ -0,0 +1,13 @@
+2DA V1.0
+*
+ HATERACE CLERICSPELL MAGESPELL STARTXP BARD THIEF LAYHANDS TURN BOOK
+UNUSED * * * * * * * 0 0
+MAGE * * MXSPLWIZ 0 * * * 0 1
+FIGHTER * * * 0 * * * 0 0
+CLERIC * MXSPLPRS * 0 * * * 1 1
+THIEF * * * 0 * SKILLS * 0 0
+BARD * * MXSPLBRD 0 * * * 0 1
+PALADIN * MXSPLPAL * 0 * * PALADIN 3 1
+FIGHTER_MAGE * * MXSPLWIZ 0 * * * 0 1
+FIGHTER_CLERIC * MXSPLPRS * 0 * * * 0 1
+FIGHTER_THIEF * * * 0 * SKILLS * 0 0
diff --git a/gemrb/override/pst/clssplab.2da b/gemrb/override/pst/clssplab.2da
new file mode 100644
index 0000000..cc94c44
--- /dev/null
+++ b/gemrb/override/pst/clssplab.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+6
+ DEX STR
+UNUSED 6 8
+MAGE 6 4
+FIGHTER 6 8
+CLERIC 4 6
+THIEF 8 6
+BARD 8 6
+PALADIN 6 8
+FIGHTER_MAGE 6 8
+FIGHTER_CLERIC 6 8
+FIGHTER_THIEF 8 8
+FIGHTER_MAGE_THIEF 8 8
+DRUID 4 6
+RANGER 6 8
+MAGE_THIEF 8 6
+CLERIC_MAGE 6 6
+CLERIC_THIEF 8 6
+FIGHTER_DRUID 6 8
+FIGHTER_MAGE_CLERIC 6 8
+CLERIC_RANGER 6 8
diff --git a/gemrb/override/pst/cocold.pro b/gemrb/override/pst/cocold.pro
new file mode 100644
index 0000000..0ac1c12
Binary files /dev/null and b/gemrb/override/pst/cocold.pro differ
diff --git a/gemrb/override/pst/colrspry.pro b/gemrb/override/pst/colrspry.pro
new file mode 100644
index 0000000..974c05f
Binary files /dev/null and b/gemrb/override/pst/colrspry.pro differ
diff --git a/gemrb/override/pst/conflag.pro b/gemrb/override/pst/conflag.pro
new file mode 100644
index 0000000..732449c
Binary files /dev/null and b/gemrb/override/pst/conflag.pro differ
diff --git a/gemrb/override/pst/conflag.spl b/gemrb/override/pst/conflag.spl
new file mode 100644
index 0000000..73345af
Binary files /dev/null and b/gemrb/override/pst/conflag.spl differ
diff --git a/gemrb/override/pst/confus.pro b/gemrb/override/pst/confus.pro
new file mode 100644
index 0000000..480f1bf
Binary files /dev/null and b/gemrb/override/pst/confus.pro differ
diff --git a/gemrb/override/pst/cursed.pro b/gemrb/override/pst/cursed.pro
new file mode 100644
index 0000000..b60784a
Binary files /dev/null and b/gemrb/override/pst/cursed.pro differ
diff --git a/gemrb/override/pst/dagger.pro b/gemrb/override/pst/dagger.pro
new file mode 100644
index 0000000..284c351
Binary files /dev/null and b/gemrb/override/pst/dagger.pro differ
diff --git a/gemrb/override/pst/daggerex.pro b/gemrb/override/pst/daggerex.pro
new file mode 100644
index 0000000..e33a595
Binary files /dev/null and b/gemrb/override/pst/daggerex.pro differ
diff --git a/gemrb/override/pst/damage.2da b/gemrb/override/pst/damage.2da
new file mode 100644
index 0000000..cbb1b63
--- /dev/null
+++ b/gemrb/override/pst/damage.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+*
+ MAIN SPARKS GRADIENT
+CRIT BLOODCR * 47
+SMALL BLOODS * 47
+MEDIUM BLOODM * 47
+LARGE BLOODL * 47
+FIRES FIRIMP SPBURN 19
+FIREM FIRIMP SPBURN 19
+FIREL FIRIMP SPBURN 19
+SPARKS SPSHKIMP SPSPARKS -1
+SPARKM SPSHKIMP SPSPARKS -1
+SPARKL SPSHKIMP SPSPARKS -1
+ICES FIRIMP * 71
+ICEM FIRIMP * 71
+ICEL FIRIMP * 71
diff --git a/gemrb/override/pst/dart.pro b/gemrb/override/pst/dart.pro
new file mode 100644
index 0000000..a8ec832
Binary files /dev/null and b/gemrb/override/pst/dart.pro differ
diff --git a/gemrb/override/pst/dartex.pro b/gemrb/override/pst/dartex.pro
new file mode 100644
index 0000000..b09ac20
Binary files /dev/null and b/gemrb/override/pst/dartex.pro differ
diff --git a/gemrb/override/pst/dbolt.pro b/gemrb/override/pst/dbolt.pro
new file mode 100644
index 0000000..7cb7a88
Binary files /dev/null and b/gemrb/override/pst/dbolt.pro differ
diff --git a/gemrb/override/pst/dbolt.spl b/gemrb/override/pst/dbolt.spl
new file mode 100644
index 0000000..b2b8fd1
Binary files /dev/null and b/gemrb/override/pst/dbolt.spl differ
diff --git a/gemrb/override/pst/defsound.2da b/gemrb/override/pst/defsound.2da
new file mode 100644
index 0000000..e901ae6
--- /dev/null
+++ b/gemrb/override/pst/defsound.2da
@@ -0,0 +1,29 @@
+2DA V1.0
+*
+ RESREF
+OPEN *
+CLOSE *
+HOPEN *
+HCLOSE *
+BUTTON1 INT_09
+BUTTON2 INT_03
+BUTTON3 INT_04
+OPENFAIL *
+CLOSEFAIL *
+ITEM_GONE INT_11
+SECRET *
+11 *
+12 *
+13 *
+14 *
+15 *
+16 *
+17 *
+18 *
+19 *
+LIGHTNING1 AMB_E13A
+LIGHTNING2 AMB_E13B
+LIGHTNING3 AMB_E13F
+RAIN AMB_E11
+SNOW AMB_E02B
+
diff --git a/gemrb/override/pst/dhell.pro b/gemrb/override/pst/dhell.pro
new file mode 100644
index 0000000..44f62b0
Binary files /dev/null and b/gemrb/override/pst/dhell.pro differ
diff --git a/gemrb/override/pst/dhell.spl b/gemrb/override/pst/dhell.spl
new file mode 100644
index 0000000..b434a03
Binary files /dev/null and b/gemrb/override/pst/dhell.spl differ
diff --git a/gemrb/override/pst/easymaze.2da b/gemrb/override/pst/easymaze.2da
new file mode 100644
index 0000000..fa6a5c7
--- /dev/null
+++ b/gemrb/override/pst/easymaze.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+3
+ OVERRIDE TRAPTYPE WALLS VISITED
+AR1301 1 -1 5 0
+AR1302 1 -1 13 0
+AR1303 1 0 9 0
+AR1309 1 -1 3 0
+AR1310 1 -1 6 0
+AR1311 1 -1 11 0
+AR1317 1 -1 6 0
+AR1318 1 -1 9 0
+AR1319 1 -1 2 0
diff --git a/gemrb/override/pst/ebomb.pro b/gemrb/override/pst/ebomb.pro
new file mode 100644
index 0000000..325fb76
Binary files /dev/null and b/gemrb/override/pst/ebomb.pro differ
diff --git a/gemrb/override/pst/effects.ids b/gemrb/override/pst/effects.ids
new file mode 100644
index 0000000..3ea2b9a
--- /dev/null
+++ b/gemrb/override/pst/effects.ids
@@ -0,0 +1,213 @@
+IDS
+0x0 ACVsDamageTypeModifier
+0x1 AttacksPerRoundModifier
+0x2 Cure:Sleep
+0x3 State:Berserk
+0x4 Cure:Berserk
+0x5 State:Charmed
+0x6 CharismaModifier
+0x7 Color:SetPalette
+0x8 Color:SetRGBGlobal
+0x9 Color:PulseRGBGlobal
+0xa ConstitutionModifier
+0xb Cure:Poison
+0xc Damage
+0xd Death
+0xe Cure:Defrost
+0xf DexterityModifier
+0x10 State:Hasted
+0x11 CurrentHPModifier
+0x12 MaximumHPModifier
+0x13 IntelligenceModifier
+0x14 State:Invisible
+0x15 LoreModifier
+0x16 LuckModifier
+0x17 MoraleModifier
+0x18 State:Panic
+0x19 State:Poisoned
+0x1a RemoveCurse
+0x1b AcidResistanceModifier
+0x1c ColdResistanceModifier
+0x1d ElectricityResistanceModifier
+0x1e FireResistanceModifier
+0x1f MagicDamageResistanceModifier
+0x20 Cure:Death
+0x21 SaveVsDeathModifier
+0x22 SaveVsWandsModifier
+0x23 SaveVsPolyModifier
+0x24 SaveVsBreathModifier
+0x25 SaveVsSpellsModifier
+0x26 State:Silenced
+0x27 State:Helpless
+0x28 State:Slowed
+0x29 Sparkle
+0x2a WizardSpellSlotsModifier
+0x2b Cure:Petrification
+0x2c StrengthModifier
+0x2d State:Stun
+0x2e Cure:Stun
+0x2f Cure:Invisible
+0x30 Cure:Silence
+0x31 WisdomModifier
+0x32 Color:BriefRGB
+0x33 Color:DarkenRGB
+0x34 Color:GlowRGB
+0x35 AnimationIDModifier
+0x36 ToHitModifier
+0x37 KillCreatureType
+0x38 Alignment:Invert
+0x39 Alignment:Change
+0x3a DispelEffects
+0x3b StealthModifier
+0x3c MiscastMagicModifier
+0x3d AlchemyModifier
+0x3e PriestSpellSlotsModifier
+0x3f State:Infravision
+0x40 Cure:Infravision
+0x41 State:Blur
+0x42 TransparencyModifier
+0x43 SummonCreature
+0x44 UnsummonCreature
+0x45 State:NonDetection
+0x46 Cure:NonDetection
+0x47 SexModifier
+0x48 AIIdentifierModifier
+0x49 DamageBonusModifier
+0x4a State:Blind
+0x4b Cure:Blind
+0x4c State:Feeblemind
+0x4d Cure:Feeblemind
+0x4e State:Diseased
+0x4f Cure:Disease
+0x50 State:Deafness
+0x51 Cure:Deafness
+0x52 SetAIScript
+0x53 Protection:Projectile
+0x54 MagicalFireResistanceModifier
+0x55 MagicalColdResistanceModifier
+0x56 SlashingResistanceModifier
+0x57 CrushingResistanceModifier
+0x58 PiercingResistanceModifier
+0x59 MissilesResistanceModifier
+0x5a OpenLocksModifier
+0x5b FindTrapsModifier
+0x5c PickPocketsModifier
+0x5d FatigueModifier
+0x5e IntoxicationModifier
+0x5f TrackingModifier
+0x60 LevelModifier
+0x61 StrengthBonusModifier
+0x62 State:Regenerating
+0x63 SpellDurationModifier
+0x64 Protection:Creature
+0x65 Protection:Opcode
+0x66 Protection:SpellLevel
+0x67 ChangeName
+0x68 ExperienceModifier
+0x69 GoldModifier
+0x6a MoraleBreakModifier
+0x6b PortraitChange
+0x6c ReputationModifier
+0x6d State:HoldNoIcon
+0x6e RetreatFrom
+0x6f Item:CreateMagic
+0x70 Item:Remove
+0x71 Item:Equip
+0x72 Dither
+0x73 DetectAlignment
+0x74 Cure:Invisible2
+0x75 Reveal:Area
+0x76 Reveal:Creatures
+0x77 MirrorImage
+0x78 Protection:Weapons
+0x79 VisualAnimationEffect
+0x7a Item:CreateInventory
+0x7b Item:RemoveInventory
+0x7c DimensionDoor
+0x7d Unlock
+0x7e MovementRateModifier
+0x7f MonsterSummoning
+0x80 State:Confused
+0x81 AidNonCumulative
+0x82 Bless
+0x83 ChantNonCumulative
+0x84 HolyNonCumulative
+0x85 LuckNonCumulative
+0x86 State:Petrification
+0x87 Polymorph
+0x88 ForceVisible
+0x89 ChantBadNonCumulative
+0x8a AnimationStateChange
+0x8b DisplayString
+0x8c CastingGlow
+0x8d VisualSpellHit
+0x8e Icon:Display
+0x8f Item:CreateInSlot
+0x90 DisableButton
+0x91 DisableCasting
+0x92 Spell:Cast
+0x93 Spell:Learn
+0x94 Spell:CastPoint
+0x95 Identify
+0x96 FindTraps
+0x97 ReplaceCreature
+0x98 PlayMovie
+0x99 Overlay:Sanctuary
+0x9a Overlay:Entangle
+0x9b Overlay:MinorGlobe
+0x9c Overlay:ShieldGlobe
+0x9d Overlay:Web
+0x9e Overlay:Grease
+0x9f MirrorImageModifier
+0xa0 Cure:Sanctuary
+0xa1 Cure:Panic
+0xa2 Cure:Hold
+0xa3 FreeAction
+0xa4 Cure:Intoxication
+0xa5 PauseTarget
+0xa6 MagicResistanceModifier
+0xa7 MissileHitModifier
+0xa8 RemoveCreature
+0xa9 Icon:Disable
+0xaa DamageAnimation
+0xab Spell:Add
+0xac Spell:Remove
+0xad PoisonResistanceModifier
+0xae PlaySound
+0xaf State:Hold
+0xb0 MovementRateModifier2
+0xb1 ApplyEffect
+0xb2 ToHitVsCreature
+0xb3 DamageVsCreature
+0xb4 CantUseItem
+0xb5 CantUseItemType
+0xb6 ApplyEffectItem
+0xb7 ApplyEffectItemType
+0xb8 DontJumpModifier
+0xb9 State:Hold2
+0xba SetStatus
+0xbb PlayBAM1
+0xbc PlayBAM2
+0xbd PlayBAM3
+0xbe PlayBAM4
+0xbf PlayBAM5
+0xc0 TransferHP
+0xc1 ScreenShake
+0xc2 FlashScreen
+0xc3 TintScreen
+0xc4 SpecialEffect
+0xc5 MultipleVVC
+0xc6 ModifyGlobalVariable
+0xc7 ChangeBackground
+0xc9 Overlay
+0xcb Curse
+0xcc Prayer
+0xcd MoveView
+0xce Embalm
+0xcf StopAllAction
+0xd0 IronFist
+0xd1 HostileImage
+0xd2 DetectEvil
+0xd3 JumbleCurse
+0xd4 SpeakWithDead
+425 ControlUndead2
diff --git a/gemrb/override/pst/efftext.2da b/gemrb/override/pst/efftext.2da
new file mode 100644
index 0000000..52398dd
--- /dev/null
+++ b/gemrb/override/pst/efftext.2da
@@ -0,0 +1,2 @@
+2DA V1.0
+-1
diff --git a/gemrb/override/pst/elyfire.pro b/gemrb/override/pst/elyfire.pro
new file mode 100644
index 0000000..6c0cd4e
Binary files /dev/null and b/gemrb/override/pst/elyfire.pro differ
diff --git a/gemrb/override/pst/elytear.pro b/gemrb/override/pst/elytear.pro
new file mode 100644
index 0000000..5859434
Binary files /dev/null and b/gemrb/override/pst/elytear.pro differ
diff --git a/gemrb/override/pst/embalm.pro b/gemrb/override/pst/embalm.pro
new file mode 100644
index 0000000..8e38883
Binary files /dev/null and b/gemrb/override/pst/embalm.pro differ
diff --git a/gemrb/override/pst/estrike.pro b/gemrb/override/pst/estrike.pro
new file mode 100644
index 0000000..b50eeec
Binary files /dev/null and b/gemrb/override/pst/estrike.pro differ
diff --git a/gemrb/override/pst/factions.2da b/gemrb/override/pst/factions.2da
new file mode 100644
index 0000000..9fb484e
--- /dev/null
+++ b/gemrb/override/pst/factions.2da
@@ -0,0 +1,20 @@
+2DA V1.0
+*
+ DESC_REF BUTTON_FRAME UNUSABLE
+UNALIGNED 34584 0 0x1000
+UNKNOWN * * *
+DUSTMEN 34588 3 0x4000
+GODSMAN 34585 7 0x100
+SENSATE 34587 2 0x40
+ANARCHIST 34589 4 0
+ATHAR * * *
+BLEAKER * * *
+XAOSITECT 34590 5 0x400
+INDEP 3789 6 0x10000
+CIPHER * * *
+SIGNER * * *
+MERCYKILLER 34586 1 0x8000
+HARMONIUM * * *
+GUVNER * * *
+FATED * * *
+DOOMGUARD * * *
diff --git a/gemrb/override/pst/faerie.spl b/gemrb/override/pst/faerie.spl
new file mode 100644
index 0000000..b04ca3f
Binary files /dev/null and b/gemrb/override/pst/faerie.spl differ
diff --git a/gemrb/override/pst/fbomb.pro b/gemrb/override/pst/fbomb.pro
new file mode 100644
index 0000000..aa03c86
Binary files /dev/null and b/gemrb/override/pst/fbomb.pro differ
diff --git a/gemrb/override/pst/fbyello.pro b/gemrb/override/pst/fbyello.pro
new file mode 100644
index 0000000..37f84cb
Binary files /dev/null and b/gemrb/override/pst/fbyello.pro differ
diff --git a/gemrb/override/pst/fearaura.pro b/gemrb/override/pst/fearaura.pro
new file mode 100644
index 0000000..329a26c
Binary files /dev/null and b/gemrb/override/pst/fearaura.pro differ
diff --git a/gemrb/override/pst/ffire.pro b/gemrb/override/pst/ffire.pro
new file mode 100644
index 0000000..0cc6255
Binary files /dev/null and b/gemrb/override/pst/ffire.pro differ
diff --git a/gemrb/override/pst/fireball.pro b/gemrb/override/pst/fireball.pro
new file mode 100644
index 0000000..ede63e1
Binary files /dev/null and b/gemrb/override/pst/fireball.pro differ
diff --git a/gemrb/override/pst/firebolt.pro b/gemrb/override/pst/firebolt.pro
new file mode 100644
index 0000000..a7b3b25
Binary files /dev/null and b/gemrb/override/pst/firebolt.pro differ
diff --git a/gemrb/override/pst/firebtbl.pro b/gemrb/override/pst/firebtbl.pro
new file mode 100644
index 0000000..5599064
Binary files /dev/null and b/gemrb/override/pst/firebtbl.pro differ
diff --git a/gemrb/override/pst/fireice.pro b/gemrb/override/pst/fireice.pro
new file mode 100644
index 0000000..bb4e6c0
Binary files /dev/null and b/gemrb/override/pst/fireice.pro differ
diff --git a/gemrb/override/pst/fireice.spl b/gemrb/override/pst/fireice.spl
new file mode 100644
index 0000000..8fa10f8
Binary files /dev/null and b/gemrb/override/pst/fireice.spl differ
diff --git a/gemrb/override/pst/firestor.pro b/gemrb/override/pst/firestor.pro
new file mode 100644
index 0000000..76e10b2
Binary files /dev/null and b/gemrb/override/pst/firestor.pro differ
diff --git a/gemrb/override/pst/fistweap.2da b/gemrb/override/pst/fistweap.2da
new file mode 100644
index 0000000..a397907
--- /dev/null
+++ b/gemrb/override/pst/fistweap.2da
@@ -0,0 +1,13 @@
+2DA V1.0
+FIST
+ RESOURCE
+0 FIST
+1 FIST
+2 FIST
+3 JUDGMENT
+4 NORDXBOW
+5 IGNUSFI
+6 GTOUCH
+7 KARACH
+8 ANNAHDAG
+9 MBITE
diff --git a/gemrb/override/pst/fizzle.spl b/gemrb/override/pst/fizzle.spl
new file mode 100644
index 0000000..b26a357
Binary files /dev/null and b/gemrb/override/pst/fizzle.spl differ
diff --git a/gemrb/override/pst/fmiss.pro b/gemrb/override/pst/fmiss.pro
new file mode 100644
index 0000000..639a1f8
Binary files /dev/null and b/gemrb/override/pst/fmiss.pro differ
diff --git a/gemrb/override/pst/fonts.2da b/gemrb/override/pst/fonts.2da
new file mode 100644
index 0000000..dfad859
--- /dev/null
+++ b/gemrb/override/pst/fonts.2da
@@ -0,0 +1,12 @@
+2DA V1.0
+FONTDLG
+ RESREF NEED_PALETTE FIRST_CHAR
+0 FONTDLG 1 0
+1 TRMTFONT 1 0
+2 EXOFONT 1 0
+3 POSTANT 1 0
+4 NUMBER 0 47
+5 NUMBER2 0 47
+6 NUMBER3 0 47
+7 SYSFONT 1 0
+8 FONTDLG 1 0
diff --git a/gemrb/override/pst/formatio.2da b/gemrb/override/pst/formatio.2da
new file mode 100644
index 0000000..be0bc17
--- /dev/null
+++ b/gemrb/override/pst/formatio.2da
@@ -0,0 +1,17 @@
+2DA V1.0
+-10
+# generated by make_formation.py, do not edit
+ X1 Y1 X2 Y2 X3 Y3 X4 Y4 X5 Y5 X6 Y6 X7 Y7 X8 Y8 X9 Y9 X10 Y10 X11 Y11 X12 Y12 X13 Y13 X14 Y14 X15 Y15 X16 Y16 X17 Y17 X18 Y18 X19 Y19 X20 Y20
+FOLLOW 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
+T 0 0 48 0 -48 0 0 48 0 84 0 120 0 156 0 192 0 228 0 264 0 300 0 336 0 372 0 408 0 444 0 480 0 516 0 552 0 588 0 624
+GATHER 0 -36 48 -24 -48 -24 48 24 -48 24 0 36 48 48 -48 48 0 72 48 72 -48 72 0 108 48 96 -48 96 0 144 48 120 -48 120 0 180 48 144 -48 144
+4AND2 0 0 64 0 -64 0 128 0 0 48 64 48 -64 48 128 48 0 96 64 96 -64 96 128 96 0 144 64 144 -64 144 128 144 0 192 64 192 -64 192 128 192
+3BY2 0 0 64 0 -64 0 0 48 64 48 -64 48 0 96 64 96 -64 96 0 144 64 144 -64 144 0 192 64 192 -64 192 0 240 64 240 -64 240 0 288 64 288
+PROTECT 0 0 0 -36 -64 0 64 0 -32 48 32 48 0 24 0 48 0 72 0 96 0 120 0 144 0 168 0 192 0 216 0 240 0 264 0 288 0 312 0 336
+2BY3 -24 0 24 0 -24 48 24 48 -24 84 24 84 -24 120 24 120 -24 156 24 156 -24 192 24 192 -24 228 24 228 -24 264 24 264 -24 300 24 300 -24 336 24 336
+RANK -32 0 32 0 -96 0 96 0 -160 0 160 0 -224 0 224 0 -288 0 288 0 -352 0 352 0 -416 0 416 0 -480 0 480 0 -544 0 544 0 -608 0 608 0
+V 0 0 64 0 -15 48 49 48 -30 96 34 96 -45 144 19 144 -60 192 4 192 -75 240 -11 240 -90 288 -26 288 -105 336 -41 336 -120 384 -56 384 -135 432 -71 432
+WEDGE 0 0 64 36 -64 36 -124 72 124 72 0 72 0 144 -124 144 124 144 0 180 -124 180 124 180 0 216 -124 216 124 216 0 252 -124 252 124 252 0 288 -124 288
+S 0 0 64 24 0 48 64 72 0 96 64 120 0 144 64 168 0 192 64 216 0 240 64 264 0 288 64 312 0 336 64 360 0 384 64 408 0 432 64 456
+LINE 0 0 0 36 0 72 0 108 0 144 0 180 0 216 0 252 0 288 0 324 0 360 0 396 0 432 0 468 0 504 0 540 0 576 0 612 0 648 0 684
+NONE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/gemrb/override/pst/frain.pro b/gemrb/override/pst/frain.pro
new file mode 100644
index 0000000..3f6bdb9
Binary files /dev/null and b/gemrb/override/pst/frain.pro differ
diff --git a/gemrb/override/pst/gametime.2da b/gemrb/override/pst/gametime.2da
new file mode 100644
index 0000000..237370a
--- /dev/null
+++ b/gemrb/override/pst/gametime.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+0
+ DURATION
+ROUND_SECONDS 5
+TURN_SECONDS 50
diff --git a/gemrb/override/pst/gaze.pro b/gemrb/override/pst/gaze.pro
new file mode 100644
index 0000000..632263a
Binary files /dev/null and b/gemrb/override/pst/gaze.pro differ
diff --git a/gemrb/override/pst/gemprjtl.ids b/gemrb/override/pst/gemprjtl.ids
new file mode 100644
index 0000000..fe6ebca
--- /dev/null
+++ b/gemrb/override/pst/gemprjtl.ids
@@ -0,0 +1,292 @@
+IDS V1.0
+1 ARROW
+2 ARROWEX
+3 ARROWFLM
+4 ARROWHVY
+5 ARROW
+6 AXE
+7 AXEEX
+8 AXEFLM
+9 AXE
+10 AXE
+11 BOLT
+12 BOLTEX
+13 ARROWHVY
+14 BOLT
+15 BOLT
+16 BULLET
+17 BULLETEX
+18 <flamingbullet>
+19 BULLET
+20 BULLET
+21 SPBRNHND
+22 SKYBOLT
+23 CHROMORB
+24 SPCONECO
+25 SPCONEFI
+26 DAGGER
+27 DAGGEREX
+28 <flamingdagger>
+29 DAGGER
+30 DAGGER
+31 DART
+32 DARTEX
+33 <flamingdart>
+34 DART
+35 DART
+36 MAGICMIS
+37 FIREBALL
+38 ICE
+39 LIGHTB
+40 STONE
+41 SLEEP
+42 <skeleton>
+43 SPSMPUFF
+44 SPSMKJET
+45 SPSMOLD
+46 SPARKLBL
+47 SPARKLGO
+48 SPARKLPU
+49 SPARKLIC
+50 SPARKLST
+51 SPARKLBK
+52 SPARKLCH
+53 SPARKLRE
+54 SPARKLGR
+55 SPEAR
+56 SPEAREX
+57 <flamingspear>
+58 SPEAR
+59 SPEAR
+60 <starsprite>
+61 <stoned>
+62 <webtravel>
+63 WEB
+64 GAZE
+65 HLYMITE
+66 FLMSTRK
+67 MAGICMIS
+68 SPMAGMIS
+69 SPMAGMIS
+70 SPMAGMIS
+71 SPMAGMIS
+72 SPMAGMIS
+73 SPMAGMIS
+74 SPMAGMIS
+75 SPMAGMIS
+76 SPMAGMIS
+77 SPMAGMIS
+78 INVTRAV
+79 FIREBOLT
+80 SKYBOLT
+81 SKYBOLT2
+82 SKYBOLT2
+83 SKYBOLT2
+84 SKYBOLT2
+85 SKYBOLT2
+86 SKYBOLT2
+87 SKYBOLT2
+88 SKYBOLT2
+89 SKYBOLT2
+90 SKYBOLT2
+91 FIRESTOR
+92 LIGHTSTO
+93 INAREA
+94 CLOUD
+95 TRAPSKUL
+96 COLRSPRY
+97 ICESTORM
+98 SPFIREWL
+99 TRAPGLYP
+100 GREASE
+101 ARROWFLG
+102 ARROWFLB
+103 FIREBLGR
+104 FIREBTBL
+105 <potion>
+106 <potionexplode>
+107 ACIDBLOB
+108 SPSCORCH
+109 SPDIMDR
+110 CGNECROM
+111 CGALTERA
+112 CGENCHAN
+113 CGABJURA
+114 CGILLUSI
+115 CGCONJUR
+116 CGINVOCA
+117 CGDIVINA
+118 SHAIR
+119 SHEARTH
+120 SHWATER
+121 SHAIR1
+122 SHEARTH1
+123 SHWATER1
+124 SHAIR2
+125 SHEARTH2
+126 SHWATER2
+127 SHAIR3
+128 SHEARTH3
+129 SHWATER3
+130 SHAIR4
+131 SHEARTH4
+132 SHWATER4
+133 SHAIR5
+134 SHEARTH5
+135 SHWATER5
+136 SHAIR6
+137 SHEARTH6
+138 SHWATER6
+139 SHAIR7
+140 SHEARTH7
+141 SHWATER7
+142 SPBOOM1
+143 SPBOOM2
+144 SPBOOM3
+145 FLMSTRK
+146 HLYMITE
+147 SHAIR7
+148 SPKLARBL
+149 SPKLARGO
+150 SPKLARPU
+151 SPKLARIC
+152 SPKLARST
+153 SPKLARBK
+154 SPKLARCH
+155 SPKLARRE
+156 SPKLARGR
+157 INAREAPA
+158 INAREANP
+159 SPARBLPA
+160 SPARGOPA
+161 SPARPUPA
+162 SPARICPA
+163 SPARSTPA
+164 SPARBKPA
+165 SPARCHPA
+166 SPARREPA
+167 SPARGRPA
+168 SPARBLNP
+169 SPARGONP
+170 SPARPUNP
+171 SPARICNP
+172 SPARSTNP
+173 SPARBKNP
+174 SPARCHNP
+175 SPARRENP
+176 SPARGRNP
+177 SPARMANP
+178 SPARORNP
+179 SPARMAPA
+180 SPARORPA
+181 SPKLARMA
+182 SPKLAROR
+183 SPARKLMA
+184 SPARKLOR
+185 INAREANS
+186 CLOUDKIL
+187 ARROWFLI
+188 COW
+189 HOLD
+190 SPSCORIC
+191 ACIDBLMU
+192 ACIDBLGR
+193 ACIDBLOC
+194 REDHOLY
+195 SHAREA
+196 SHAREA1
+197 SHAREA
+198 SHAREA2
+199 SHAREA3
+200 SHAREA4
+201 SHAREA5
+202 SHAREA6
+203 LITANY
+204 STORIES
+205 MMISS
+206 MMISS2
+207 MMISS2
+208 MMISS2
+209 MMISS2
+210 MMISS2
+211 MMISS2
+212 MMISS2
+213 MMISS2
+214 MMISS2
+215 SKMOB
+216 SKMOB2
+217 ISWARM
+218 ISWARM2
+219 ADDER
+220 IKNIFE
+221 PACIFY
+222 STRENGTH
+223 BLIGHT
+224 BLIGHT2
+225 BBRIDGE
+226 FMISS
+227 ISTR
+228 SSHADOW
+229 CLOUDKIL
+230 HOPAND
+231 BSTORM
+232 ELYFIRE
+233 FURY
+234 HORROR
+235 KNOCK
+236 HOLDUND
+237 LMISS
+238 ELYTEAR
+239 TLAUGH
+240 AOTORM
+241 BSPHERE
+242 COCOLD
+243 DHELL
+244 FIREICE
+245 LSTORM
+246 ASTORM
+247 ISTORM
+248 MSWARM
+249 DBOLT
+250 IFURY
+251 PWBLIND
+252 MCANNON
+253 CHOST
+254 ROTORM
+255 BLESSED
+256 CURSED
+258 FFIRE
+259 SHAMMER
+260 CALLIH
+262 VTOUCH
+263 CONFUS
+264 PWKILL
+265 GOINVUL
+266 NBOLT
+267 RDEAD
+268 FEARAURA
+269 CONFLAG
+271 FBYELLO
+272 IMISS
+273 IMISS2
+274 IMISS2
+275 IMISS2
+276 IMISS2
+277 ITERROR
+278 IGNUSORB
+279 FRAIN
+280 ESTRIKE
+281 AMISS
+282 AMISS2
+283 AMISS2
+284 AMISS2
+285 AMISS2
+286 POONE
+287 KISS
+288 EMBALM
+289 FBOMB
+290 EBOMB
+291 RUNE
+292 LSTORM2
+293 CHOST2
+294 BSTORM2
diff --git a/gemrb/override/pst/gemrb.ini b/gemrb/override/pst/gemrb.ini
new file mode 100644
index 0000000..367a2e0
--- /dev/null
+++ b/gemrb/override/pst/gemrb.ini
@@ -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.
+;
+
+
+; gemrb.ini - game type-specific settings for GemRB engine
+
+[resources]
+
+; Bitmap resource for cursors
+CursorBAM = CARET
+
+; Bitmap resource for scroll cursor arrow
+ScrollCursorBAM = CURSARW
+
+; Bitmap resource for dialog buttons font
+ButtonFont = FONTDLG
+
+; Font used to display tooltips
+TooltipFont = TRMTFONT
+
+; Bitmap resource for ground circles
+GroundCircleBAM1 = WMPICKL/3
+GroundCircleBAM2 = WMPICKR
+GroundCircleBAM3 = WMPICKL
+NoteString = NOTE:
+
+; INI file from the original games
+INIConfig = torment.ini
+
+; Palette bitmaps in various widths
+Palette16 = MPALETTE
+Palette32 = PAL32
+Palette256 = MPAL256
+
+;other games got a class specific fist (for monk)
+FistStat = 235
+MaximumAbility = 25
+IgnoreButtonFrames = 1
+AllStringsTagged = 1
+HasDPLAYER = 0
+HasPickSound = 1
+HasDescIcon = 0
+HasEXPTABLE = 0
+SoundFolders = 0
+HasSongList = 0
+UpperButtonText = 0
+ReverseToHit = 1
+LowerLabelText = 0
+HasPartyINI = 0
+HasBeastsINI = 1
+ForceStereo = 1
+TeamMovement = 1
+OneByteAnimationID = 1
+AutomapINI = 1
+SmallFog = 1
+ReverseDoor = 1
+ProtagonistTalks = 1
+HasKaputz = 1
+DialogueScrolls = 1
+SaveForHalfDamage = 1
+SpawnIni = 1
+HasResDataIni = 1
+OverrideCursorPos = 1
+OnScreenText = 1
+HasSpecificDamageBonus = 0
+HasWisdomBonusTable = 1
+AutoSearchHidden = 1
+CutsceneAreascripts = 1
+PSTStateFlags = 1
+NoDropCanMove = 1
+AreaOverride = 1
+NoNewVariables = 1
+
+[charset]
+CharCount = 31
+Letter1 = 192,224
+Letter2 = 193,225
+Letter3 = 194,226
+Letter4 = 195,227
+Letter5 = 196,228
+Letter6 = 197,229
+Letter7 = 198,230
+Letter8 = 199,231
+Letter9 = 200,232
+Letter10 = 201,233
+Letter11 = 202,234
+Letter12 = 203,235
+Letter13 = 204,236
+Letter14 = 205,237
+Letter15 = 206,238
+Letter16 = 207,239
+Letter17 = 208,240
+Letter18 = 209,241
+Letter19 = 210,242
+Letter20 = 211,243
+Letter21 = 212,244
+Letter22 = 213,245
+Letter23 = 214,246
+Letter24 = 140,156
+Letter25 = 216,248
+Letter26 = 217,249
+Letter27 = 218,250
+Letter28 = 219,251
+Letter29 = 220,252
+Letter30 = 221,253
+Letter31 = 222,254
diff --git a/gemrb/override/pst/genders.2da b/gemrb/override/pst/genders.2da
new file mode 100644
index 0000000..62d1469
--- /dev/null
+++ b/gemrb/override/pst/genders.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+*
+ NAME_REF
+MALE 4190
+FEMALE 4191
+OTHER 19862
+NIETHER 19863
+BOTH 19864
diff --git a/gemrb/override/pst/goinvul.pro b/gemrb/override/pst/goinvul.pro
new file mode 100644
index 0000000..db6bf73
Binary files /dev/null and b/gemrb/override/pst/goinvul.pro differ
diff --git a/gemrb/override/pst/goinvul.spl b/gemrb/override/pst/goinvul.spl
new file mode 100644
index 0000000..3bc7a72
Binary files /dev/null and b/gemrb/override/pst/goinvul.spl differ
diff --git a/gemrb/override/pst/grease.pro b/gemrb/override/pst/grease.pro
new file mode 100644
index 0000000..bf273b6
Binary files /dev/null and b/gemrb/override/pst/grease.pro differ
diff --git a/gemrb/override/pst/guibtact.2da b/gemrb/override/pst/guibtact.2da
new file mode 100644
index 0000000..e5a68d0
--- /dev/null
+++ b/gemrb/override/pst/guibtact.2da
@@ -0,0 +1,35 @@
+2DA V1.0
+-1
+ 1 2 3 4 TOOLTIP RESREF
+Stealth * * * * * *
+Thieving * * * * * *
+Cast * * * * * *
+QSpell1 * * * * * *
+QSpell2 * * * * * *
+QSpell3 * * * * * *
+Turn * * * * * *
+Talk 0 1 2 3 41653 AMTLK
+UseItem * * * * * *
+QItem1 * * * * * *
+QItem4 * * * * * *
+QItem2 * * * * * *
+QItem3 * * * * * *
+Innate * * * * * *
+Defend 0 1 2 3 31657 AMGUARD
+Attack 0 1 2 3 41654 AMATTCK
+QWeapon1 * * * * * *
+QWeapon2 * * * * * *
+QWeapon3 * * * * * *
+QWeapon4 * * * * * *
+BardSong * * * * 11798 *
+Stop 0 1 2 3 41655 AMALLSTP
+Search 0 1 2 3 -1 AMGENS
+23 * * * * * *
+24 * * * * * *
+25 * * * * * *
+26 * * * * * *
+27 * * * * * *
+28 * * * * * *
+29 * * * * * *
+30 * * * * * *
+QItem5 * * * * * *
diff --git a/gemrb/override/pst/guiid.chu b/gemrb/override/pst/guiid.chu
new file mode 100644
index 0000000..5b83daa
Binary files /dev/null and b/gemrb/override/pst/guiid.chu differ
diff --git a/gemrb/override/pst/guils.chu b/gemrb/override/pst/guils.chu
new file mode 100644
index 0000000..ab12019
Binary files /dev/null and b/gemrb/override/pst/guils.chu differ
diff --git a/gemrb/override/pst/guiw08.chu b/gemrb/override/pst/guiw08.chu
new file mode 100644
index 0000000..b181e9d
Binary files /dev/null and b/gemrb/override/pst/guiw08.chu differ
diff --git a/gemrb/override/pst/hlymite.pro b/gemrb/override/pst/hlymite.pro
new file mode 100644
index 0000000..c67eabc
Binary files /dev/null and b/gemrb/override/pst/hlymite.pro differ
diff --git a/gemrb/override/pst/hold.pro b/gemrb/override/pst/hold.pro
new file mode 100644
index 0000000..bc19f3b
Binary files /dev/null and b/gemrb/override/pst/hold.pro differ
diff --git a/gemrb/override/pst/holdund.pro b/gemrb/override/pst/holdund.pro
new file mode 100644
index 0000000..aebb95c
Binary files /dev/null and b/gemrb/override/pst/holdund.pro differ
diff --git a/gemrb/override/pst/holdund.spl b/gemrb/override/pst/holdund.spl
new file mode 100644
index 0000000..ff2e72c
Binary files /dev/null and b/gemrb/override/pst/holdund.spl differ
diff --git a/gemrb/override/pst/horror.pro b/gemrb/override/pst/horror.pro
new file mode 100644
index 0000000..4ac0053
Binary files /dev/null and b/gemrb/override/pst/horror.pro differ
diff --git a/gemrb/override/pst/ice.pro b/gemrb/override/pst/ice.pro
new file mode 100644
index 0000000..c1daaf2
Binary files /dev/null and b/gemrb/override/pst/ice.pro differ
diff --git a/gemrb/override/pst/ice.spl b/gemrb/override/pst/ice.spl
new file mode 100644
index 0000000..8fa10f8
Binary files /dev/null and b/gemrb/override/pst/ice.spl differ
diff --git a/gemrb/override/pst/icestorm.pro b/gemrb/override/pst/icestorm.pro
new file mode 100644
index 0000000..32d2a98
Binary files /dev/null and b/gemrb/override/pst/icestorm.pro differ
diff --git a/gemrb/override/pst/ifury.pro b/gemrb/override/pst/ifury.pro
new file mode 100644
index 0000000..009f902
Binary files /dev/null and b/gemrb/override/pst/ifury.pro differ
diff --git a/gemrb/override/pst/ifury.spl b/gemrb/override/pst/ifury.spl
new file mode 100644
index 0000000..4f10f53
Binary files /dev/null and b/gemrb/override/pst/ifury.spl differ
diff --git a/gemrb/override/pst/ignusorb.pro b/gemrb/override/pst/ignusorb.pro
new file mode 100644
index 0000000..39d76fa
Binary files /dev/null and b/gemrb/override/pst/ignusorb.pro differ
diff --git a/gemrb/override/pst/ignusorb.vvc b/gemrb/override/pst/ignusorb.vvc
new file mode 100644
index 0000000..54d0e74
Binary files /dev/null and b/gemrb/override/pst/ignusorb.vvc differ
diff --git a/gemrb/override/pst/iknife.pro b/gemrb/override/pst/iknife.pro
new file mode 100644
index 0000000..84cee9b
Binary files /dev/null and b/gemrb/override/pst/iknife.pro differ
diff --git a/gemrb/override/pst/imiss.pro b/gemrb/override/pst/imiss.pro
new file mode 100644
index 0000000..a5212eb
Binary files /dev/null and b/gemrb/override/pst/imiss.pro differ
diff --git a/gemrb/override/pst/imiss2.pro b/gemrb/override/pst/imiss2.pro
new file mode 100644
index 0000000..c924ff9
Binary files /dev/null and b/gemrb/override/pst/imiss2.pro differ
diff --git a/gemrb/override/pst/impstr.spl b/gemrb/override/pst/impstr.spl
new file mode 100644
index 0000000..746f5a9
Binary files /dev/null and b/gemrb/override/pst/impstr.spl differ
diff --git a/gemrb/override/pst/inarea.pro b/gemrb/override/pst/inarea.pro
new file mode 100644
index 0000000..ec6a50d
Binary files /dev/null and b/gemrb/override/pst/inarea.pro differ
diff --git a/gemrb/override/pst/istorm.pro b/gemrb/override/pst/istorm.pro
new file mode 100644
index 0000000..7a91404
Binary files /dev/null and b/gemrb/override/pst/istorm.pro differ
diff --git a/gemrb/override/pst/istorm.spl b/gemrb/override/pst/istorm.spl
new file mode 100644
index 0000000..0ef261a
Binary files /dev/null and b/gemrb/override/pst/istorm.spl differ
diff --git a/gemrb/override/pst/istr.pro b/gemrb/override/pst/istr.pro
new file mode 100644
index 0000000..a3396ad
Binary files /dev/null and b/gemrb/override/pst/istr.pro differ
diff --git a/gemrb/override/pst/iswarm.pro b/gemrb/override/pst/iswarm.pro
new file mode 100644
index 0000000..5005efa
Binary files /dev/null and b/gemrb/override/pst/iswarm.pro differ
diff --git a/gemrb/override/pst/iswarm.spl b/gemrb/override/pst/iswarm.spl
new file mode 100644
index 0000000..5734bce
Binary files /dev/null and b/gemrb/override/pst/iswarm.spl differ
diff --git a/gemrb/override/pst/item_use.2da b/gemrb/override/pst/item_use.2da
new file mode 100644
index 0000000..749c8fd
--- /dev/null
+++ b/gemrb/override/pst/item_use.2da
@@ -0,0 +1,27 @@
+2DA V1.0
+*
+ USER STRREF FLAG
+AVEST ANNAH 59142 7
+JER_BR ANNAH 61795 7
+ ANNAH 61796 7
+JER_FS ANNAH 61797 7
+ ANNAH 61798 7
+ZERTH DAKKON 59143 7
+ GRACE 59144 7
+BOD_PQ GRACE 59145 7
+BOD_GP GRACE 59146 7
+CHASTITY GRACE 59147 7
+VHAILOR VHAILOR 59148 7
+CIRCLEZ DAKKON 59149 2
+KARACH DAKKON 59149 7
+KARACH2 DAKKON 59149 7
+GDIARY GRACE 59150 1
+GDIARY GRACE 59151 1
+GDIARY GRACE 59152 1
+NORDBOLT NORDOM 59153 7
+EYEBALL NAMELESS 59154 7
+EYEGLAS1 NAMELESS 59154 7
+EYEGLAS2 NAMELESS 59154 7
+EYEKAL NAMELESS 59154 7
+EYEKALEM NAMELESS 59154 7
+EYEVECNA NAMELESS 59154 7
diff --git a/gemrb/override/pst/itemsnd.2da b/gemrb/override/pst/itemsnd.2da
new file mode 100644
index 0000000..268ae42
--- /dev/null
+++ b/gemrb/override/pst/itemsnd.2da
@@ -0,0 +1,49 @@
+2DA V1.0
+*
+ DROP TAKE
+MISC INT_12R1 INT_12R
+AMULET INT_12M1 INT_12M
+ARMOR INT_12G1 INT_12G
+BELT INT_12R1 INT_12R
+BOOT INT_12R1 INT_12R
+ARROW INT_12R1 INT_12R
+PET INT_12A1 INT_12A
+HELMET INT_12R1 INT_12R
+KEY Z_KEY2 Z_KEY1
+POTION Z_DRINK2 Z_DRINK1
+RING INT_12C1 INT_12C
+SCROLL INT_12Q1 INT_12Q
+SHIELD INT_12R1 INT_12R
+FOOD INT_12R1 INT_12R
+BULLET INT_12R1 INT_12R
+BOW INT_12R1 INT_12R
+DAGGER Z_DAGGR2 Z_DAGGR1
+MACE INT_12F1 INT_12F
+SLING INT_12R1 INT_12R
+SMSWORD Z_SWRD2 Z_SWRD1
+BGSWORD Z_SWRD2 Z_SWRD1
+HAMMER Z_WEPN2 Z_WEPN1
+MSTAR INT_12R1 INT_12R
+FLAIL INT_12R1 INT_12R
+DART INT_12R1 INT_12R
+AXE INT_12B1 INT_12B
+STAFF INT_12R1 INT_12R
+XBOW Z_NORD4 Z_NORD3
+FIST INT_12R1 INT_12R
+SPEAR INT_12R1 INT_12R
+POLEARM INT_12R1 INT_12R
+BOLT Z_NORD2 Z_NORD1
+CLOAK INT_12R1 INT_12R
+COIN Z_COIN2 Z_COIN1
+GEM Z_GEM2B Z_GEM2A
+WAND Z_GEM1B Z_GEM1A
+EYE INT_12L1 INT_12L
+BRACER Z_METAL8 Z_METAL7
+EARRING INT_12C1 INT_12C
+TATTOO INT_12K1 INT_12K
+LENS Z_METAL4 Z_METAL3
+TEETH INT_12P1 INT_12P
+*
+*
+*
+*
diff --git a/gemrb/override/pst/itemtype.2da b/gemrb/override/pst/itemtype.2da
new file mode 100644
index 0000000..3c0bce7
--- /dev/null
+++ b/gemrb/override/pst/itemtype.2da
@@ -0,0 +1,45 @@
+2DA V1.0
+0
+ HELMET ARMOR SHIELD HAND RING AMULET BELT BOOTS WEAPON QUIVER CLOAK QUICK SCROLL BAG POTION
+MISC 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+AMULET 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+ARMOR 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
+BELT 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BOOT 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
+ARROW 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+PET 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
+HELMET 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+KEY 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+POTION 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
+RING 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
+SCROLL 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0
+SHIELD 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+FOOD 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+BULLET 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+BOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DAGGER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MACE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SLING 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SMSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BGSWORD 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+HAMMER 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+MSTAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FLAIL 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+DART 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+AXE 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+STAFF 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+XBOW 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+FIST 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+SPEAR 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+POLEARM 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
+BOLT 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
+CLOAK 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
+COIN 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+GEM 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+WAND 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+EYE 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+BRACER 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
+EARRING 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0
+TATTOO 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
+LENS 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+TEETH 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
diff --git a/gemrb/override/pst/itemuse.2da b/gemrb/override/pst/itemuse.2da
new file mode 100644
index 0000000..9ca43d5
--- /dev/null
+++ b/gemrb/override/pst/itemuse.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+*
+ STAT FILE MCOL VCOL WHICH
+0 ALIGNMENT aligns 3 5 0
+1 FACTION factions -1 2 0
+2 CLASS classes 5 7 0
+3 SPECIFIC avslots -1 0 0
diff --git a/gemrb/override/pst/iterror.pro b/gemrb/override/pst/iterror.pro
new file mode 100644
index 0000000..ec48299
Binary files /dev/null and b/gemrb/override/pst/iterror.pro differ
diff --git a/gemrb/override/pst/kiss.pro b/gemrb/override/pst/kiss.pro
new file mode 100644
index 0000000..adfc682
Binary files /dev/null and b/gemrb/override/pst/kiss.pro differ
diff --git a/gemrb/override/pst/kitlist.2da b/gemrb/override/pst/kitlist.2da
new file mode 100644
index 0000000..865e21f
--- /dev/null
+++ b/gemrb/override/pst/kitlist.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ ROWNAME LOWER MIXED HELP ABILITIES PROFICIENCY UNUSABLE CLASS
+0 RESERVE * * * * * * *
diff --git a/gemrb/override/pst/knock.pro b/gemrb/override/pst/knock.pro
new file mode 100644
index 0000000..2a63ee7
Binary files /dev/null and b/gemrb/override/pst/knock.pro differ
diff --git a/gemrb/override/pst/lightb.pro b/gemrb/override/pst/lightb.pro
new file mode 100644
index 0000000..fa7f84a
Binary files /dev/null and b/gemrb/override/pst/lightb.pro differ
diff --git a/gemrb/override/pst/lightnin.spl b/gemrb/override/pst/lightnin.spl
new file mode 100644
index 0000000..d59e10c
Binary files /dev/null and b/gemrb/override/pst/lightnin.spl differ
diff --git a/gemrb/override/pst/lightsto.pro b/gemrb/override/pst/lightsto.pro
new file mode 100644
index 0000000..7b502b1
Binary files /dev/null and b/gemrb/override/pst/lightsto.pro differ
diff --git a/gemrb/override/pst/litany.pro b/gemrb/override/pst/litany.pro
new file mode 100644
index 0000000..cf45572
Binary files /dev/null and b/gemrb/override/pst/litany.pro differ
diff --git a/gemrb/override/pst/litany.spl b/gemrb/override/pst/litany.spl
new file mode 100644
index 0000000..6f1399a
Binary files /dev/null and b/gemrb/override/pst/litany.spl differ
diff --git a/gemrb/override/pst/lmiss.pro b/gemrb/override/pst/lmiss.pro
new file mode 100644
index 0000000..29fcf25
Binary files /dev/null and b/gemrb/override/pst/lmiss.pro differ
diff --git a/gemrb/override/pst/lstorm.2da b/gemrb/override/pst/lstorm.2da
new file mode 100644
index 0000000..b192dcf
--- /dev/null
+++ b/gemrb/override/pst/lstorm.2da
@@ -0,0 +1,15 @@
+2DA V1.0
+0
+ XPOS YPOS RESREF DELAY DURATION
+upper -100 -100 S064UPLT 0 40
+lower 100 100 S064LORT 0 30
+1 0 -200 S064BGLT 3 2
+2 120 -120 S064BGLT 5 2
+3 200 0 S064BGLT 7 2
+4 120 120 S064BGLT 9 2
+5 0 200 S064BGLT 11 2
+6 -120 120 S064BGLT 13 2
+7 -200 0 S064BGLT 15 2
+8 -120 -120 S064BGLT 17 2
+9 0 -200 S064BGLT 19 2
+center 0 0 S064HGLT 23 2
diff --git a/gemrb/override/pst/lstorm.pro b/gemrb/override/pst/lstorm.pro
new file mode 100644
index 0000000..9402d7f
Binary files /dev/null and b/gemrb/override/pst/lstorm.pro differ
diff --git a/gemrb/override/pst/lstorm2.pro b/gemrb/override/pst/lstorm2.pro
new file mode 100644
index 0000000..84d3f27
Binary files /dev/null and b/gemrb/override/pst/lstorm2.pro differ
diff --git a/gemrb/override/pst/lstorm2.spl b/gemrb/override/pst/lstorm2.spl
new file mode 100644
index 0000000..0712b24
Binary files /dev/null and b/gemrb/override/pst/lstorm2.spl differ
diff --git a/gemrb/override/pst/magicmis.pro b/gemrb/override/pst/magicmis.pro
new file mode 100644
index 0000000..33ceed1
Binary files /dev/null and b/gemrb/override/pst/magicmis.pro differ
diff --git a/gemrb/override/pst/mcannon.pro b/gemrb/override/pst/mcannon.pro
new file mode 100644
index 0000000..f12708b
Binary files /dev/null and b/gemrb/override/pst/mcannon.pro differ
diff --git a/gemrb/override/pst/mcannon.spl b/gemrb/override/pst/mcannon.spl
new file mode 100644
index 0000000..f10253e
Binary files /dev/null and b/gemrb/override/pst/mcannon.spl differ
diff --git a/gemrb/override/pst/mmiss.pro b/gemrb/override/pst/mmiss.pro
new file mode 100644
index 0000000..2910b69
Binary files /dev/null and b/gemrb/override/pst/mmiss.pro differ
diff --git a/gemrb/override/pst/mmiss2.pro b/gemrb/override/pst/mmiss2.pro
new file mode 100644
index 0000000..75e4b0b
Binary files /dev/null and b/gemrb/override/pst/mmiss2.pro differ
diff --git a/gemrb/override/pst/modal.2da b/gemrb/override/pst/modal.2da
new file mode 100644
index 0000000..fcea55e
--- /dev/null
+++ b/gemrb/override/pst/modal.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+*
+ SPELL ACTION STR_ON STR_OFF STR_FAIL AOESPELL
+NONE * * 0 0 0 0
+BARDSONG BARDSONG BattleSong -1 -1 -1 1
+DETECTTRAPS FINDTRAP FindTraps -1 -1 -1 0
+STEALTH SNEAK Hide 19291 19292 19290 0
+TURNUNDEAD TURN Turn -1 -1 -1 1
diff --git a/gemrb/override/pst/mpal256.bmp b/gemrb/override/pst/mpal256.bmp
new file mode 100644
index 0000000..9eec246
Binary files /dev/null and b/gemrb/override/pst/mpal256.bmp differ
diff --git a/gemrb/override/pst/mswarm.pro b/gemrb/override/pst/mswarm.pro
new file mode 100644
index 0000000..31e684a
Binary files /dev/null and b/gemrb/override/pst/mswarm.pro differ
diff --git a/gemrb/override/pst/mswarm.spl b/gemrb/override/pst/mswarm.spl
new file mode 100644
index 0000000..707a5c0
Binary files /dev/null and b/gemrb/override/pst/mswarm.spl differ
diff --git a/gemrb/override/pst/music.2da b/gemrb/override/pst/music.2da
new file mode 100644
index 0000000..b524047
--- /dev/null
+++ b/gemrb/override/pst/music.2da
@@ -0,0 +1,39 @@
+2DA V1.0
+*
+0 *
+1 *
+1 MAIN.mus
+2 CHAR_01.mus
+3 CHAR_02.mus
+4 CHAR_03.mus
+5 CHAR_04.mus
+6 CHAR_05.mus
+7 CHAR_06.mus
+8 CHAR_07.mus
+9 CHAR_09.mus
+10 CHAR_10.mus
+11 CHAR_01.mus
+12 CHAR_12.mus
+13 CHAR_13.mus
+14 CHAR_14.mus
+15 CHAR_15.mus
+16 MORT.mus
+17 SIG.mus
+18 SMOL.mus
+19 BONE.mus
+20 CIVIC.mus
+21 RAVE.mus
+22 MODR.mus
+23 CUR.mus
+24 BAAT.mus
+25 FORT.mus
+26 CHAR13b.mus
+27 BT1.mus
+28 BT2.mus
+29 BT3.mus
+30 BT4.mus
+31 BT5.mus
+32 BT5.mus
+33 END_01G.mus
+34 END_01B.mus
+35 END_02.mus
diff --git a/gemrb/override/pst/nbolt.pro b/gemrb/override/pst/nbolt.pro
new file mode 100644
index 0000000..db69c50
Binary files /dev/null and b/gemrb/override/pst/nbolt.pro differ
diff --git a/gemrb/override/pst/orb.spl b/gemrb/override/pst/orb.spl
new file mode 100644
index 0000000..7cc0e6f
Binary files /dev/null and b/gemrb/override/pst/orb.spl differ
diff --git a/gemrb/override/pst/orngtint.spl b/gemrb/override/pst/orngtint.spl
new file mode 100644
index 0000000..00f97a2
Binary files /dev/null and b/gemrb/override/pst/orngtint.spl differ
diff --git a/gemrb/override/pst/overlay.2da b/gemrb/override/pst/overlay.2da
new file mode 100644
index 0000000..d972ec7
--- /dev/null
+++ b/gemrb/override/pst/overlay.2da
@@ -0,0 +1,35 @@
+2DA V1.0
+*
+ VVC UNDER FLAGS
+SANCTUARY SANCTRY 0 1
+ENTANGLE WEBENTD 1 0
+WISP WISP 0 0
+SHIELDGLOBE SPSHIELD 0 1
+GREASE GREASED 1 0
+WEB WEBENTD 1 0
+MINORGLOBE MINORGLB 0 1
+GLOBE GOINVUC 0 1
+FLAMESHROUD SOFLAMC 0 0
+ANTIMAGIC AMSHELC 0 0
+RESILIENT ORSPHEC 0 0
+PROTFROMMISS PFNMISC 0 0
+CLOAKOFFEAR COFEARC 0 0
+ENTROPY ESHIELC 0 0
+FIREAURA FIAURAC 0 0
+FROSTAURA FRAURAC 0 0
+INSECT IPLAGUC 0 0
+STORMSHELL SSHELLC 0 0
+LATHANDER1 SOLATC1 0 0
+LATHANDER2 SOLATC2 1 0
+GLATHANDER1 GSOLAC1 0 0
+GLATHANDER2 GSOLAC2 1 0
+SEVENEYES1 SEYESC1 0 0
+SEVENEYES2 SEYESC2 1 0
+BOUNCE SPTURNI2 1 0
+BOUNCE2 SPTURNI 1 0
+FIRESHIELD1 FSHIRC1 0 0
+FIRESHIELD2 FSHIRC2 1 0
+ICESHIELD1 FSHIBC1 0 0
+ICESHIELD2 FSHIBC2 1 0
+TORTOISE TSHELLC 0 0
+DEATHARMOR DARMORC 0 0
diff --git a/gemrb/override/pst/pacify.pro b/gemrb/override/pst/pacify.pro
new file mode 100644
index 0000000..ec74078
Binary files /dev/null and b/gemrb/override/pst/pacify.pro differ
diff --git a/gemrb/override/pst/pacify.spl b/gemrb/override/pst/pacify.spl
new file mode 100644
index 0000000..3b61490
Binary files /dev/null and b/gemrb/override/pst/pacify.spl differ
diff --git a/gemrb/override/pst/pathfind.2da b/gemrb/override/pst/pathfind.2da
new file mode 100644
index 0000000..b331d3f
--- /dev/null
+++ b/gemrb/override/pst/pathfind.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+*
+ 0 1 2 3 4 5 6 7 8 9 a b c d e f
+PASSABLE 8 1 1 1 1 1 1 1 0 1 8 0 0 8 3 1
+COST 10 4
diff --git a/gemrb/override/pst/pdolls.2da b/gemrb/override/pst/pdolls.2da
new file mode 100644
index 0000000..9251a0e
--- /dev/null
+++ b/gemrb/override/pst/pdolls.2da
@@ -0,0 +1,18 @@
+2DA V1.0
+*
+ INVENTORY STATS FMENU
+0x02 IVPANN STPANN AMPANN
+0x0D IVPDKK STPDKK AMPDKK
+0x16 IVPFFG STPFFG AMPFFG
+0x24 IVPIGY STPIGY AMPIGY
+0x2E IVPMRT STPMRT AMPMRT
+0x2F IVPNOA STPNOA AMPNM1
+0x30 IVPNOC STPNOC AMPNM1
+0x31 IVPNOD STPNOD AMPNM1
+0x32 IVPNOF STPNOF AMPNM1
+0x33 IVPNOH STPNOH AMPNM1
+0x34 IVPNDM STPNDM AMPNDM
+0x47 IVPVHA STPVHA AMPVHA
+0x51 IVPNOS STPNOS AMPNM1
+0x52 IVPNOZ STPNOZ AMPNM1
+0x5D IVPNOM STPNOM AMPNM1
diff --git a/gemrb/override/pst/poo.spl b/gemrb/override/pst/poo.spl
new file mode 100644
index 0000000..d303b32
Binary files /dev/null and b/gemrb/override/pst/poo.spl differ
diff --git a/gemrb/override/pst/poone.pro b/gemrb/override/pst/poone.pro
new file mode 100644
index 0000000..966c027
Binary files /dev/null and b/gemrb/override/pst/poone.pro differ
diff --git a/gemrb/override/pst/pwb.spl b/gemrb/override/pst/pwb.spl
new file mode 100644
index 0000000..c1533f6
Binary files /dev/null and b/gemrb/override/pst/pwb.spl differ
diff --git a/gemrb/override/pst/pwblind.pro b/gemrb/override/pst/pwblind.pro
new file mode 100644
index 0000000..3482049
Binary files /dev/null and b/gemrb/override/pst/pwblind.pro differ
diff --git a/gemrb/override/pst/pwk.spl b/gemrb/override/pst/pwk.spl
new file mode 100644
index 0000000..dc27305
Binary files /dev/null and b/gemrb/override/pst/pwk.spl differ
diff --git a/gemrb/override/pst/pwkill.pro b/gemrb/override/pst/pwkill.pro
new file mode 100644
index 0000000..f48a5d7
Binary files /dev/null and b/gemrb/override/pst/pwkill.pro differ
diff --git a/gemrb/override/pst/races.2da b/gemrb/override/pst/races.2da
new file mode 100644
index 0000000..dff0ce2
--- /dev/null
+++ b/gemrb/override/pst/races.2da
@@ -0,0 +1,78 @@
+2DA V1.0
+*
+ NAME_REF
+HUMAN 19746
+AASIMAR 64669
+ABISHAI_BLACK 3259
+ABISHAI_GREEN 3263
+ABISHAI_RED 3282
+ANNAH 728
+BAATEZU *
+CAMPATUSKANELLA *
+CORNUGON 3281
+Cranium_RAT 3287
+DABUS 1735
+DAKKON 3124
+DEIONARRA 263
+DEVA 3274
+DRONE_BOSS *
+DRONE_HEAVY *
+DRONE_LIGHT *
+DRONE_MEDIUM *
+FALL_FROM_GRACE *
+FELL 3118
+GEHRELETH 3286
+GENSAI_AIR *
+GENSAI_FIRE *
+GENSAI_STONE *
+GENSAI_WATER *
+GHOST *
+GHOUL 3265
+GIANT_SKELETON 272
+GITHYANKI 3138
+GITHZERAI 1741
+GLABREZU 3283
+GRILLIG 3307
+GRONK 2629
+HARGRIMM *
+IGNUS 3126
+IRON_GOLEM 3147
+JAMES_BIRD
+LADY_OF_PAIN 3288
+LEMURE 3285
+LIM_LIM 3322
+LOTHAR 3220
+MANTUOK 3221
+MANY_AS_ONE 3225
+MODRON 3234
+MORTE 274
+NIGHT_HAG 3269
+NO_RACE *
+NORDOM 3266
+NUPPERIBO 3284
+PILLAR_OF_SKULLS *
+RESTLESS_SPIRIT 3240
+ROGUE_MODRON 2798
+SHADOW_MARK_1 *
+SHADOW_MARK_2 *
+SHADOW_MARK_3 *
+SKELETON 262
+SKELETON_GIANT 272
+SKELETON_PRIEST 3136
+SOHMIEN 3319
+STONE_GOLEM *
+SUCCUBUS 3135
+TANARRI 3135
+THOKOLA 3295
+TIEFLING 1737
+TRIGIT 3314
+TROCOPOTOCA *
+VARGOUILLE *
+VHAILOR 3254
+WERERAT 3231
+WERERAT_MANTUOK *
+WORM 3320
+ZOMBIE 265
+BARIAUR 9045
+UNKNOWN_RACE *
+NO_RACE *
diff --git a/gemrb/override/pst/randitem.2da b/gemrb/override/pst/randitem.2da
new file mode 100644
index 0000000..84776a7
--- /dev/null
+++ b/gemrb/override/pst/randitem.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+*
+ RESREF FURY_MODE
+GOLD COPPER
+RND 1 1
+RNDTRE RNDTREAS RNDTREAS
diff --git a/gemrb/override/pst/rdead.pro b/gemrb/override/pst/rdead.pro
new file mode 100644
index 0000000..8f1f1d6
Binary files /dev/null and b/gemrb/override/pst/rdead.pro differ
diff --git a/gemrb/override/pst/rdead.spl b/gemrb/override/pst/rdead.spl
new file mode 100644
index 0000000..337e5e2
Binary files /dev/null and b/gemrb/override/pst/rdead.spl differ
diff --git a/gemrb/override/pst/rndtreas.2da b/gemrb/override/pst/rndtreas.2da
new file mode 100644
index 0000000..1d764f9
--- /dev/null
+++ b/gemrb/override/pst/rndtreas.2da
@@ -0,0 +1,10 @@
+2DA V1.0
+*
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
+1 1*10 COPEAR BRORING BROBRA RUSTDAG CLOTCHRM DRATCHRM 1*10 BRORING 1*10 SILEAR SILRING SILBRA STILETTO HAMMER AX BLOOCHRM CRATCHRM 1*10 4*5
+2 1*10 COPEAR BRORING BROBRA RUSTDAG CLOTCHRM DRATCHRM 1*10 BRORING 1*10 SILEAR SILRING SILBRA STILETTO HAMMER AX BLOOCHRM CRATCHRM 1*10 4*5
+3 1*10 COPEAR BRORING BROBRA RUSTDAG CLOTCHRM DRATCHRM 1*10 BRORING 1*10 SILEAR SILRING SILBRA STILETTO HAMMER AX BLOOCHRM CRATCHRM 1*10 4*5
+4 4*5 STILETTO SILEAR SILRING SILBRA 4*5 AX SILRING CRATCHRM 5*100 GOLEAR GOLRING GOLBRA AXQUAL QSTLO GSKNIFE GSDAGGER HEARCHRM KNOTCHRM BONECHRM
+5 HEAVEN UMEI ZERO BELL AEGIS HEARCHRM 2*500 5*1000 RNDTRE04 RNDTRE04 * * * * * * * * * *
+6 NO_DROP NO_DROP NO_DROP NO_DROP ACLUE ACLUE AGOODY AGOODY BAGOCOIN BAGOCOIN MAGIGM MAGITM MIRIMAG MODMITE PORLENS RNDTRE07 RNDTRE07 * * *
+7 NO_DROP NO_DROP BOLT01 BOLT01 BOLT02 BOLT02 BOLT04 BOLT04 LENS09 LENS09 LENS10 LENS10 LENS11 LENS11 LENS13 LENS13 * * * *
diff --git a/gemrb/override/pst/rock.pro b/gemrb/override/pst/rock.pro
new file mode 100644
index 0000000..476b717
Binary files /dev/null and b/gemrb/override/pst/rock.pro differ
diff --git a/gemrb/override/pst/rotorm.pro b/gemrb/override/pst/rotorm.pro
new file mode 100644
index 0000000..46e4f62
Binary files /dev/null and b/gemrb/override/pst/rotorm.pro differ
diff --git a/gemrb/override/pst/rune.2da b/gemrb/override/pst/rune.2da
new file mode 100644
index 0000000..51273fc
--- /dev/null
+++ b/gemrb/override/pst/rune.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+*
+ XPOS YPOS RESREF DELAY DURATION
+0 0 0 S914RUNE 0 8
+1 -196 28 S914BGBL 4 4
+2 -16 62 S914BGBL 5 3
+3 24 -68 S914BGBL 6 2
+4 201 40 S914BGBL 7 1
+5 0 0 S914HGBL 8 2
diff --git a/gemrb/override/pst/rune.pro b/gemrb/override/pst/rune.pro
new file mode 100644
index 0000000..af7229e
Binary files /dev/null and b/gemrb/override/pst/rune.pro differ
diff --git a/gemrb/override/pst/rune.spl b/gemrb/override/pst/rune.spl
new file mode 100644
index 0000000..0aec895
Binary files /dev/null and b/gemrb/override/pst/rune.spl differ
diff --git a/gemrb/override/pst/s015hwav.vvc b/gemrb/override/pst/s015hwav.vvc
new file mode 100644
index 0000000..d3af060
Binary files /dev/null and b/gemrb/override/pst/s015hwav.vvc differ
diff --git a/gemrb/override/pst/s025melt.vvc b/gemrb/override/pst/s025melt.vvc
new file mode 100644
index 0000000..925e211
Binary files /dev/null and b/gemrb/override/pst/s025melt.vvc differ
diff --git a/gemrb/override/pst/s046ist2.vvc b/gemrb/override/pst/s046ist2.vvc
new file mode 100644
index 0000000..97a9d94
Binary files /dev/null and b/gemrb/override/pst/s046ist2.vvc differ
diff --git a/gemrb/override/pst/s046istm.vvc b/gemrb/override/pst/s046istm.vvc
new file mode 100644
index 0000000..463d0d6
Binary files /dev/null and b/gemrb/override/pst/s046istm.vvc differ
diff --git a/gemrb/override/pst/s052cone.vvc b/gemrb/override/pst/s052cone.vvc
new file mode 100644
index 0000000..961a4d3
Binary files /dev/null and b/gemrb/override/pst/s052cone.vvc differ
diff --git a/gemrb/override/pst/s056itrl.vvc b/gemrb/override/pst/s056itrl.vvc
new file mode 100644
index 0000000..b5ecebe
Binary files /dev/null and b/gemrb/override/pst/s056itrl.vvc differ
diff --git a/gemrb/override/pst/s061beam.vvc b/gemrb/override/pst/s061beam.vvc
new file mode 100644
index 0000000..b8acc14
Binary files /dev/null and b/gemrb/override/pst/s061beam.vvc differ
diff --git a/gemrb/override/pst/s064bglt.vvc b/gemrb/override/pst/s064bglt.vvc
new file mode 100644
index 0000000..1d83c0f
Binary files /dev/null and b/gemrb/override/pst/s064bglt.vvc differ
diff --git a/gemrb/override/pst/s064hglt.vvc b/gemrb/override/pst/s064hglt.vvc
new file mode 100644
index 0000000..548af6b
Binary files /dev/null and b/gemrb/override/pst/s064hglt.vvc differ
diff --git a/gemrb/override/pst/s064lort.vvc b/gemrb/override/pst/s064lort.vvc
new file mode 100644
index 0000000..ff2b779
Binary files /dev/null and b/gemrb/override/pst/s064lort.vvc differ
diff --git a/gemrb/override/pst/s064uplt.vvc b/gemrb/override/pst/s064uplt.vvc
new file mode 100644
index 0000000..f0915c4
Binary files /dev/null and b/gemrb/override/pst/s064uplt.vvc differ
diff --git a/gemrb/override/pst/s070boom.vvc b/gemrb/override/pst/s070boom.vvc
new file mode 100644
index 0000000..c42ae5d
Binary files /dev/null and b/gemrb/override/pst/s070boom.vvc differ
diff --git a/gemrb/override/pst/s075wdsh.vvc b/gemrb/override/pst/s075wdsh.vvc
new file mode 100644
index 0000000..4c88329
Binary files /dev/null and b/gemrb/override/pst/s075wdsh.vvc differ
diff --git a/gemrb/override/pst/s914bgbl.vvc b/gemrb/override/pst/s914bgbl.vvc
new file mode 100644
index 0000000..095df1b
Binary files /dev/null and b/gemrb/override/pst/s914bgbl.vvc differ
diff --git a/gemrb/override/pst/s914hgbl.vvc b/gemrb/override/pst/s914hgbl.vvc
new file mode 100644
index 0000000..f746f20
Binary files /dev/null and b/gemrb/override/pst/s914hgbl.vvc differ
diff --git a/gemrb/override/pst/s914rune.vvc b/gemrb/override/pst/s914rune.vvc
new file mode 100644
index 0000000..3308b9a
Binary files /dev/null and b/gemrb/override/pst/s914rune.vvc differ
diff --git a/gemrb/override/pst/savegame.2da b/gemrb/override/pst/savegame.2da
new file mode 100644
index 0000000..78673a2
--- /dev/null
+++ b/gemrb/override/pst/savegame.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+Auto-Save
+ SLOTNAME QSAVE
+0 Auto-Save 0
+1 Quick-Save 1
diff --git a/gemrb/override/pst/script.2da b/gemrb/override/pst/script.2da
new file mode 100644
index 0000000..c06bc9e
--- /dev/null
+++ b/gemrb/override/pst/script.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+0
+ DATA 1 2 3 4 5 6 7 8 9
+OBJECT_IDS_COUNT 9 ea faction team general race class specific gender align
+MAX_OBJECT_NESTING 5
+ADDITIONAL_RECT 1
+EXTRA_PARAMETERS_COUNT 0
+TRIGGER_POINT 1
diff --git a/gemrb/override/pst/shammer.pro b/gemrb/override/pst/shammer.pro
new file mode 100644
index 0000000..e555103
Binary files /dev/null and b/gemrb/override/pst/shammer.pro differ
diff --git a/gemrb/override/pst/shammer.spl b/gemrb/override/pst/shammer.spl
new file mode 100644
index 0000000..3242a38
Binary files /dev/null and b/gemrb/override/pst/shammer.spl differ
diff --git a/gemrb/override/pst/skmob.pro b/gemrb/override/pst/skmob.pro
new file mode 100644
index 0000000..55fb662
Binary files /dev/null and b/gemrb/override/pst/skmob.pro differ
diff --git a/gemrb/override/pst/skmob.spl b/gemrb/override/pst/skmob.spl
new file mode 100644
index 0000000..42bad05
Binary files /dev/null and b/gemrb/override/pst/skmob.spl differ
diff --git a/gemrb/override/pst/skmob2.pro b/gemrb/override/pst/skmob2.pro
new file mode 100644
index 0000000..1edcd14
Binary files /dev/null and b/gemrb/override/pst/skmob2.pro differ
diff --git a/gemrb/override/pst/sleep.pro b/gemrb/override/pst/sleep.pro
new file mode 100644
index 0000000..06568c9
Binary files /dev/null and b/gemrb/override/pst/sleep.pro differ
diff --git a/gemrb/override/pst/slots.ids b/gemrb/override/pst/slots.ids
new file mode 100644
index 0000000..8fa1c3b
--- /dev/null
+++ b/gemrb/override/pst/slots.ids
@@ -0,0 +1,50 @@
+0 SLOT_HAND
+1 SLOT_EAR_RIGHT
+2 SLOT_TATTOO_CLOAK
+3 SLOT_WRIST
+4 SLOT_RING_LEFT
+5 SLOT_TATTOO_LEFT
+6 SLOT_CHEST
+7 SLOT_EAR_LEFT
+7 SLOT_LENS_LEFT
+8 SLOT_RING_RIGHT
+9 SLOT_TATTOO_RIGHT
+10 SLOT_FIST
+11 SLOT_AMMO
+17 SLOT_MISC
+43 SLOT_WEAPON
+11 SLOT_AMMO0
+12 SLOT_AMMO1
+13 SLOT_AMMO2
+14 SLOT_AMMO3
+15 SLOT_AMMO4
+17 SLOT_MISC0
+18 SLOT_MISC1
+19 SLOT_MISC2
+20 SLOT_MISC3
+21 SLOT_MISC4
+22 SLOT_INV0
+23 SLOT_INV1
+24 SLOT_INV2
+25 SLOT_INV3
+26 SLOT_INV4
+27 SLOT_INV5
+28 SLOT_INV6
+29 SLOT_INV7
+30 SLOT_INV8
+31 SLOT_INV9
+32 SLOT_INV10
+33 SLOT_INV11
+34 SLOT_INV12
+35 SLOT_INV13
+36 SLOT_INV14
+37 SLOT_INV15
+38 SLOT_INV16
+39 SLOT_INV17
+40 SLOT_INV18
+41 SLOT_INV19
+42 SLOT_MAGIC
+43 SLOT_WEAPON0
+44 SLOT_WEAPON1
+45 SLOT_WEAPON2
+46 SLOT_WEAPON3
\ No newline at end of file
diff --git a/gemrb/override/pst/slottype.2da b/gemrb/override/pst/slottype.2da
new file mode 100644
index 0000000..49762d5
--- /dev/null
+++ b/gemrb/override/pst/slottype.2da
@@ -0,0 +1,58 @@
+2DA V1.0
+*
+ BITS SCRIPT ICON STRREF EFFECTS FLAGS
+10 0 -1 * 0 2 0
+7 1 5 IVPIEARL 31580 1 0
+6 2 6 IVPICHST 32798 1 0
+9 4 4 IVPITATT 32797 1 0
+0 8 3 IVPIHNDR 32801 1 0
+4 16 7 IVPIRING 4268 1 1
+8 16 2 IVPIRING 4269 1 1
+1 1 0 IVPIEARR 32857 1 0
+2 4 1 IVPITATT 32797 1 0
+3 64 8 IVPIWRST 32802 1 0
+43 256 10 IVQIWEAP 4261 4 1
+44 256 11 IVQIWEAP 4261 4 1
+45 256 12 IVQIWEAP 4261 4 1
+46 256 13 IVQIWEAP 4261 4 1
+11 512 19 IVQIARRW 4262 5 1
+12 512 20 IVQIARRW 4262 5 1
+13 512 21 IVQIARRW 4262 5 1
+14 512 22 IVQIARRW 4262 5 1
+15 512 23 IVQIARRW 4262 5 1
+16 0 -1 - - 5 0
+5 4 9 IVPITATT 32797 1 0
+17 2048 14 IVQIITM 4274 0 1
+18 2048 15 IVQIITM 4274 0 1
+19 2048 16 IVQIITM 4274 0 1
+20 2048 17 IVQIITM 4274 0 1
+21 2048 18 IVQIITM 4274 0 1
+22 -1 24 IVSLOT 4275 0 1
+23 -1 25 IVSLOT 4275 0 1
+24 -1 26 IVSLOT 4275 0 1
+25 -1 27 IVSLOT 4275 0 1
+26 -1 28 IVSLOT 4275 0 1
+27 -1 29 IVSLOT 4275 0 1
+28 -1 30 IVSLOT 4275 0 1
+29 -1 31 IVSLOT 4275 0 1
+30 -1 32 IVSLOT 4275 0 1
+31 -1 33 IVSLOT 4275 0 1
+32 -1 34 IVSLOT 4275 0 1
+33 -1 35 IVSLOT 4275 0 1
+34 -1 36 IVSLOT 4275 0 1
+35 -1 37 IVSLOT 4275 0 1
+36 -1 38 IVSLOT 4275 0 1
+37 -1 39 IVSLOT 4275 0 1
+38 -1 40 IVSLOT 4275 0 1
+39 -1 41 IVSLOT 4275 0 1
+40 -1 42 IVSLOT 4275 0 1
+41 -1 43 IVSLOT 4275 0 1
+42 0 -1 - - 3 0
+7 1 5 IVPILENS 32795 1 0
+1 1 0 IVPIEYER 32787 1 0
+43 256 10 IVPIBOW 32803 4 0
+44 256 11 IVPIBOW 32803 4 0
+45 256 12 IVPIBOW 32803 4 0
+46 256 13 IVPIBOW 32803 4 0
+43 256 10 IVPITETH 32799 4 0
+44 256 11 IVPITETH 32799 4 0
diff --git a/gemrb/override/pst/sparbknp.pro b/gemrb/override/pst/sparbknp.pro
new file mode 100644
index 0000000..aba7673
Binary files /dev/null and b/gemrb/override/pst/sparbknp.pro differ
diff --git a/gemrb/override/pst/sparbkpa.pro b/gemrb/override/pst/sparbkpa.pro
new file mode 100644
index 0000000..7a51eed
Binary files /dev/null and b/gemrb/override/pst/sparbkpa.pro differ
diff --git a/gemrb/override/pst/sparblnp.pro b/gemrb/override/pst/sparblnp.pro
new file mode 100644
index 0000000..f858276
Binary files /dev/null and b/gemrb/override/pst/sparblnp.pro differ
diff --git a/gemrb/override/pst/sparblpa.pro b/gemrb/override/pst/sparblpa.pro
new file mode 100644
index 0000000..4124a9d
Binary files /dev/null and b/gemrb/override/pst/sparblpa.pro differ
diff --git a/gemrb/override/pst/sparchnp.pro b/gemrb/override/pst/sparchnp.pro
new file mode 100644
index 0000000..7dd540e
Binary files /dev/null and b/gemrb/override/pst/sparchnp.pro differ
diff --git a/gemrb/override/pst/sparchpa.pro b/gemrb/override/pst/sparchpa.pro
new file mode 100644
index 0000000..101f9a2
Binary files /dev/null and b/gemrb/override/pst/sparchpa.pro differ
diff --git a/gemrb/override/pst/spargonp.pro b/gemrb/override/pst/spargonp.pro
new file mode 100644
index 0000000..234aca6
Binary files /dev/null and b/gemrb/override/pst/spargonp.pro differ
diff --git a/gemrb/override/pst/spargopa.pro b/gemrb/override/pst/spargopa.pro
new file mode 100644
index 0000000..2665825
Binary files /dev/null and b/gemrb/override/pst/spargopa.pro differ
diff --git a/gemrb/override/pst/spargrnp.pro b/gemrb/override/pst/spargrnp.pro
new file mode 100644
index 0000000..a1d4dbb
Binary files /dev/null and b/gemrb/override/pst/spargrnp.pro differ
diff --git a/gemrb/override/pst/spargrpa.pro b/gemrb/override/pst/spargrpa.pro
new file mode 100644
index 0000000..55a741b
Binary files /dev/null and b/gemrb/override/pst/spargrpa.pro differ
diff --git a/gemrb/override/pst/sparicnp.pro b/gemrb/override/pst/sparicnp.pro
new file mode 100644
index 0000000..77ff12b
Binary files /dev/null and b/gemrb/override/pst/sparicnp.pro differ
diff --git a/gemrb/override/pst/sparicpa.pro b/gemrb/override/pst/sparicpa.pro
new file mode 100644
index 0000000..2d8b9d7
Binary files /dev/null and b/gemrb/override/pst/sparicpa.pro differ
diff --git a/gemrb/override/pst/sparklbk.pro b/gemrb/override/pst/sparklbk.pro
new file mode 100644
index 0000000..147bd43
Binary files /dev/null and b/gemrb/override/pst/sparklbk.pro differ
diff --git a/gemrb/override/pst/sparklbl.pro b/gemrb/override/pst/sparklbl.pro
new file mode 100644
index 0000000..4928b0c
Binary files /dev/null and b/gemrb/override/pst/sparklbl.pro differ
diff --git a/gemrb/override/pst/sparklch.pro b/gemrb/override/pst/sparklch.pro
new file mode 100644
index 0000000..c6ad4c8
Binary files /dev/null and b/gemrb/override/pst/sparklch.pro differ
diff --git a/gemrb/override/pst/sparklgo.pro b/gemrb/override/pst/sparklgo.pro
new file mode 100644
index 0000000..748d91d
Binary files /dev/null and b/gemrb/override/pst/sparklgo.pro differ
diff --git a/gemrb/override/pst/sparklgr.pro b/gemrb/override/pst/sparklgr.pro
new file mode 100644
index 0000000..ac152c1
Binary files /dev/null and b/gemrb/override/pst/sparklgr.pro differ
diff --git a/gemrb/override/pst/sparklic.pro b/gemrb/override/pst/sparklic.pro
new file mode 100644
index 0000000..e833132
Binary files /dev/null and b/gemrb/override/pst/sparklic.pro differ
diff --git a/gemrb/override/pst/sparklma.pro b/gemrb/override/pst/sparklma.pro
new file mode 100644
index 0000000..50d7be0
Binary files /dev/null and b/gemrb/override/pst/sparklma.pro differ
diff --git a/gemrb/override/pst/sparklor.pro b/gemrb/override/pst/sparklor.pro
new file mode 100644
index 0000000..dc02305
Binary files /dev/null and b/gemrb/override/pst/sparklor.pro differ
diff --git a/gemrb/override/pst/sparklpu.pro b/gemrb/override/pst/sparklpu.pro
new file mode 100644
index 0000000..ef0f1fd
Binary files /dev/null and b/gemrb/override/pst/sparklpu.pro differ
diff --git a/gemrb/override/pst/sparklre.pro b/gemrb/override/pst/sparklre.pro
new file mode 100644
index 0000000..1a15bfd
Binary files /dev/null and b/gemrb/override/pst/sparklre.pro differ
diff --git a/gemrb/override/pst/sparklst.pro b/gemrb/override/pst/sparklst.pro
new file mode 100644
index 0000000..543ea91
Binary files /dev/null and b/gemrb/override/pst/sparklst.pro differ
diff --git a/gemrb/override/pst/sparmanp.pro b/gemrb/override/pst/sparmanp.pro
new file mode 100644
index 0000000..c76d137
Binary files /dev/null and b/gemrb/override/pst/sparmanp.pro differ
diff --git a/gemrb/override/pst/sparmapa.pro b/gemrb/override/pst/sparmapa.pro
new file mode 100644
index 0000000..6a21bd1
Binary files /dev/null and b/gemrb/override/pst/sparmapa.pro differ
diff --git a/gemrb/override/pst/sparornp.pro b/gemrb/override/pst/sparornp.pro
new file mode 100644
index 0000000..abf33b8
Binary files /dev/null and b/gemrb/override/pst/sparornp.pro differ
diff --git a/gemrb/override/pst/sparorpa.pro b/gemrb/override/pst/sparorpa.pro
new file mode 100644
index 0000000..686dd90
Binary files /dev/null and b/gemrb/override/pst/sparorpa.pro differ
diff --git a/gemrb/override/pst/sparpunp.pro b/gemrb/override/pst/sparpunp.pro
new file mode 100644
index 0000000..23f98f2
Binary files /dev/null and b/gemrb/override/pst/sparpunp.pro differ
diff --git a/gemrb/override/pst/sparpupa.pro b/gemrb/override/pst/sparpupa.pro
new file mode 100644
index 0000000..322a995
Binary files /dev/null and b/gemrb/override/pst/sparpupa.pro differ
diff --git a/gemrb/override/pst/sparrenp.pro b/gemrb/override/pst/sparrenp.pro
new file mode 100644
index 0000000..7718874
Binary files /dev/null and b/gemrb/override/pst/sparrenp.pro differ
diff --git a/gemrb/override/pst/sparrepa.pro b/gemrb/override/pst/sparrepa.pro
new file mode 100644
index 0000000..a5edc18
Binary files /dev/null and b/gemrb/override/pst/sparrepa.pro differ
diff --git a/gemrb/override/pst/sparstnp.pro b/gemrb/override/pst/sparstnp.pro
new file mode 100644
index 0000000..7d4b4c3
Binary files /dev/null and b/gemrb/override/pst/sparstnp.pro differ
diff --git a/gemrb/override/pst/sparstpa.pro b/gemrb/override/pst/sparstpa.pro
new file mode 100644
index 0000000..4d55459
Binary files /dev/null and b/gemrb/override/pst/sparstpa.pro differ
diff --git a/gemrb/override/pst/speak.eff b/gemrb/override/pst/speak.eff
new file mode 100644
index 0000000..7b9cf74
Binary files /dev/null and b/gemrb/override/pst/speak.eff differ
diff --git a/gemrb/override/pst/spear.pro b/gemrb/override/pst/spear.pro
new file mode 100644
index 0000000..bc9288c
Binary files /dev/null and b/gemrb/override/pst/spear.pro differ
diff --git a/gemrb/override/pst/spearex.pro b/gemrb/override/pst/spearex.pro
new file mode 100644
index 0000000..4bb5786
Binary files /dev/null and b/gemrb/override/pst/spearex.pro differ
diff --git a/gemrb/override/pst/spfirebl.pro b/gemrb/override/pst/spfirebl.pro
new file mode 100644
index 0000000..3b44f50
Binary files /dev/null and b/gemrb/override/pst/spfirebl.pro differ
diff --git a/gemrb/override/pst/spklarbk.pro b/gemrb/override/pst/spklarbk.pro
new file mode 100644
index 0000000..f8f12c0
Binary files /dev/null and b/gemrb/override/pst/spklarbk.pro differ
diff --git a/gemrb/override/pst/spklarbl.pro b/gemrb/override/pst/spklarbl.pro
new file mode 100644
index 0000000..eddae9d
Binary files /dev/null and b/gemrb/override/pst/spklarbl.pro differ
diff --git a/gemrb/override/pst/spklarch.pro b/gemrb/override/pst/spklarch.pro
new file mode 100644
index 0000000..82766f4
Binary files /dev/null and b/gemrb/override/pst/spklarch.pro differ
diff --git a/gemrb/override/pst/spklargo.pro b/gemrb/override/pst/spklargo.pro
new file mode 100644
index 0000000..e4323ac
Binary files /dev/null and b/gemrb/override/pst/spklargo.pro differ
diff --git a/gemrb/override/pst/spklargr.pro b/gemrb/override/pst/spklargr.pro
new file mode 100644
index 0000000..38fc137
Binary files /dev/null and b/gemrb/override/pst/spklargr.pro differ
diff --git a/gemrb/override/pst/spklaric.pro b/gemrb/override/pst/spklaric.pro
new file mode 100644
index 0000000..4fd7d7e
Binary files /dev/null and b/gemrb/override/pst/spklaric.pro differ
diff --git a/gemrb/override/pst/spklarma.pro b/gemrb/override/pst/spklarma.pro
new file mode 100644
index 0000000..d8f18c2
Binary files /dev/null and b/gemrb/override/pst/spklarma.pro differ
diff --git a/gemrb/override/pst/spklaror.pro b/gemrb/override/pst/spklaror.pro
new file mode 100644
index 0000000..7e395f4
Binary files /dev/null and b/gemrb/override/pst/spklaror.pro differ
diff --git a/gemrb/override/pst/spklarpu.pro b/gemrb/override/pst/spklarpu.pro
new file mode 100644
index 0000000..e70bb75
Binary files /dev/null and b/gemrb/override/pst/spklarpu.pro differ
diff --git a/gemrb/override/pst/spklarre.pro b/gemrb/override/pst/spklarre.pro
new file mode 100644
index 0000000..29d636c
Binary files /dev/null and b/gemrb/override/pst/spklarre.pro differ
diff --git a/gemrb/override/pst/spklarst.pro b/gemrb/override/pst/spklarst.pro
new file mode 100644
index 0000000..ba5633b
Binary files /dev/null and b/gemrb/override/pst/spklarst.pro differ
diff --git a/gemrb/override/pst/splspec.2da b/gemrb/override/pst/splspec.2da
new file mode 100644
index 0000000..515a410
--- /dev/null
+++ b/gemrb/override/pst/splspec.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+*
+ IDENTIFY_SILENCE_DEAD
+SPWI105 1
+SPIN103 4
+SPIN108 4
+SPPR502 4
diff --git a/gemrb/override/pst/spscorch.pro b/gemrb/override/pst/spscorch.pro
new file mode 100644
index 0000000..3ad6c32
Binary files /dev/null and b/gemrb/override/pst/spscorch.pro differ
diff --git a/gemrb/override/pst/spscoric.pro b/gemrb/override/pst/spscoric.pro
new file mode 100644
index 0000000..3d7e0b7
Binary files /dev/null and b/gemrb/override/pst/spscoric.pro differ
diff --git a/gemrb/override/pst/start.2da b/gemrb/override/pst/start.2da
new file mode 100644
index 0000000..1805aab
--- /dev/null
+++ b/gemrb/override/pst/start.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ XPOS YPOS AREA ROT
+NORMAL START_XPOS START_YPOS START_AREA *
diff --git a/gemrb/override/pst/states.2da b/gemrb/override/pst/states.2da
new file mode 100644
index 0000000..cb10988
--- /dev/null
+++ b/gemrb/override/pst/states.2da
@@ -0,0 +1,36 @@
+2DA V1.0
+*
+ NAME_REF
+0 67220
+1 59824
+2 59825
+4 59826
+8 59830
+16 59831
+32 59832
+64 *
+128 *
+256 59833
+512 *
+1024 *
+2048 *
+4096 59834
+8192 59835
+16384 59836
+32768 59837
+65536 59838
+131072 59839
+262144 59840
+524288 59841
+1048576 59842
+2097152 59843
+4194304 59844
+8388608 59845
+16777216 59846
+33554432 59847
+67108864 59848
+134217728 59849
+268435456 59850
+536870912 59851
+1073741824 59852
+2147483648 59853
\ No newline at end of file
diff --git a/gemrb/override/pst/stone.pro b/gemrb/override/pst/stone.pro
new file mode 100644
index 0000000..13ce3b0
Binary files /dev/null and b/gemrb/override/pst/stone.pro differ
diff --git a/gemrb/override/pst/stories.pro b/gemrb/override/pst/stories.pro
new file mode 100644
index 0000000..698c287
Binary files /dev/null and b/gemrb/override/pst/stories.pro differ
diff --git a/gemrb/override/pst/stories.spl b/gemrb/override/pst/stories.spl
new file mode 100644
index 0000000..56ef6b7
Binary files /dev/null and b/gemrb/override/pst/stories.spl differ
diff --git a/gemrb/override/pst/str.spl b/gemrb/override/pst/str.spl
new file mode 100644
index 0000000..046d3e2
Binary files /dev/null and b/gemrb/override/pst/str.spl differ
diff --git a/gemrb/override/pst/strength.pro b/gemrb/override/pst/strength.pro
new file mode 100644
index 0000000..eb42785
Binary files /dev/null and b/gemrb/override/pst/strength.pro differ
diff --git a/gemrb/override/pst/strings.2da b/gemrb/override/pst/strings.2da
new file mode 100644
index 0000000..c42108a
--- /dev/null
+++ b/gemrb/override/pst/strings.2da
@@ -0,0 +1,170 @@
+2DA V1.0
+-1
+ STRREF
+SCATTERED 55021
+WHOLEPARTY -1
+DOORLOCKED 19309
+MAGICTRAP 19289
+NORMALTRAP 19289
+TRAP -1
+CANNOTGO -1
+TRAPREMOVED 19287
+OVERSTOCKED 19283
+SLEEP -1
+AMBUSH -1
+CONTLOCKED 22031
+NOMONEY -1
+CURSED 50538
+SPELLDISRUPT -1
+DIED -1
+MAYNOTREST 34599
+CANTRESTMONS 34600
+CANTSAVEMONS 34598
+CANTSAVE 34592
+NODIALOG -1
+CANTSAVEDIALOG 34593
+CANTSAVEDIALOG2 -1
+CANTSAVEMOVIE 34594
+TARGETBUSY -1
+CANTTALKTRANS -1
+GOTGOLD 19252
+LOSTGOLD 19253
+GOTXP 19254
+LOSTXP 19255
+GOTITEM 19256
+LOSTITEM 19258
+GOTREP 19259
+LOSTREP 19260
+GOTABILITY 19307
+GOTSPELL -1
+GOTSONG -1
+NOTHINGTOSAY -1
+JOURNALCHANGE 37096
+WORLDMAPCHANGE 19264
+PAUSED 39436
+UNPAUSED -1
+SCRIPTPAUSED 39436
+AP_UNUSABLE 39436
+AP_ATTACKED 39436
+AP_HIT 39436
+AP_WOUNDED 39436
+AP_DEAD 39436
+AP_NOTARGET 39436
+AP_ENDROUND 39436
+AP_ENEMY 39436
+AP_TRAP 39436
+AP_SPELLCAST 39436
+AP_RESERVED1 39436
+AP_RESERVED2 39436
+AP_RESERVED3 39436
+AP_RESERVED4 39436
+CHARMED -1
+DIRECHARMED -1
+CONTROLLED -1
+EVIL -1
+GNE_NEUTRAL -1
+GOOD -1
+STR_LAWFUL -1
+LNC_NEUTRAL -1
+CHAOTIC -1
+ACTION_CAST -1
+ACTION_ATTACK -1
+ACTION_TURN -1
+ACTION_SONG -1
+ACTION_FINDTRAP -1
+MAGICWEAPON -1
+OFFHAND_USED -1
+TWOHANDED_USED -1
+CANNOT_USE_ITEM 50532
+CANT_DROP_ITEM 50569
+NOT_IN_OFFHAND -1
+ITEM_IS_CURSED 50538
+NO_CRITICAL 19273
+TRACKING -1
+TRACKINGFAILED -1
+DOOR_NOPICK 23169
+CONT_NOPICK 23169
+CANTSAVECOMBAT -1
+CANTSAVENOCTRL -1
+LOCKPICK_DONE 19281
+LOCKPICK_FAILED 19282
+STATIC_DISSIPATE -1
+LIGHTNING_DISSIPATE -1
+HAS_NO_ABILITY 50545
+NEEDS_IDENTIFY 50544
+WRONG_ITEMTYPE 50537
+HAS_ITEMEXCL -1
+PICKPOCKET_DONE -1
+PICKPOCKET_NONE 19304
+PICKPOCKET_FAIL 19303
+PICKPOCKET_EVIL 19302
+PICKPOCKET_ARMOR 19301
+USING_FEAT -1
+STOPPED_FEAT -1
+DISARM_DONE 16520
+DISARM_FAIL 1608
+DOORBASH_DONE -1
+DOORBASH_FAIL -1
+CONTBASH_DONE -1
+CONTBASH_FAIL -1
+MAYNOTSETTRAP -1
+SNAREFAILED -1
+SNARESUCCEED -1
+NOMORETRAP -1
+DISABLEDMAGE -1
+SAVESUCCEED 28646
+QSAVESUCCEED 50461
+UNINJURED -1
+INJURED1 -1
+INJURED2 -1
+INJURED3 -1
+INJURED4 -1
+HOURS -1
+HOUR -1
+DAYS -1
+DAY -1
+REST -1
+JOURNEY -1
+PST_REST 19262
+PST_HOUR 19312
+PST_HOURS 19313
+DAMAGE_IMMUNITY -1
+DAMAGE_STR1 -1
+DAMAGE_STR2 -1
+DAMAGE_STR3 -1
+DMG_POISON -1
+DMG_MAGIC -1
+DMG_MISSILE -1
+DMG_SLASHING -1
+DMG_PIERCING -1
+DMG_CRUSHING -1
+DMG_FIRE -1
+DMG_ELECTRIC -1
+DMG_COLD -1
+DMG_ACID -1
+DMG_OTHER -1
+GOTQUESTXP -1
+LEVELUP 4246
+INVFULL_ITEMDROP 37079
+CONT_DUP -1
+CONT_TRIG -1
+CONT_FAIL -1
+SEQ_DUP -1
+CRITICAL_HIT 19272
+CRITICAL_MISS 19274
+DEATH -1
+BACKSTAB 12128
+BACKSTAB_BAD 19271
+BACKSTAB_FAIL -1
+CASTER_LVL_INC -1
+CASTER_LVL_DEC -1
+CHARS_EXPORTED -1
+PALADIN_FALL -1
+RANGER_FALL -1
+RES_RESISTED -1
+DEADMAGIC_FAIL -1
+MISCASTMAGIC -1
+WILDSURGE -1
+FAMBLOCK -1
+FAMPROTAGONIST -1
+
diff --git a/gemrb/override/pst/tlaugh.pro b/gemrb/override/pst/tlaugh.pro
new file mode 100644
index 0000000..cd6596a
Binary files /dev/null and b/gemrb/override/pst/tlaugh.pro differ
diff --git a/gemrb/override/pst/trapglyp.pro b/gemrb/override/pst/trapglyp.pro
new file mode 100644
index 0000000..5542681
Binary files /dev/null and b/gemrb/override/pst/trapglyp.pro differ
diff --git a/gemrb/override/pst/trapskul.pro b/gemrb/override/pst/trapskul.pro
new file mode 100644
index 0000000..5706fec
Binary files /dev/null and b/gemrb/override/pst/trapskul.pro differ
diff --git a/gemrb/override/pst/vtouch.pro b/gemrb/override/pst/vtouch.pro
new file mode 100644
index 0000000..34760b0
Binary files /dev/null and b/gemrb/override/pst/vtouch.pro differ
diff --git a/gemrb/override/pst/weapprof.2da b/gemrb/override/pst/weapprof.2da
new file mode 100644
index 0000000..616555b
--- /dev/null
+++ b/gemrb/override/pst/weapprof.2da
@@ -0,0 +1,9 @@
+2DA V1.0
+ ID NAME_REF DESC_REF NAMELESS ANNAH MORTE NORDOM DAKKON GRACE IGNUS VHAILOR
+FIST 0 64190 * 1 1 1 0 0 1 1 0
+DAGGER 1 64187 * 1 0 0 0 1 0 0 0
+HAMMER 2 * * 1 0 0 0 0 0 0 0
+AXE 3 64623 * 1 0 0 0 0 0 0 1
+CLUB 4 * * 1 0 0 0 0 0 0 0
+BOW 5 64180 * 0 0 0 1 0 0 0 0
+
diff --git a/gemrb/override/pst/web.pro b/gemrb/override/pst/web.pro
new file mode 100644
index 0000000..6b6436a
Binary files /dev/null and b/gemrb/override/pst/web.pro differ
diff --git a/gemrb/override/pst/wmmos2b.mos b/gemrb/override/pst/wmmos2b.mos
new file mode 100644
index 0000000..3dc7526
Binary files /dev/null and b/gemrb/override/pst/wmmos2b.mos differ
diff --git a/gemrb/override/shared/CMakeLists.txt b/gemrb/override/shared/CMakeLists.txt
new file mode 100644
index 0000000..e954707
--- /dev/null
+++ b/gemrb/override/shared/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_OVERRIDE (shared)
diff --git a/gemrb/override/shared/Makefile.am b/gemrb/override/shared/Makefile.am
new file mode 100644
index 0000000..752a81a
--- /dev/null
+++ b/gemrb/override/shared/Makefile.am
@@ -0,0 +1,3 @@
+sharedoverride_DATA = *.2da *.ids *.spl *.pro
+sharedoverridedir = $(moddir)/override/shared/
+EXTRA_DIST = *.2da *.ids *.spl *.pro
diff --git a/gemrb/override/shared/avprefg.2da b/gemrb/override/shared/avprefg.2da
new file mode 100644
index 0000000..9135648
--- /dev/null
+++ b/gemrb/override/shared/avprefg.2da
@@ -0,0 +1,6 @@
+2DA V1.0
+*
+ GENDER
+TYPE 35
+MALE 0
+FEMALE 0x10
diff --git a/gemrb/override/shared/avprefix.2da b/gemrb/override/shared/avprefix.2da
new file mode 100644
index 0000000..1514535
--- /dev/null
+++ b/gemrb/override/shared/avprefix.2da
@@ -0,0 +1,7 @@
+2DA V1.0
+*
+ RESOURCE
+0 0x6000
+1 avprefr
+2 avprefg
+3 avprefc
diff --git a/gemrb/override/shared/axeflm.pro b/gemrb/override/shared/axeflm.pro
new file mode 100644
index 0000000..f81d544
Binary files /dev/null and b/gemrb/override/shared/axeflm.pro differ
diff --git a/gemrb/override/shared/bardsong.spl b/gemrb/override/shared/bardsong.spl
new file mode 100644
index 0000000..38cd8f2
Binary files /dev/null and b/gemrb/override/shared/bardsong.spl differ
diff --git a/gemrb/override/shared/containr.2da b/gemrb/override/shared/containr.2da
new file mode 100644
index 0000000..fd0012d
--- /dev/null
+++ b/gemrb/override/shared/containr.2da
@@ -0,0 +1,16 @@
+2DA V1.0
+*
+ SOUND BAM CLOSE
+* * *
+BAG GAM_12A1 CONTSACK GAM_12A
+CHEST AMB_D05A CONTCHST AMB_D05B
+DRAWER AMB_D05A CONTDRWR AMB_D05B
+PILE AMB_D18 CONTGRND *
+TABLE AMB_D08 CONTTABL *
+SHELF AMB_D07 CONTSHLF *
+ALTAR AMB_D07 CONTALTR *
+NONVISIBLE AMB_D18 * *
+SPELLBOOK GAM_06 CONTBOOK GAM_05
+BODY AMB_D08G CONTBODY *
+BARREL AMB_D12 CONTBARL AMB_D13
+CRATE AMB_D05A CONTCRAT AMB_D05B
diff --git a/gemrb/override/shared/cow.pro b/gemrb/override/shared/cow.pro
new file mode 100644
index 0000000..3c91f27
Binary files /dev/null and b/gemrb/override/shared/cow.pro differ
diff --git a/gemrb/override/shared/detect.spl b/gemrb/override/shared/detect.spl
new file mode 100644
index 0000000..ca5350a
Binary files /dev/null and b/gemrb/override/shared/detect.spl differ
diff --git a/gemrb/override/shared/dmgtypes.2da b/gemrb/override/shared/dmgtypes.2da
new file mode 100644
index 0000000..0d7516a
--- /dev/null
+++ b/gemrb/override/shared/dmgtypes.2da
@@ -0,0 +1,22 @@
+2DA V1.0
+-1
+ LOWER RESIST_STAT VALUE IWDMOD
+DAMAGE_CRUSHING 136 RESISTCRUSHING 0 9
+DAMAGE_ACID 140 RESISTACID 1 4
+DAMAGE_COLD 139 RESISTCOLD 2 2
+DAMAGE_ELECTRICITY 138 RESISTELECTRICITY 4 3
+DAMAGE_FIRE 137 RESISTFIRE 8 1
+DAMAGE_PIERCING 135 RESISTPIERCING 0x10 8
+DAMAGE_POISON 131 RESISTPOISON 0x20 6
+DAMAGE_MAGIC 132 MAGICDAMAGERESISTANCE 0x40 5
+DAMAGE_MISSILE 133 RESISTMISSILE 0x80 10
+DAMAGE_SLASHING 134 RESISTSLASHING 0x100 7
+DAMAGE_MAGICFIRE 137 RESISTMAGICFIRE 0x200 -1
+DAMAGE_PIERCINGMISSILE 133 RESISTMISSILE 0x80 -1
+DAMAGE_MAGICCOLD 139 RESISTMAGICCOLD 0x400 -1
+DAMAGE_CRUSHINGMISSILE 133 RESISTMISSILE 0x80 -1
+DAMAGE_STUNNING 141 0 0x800 -1
+DAMAGE_SOULEATER 132 0 0x1000 -1
+DAMAGE_DISEASE 141 0 0x4000 -1
+DAMAGE_CHUNKING 141 0 0x8000 -1
+
diff --git a/gemrb/override/shared/dummy.spl b/gemrb/override/shared/dummy.spl
new file mode 100644
index 0000000..384d0c4
Binary files /dev/null and b/gemrb/override/shared/dummy.spl differ
diff --git a/gemrb/override/shared/findtrap.spl b/gemrb/override/shared/findtrap.spl
new file mode 100644
index 0000000..19c1423
Binary files /dev/null and b/gemrb/override/shared/findtrap.spl differ
diff --git a/gemrb/override/shared/flmstrk.pro b/gemrb/override/shared/flmstrk.pro
new file mode 100644
index 0000000..ab09af3
Binary files /dev/null and b/gemrb/override/shared/flmstrk.pro differ
diff --git a/gemrb/override/shared/gemact.ids b/gemrb/override/shared/gemact.ids
new file mode 100644
index 0000000..b23f6b2
--- /dev/null
+++ b/gemrb/override/shared/gemact.ids
@@ -0,0 +1,10 @@
+IDS
+399 ApplySpellPoint(P:Target*,I:Spell*Spell)
+399 ApplySpellPointRES(P:Target*,S:SpellName*)
+398 ChangeDestination(O:Object*,S:Destination*)
+397 UnmakeGlobal(O:Object*)
+396 SetToken2DA(S:ResRef*)
+395 StartDialogOverride(S:DialogFile*,O:Target*,I:Unused,I:Unused,I:ConverseAsItem)
+394 SetTrackString(I:StrRef, I:Flags, I:Difficulty)
+148 BashDoor(O:Object)
+93 LeaveAreaName(O:Target*)
diff --git a/gemrb/override/shared/invtrav.pro b/gemrb/override/shared/invtrav.pro
new file mode 100644
index 0000000..e954182
Binary files /dev/null and b/gemrb/override/shared/invtrav.pro differ
diff --git a/gemrb/override/shared/itemspec.2da b/gemrb/override/shared/itemspec.2da
new file mode 100644
index 0000000..6872708
--- /dev/null
+++ b/gemrb/override/shared/itemspec.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ IDENTIFY
+SCRL75 1
diff --git a/gemrb/override/shared/lightbnb.pro b/gemrb/override/shared/lightbnb.pro
new file mode 100644
index 0000000..0372658
Binary files /dev/null and b/gemrb/override/shared/lightbnb.pro differ
diff --git a/gemrb/override/shared/modal.2da b/gemrb/override/shared/modal.2da
new file mode 100644
index 0000000..cbecc8d
--- /dev/null
+++ b/gemrb/override/shared/modal.2da
@@ -0,0 +1,8 @@
+2DA V1.0
+*
+ SPELL ACTION STR_ON STR_OFF STR_FAIL AOESPELL
+NONE * * 0 0 0 0
+BARDSONG BARDSONG BattleSong -1 -1 -1 1
+DETECTTRAPS FINDTRAP FindTraps -1 -1 -1 0
+STEALTH SNEAK Hide 19944 4188 17120 0
+TURNUNDEAD TURN Turn -1 -1 -1 1
diff --git a/gemrb/override/shared/panic.spl b/gemrb/override/shared/panic.spl
new file mode 100644
index 0000000..e875f92
Binary files /dev/null and b/gemrb/override/shared/panic.spl differ
diff --git a/gemrb/override/shared/polystat.2da b/gemrb/override/shared/polystat.2da
new file mode 100644
index 0000000..f9e2ef0
--- /dev/null
+++ b/gemrb/override/shared/polystat.2da
@@ -0,0 +1,27 @@
+2DA V1.0
+-1
+ STATS
+0 ARMORCLASS
+1 ACCRUSHINGMOD
+2 ACMISSILEMOD
+3 ACPIERCINGMOD
+4 ACSLASHINGMOD
+5 NUMBEROFATTACKS
+6 RESISTFIRE
+7 RESISTCOLD
+8 RESISTELECTRICITY
+9 RESISTACID
+10 RESISTMAGIC
+11 RESISTMAGICFIRE
+12 RESISTMAGICCOLD
+13 RESISTSLASHING
+14 RESISTCRUSHING
+15 RESISTPIERCING
+16 RESISTMISSILE
+17 STR
+18 STREXTRA
+19 DEX
+20 CON
+21 MAGICDAMAGERESISTANCE
+22 RESISTPOISON
+23 ANIMATIONID
diff --git a/gemrb/override/shared/redholy.pro b/gemrb/override/shared/redholy.pro
new file mode 100644
index 0000000..35b9c88
Binary files /dev/null and b/gemrb/override/shared/redholy.pro differ
diff --git a/gemrb/override/shared/sanctuc.vvc b/gemrb/override/shared/sanctuc.vvc
new file mode 100644
index 0000000..b5ccce5
Binary files /dev/null and b/gemrb/override/shared/sanctuc.vvc differ
diff --git a/gemrb/override/shared/shair.pro b/gemrb/override/shared/shair.pro
new file mode 100644
index 0000000..9023963
Binary files /dev/null and b/gemrb/override/shared/shair.pro differ
diff --git a/gemrb/override/shared/shair1.pro b/gemrb/override/shared/shair1.pro
new file mode 100644
index 0000000..b711b2f
Binary files /dev/null and b/gemrb/override/shared/shair1.pro differ
diff --git a/gemrb/override/shared/shair2.pro b/gemrb/override/shared/shair2.pro
new file mode 100644
index 0000000..5c7d9c1
Binary files /dev/null and b/gemrb/override/shared/shair2.pro differ
diff --git a/gemrb/override/shared/shair3.pro b/gemrb/override/shared/shair3.pro
new file mode 100644
index 0000000..1ef6b08
Binary files /dev/null and b/gemrb/override/shared/shair3.pro differ
diff --git a/gemrb/override/shared/shair4.pro b/gemrb/override/shared/shair4.pro
new file mode 100644
index 0000000..435cb0b
Binary files /dev/null and b/gemrb/override/shared/shair4.pro differ
diff --git a/gemrb/override/shared/shair5.pro b/gemrb/override/shared/shair5.pro
new file mode 100644
index 0000000..f0d7cf5
Binary files /dev/null and b/gemrb/override/shared/shair5.pro differ
diff --git a/gemrb/override/shared/shair6.pro b/gemrb/override/shared/shair6.pro
new file mode 100644
index 0000000..b77879d
Binary files /dev/null and b/gemrb/override/shared/shair6.pro differ
diff --git a/gemrb/override/shared/shair7.pro b/gemrb/override/shared/shair7.pro
new file mode 100644
index 0000000..1591bac
Binary files /dev/null and b/gemrb/override/shared/shair7.pro differ
diff --git a/gemrb/override/shared/sharea.pro b/gemrb/override/shared/sharea.pro
new file mode 100644
index 0000000..08d0a65
Binary files /dev/null and b/gemrb/override/shared/sharea.pro differ
diff --git a/gemrb/override/shared/sharea1.pro b/gemrb/override/shared/sharea1.pro
new file mode 100644
index 0000000..9d1aece
Binary files /dev/null and b/gemrb/override/shared/sharea1.pro differ
diff --git a/gemrb/override/shared/sharea2.pro b/gemrb/override/shared/sharea2.pro
new file mode 100644
index 0000000..f99393a
Binary files /dev/null and b/gemrb/override/shared/sharea2.pro differ
diff --git a/gemrb/override/shared/sharea3.pro b/gemrb/override/shared/sharea3.pro
new file mode 100644
index 0000000..00ada77
Binary files /dev/null and b/gemrb/override/shared/sharea3.pro differ
diff --git a/gemrb/override/shared/sharea4.pro b/gemrb/override/shared/sharea4.pro
new file mode 100644
index 0000000..2b07f7e
Binary files /dev/null and b/gemrb/override/shared/sharea4.pro differ
diff --git a/gemrb/override/shared/sharea5.pro b/gemrb/override/shared/sharea5.pro
new file mode 100644
index 0000000..8ad509a
Binary files /dev/null and b/gemrb/override/shared/sharea5.pro differ
diff --git a/gemrb/override/shared/sharea6.pro b/gemrb/override/shared/sharea6.pro
new file mode 100644
index 0000000..603906c
Binary files /dev/null and b/gemrb/override/shared/sharea6.pro differ
diff --git a/gemrb/override/shared/shearth.pro b/gemrb/override/shared/shearth.pro
new file mode 100644
index 0000000..4707876
Binary files /dev/null and b/gemrb/override/shared/shearth.pro differ
diff --git a/gemrb/override/shared/shearth1.pro b/gemrb/override/shared/shearth1.pro
new file mode 100644
index 0000000..7262ca8
Binary files /dev/null and b/gemrb/override/shared/shearth1.pro differ
diff --git a/gemrb/override/shared/shearth2.pro b/gemrb/override/shared/shearth2.pro
new file mode 100644
index 0000000..c2b17c7
Binary files /dev/null and b/gemrb/override/shared/shearth2.pro differ
diff --git a/gemrb/override/shared/shearth3.pro b/gemrb/override/shared/shearth3.pro
new file mode 100644
index 0000000..dd667ae
Binary files /dev/null and b/gemrb/override/shared/shearth3.pro differ
diff --git a/gemrb/override/shared/shearth4.pro b/gemrb/override/shared/shearth4.pro
new file mode 100644
index 0000000..76d9671
Binary files /dev/null and b/gemrb/override/shared/shearth4.pro differ
diff --git a/gemrb/override/shared/shearth5.pro b/gemrb/override/shared/shearth5.pro
new file mode 100644
index 0000000..dd345a4
Binary files /dev/null and b/gemrb/override/shared/shearth5.pro differ
diff --git a/gemrb/override/shared/shearth6.pro b/gemrb/override/shared/shearth6.pro
new file mode 100644
index 0000000..20798b1
Binary files /dev/null and b/gemrb/override/shared/shearth6.pro differ
diff --git a/gemrb/override/shared/shearth7.pro b/gemrb/override/shared/shearth7.pro
new file mode 100644
index 0000000..516baeb
Binary files /dev/null and b/gemrb/override/shared/shearth7.pro differ
diff --git a/gemrb/override/shared/shwater.pro b/gemrb/override/shared/shwater.pro
new file mode 100644
index 0000000..260ca26
Binary files /dev/null and b/gemrb/override/shared/shwater.pro differ
diff --git a/gemrb/override/shared/shwater1.pro b/gemrb/override/shared/shwater1.pro
new file mode 100644
index 0000000..ef01637
Binary files /dev/null and b/gemrb/override/shared/shwater1.pro differ
diff --git a/gemrb/override/shared/shwater2.pro b/gemrb/override/shared/shwater2.pro
new file mode 100644
index 0000000..e92cd8e
Binary files /dev/null and b/gemrb/override/shared/shwater2.pro differ
diff --git a/gemrb/override/shared/shwater3.pro b/gemrb/override/shared/shwater3.pro
new file mode 100644
index 0000000..7fe4a46
Binary files /dev/null and b/gemrb/override/shared/shwater3.pro differ
diff --git a/gemrb/override/shared/shwater4.pro b/gemrb/override/shared/shwater4.pro
new file mode 100644
index 0000000..b73c5e5
Binary files /dev/null and b/gemrb/override/shared/shwater4.pro differ
diff --git a/gemrb/override/shared/shwater5.pro b/gemrb/override/shared/shwater5.pro
new file mode 100644
index 0000000..43218a2
Binary files /dev/null and b/gemrb/override/shared/shwater5.pro differ
diff --git a/gemrb/override/shared/shwater6.pro b/gemrb/override/shared/shwater6.pro
new file mode 100644
index 0000000..168559d
Binary files /dev/null and b/gemrb/override/shared/shwater6.pro differ
diff --git a/gemrb/override/shared/shwater7.pro b/gemrb/override/shared/shwater7.pro
new file mode 100644
index 0000000..e822298
Binary files /dev/null and b/gemrb/override/shared/shwater7.pro differ
diff --git a/gemrb/override/shared/skybolt.pro b/gemrb/override/shared/skybolt.pro
new file mode 100644
index 0000000..7e34777
Binary files /dev/null and b/gemrb/override/shared/skybolt.pro differ
diff --git a/gemrb/override/shared/sneak.spl b/gemrb/override/shared/sneak.spl
new file mode 100644
index 0000000..95e0075
Binary files /dev/null and b/gemrb/override/shared/sneak.spl differ
diff --git a/gemrb/override/shared/souleatr.2da b/gemrb/override/shared/souleatr.2da
new file mode 100644
index 0000000..2a70a52
--- /dev/null
+++ b/gemrb/override/shared/souleatr.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+0
+ RESREF HitAnimation AreaHitAnimation
+1 AD3sklm MSumm1H MSumm1X
diff --git a/gemrb/override/shared/spboom1.pro b/gemrb/override/shared/spboom1.pro
new file mode 100644
index 0000000..257d603
Binary files /dev/null and b/gemrb/override/shared/spboom1.pro differ
diff --git a/gemrb/override/shared/spboom2.pro b/gemrb/override/shared/spboom2.pro
new file mode 100644
index 0000000..2805a62
Binary files /dev/null and b/gemrb/override/shared/spboom2.pro differ
diff --git a/gemrb/override/shared/spboom3.pro b/gemrb/override/shared/spboom3.pro
new file mode 100644
index 0000000..f9e8d04
Binary files /dev/null and b/gemrb/override/shared/spboom3.pro differ
diff --git a/gemrb/override/shared/spdimdr.pro b/gemrb/override/shared/spdimdr.pro
new file mode 100644
index 0000000..9297a5f
Binary files /dev/null and b/gemrb/override/shared/spdimdr.pro differ
diff --git a/gemrb/override/shared/spmagmis.pro b/gemrb/override/shared/spmagmis.pro
new file mode 100644
index 0000000..190d9c0
Binary files /dev/null and b/gemrb/override/shared/spmagmis.pro differ
diff --git a/gemrb/override/shared/spsmkjet.pro b/gemrb/override/shared/spsmkjet.pro
new file mode 100644
index 0000000..d7bc0b6
Binary files /dev/null and b/gemrb/override/shared/spsmkjet.pro differ
diff --git a/gemrb/override/shared/spsmold.pro b/gemrb/override/shared/spsmold.pro
new file mode 100644
index 0000000..1dc8499
Binary files /dev/null and b/gemrb/override/shared/spsmold.pro differ
diff --git a/gemrb/override/shared/spsmpuff.pro b/gemrb/override/shared/spsmpuff.pro
new file mode 100644
index 0000000..82f8011
Binary files /dev/null and b/gemrb/override/shared/spsmpuff.pro differ
diff --git a/gemrb/override/shared/stats.ids b/gemrb/override/shared/stats.ids
new file mode 100644
index 0000000..f1f1a70
--- /dev/null
+++ b/gemrb/override/shared/stats.ids
@@ -0,0 +1,312 @@
+IDS
+0 HITPOINTS
+1 MAXHITPOINTS
+2 ARMORCLASS
+3 ACCRUSHINGMOD
+4 ACMISSILEMOD
+5 ACPIERCINGMOD
+6 ACSLASHINGMOD
+7 THAC0
+8 NUMBEROFATTACKS
+9 SAVEVSDEATH
+10 SAVEVSWANDS
+11 SAVEVSPOLY
+12 SAVEVSBREATH
+13 SAVEVSSPELL
+9 SAVEFORTITUDE
+10 SAVEREFLEX
+11 SAVEWILL
+14 RESISTFIRE
+15 RESISTCOLD
+16 RESISTELECTRICITY
+17 RESISTACID
+18 RESISTMAGIC
+19 RESISTMAGICFIRE
+20 RESISTMAGICCOLD
+21 RESISTSLASHING
+22 RESISTCRUSHING
+23 RESISTPIERCING
+24 RESISTMISSILE
+25 LORE
+26 LOCKPICKING
+27 STEALTH
+28 TRAPS
+29 PICKPOCKET
+30 FATIGUE
+31 INTOXICATION
+32 LUCK
+33 TRACKING
+34 LEVEL
+34 CLASSLEVELFIGHTER
+35 SEX
+36 STR
+37 STREXTRA
+38 INT
+39 WIS
+40 DEX
+41 CON
+42 CHR
+43 XPVALUE
+44 XP
+45 GOLD
+46 MORALEBREAK
+47 MORALERECOVERYTIME
+48 REPUTATION
+49 HATEDRACE
+50 DAMAGEBONUS
+51 SPELLFAILUREMAGE
+52 SPELLFAILUREPRIEST
+53 SPELLDURATIONMODMAGE
+54 SPELLDURATIONMODPRIEST
+55 TURNUNDEADLEVEL
+56 BACKSTABDAMAGEMULTIPLIER
+57 LAYONHANDSAMOUNT
+58 HELD
+59 POLYMORPHED
+60 TRANSLUCENT
+61 IDENTIFYMODE
+62 ENTANGLE
+63 SANCTUARY
+64 MINORGLOBE
+65 SHIELDGLOBE
+66 GREASE
+67 WEB
+68 LEVEL2
+68 CLASSLEVELMAGE
+69 LEVEL3
+69 CLASSLEVELTHIEF
+70 CASTERHOLD
+71 ENCUMBRANCE
+72 MISSILETHAC0BONUS
+73 MAGICDAMAGERESISTANCE
+74 RESISTPOISON
+75 DONOTJUMP
+76 AURACLEANSING
+77 MENTALSPEED
+78 PHYSICALSPEED
+79 CASTINGLEVELBONUSMAGE
+80 CASTINGLEVELBONUSCLERIC
+81 SEEINVISIBLE
+82 IGNOREDIALOGPAUSE
+83 MINHITPOINTS
+84 THAC0BONUSRIGHT
+85 THAC0BONUSLEFT
+86 DAMAGEBONUSRIGHT
+87 DAMAGEBONUSLEFT
+88 STONESKINS
+89 FEAT_BOW
+90 FEAT_CROSSBOW
+91 FEAT_SLING
+92 FEAT_AXE
+93 FEAT_MACE
+94 FEAT_FLAIL
+95 FEAT_POLEARM
+96 FEAT_HAMMER
+97 FEAT_STAFF
+98 FEAT_GREAT_SWORD
+99 FEAT_LARGE_SWORD
+100 FEAT_SMALL_SWORD
+101 FEAT_TOUGHNESS
+102 FEAT_ARMORED_ARCANA
+103 FEAT_CLEAVE
+104 FEAT_ARMOUR
+105 FEAT_ENCHANTMENT
+106 FEAT_EVOCATION
+107 FEAT_NECROMANCY
+108 FEAT_TRANSMUTE
+109 FEAT_SPELL_PENETRATION
+110 FEAT_EXTRA_RAGE
+111 FEAT_EXTRA_SHAPE
+112 FEAT_EXTRA_SMITING
+113 FEAT_EXTRA_TURNING
+114 FEAT_BASTARDSWORD
+89 PROFICIENCYBASTARDSWORD
+90 PROFICIENCYLONGSWORD
+91 PROFICIENCYSHORTSWORD
+92 PROFICIENCYAXE
+93 PROFICIENCYTWOHANDEDSWORD
+94 PROFICIENCYKATANA
+95 PROFICIENCYSCIMITARWAKISASHININJATO
+96 PROFICIENCYDAGGER
+97 PROFICIENCYWARHAMMER
+98 PROFICIENCYSPEAR
+99 PROFICIENCYHALBERD
+100 PROFICIENCYFLAILMORNINGSTAR
+101 PROFICIENCYMACE
+102 PROFICIENCYQUARTERSTAFF
+103 PROFICIENCYCROSSBOW
+104 PROFICIENCYLONGBOW
+105 PROFICIENCYSHORTBOW
+106 PROFICIENCYDART
+107 PROFICIENCYSLING
+108 PROFICIENCYBLACKJACK
+109 PROFICIENCYGUN
+110 PROFICIENCYMARTIALARTS
+111 PROFICIENCY2HANDED
+112 PROFICIENCYSWORDANDSHIELD
+113 PROFICIENCYSINGLEWEAPON
+114 PROFICIENCY2WEAPON
+115 EXTRAPROFICIENCY1
+115 ALCHEMY
+116 EXTRAPROFICIENCY2
+116 ANIMALS
+117 EXTRAPROFICIENCY3
+117 BLUFF
+118 EXTRAPROFICIENCY4
+118 CONCENTRATION
+119 EXTRAPROFICIENCY5
+119 DIPLOMACY
+120 EXTRAPROFICIENCY6
+120 INTIMIDATE
+121 EXTRAPROFICIENCY7
+121 SEARCH
+122 EXTRAPROFICIENCY8
+122 SPELLCRAFT
+123 EXTRAPROFICIENCY9
+123 MAGICDEVICE
+124 EXTRAPROFICIENCY10
+125 EXTRAPROFICIENCY11
+126 EXTRAPROFICIENCY12
+127 EXTRAPROFICIENCY13
+128 EXTRAPROFICIENCY14
+129 EXTRAPROFICIENCY15
+130 EXTRAPROFICIENCY16
+131 EXTRAPROFICIENCY17
+131 FEATS1
+132 EXTRAPROFICIENCY18
+132 FEATS2
+133 EXTRAPROFICIENCY19
+133 FEATS3
+134 EXTRAPROFICIENCY20
+134 FREESLOTS
+135 HIDEINSHADOWS
+136 DETECTILLUSIONS
+137 SETTRAPS
+138 PUPPETMASTERID
+139 PUPPETMASTERTYPE
+140 PUPPETTYPE
+141 PUPPETID
+142 CHECKFORBERSERK
+143 BERSERKSTAGE1
+144 BERSERKSTAGE2
+145 DAMAGELUCK
+146 CRITICALHITBONUS
+147 VISUALRANGE
+148 EXPLORE
+149 THRULLCHARM
+150 SUMMONDISABLE
+151 HITBONUS
+152 KIT
+153 FORCESURGE
+154 SURGEMOD
+155 IMPROVEDHASTE
+156 INTERNAL_0
+157 INTERNAL_1
+158 INTERNAL_2
+159 INTERNAL_3
+160 INTERNAL_4
+161 INTERNAL_5
+162 INTERNAL_6
+163 INTERNAL_7
+164 INTERNAL_8
+165 INTERNAL_9
+156 SCRIPTINGSTATE1
+157 SCRIPTINGSTATE2
+158 SCRIPTINGSTATE3
+159 SCRIPTINGSTATE4
+160 SCRIPTINGSTATE5
+161 SCRIPTINGSTATE6
+162 SCRIPTINGSTATE7
+163 SCRIPTINGSTATE8
+164 SCRIPTINGSTATE9
+165 SCRIPTINGSTATE10
+166 MELEETHAC0
+167 MELEEDAMAGE
+168 MISSILEDAMAGE
+169 NOCIRCLE
+170 FISTTHAC0
+171 FISTDAMAGE
+172 TITLE1
+173 TITLE2
+174 DISABLEOVERLAY
+175 DISABLEBACKSTAB
+176 XP_MAGE
+177 XP_THIEF
+178 DIALOGRANGE
+179 MOVEMENTRATE
+180 MORALE
+181 BOUNCE
+182 MIRRORIMAGES
+183 ENABLEOFFSCREENAI
+184 EXISTANCEDELAY
+185 ATTACKNUMBERDOUBLE
+186 DISABLECHUNKING
+187 NOTURNABLE
+189 CHAOSSHIELD
+190 NPCBUMP
+191 CANUSEANYITEM
+192 ALWAYSBACKSTAB
+193 SEXCHANGED
+194 SPELLFAILUREINNATE
+195 NOTRACKING
+196 DEADMAGIC
+197 DISABLETIMESTOP
+198 NOSEQUESTER
+199 STONESKINGOLEM
+200 LEVELDRAIN
+201 AVATARREMOVAL
+203 IMMUNITY
+204 DISABLEDBUTTON
+205 ANIMATIONID
+206 STATE
+207 EXTSTATE
+208 METAL_COLOR
+208 COLORS
+209 MINOR_COLOR
+210 MAJOR_COLOR
+211 SKIN_COLOR
+212 LEATHER_COLOR
+213 ARMOR_COLOR
+214 HAIR_COLOR
+214 COLORCOUNT
+215 MC_FLAGS
+216 CLASSLEVELSUM
+217 ALIGNMENT
+218 UNSELECTABLE
+219 ARMORTYPE
+220 TEAM
+221 FACTION
+222 SUBRACE
+223 SPECIES
+224 HATEDRACE2
+225 HATEDRACE3
+226 HATEDRACE4
+227 HATEDRACE5
+228 HATEDRACE6
+229 HATEDRACE7
+230 HATEDRACE8
+231 RACE
+232 CLASS
+233 GENERAL
+234 EA
+235 SPECIFIC
+236 SAVEDXPOS
+237 SAVEDYPOS
+238 SAVEDFACE
+239 USERSTAT
+240 CLASSLEVELBARBARIAN
+241 CLASSLEVELBARD
+242 CLASSLEVELCLERIC
+243 CLASSLEVELDRUID
+244 CLASSLEVELMONK
+245 CLASSLEVELPALADIN
+246 CLASSLEVELRANGER
+247 CLASSLEVELSORCEROR
+248 CLASSLEVEL12
+249 CLASSLEVEL13
+250 SPLSTATE1
+251 SPLSTATE2
+252 SPLSTATE3
+253 SPLSTATE4
+254 SPLSTATE5
+255 SPLSTATE6
diff --git a/gemrb/override/shared/turn.spl b/gemrb/override/shared/turn.spl
new file mode 100644
index 0000000..2dafb19
Binary files /dev/null and b/gemrb/override/shared/turn.spl differ
diff --git a/gemrb/plugins-prepare.sh b/gemrb/plugins-prepare.sh
new file mode 100755
index 0000000..ea3c18e
--- /dev/null
+++ b/gemrb/plugins-prepare.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# 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.
+#
+#
+# link all plugins to the plugin dir, so gemrb can run from the build dir
+dir=$1
+if test -z "$dir"; then
+ cd `dirname $0`/plugins
+else
+ cd "$dir"
+fi || return 1
+
+if test -d 2DAImporter/.libs; then
+ ln -sf */.libs/*.so .
+else
+ # cmake; expect to be in the build dir since it is arbitrary
+ if test -z "$dir"; then
+ echo missing dir parameter - pass the path to the build dir
+ exit 1
+ fi &&
+ cd gemrb/plugins &&
+ ln -sf */*.so .
+fi || return 2
diff --git a/gemrb/plugins/2DAImporter/2DAImporter.cpp b/gemrb/plugins/2DAImporter/2DAImporter.cpp
new file mode 100644
index 0000000..fcb046f
--- /dev/null
+++ b/gemrb/plugins/2DAImporter/2DAImporter.cpp
@@ -0,0 +1,120 @@
+/* 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 "2DAImporter.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+#include "System/FileStream.h"
+
+#define MAXLENGTH 4096 //if a 2da has longer lines, change this
+#define SIGNLENGTH 256 //if a 2da has longer default value, change this
+
+p2DAImporter::p2DAImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+p2DAImporter::~p2DAImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+ for (unsigned int i = 0; i < ptrs.size(); i++) {
+ free( ptrs[i] );
+ }
+}
+
+bool p2DAImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[SIGNLENGTH];
+ str->CheckEncrypted();
+
+ str->ReadLine( Signature, sizeof(Signature) );
+ char* strp = Signature;
+ while (*strp == ' ')
+ strp++;
+ if (strncmp( strp, "2DA V1.0", 8 ) != 0) {
+ printMessage("2DAImporter", "Bad signature!\n",YELLOW );
+// we don't care about this, so exptable.2da of iwd2 won't cause a bigger problem
+// return false;
+ }
+ Signature[0] = 0;
+ str->ReadLine( Signature, sizeof(Signature) );
+ char* token = strtok( Signature, " " );
+ if (token) {
+ strncpy(defVal, token, sizeof(defVal) );
+ } else { // no whitespace
+ strncpy(defVal, Signature, sizeof(defVal) );
+ }
+ bool colHead = true;
+ int row = 0;
+ while (true) {
+ char* line = ( char* ) malloc( MAXLENGTH );
+ int len = str->ReadLine( line, MAXLENGTH-1 );
+ if (len <= 0) {
+ free( line );
+ break;
+ }
+ if (line[0] == '#') { // allow comments
+ free( line );
+ continue;
+ }
+ if (len < MAXLENGTH)
+ line = ( char * ) realloc( line, len + 1 );
+ ptrs.push_back( line );
+ if (colHead) {
+ colHead = false;
+ char* str = strtok( line, " " );
+ while (str != NULL) {
+ colNames.push_back( str );
+ str = strtok( NULL, " " );
+ }
+ } else {
+ char* str = strtok( line, " " );
+ if (str == NULL)
+ continue;
+ rowNames.push_back( str );
+ RowEntry r;
+ rows.push_back( r );
+ while (( str = strtok( NULL, " " ) ) != NULL) {
+ rows[row].push_back( str );
+ }
+ row++;
+ }
+ }
+ return true;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xB22F938, "2DA File Importer")
+PLUGIN_CLASS(IE_2DA_CLASS_ID, p2DAImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/2DAImporter/2DAImporter.h b/gemrb/plugins/2DAImporter/2DAImporter.h
new file mode 100644
index 0000000..0e83b5c
--- /dev/null
+++ b/gemrb/plugins/2DAImporter/2DAImporter.h
@@ -0,0 +1,169 @@
+/* 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 P2DAIMPORTER_H
+#define P2DAIMPORTER_H
+
+#include "TableMgr.h"
+
+#include "globals.h"
+
+#include <cstring>
+
+typedef std::vector< char*> RowEntry;
+
+class p2DAImporter : public TableMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ std::vector< char*> colNames;
+ std::vector< char*> rowNames;
+ std::vector< char*> ptrs;
+ std::vector< RowEntry> rows;
+ char defVal[32];
+public:
+ p2DAImporter(void);
+ ~p2DAImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ /** Returns the actual number of Rows in the Table */
+ inline ieDword GetRowCount() const
+ {
+ return ( ieDword ) rows.size();
+ }
+
+ inline ieDword GetColNamesCount() const
+ {
+ return (ieDword) colNames.size();
+ }
+
+ /** Returns the actual number of Columns in the Table */
+ inline ieDword GetColumnCount(unsigned int row = 0) const
+ {
+ if (rows.size() <= row) {
+ return 0;
+ }
+ return ( ieDword ) rows[row].size();
+ }
+ /** Returns a pointer to a zero terminated 2da element,
+ if it cannot return a value, it returns the default */
+ inline const char* QueryField(unsigned int row = 0, unsigned int column = 0) const
+ {
+ if (rows.size() <= row) {
+ return ( char * ) defVal;
+ }
+ if (rows[row].size() <= column) {
+ return ( char * ) defVal;
+ }
+ if (rows[row][column][0]=='*' && !rows[row][column][1]) {
+ return ( char * ) defVal;
+ }
+ return rows[row][column];
+ }
+ /** Returns a pointer to a zero terminated 2da element,
+ uses column name and row name to search the field */
+ inline const char* QueryField(const char* row, const char* column) const
+ {
+ int rowi, coli;
+
+ rowi = GetRowIndex(row);
+
+ if (rowi < 0) {
+ return ( char * ) defVal;
+ }
+
+ coli = GetColumnIndex(column);
+
+ if (coli < 0) {
+ return ( char * ) defVal;
+ }
+
+ return QueryField((unsigned int) rowi, (unsigned int) coli);
+ }
+
+ virtual const char* QueryDefault() const
+ {
+ return defVal;
+ }
+
+ inline int GetRowIndex(const char* string) const
+ {
+ for (unsigned int index = 0; index < rowNames.size(); index++) {
+ if (stricmp( rowNames[index], string ) == 0) {
+ return (int) index;
+ }
+ }
+ return -1;
+ }
+
+ inline int GetColumnIndex(const char* string) const
+ {
+ for (unsigned int index = 0; index < colNames.size(); index++) {
+ if (stricmp( colNames[index], string ) == 0) {
+ return (int) index;
+ }
+ }
+ return -1;
+ }
+
+ inline const char* GetColumnName(unsigned int index) const
+ {
+ if (index < colNames.size()) {
+ return colNames[index];
+ }
+ return "";
+ }
+
+ inline const char* GetRowName(unsigned int index) const
+ {
+ if (index < rowNames.size()) {
+ return rowNames[index];
+ }
+ return "";
+ }
+
+ inline int FindTableValue(unsigned int col, long val, int start) const
+ {
+ ieDword row, max;
+
+ max = GetRowCount();
+ for (row = start; row < max; row++) {
+ const char* ret = QueryField( row, col );
+ long Value;
+ if (valid_number( ret, Value ) && (Value == val) )
+ return (int) row;
+ }
+ return -1;
+ }
+
+ inline int FindTableValue(unsigned int col, const char* val, int start) const
+ {
+ ieDword row, max;
+
+ max = GetRowCount();
+ for (row = start; row < max; row++) {
+ const char* ret = QueryField( row, col );
+ if (stricmp(ret, val) == 0)
+ return (int) row;
+ }
+ return -1;
+ }
+};
+
+#endif
diff --git a/gemrb/plugins/2DAImporter/CMakeLists.txt b/gemrb/plugins/2DAImporter/CMakeLists.txt
new file mode 100644
index 0000000..b669d2b
--- /dev/null
+++ b/gemrb/plugins/2DAImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN(2DAImporter 2DAImporter.cpp 2DAImporter.h )
diff --git a/gemrb/plugins/2DAImporter/Makefile.am b/gemrb/plugins/2DAImporter/Makefile.am
new file mode 100644
index 0000000..2dba0fe
--- /dev/null
+++ b/gemrb/plugins/2DAImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = 2DAImporter.la
+2DAImporter_la_LDFLAGS = -module -avoid-version -shared
+2DAImporter_la_SOURCES = 2DAImporter.cpp 2DAImporter.h
diff --git a/gemrb/plugins/ACMReader/ACMReader.cpp b/gemrb/plugins/ACMReader/ACMReader.cpp
new file mode 100644
index 0000000..f0c5f9c
--- /dev/null
+++ b/gemrb/plugins/ACMReader/ACMReader.cpp
@@ -0,0 +1,117 @@
+/* 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 "ACMReader.h"
+
+#include "general.h"
+
+bool ACMReader::Open(DataStream* stream)
+{
+ str = stream;
+ Close();
+
+ ACM_Header hdr;
+
+ char Signature[4];
+ ieDword SignatureDword;
+ stream->Read( Signature, 4 );
+ stream->Seek( 0, GEM_STREAM_START );
+ stream->ReadDword( &SignatureDword );
+ if (!memcmp( Signature, "WAVC", 4 )) {
+ str->Seek( 28, GEM_STREAM_START );
+ } else if (SignatureDword == IP_ACM_SIG) {
+ stream->Seek( 0, GEM_STREAM_START );
+ } else {
+ return false;
+ }
+
+ str->ReadDword( &hdr.signature );
+ str->ReadDword( &hdr.samples );
+ str->ReadWord( &hdr.channels );
+ str->ReadWord( &hdr.rate );
+ ieWord tmpword;
+ str->ReadWord( &tmpword );
+ //subblocks = (int) (tmpword&0xfff);
+ //levels = (int) (tmpword>>12);
+ subblocks = (int) (tmpword>>4);
+ levels = (int) (tmpword&15);
+
+ if (hdr.signature != IP_ACM_SIG) {
+ return false;
+ }
+ samples_left = ( samples = hdr.samples );
+ channels = hdr.channels;
+ samplerate = hdr.rate;
+ //levels = hdr.levels;
+ //subblocks = hdr.subblocks;
+
+ block_size = ( 1 << levels ) * subblocks;
+ //using malloc for simple arrays (supposed to be faster)
+ block = (int *) malloc(sizeof(int)*block_size);
+ if (!block) {
+ return false;
+ }
+ unpacker = new CValueUnpacker( levels, subblocks, str );
+ if (!unpacker || !unpacker->init_unpacker()) {
+ return false;
+ }
+ decoder = new CSubbandDecoder( levels );
+ if (!decoder || !decoder->init_decoder()) {
+ return false;
+ }
+ return true;
+}
+int ACMReader::make_new_samples()
+{
+ if (!unpacker->get_one_block( block )) {
+ return 0;
+ }
+
+ decoder->decode_data( block, subblocks );
+ values = block;
+ samples_ready = ( block_size > samples_left ) ? samples_left : block_size;
+ samples_left -= samples_ready;
+
+ return 1;
+}
+
+int ACMReader::read_samples(short* buffer, int count)
+{
+ int res = 0;
+ while (res < count) {
+ if (samples_ready == 0) {
+ if (samples_left == 0)
+ break;
+ if (!make_new_samples())
+ break;
+ }
+ *buffer = ( short ) ( ( *values ) >> levels );
+ values++;
+ buffer++;
+ res += 1;
+ samples_ready--;
+ }
+ return res;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x10373EE, "ACM File Importer")
+PLUGIN_IE_RESOURCE(ACMReader, ".acm", (ieWord)IE_ACM_CLASS_ID)
+PLUGIN_IE_RESOURCE(ACMReader, ".wav", (ieWord)IE_WAV_CLASS_ID)
+END_PLUGIN()
diff --git a/gemrb/plugins/ACMReader/ACMReader.h b/gemrb/plugins/ACMReader/ACMReader.h
new file mode 100644
index 0000000..d34615c
--- /dev/null
+++ b/gemrb/plugins/ACMReader/ACMReader.h
@@ -0,0 +1,73 @@
+/* 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 ACMREADER_H
+#define ACMREADER_H
+
+#include "SoundMgr.h"
+
+#include "decoder.h"
+#include "general.h"
+#include "unpacker.h"
+
+#include "System/DataStream.h"
+
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+
+// IP's ACM files
+class ACMReader : public SoundMgr {
+private:
+ int samples_left; // count of unread samples
+ int levels, subblocks;
+ int block_size;
+ int* block, * values;
+ int samples_ready;
+ CValueUnpacker* unpacker; // ACM-stream unpacker
+ CSubbandDecoder* decoder; // IP's subband decoder
+
+ int make_new_samples();
+public:
+ ACMReader()
+ : samples_left( 0 ), block( NULL ), values( NULL ),
+ samples_ready( 0 ), unpacker( NULL ), decoder( NULL )
+ {
+ }
+ virtual ~ACMReader()
+ {
+ Close();
+ }
+ void Close()
+ {
+ if (block) {
+ free(block);
+ }
+ if (unpacker) {
+ delete unpacker;
+ }
+ if (decoder) {
+ delete decoder;
+ }
+ }
+
+ bool Open(DataStream* stream);
+ virtual int read_samples(short* buffer, int count);
+};
+
+#endif
diff --git a/gemrb/plugins/ACMReader/CMakeLists.txt b/gemrb/plugins/ACMReader/CMakeLists.txt
new file mode 100644
index 0000000..04683d3
--- /dev/null
+++ b/gemrb/plugins/ACMReader/CMakeLists.txt
@@ -0,0 +1,3 @@
+FILE( GLOB ACMReader_files *.cpp )
+
+ADD_GEMRB_PLUGIN (ACMReader ${ACMReader_files})
diff --git a/gemrb/plugins/ACMReader/Makefile.am b/gemrb/plugins/ACMReader/Makefile.am
new file mode 100644
index 0000000..bda9589
--- /dev/null
+++ b/gemrb/plugins/ACMReader/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = ACMReader.la
+ACMReader_la_LDFLAGS = -module -avoid-version -shared
+ACMReader_la_SOURCES = ACMReader.cpp ACMReader.h decoder.cpp decoder.h unpacker.cpp unpacker.h general.h
diff --git a/gemrb/plugins/ACMReader/decoder.cpp b/gemrb/plugins/ACMReader/decoder.cpp
new file mode 100644
index 0000000..8d59009
--- /dev/null
+++ b/gemrb/plugins/ACMReader/decoder.cpp
@@ -0,0 +1,177 @@
+/* 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 "decoder.h"
+
+#include <cstdlib>
+
+int CSubbandDecoder::init_decoder()
+{
+ int memory_size = ( levels == 0 ) ? 0 : ( 3 * ( block_size >> 1 ) - 2 );
+ if (memory_size) {
+ memory_buffer = ( int * ) calloc( memory_size, sizeof( int ) );
+ if (!memory_buffer)
+ return 0;
+ }
+ return 1;
+}
+void CSubbandDecoder::decode_data(int* buffer, int blocks)
+{
+ if (!levels) {
+ return;
+ } // no levels - no work
+
+ int* buff_ptr = buffer, * mem_ptr = memory_buffer;
+ int sb_size = block_size >> 1; // current subband size
+
+ blocks <<= 1;
+ sub_4d3fcc( ( short * ) mem_ptr, buff_ptr, sb_size, blocks );
+ mem_ptr += sb_size;
+
+ for (int i = 0; i < blocks; i++)
+ buff_ptr[i * sb_size]++;
+
+ sb_size >>= 1;
+ blocks <<= 1;
+
+ while (sb_size != 0) {
+ sub_4d420c( mem_ptr, buff_ptr, sb_size, blocks );
+ mem_ptr += sb_size << 1;
+ sb_size >>= 1;
+ blocks <<= 1;
+ }
+}
+void CSubbandDecoder::sub_4d3fcc(short* memory, int* buffer, int sb_size,
+ int blocks)
+{
+ int row_0, row_1, row_2 = 0, row_3 = 0, db_0, db_1;
+ int i;
+ int sb_size_2 = sb_size * 2, sb_size_3 = sb_size * 3;
+ if (blocks == 2) {
+ for (i = 0; i < sb_size; i++) {
+ row_0 = buffer[0];
+ row_1 = buffer[sb_size];
+ buffer[0] = buffer[0] + memory[0] + 2 * memory[1];
+ buffer[sb_size] = 2 * row_0 - memory[1] - buffer[sb_size];
+ memory[0] = ( short ) row_0;
+ memory[1] = ( short ) row_1;
+
+ memory += 2;
+ buffer++;
+ }
+ } else if (blocks == 4) {
+ for (i = 0; i < sb_size; i++) {
+ row_0 = buffer[0];
+ row_1 = buffer[sb_size];
+ row_2 = buffer[sb_size_2];
+ row_3 = buffer[sb_size_3];
+
+ buffer[0] = memory[0] + 2 * memory[1] + row_0;
+ buffer[sb_size] = -memory[1] + 2 * row_0 - row_1;
+ buffer[sb_size_2] = row_0 + 2 * row_1 + row_2;
+ buffer[sb_size_3] = -row_1 + 2 * row_2 - row_3;
+
+ memory[0] = ( short ) row_2;
+ memory[1] = ( short ) row_3;
+
+ memory += 2;
+ buffer++;
+ }
+ } else {
+ for (i = 0; i < sb_size; i++) {
+ int* buff_ptr = buffer;
+ if (( blocks >> 1 ) & 1) {
+ row_0 = buff_ptr[0];
+ row_1 = buff_ptr[sb_size];
+
+ buff_ptr[0] = memory[0] + 2 * memory[1] + row_0;
+ buff_ptr[sb_size] = -memory[1] + 2 * row_0 - row_1;
+ buff_ptr += sb_size_2;
+
+ db_0 = row_0;
+ db_1 = row_1;
+ } else {
+ db_0 = memory[0];
+ db_1 = memory[1];
+ }
+
+ for (int j = 0; j < blocks >> 2; j++) {
+ row_0 = buff_ptr[0]; buff_ptr[0] = db_0 + 2 * db_1 + row_0; buff_ptr += sb_size;
+ row_1 = buff_ptr[0]; buff_ptr[0] = -db_1 + 2 * row_0 - row_1; buff_ptr += sb_size;
+ row_2 = buff_ptr[0]; buff_ptr[0] = row_0 + 2 * row_1 + row_2; buff_ptr += sb_size;
+ row_3 = buff_ptr[0]; buff_ptr[0] = -row_1 + 2 * row_2 -
+ row_3; buff_ptr += sb_size;
+
+ db_0 = row_2;
+ db_1 = row_3;
+ }
+ memory[0] = ( short ) row_2;
+ memory[1] = ( short ) row_3;
+ memory += 2;
+ buffer++;
+ }
+ }
+}
+void CSubbandDecoder::sub_4d420c(int* memory, int* buffer, int sb_size,
+ int blocks)
+{
+ int row_0, row_1, row_2 = 0, row_3 = 0, db_0, db_1;
+ int i;
+ int sb_size_2 = sb_size * 2, sb_size_3 = sb_size * 3;
+ if (blocks == 4) {
+ for (i = 0; i < sb_size; i++) {
+ row_0 = buffer[0];
+ row_1 = buffer[sb_size];
+ row_2 = buffer[sb_size_2];
+ row_3 = buffer[sb_size_3];
+
+ buffer[0] = memory[0] + 2 * memory[1] + row_0;
+ buffer[sb_size] = -memory[1] + 2 * row_0 - row_1;
+ buffer[sb_size_2] = row_0 + 2 * row_1 + row_2;
+ buffer[sb_size_3] = -row_1 + 2 * row_2 - row_3;
+
+ memory[0] = row_2;
+ memory[1] = row_3;
+
+ memory += 2;
+ buffer++;
+ }
+ } else {
+ for (i = 0; i < sb_size; i++) {
+ int* buff_ptr = buffer;
+ db_0 = memory[0]; db_1 = memory[1];
+ for (int j = 0; j < blocks >> 2; j++) {
+ row_0 = buff_ptr[0]; buff_ptr[0] = db_0 + 2 * db_1 + row_0; buff_ptr += sb_size;
+ row_1 = buff_ptr[0]; buff_ptr[0] = -db_1 + 2 * row_0 - row_1; buff_ptr += sb_size;
+ row_2 = buff_ptr[0]; buff_ptr[0] = row_0 + 2 * row_1 + row_2; buff_ptr += sb_size;
+ row_3 = buff_ptr[0]; buff_ptr[0] = -row_1 + 2 * row_2 -
+ row_3; buff_ptr += sb_size;
+
+ db_0 = row_2;
+ db_1 = row_3;
+ }
+ memory[0] = row_2;
+ memory[1] = row_3;
+
+ memory += 2;
+ buffer++;
+ }
+ }
+}
diff --git a/gemrb/plugins/ACMReader/decoder.h b/gemrb/plugins/ACMReader/decoder.h
new file mode 100644
index 0000000..82d2d65
--- /dev/null
+++ b/gemrb/plugins/ACMReader/decoder.h
@@ -0,0 +1,48 @@
+/* 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 _ACM_LAB_SUBBAND_DECODER_H
+#define _ACM_LAB_SUBBAND_DECODER_H
+
+#include <cstdlib>
+
+class CSubbandDecoder {
+private:
+ int levels, block_size;
+ int* memory_buffer;
+ void sub_4d3fcc(short* memory, int* buffer, int sb_size, int blocks);
+ void sub_4d420c(int* memory, int* buffer, int sb_size, int blocks);
+public:
+ CSubbandDecoder(int lev_cnt)
+ : levels( lev_cnt ), block_size( 1 << lev_cnt ), memory_buffer( NULL )
+ {
+ }
+ virtual ~CSubbandDecoder()
+ {
+ if (memory_buffer) {
+ free( memory_buffer );
+ }
+ }
+
+ int init_decoder();
+ void decode_data(int* buffer, int blocks);
+};
+
+#endif
diff --git a/gemrb/plugins/ACMReader/general.h b/gemrb/plugins/ACMReader/general.h
new file mode 100644
index 0000000..c902fda
--- /dev/null
+++ b/gemrb/plugins/ACMReader/general.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 _ACM_LAB_GENERAL_H
+#define _ACM_LAB_GENERAL_H
+
+// Interplay ACM signature
+#define IP_ACM_SIG 0x01032897
+
+struct ACM_Header {
+ unsigned int signature;
+ unsigned int samples;
+ unsigned short channels;
+ unsigned short rate;
+ unsigned short levels : 4;
+ unsigned short subblocks : 12;
+};
+
+#endif
diff --git a/gemrb/plugins/ACMReader/unpacker.cpp b/gemrb/plugins/ACMReader/unpacker.cpp
new file mode 100644
index 0000000..ac5ec3d
--- /dev/null
+++ b/gemrb/plugins/ACMReader/unpacker.cpp
@@ -0,0 +1,454 @@
+/* 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.
+ *
+ *
+ */
+
+// IP's ACM-stream unpacker.
+
+#include "unpacker.h"
+
+#include <cstdio>
+
+char Table1[27] = {
+ 0, 1, 2, 4, 5, 6, 8, 9, 10, 16, 17, 18, 20, 21, 22, 24, 25, 26, 32, 33,
+ 34, 36, 37, 38, 40, 41, 42
+};
+//Eng: in base-4 system it is:
+//Rus: � ����������� ������� ��� �����:
+// 000 001 002 010 011 012 020 021 022
+// 100 101 102 110 111 112 120 121 122
+// 200 201 202 210 211 212 220 221 222
+short Table2[125] = {
+ 0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 16, 17, 18, 19, 20, 24, 25, 26, 27, 28,
+ 32, 33, 34, 35, 36, 64, 65, 66, 67, 68, 72, 73, 74, 75, 76, 80, 81, 82,
+ 83, 84, 88, 89, 90, 91, 92, 96, 97, 98, 99, 100, 128, 129, 130, 131, 132,
+ 136, 137, 138, 139, 140, 144, 145, 146, 147, 148, 152, 153, 154, 155, 156,
+ 160, 161, 162, 163, 164, 192, 193, 194, 195, 196, 200, 201, 202, 203, 204,
+ 208, 209, 210, 211, 212, 216, 217, 218, 219, 220, 224, 225, 226, 227, 228,
+ 256, 257, 258, 259, 260, 264, 265, 266, 267, 268, 272, 273, 274, 275, 276,
+ 280, 281, 282, 283, 284, 288, 289, 290, 291, 292
+};
+//Eng: in base-8 system:
+//Rus: � ������������ �������:
+// 000 001 002 003 004 010 011 012 013 014 ...
+// 100 101 102 103 104 ...
+// 200 ...
+// ...
+unsigned char Table3[121] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x20, 0x21,
+ 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x40, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x50, 0x51, 0x52, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7A, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9A, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
+ 0xAA
+};
+
+FillerProc Fillers[32] = {
+ &CValueUnpacker::zero_fill, & CValueUnpacker::return0,
+ & CValueUnpacker::return0, & CValueUnpacker::linear_fill,
+ & CValueUnpacker::linear_fill, & CValueUnpacker::linear_fill,
+ & CValueUnpacker::linear_fill, & CValueUnpacker::linear_fill,
+ & CValueUnpacker::linear_fill, & CValueUnpacker::linear_fill,
+ & CValueUnpacker::linear_fill, & CValueUnpacker::linear_fill,
+ & CValueUnpacker::linear_fill, & CValueUnpacker::linear_fill,
+ & CValueUnpacker::linear_fill, & CValueUnpacker::linear_fill,
+ & CValueUnpacker::linear_fill, & CValueUnpacker::k1_3bits,
+ & CValueUnpacker::k1_2bits, & CValueUnpacker::t1_5bits,
+ & CValueUnpacker::k2_4bits, & CValueUnpacker::k2_3bits,
+ & CValueUnpacker::t2_7bits, & CValueUnpacker::k3_5bits,
+ & CValueUnpacker::k3_4bits, & CValueUnpacker::return0,
+ & CValueUnpacker::k4_5bits, & CValueUnpacker::k4_4bits,
+ & CValueUnpacker::return0, & CValueUnpacker::t3_7bits,
+ & CValueUnpacker::return0, & CValueUnpacker::return0
+};
+
+inline void CValueUnpacker::prepare_bits(int bits)
+{
+ while (bits > avail_bits) {
+ unsigned char one_byte;
+ if (buffer_bit_offset == UNPACKER_BUFFER_SIZE) {
+ unsigned long remains = stream->Remains();
+ if (remains > UNPACKER_BUFFER_SIZE)
+ remains = UNPACKER_BUFFER_SIZE;
+ buffer_bit_offset = UNPACKER_BUFFER_SIZE - remains;
+ if (buffer_bit_offset != UNPACKER_BUFFER_SIZE)
+ stream->Read( bits_buffer + buffer_bit_offset, remains);
+ }
+ //our stream read returns -1 instead of 0 on failure
+ //comparing with 1 will solve annoying interface changes
+ if (buffer_bit_offset < UNPACKER_BUFFER_SIZE) {
+ one_byte = bits_buffer[buffer_bit_offset];
+ buffer_bit_offset++;
+ } else {
+ one_byte = 0;
+ }
+ next_bits |= ( ( unsigned int ) one_byte << avail_bits );
+ avail_bits += 8;
+ }
+}
+int CValueUnpacker::get_bits(int bits)
+{
+ prepare_bits( bits );
+ int res = next_bits;
+ avail_bits -= bits;
+ next_bits >>= bits;
+ return res;
+}
+int CValueUnpacker::init_unpacker()
+{
+ //using malloc, supposed to be faster
+ amp_buffer =(short *) malloc(sizeof(short)*0x10000);
+ if (!amp_buffer) {
+ return 0;
+ }
+ buff_middle = amp_buffer + 0x8000;
+ return 1;
+}
+int CValueUnpacker::get_one_block(int* block)
+{
+ block_ptr = block;
+ int pwr = get_bits( 4 ) & 0xF, val = get_bits( 16 ) & 0xFFFF,
+ count = 1 << pwr, v = 0;
+ int i;
+ for (i = 0; i < count; i++) {
+ buff_middle[i] = ( short ) v;
+ v += val;
+ }
+ v = -val;
+ for (i = 0; i < count; i++) {
+ buff_middle[-i - 1] = ( short ) v;
+ v -= val;
+ }
+
+ for (int pass = 0; pass < sb_size; pass++) {
+ int ind = get_bits( 5 ) & 0x1F;
+ if (!( ( this->*Fillers[ind] ) ( pass, ind ) )) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+// Filling functions:
+// int CValueUnpacker::FillerProc (int pass, int ind)
+int CValueUnpacker::return0(int /*pass*/, int /*ind*/)
+{
+ return 0;
+}
+int CValueUnpacker::zero_fill(int pass, int /*ind*/)
+{
+ //Eng: used when the whole column #pass is zero-filled
+ //Rus: ������������, ����� ���� ������� � ������� pass �������� ������
+ int* sb_ptr = &block_ptr[pass], step = sb_size, i = subblocks;
+ do {
+ *sb_ptr = 0;
+ sb_ptr += step;
+ } while (( --i ) != 0);
+ return 1;
+}
+int CValueUnpacker::linear_fill(int pass, int ind)
+{
+ int mask = ( 1 << ind ) - 1;
+ short* lb_ptr = buff_middle + ( ( -1l ) << ( ind - 1 ) );
+
+ for (int i = 0; i < subblocks; i++)
+ block_ptr[i * sb_size + pass] = lb_ptr[get_bits( ind ) & mask];
+ return 1;
+}
+int CValueUnpacker::k1_3bits(int pass, int /*ind*/)
+{
+ //Eng: column with number pass is filled with zeros, and also +/-1, zeros are repeated frequently
+ //Rus: c������ pass �������� ������, � ����� +/- 1, �� ���� ����� ���� ������
+ // efficiency (bits per value): 3-p0-2.5*p00, p00 - cnt of paired zeros, p0 - cnt of single zeros.
+ //Eng: it makes sense to use, when the freqnecy of paired zeros (p00) is greater than 2/3
+ //Rus: ����� ����� ������������, ����� ����������� ������ ����� (p00) ������ 2/3
+ for (int i = 0; i < subblocks; i++) {
+ prepare_bits( 3 );
+ if (( next_bits & 1 ) == 0) {
+ avail_bits--;
+ next_bits >>= 1;
+ block_ptr[i * sb_size + pass] = 0; if (( ++i ) == subblocks)
+ break;
+ block_ptr[i * sb_size + pass] = 0;
+ } else if (( next_bits & 2 ) == 0) {
+ avail_bits -= 2;
+ next_bits >>= 2;
+ block_ptr[i * sb_size + pass] = 0;
+ } else {
+ block_ptr[i * sb_size + pass] = ( next_bits & 4 ) ?
+ buff_middle[1] :
+ buff_middle[-1];
+ avail_bits -= 3;
+ next_bits >>= 3;
+ }
+ }
+ return 1;
+}
+int CValueUnpacker::k1_2bits(int pass, int /*ind*/)
+{
+ //Eng: column is filled with zero and +/-1
+ //Rus: c������ pass �������� ������, � ����� +/- 1
+ // efficiency: 2-P0. P0 - cnt of any zero (P0 = p0 + p00)
+ //Eng: use it when P0 > 1/3
+ //Rus: ����� ����� ������������, ����� ����������� ���� ������ 1/3
+ for (int i = 0; i < subblocks; i++) {
+ prepare_bits( 2 );
+ if (( next_bits & 1 ) == 0) {
+ avail_bits--;
+ next_bits >>= 1;
+ block_ptr[i * sb_size + pass] = 0;
+ } else {
+ block_ptr[i * sb_size + pass] = ( next_bits & 2 ) ?
+ buff_middle[1] :
+ buff_middle[-1];
+ avail_bits -= 2;
+ next_bits >>= 2;
+ }
+ }
+ return 1;
+}
+int CValueUnpacker::t1_5bits(int pass, int /*ind*/)
+{
+ //Eng: all the -1, 0, +1 triplets
+ //Rus: ��� ���������� ����� -1, 0, +1.
+ // efficiency: always 5/3 bits per value
+ // use it when P0 <= 1/3
+ for (int i = 0; i < subblocks; i++) {
+ int bits = ( int ) ( get_bits( 5 ) & 0x1f );
+ bits = ( int ) Table1[bits];
+
+ block_ptr[i * sb_size + pass] = buff_middle[-1 + ( bits & 3 )];
+ if (( ++i ) == subblocks)
+ break;
+ bits >>= 2;
+ block_ptr[i * sb_size + pass] = buff_middle[-1 + ( bits & 3 )];
+ if (( ++i ) == subblocks)
+ break;
+ bits >>= 2;
+ block_ptr[i * sb_size + pass] = buff_middle[-1 + bits];
+ }
+ return 1;
+}
+int CValueUnpacker::k2_4bits(int pass, int /*ind*/)
+{
+ // -2, -1, 0, 1, 2, and repeating zeros
+ // efficiency: 4-2*p0-3.5*p00, p00 - cnt of paired zeros, p0 - cnt of single zeros.
+ //Eng: makes sense to use when p00>2/3
+ //Rus: ����� ����� ������������, ����� ����������� ������ ����� (p00) ������ 2/3
+ for (int i = 0; i < subblocks; i++) {
+ prepare_bits( 4 );
+ if (( next_bits & 1 ) == 0) {
+ avail_bits--;
+ next_bits >>= 1;
+ block_ptr[i * sb_size + pass] = 0; if (( ++i ) == subblocks)
+ break;
+ block_ptr[i * sb_size + pass] = 0;
+ } else if (( next_bits & 2 ) == 0) {
+ avail_bits -= 2;
+ next_bits >>= 2;
+ block_ptr[i * sb_size + pass] = 0;
+ } else {
+ block_ptr[i * sb_size + pass] = ( next_bits & 8 ) ?
+ ( ( next_bits & 4 ) ? buff_middle[2] : buff_middle[1] ) :
+ ( ( next_bits & 4 ) ? buff_middle[-1] : buff_middle[-2] );
+ avail_bits -= 4;
+ next_bits >>= 4;
+ }
+ }
+ return 1;
+}
+int CValueUnpacker::k2_3bits(int pass, int /*ind*/)
+{
+ // -2, -1, 0, 1, 2
+ // efficiency: 3-2*P0, P0 - cnt of any zero (P0 = p0 + p00)
+ //Eng: use when P0>1/3
+ //Rus: ����� ����� ������������, ����� ����������� ���� ������ 1/3
+ for (int i = 0; i < subblocks; i++) {
+ prepare_bits( 3 );
+ if (( next_bits & 1 ) == 0) {
+ avail_bits--;
+ next_bits >>= 1;
+ block_ptr[i * sb_size + pass] = 0;
+ } else {
+ block_ptr[i * sb_size + pass] = ( next_bits & 4 ) ?
+ ( ( next_bits & 2 ) ? buff_middle[2] : buff_middle[1] ) :
+ ( ( next_bits & 2 ) ? buff_middle[-1] : buff_middle[-2] );
+ avail_bits -= 3;
+ next_bits >>= 3;
+ }
+ }
+ return 1;
+}
+int CValueUnpacker::t2_7bits(int pass, int /*ind*/)
+{
+ //Eng: all the +/-2, +/-1, 0 triplets
+ // efficiency: always 7/3 bits per value
+ //Rus: ��� ���������� ����� -2, -1, 0, +1, 2.
+ // �������������: 7/3 ���� �� �������� - ������
+ // use it when p0 <= 1/3
+ for (int i = 0; i < subblocks; i++) {
+ int bits = ( int ) ( get_bits( 7 ) & 0x7f );
+ short val = Table2[bits];
+
+ block_ptr[i * sb_size + pass] = buff_middle[-2 + ( val & 7 )];
+ if (( ++i ) == subblocks)
+ break;
+ val >>= 3;
+ block_ptr[i * sb_size + pass] = buff_middle[-2 + ( val & 7 )];
+ if (( ++i ) == subblocks)
+ break;
+ val >>= 3;
+ block_ptr[i * sb_size + pass] = buff_middle[-2 + val];
+ }
+ return 1;
+}
+int CValueUnpacker::k3_5bits(int pass, int /*ind*/)
+{
+ // fills with values: -3, -2, -1, 0, 1, 2, 3, and double zeros
+ // efficiency: 5-3*p0-4.5*p00-p1, p00 - cnt of paired zeros, p0 - cnt of single zeros, p1 - cnt of +/- 1.
+ // can be used when frequency of paired zeros (p00) is greater than 2/3
+ for (int i = 0; i < subblocks; i++) {
+ prepare_bits( 5 );
+ if (( next_bits & 1 ) == 0) {
+ avail_bits--;
+ next_bits >>= 1;
+ block_ptr[i * sb_size + pass] = 0; if (( ++i ) == subblocks)
+ break;
+ block_ptr[i * sb_size + pass] = 0;
+ } else if (( next_bits & 2 ) == 0) {
+ avail_bits -= 2;
+ next_bits >>= 2;
+ block_ptr[i * sb_size + pass] = 0;
+ } else if (( next_bits & 4 ) == 0) {
+ block_ptr[i * sb_size + pass] = ( next_bits & 8 ) ?
+ buff_middle[1] :
+ buff_middle[-1];
+ avail_bits -= 4;
+ next_bits >>= 4;
+ } else {
+ avail_bits -= 5;
+ int val = ( next_bits & 0x18 ) >> 3;
+ next_bits >>= 5;
+ if (val >= 2)
+ val += 3;
+ block_ptr[i * sb_size + pass] = buff_middle[-3 + val];
+ }
+ }
+ return 1;
+}
+int CValueUnpacker::k3_4bits(int pass, int /*ind*/)
+{
+ // fills with values: -3, -2, -1, 0, 1, 2, 3.
+ // efficiency: 4-3*P0-p1, P0 - cnt of all zeros (P0 = p0 + p00), p1 - cnt of +/- 1.
+ for (int i = 0; i < subblocks; i++) {
+ prepare_bits( 4 );
+ if (( next_bits & 1 ) == 0) {
+ avail_bits--;
+ next_bits >>= 1;
+ block_ptr[i * sb_size + pass] = 0;
+ } else if (( next_bits & 2 ) == 0) {
+ avail_bits -= 3;
+ block_ptr[i * sb_size + pass] = ( next_bits & 4 ) ?
+ buff_middle[1] :
+ buff_middle[-1];
+ next_bits >>= 3;
+ } else {
+ int val = ( next_bits &0xC ) >> 2;
+ avail_bits -= 4;
+ next_bits >>= 4;
+ if (val >= 2)
+ val += 3;
+ block_ptr[i * sb_size + pass] = buff_middle[-3 + val];
+ }
+ }
+ return 1;
+}
+int CValueUnpacker::k4_5bits(int pass, int /*ind*/)
+{
+ // fills with values: +/-4, +/-3, +/-2, +/-1, 0, and double zeros
+ // efficiency: 5-3*p0-4.5*p00, p00 - cnt of paired zeros, p0 - cnt of single zeros.
+ //Eng: makes sense to use when p00>2/3
+ //Rus: ����� ����� ������������, ����� ����������� ������ ����� (p00) ������ 2/3
+ for (int i = 0; i < subblocks; i++) {
+ prepare_bits( 5 );
+ if (( next_bits & 1 ) == 0) {
+ avail_bits--;
+ next_bits >>= 1;
+ block_ptr[i * sb_size + pass] = 0; if (( ++i ) == subblocks)
+ break;
+ block_ptr[i * sb_size + pass] = 0;
+ } else if (( next_bits & 2 ) == 0) {
+ avail_bits -= 2;
+ next_bits >>= 2;
+ block_ptr[i * sb_size + pass] = 0;
+ } else {
+ int val = ( next_bits &0x1C ) >> 2;
+ if (val >= 4)
+ val++;
+ block_ptr[i * sb_size + pass] = buff_middle[-4 + val];
+ avail_bits -= 5;
+ next_bits >>= 5;
+ }
+ }
+ return 1;
+}
+int CValueUnpacker::k4_4bits(int pass, int /*ind*/)
+{
+ // fills with values: +/-4, +/-3, +/-2, +/-1, 0, and double zeros
+ // efficiency: 4-3*P0, P0 - cnt of all zeros (both single and paired).
+ for (int i = 0; i < subblocks; i++) {
+ prepare_bits( 4 );
+ if (( next_bits & 1 ) == 0) {
+ avail_bits--;
+ next_bits >>= 1;
+ block_ptr[i * sb_size + pass] = 0;
+ } else {
+ int val = ( next_bits &0xE ) >> 1;
+ avail_bits -= 4;
+ next_bits >>= 4;
+ if (val >= 4)
+ val++;
+ block_ptr[i * sb_size + pass] = buff_middle[-4 + val];
+ }
+ }
+ return 1;
+}
+int CValueUnpacker::t3_7bits(int pass, int /*ind*/)
+{
+ //Eng: all the pairs of values from -5 to +5
+ // efficiency: 7/2 bits per value
+ //Rus: ��� ���������� ��� �� -5 �� +5
+ // �������������: 7/2 ���� �� �������� - ������
+ for (int i = 0; i < subblocks; i++) {
+ int bits = ( int ) ( get_bits( 7 ) & 0x7f );
+ unsigned char val = Table3[bits];
+
+ block_ptr[i * sb_size + pass] = buff_middle[-5 + ( val & 0xF )];
+ if (( ++i ) == subblocks)
+ break;
+ val >>= 4;
+ block_ptr[i * sb_size + pass] = buff_middle[-5 + val];
+ }
+ return 1;
+}
diff --git a/gemrb/plugins/ACMReader/unpacker.h b/gemrb/plugins/ACMReader/unpacker.h
new file mode 100644
index 0000000..64eaace
--- /dev/null
+++ b/gemrb/plugins/ACMReader/unpacker.h
@@ -0,0 +1,93 @@
+/* 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 _ACM_LAB_VALUE_UNPACKER_H
+#define _ACM_LAB_VALUE_UNPACKER_H
+
+#include "System/DataStream.h"
+
+#define UNPACKER_BUFFER_SIZE 16384
+
+class CValueUnpacker {
+private:
+ // Parameters of ACM stream
+ int levels, subblocks;
+ //FILE* file;
+ DataStream* stream;
+ // Bits
+ unsigned int next_bits; // new bits
+ int avail_bits; // count of new bits
+ unsigned char bits_buffer[UNPACKER_BUFFER_SIZE];
+ unsigned int buffer_bit_offset;
+
+ int sb_size, block_size;
+ short* amp_buffer, * buff_middle;
+ int* block_ptr;
+
+ // Reading routines
+ void prepare_bits(int bits); // request bits
+ int get_bits(int bits); // request and return next bits
+public:
+ // These functions are used to fill the buffer with the amplitude values
+ int return0(int pass, int ind);
+ int zero_fill(int pass, int ind);
+ int linear_fill(int pass, int ind);
+
+ int k1_3bits(int pass, int ind);
+ int k1_2bits(int pass, int ind);
+ int t1_5bits(int pass, int ind);
+
+ int k2_4bits(int pass, int ind);
+ int k2_3bits(int pass, int ind);
+ int t2_7bits(int pass, int ind);
+
+ int k3_5bits(int pass, int ind);
+ int k3_4bits(int pass, int ind);
+
+ int k4_5bits(int pass, int ind);
+ int k4_4bits(int pass, int ind);
+
+ int t3_7bits(int pass, int ind);
+
+
+ CValueUnpacker(int lev_cnt, int sb_count, DataStream* stream)
+ : levels( lev_cnt ), subblocks( sb_count ), next_bits( 0 ),
+ avail_bits( 0 ), buffer_bit_offset( UNPACKER_BUFFER_SIZE ),
+ sb_size( 1 << levels ),
+ block_size( sb_size*subblocks ), amp_buffer( NULL ),
+ buff_middle( NULL ), block_ptr( NULL )
+ {
+ this->stream = stream;
+ }
+ virtual ~CValueUnpacker()
+ {
+ if (amp_buffer) {
+ free(amp_buffer);
+ amp_buffer = NULL;
+ }
+ }
+
+ int init_unpacker();
+ int get_one_block(int* block);
+};
+
+typedef int (CValueUnpacker::* FillerProc) (int pass, int ind);
+
+#endif
diff --git a/gemrb/plugins/AREImporter/AREImporter.cpp b/gemrb/plugins/AREImporter/AREImporter.cpp
new file mode 100644
index 0000000..45e1a81
--- /dev/null
+++ b/gemrb/plugins/AREImporter/AREImporter.cpp
@@ -0,0 +1,2334 @@
+/* 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 "AREImporter.h"
+
+#include "win32def.h"
+#include "strrefs.h"
+
+#include "ActorMgr.h"
+#include "Ambient.h"
+#include "DataFileMgr.h"
+#include "DisplayMessage.h"
+#include "EffectMgr.h"
+#include "Game.h"
+#include "GameData.h"
+#include "ImageMgr.h"
+#include "Interface.h"
+#include "Palette.h"
+#include "ProjectileServer.h"
+#include "TileMapMgr.h"
+#include "Video.h"
+#include "GameScript/GameScript.h"
+#include "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.h"
+#include "System/CachedFileStream.h"
+#include "System/FileStream.h"
+
+#define DEF_OPEN 0
+#define DEF_CLOSE 1
+#define DEF_HOPEN 2
+#define DEF_HCLOSE 3
+
+#define DEF_COUNT 4
+
+#define DOOR_HIDDEN 128
+
+//something non signed, non ascii
+#define UNINITIALIZED_BYTE 0x11
+
+static ieResRef Sounds[DEF_COUNT] = {
+ {UNINITIALIZED_BYTE},
+};
+
+struct ResRefToStrRef {
+ ieResRef areaName;
+ ieStrRef text;
+ bool trackFlag;
+ int difficulty;
+};
+
+Holder<DataFileMgr> INInote;
+ResRefToStrRef *tracks = NULL;
+int trackcount = 0;
+
+void ReleaseMemory()
+{
+ INInote.release();
+
+ delete [] tracks;
+ tracks = NULL;
+}
+
+void ReadAutonoteINI()
+{
+ INInote = PluginHolder<DataFileMgr>(IE_INI_CLASS_ID);
+ FileStream* fs = new FileStream();
+ char tINInote[_MAX_PATH];
+ PathJoin( tINInote, core->GamePath, "autonote.ini", NULL );
+ fs->Open( tINInote, true );
+ INInote->Open( fs, true );
+}
+
+int GetTrackString(const ieResRef areaName)
+{
+ int i;
+ bool trackflag = displaymsg->HasStringReference(STR_TRACKING);
+
+ if (!tracks) {
+ AutoTable tm("tracking");
+ if (!tm.ok())
+ return -1;
+ trackcount = tm->GetRowCount();
+ tracks = new ResRefToStrRef[trackcount];
+ for (i=0;i<trackcount;i++) {
+ const char *poi = tm->QueryField(i,0);
+ if (poi[0]=='O' && poi[1]=='_') {
+ tracks[i].trackFlag=false;
+ poi+=2;
+ } else {
+ tracks[i].trackFlag=trackflag;
+ }
+ tracks[i].text=(ieStrRef) atoi(poi);
+ tracks[i].difficulty=atoi(tm->QueryField(i,1));
+ strnlwrcpy(tracks[i].areaName, tm->GetRowName(i), 8 );
+ }
+ }
+
+ for (i=0;i<trackcount;i++) {
+ if (!strnicmp(tracks[i].areaName, areaName, 8)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+AREImporter::AREImporter(void)
+{
+ autoFree = false;
+ str = NULL;
+ if (Sounds[0][0] == UNINITIALIZED_BYTE) {
+ memset( Sounds, 0, sizeof( Sounds ) );
+ AutoTable at("defsound");
+ if (at.ok()) {
+ for (int i = 0; i < DEF_COUNT; i++) {
+ strncpy( Sounds[i], at->QueryField( i, 0 ), 8 );
+ if(Sounds[i][0]=='*') {
+ Sounds[i][0]=0;
+ }
+ }
+ }
+ }
+}
+
+AREImporter::~AREImporter(void)
+{
+ if (autoFree) {
+ delete str;
+ }
+ Sounds[0][0]=UNINITIALIZED_BYTE;
+}
+
+bool AREImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (this->autoFree) {
+ delete str;
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+
+ if (strncmp( Signature, "AREAV1.0", 8 ) != 0) {
+ if (strncmp( Signature, "AREAV9.1", 8 ) != 0) {
+ return false;
+ } else {
+ bigheader = 16;
+ }
+ } else {
+ bigheader = 0;
+ }
+ //TEST VERSION: SKIPPING VALUES
+ str->ReadResRef( WEDResRef );
+ str->ReadDword( &LastSave );
+ str->ReadDword( &AreaFlags );
+ //skipping bg1 area connection fields
+ str->Seek( 0x48, GEM_STREAM_START );
+ str->ReadWord( &AreaType );
+ str->ReadWord( &WRain );
+ str->ReadWord( &WSnow );
+ str->ReadWord( &WFog );
+ str->ReadWord( &WLightning );
+ str->ReadWord( &WUnknown );
+ //bigheader gap is here
+ str->Seek( 0x54 + bigheader, GEM_STREAM_START );
+ str->ReadDword( &ActorOffset );
+ str->ReadWord( &ActorCount );
+ str->ReadWord( &InfoPointsCount );
+ str->ReadDword( &InfoPointsOffset );
+ str->ReadDword( &SpawnOffset );
+ str->ReadDword( &SpawnCount );
+ str->ReadDword( &EntrancesOffset );
+ str->ReadDword( &EntrancesCount );
+ str->ReadDword( &ContainersOffset );
+ str->ReadWord( &ContainersCount );
+ str->ReadWord( &ItemsCount );
+ str->ReadDword( &ItemsOffset );
+ str->ReadDword( &VerticesOffset );
+ str->ReadWord( &VerticesCount );
+ str->ReadWord( &AmbiCount );
+ str->ReadDword( &AmbiOffset );
+ str->ReadDword( &VariablesOffset );
+ str->ReadDword( &VariablesCount );
+ ieDword tmp;
+ str->ReadDword( &tmp );
+ str->ReadResRef( Script );
+ str->ReadDword( &ExploredBitmapSize );
+ str->ReadDword( &ExploredBitmapOffset );
+ str->ReadDword( &DoorsCount );
+ str->ReadDword( &DoorsOffset );
+ str->ReadDword( &AnimCount );
+ str->ReadDword( &AnimOffset );
+ str->ReadDword( &TileCount );
+ str->ReadDword( &TileOffset );
+ str->ReadDword( &SongHeader );
+ str->ReadDword( &RestHeader );
+ if (core->HasFeature(GF_AUTOMAP_INI) ) {
+ str->ReadDword( &tmp ); //skipping unknown in PST
+ }
+ str->ReadDword( &NoteOffset );
+ str->ReadDword( &NoteCount );
+ str->ReadDword( &TrapOffset );
+ str->ReadDword( &TrapCount );
+ str->ReadResRef( Dream1 );
+ str->ReadResRef( Dream2 );
+ return true;
+}
+
+//alter a map to the night/day version in case of an extended night map (bg2 specific)
+//return true, if change happened, in which case a movie is played by the Game object
+bool AREImporter::ChangeMap(Map *map, bool day_or_night)
+{
+ ieResRef TmpResRef;
+
+ //get the right tilemap name
+ if (day_or_night) {
+ memcpy( TmpResRef, map->WEDResRef, 9);
+ } else {
+ snprintf( TmpResRef, 9, "%sN", map->WEDResRef);
+ }
+ PluginHolder<TileMapMgr> tmm(IE_WED_CLASS_ID);
+ DataStream* wedfile = gamedata->GetResource( TmpResRef, IE_WED_CLASS_ID );
+ tmm->Open( wedfile );
+
+ //alter the tilemap object, not all parts of that object are coming from the wed/tis
+ //this is why we have to be careful
+ //TODO: consider refactoring TileMap so invariable data coming from the .ARE file
+ //are not handled by it, then TileMap could be simply swapped
+ TileMap* tm = map->GetTileMap();
+
+ if (tm) {
+ tm->ClearOverlays();
+ }
+ tm = tmm->GetTileMap(tm);
+ if (!tm) {
+ printf( "[AREImporter]: No Tile Map Available.\n" );
+ return false;
+ }
+
+ // Small map for MapControl
+ ResourceHolder<ImageMgr> sm(TmpResRef);
+ // small map is *optional*!
+
+ //the map state was altered, no need to hold this off for any later
+ map->DayNight = day_or_night;
+
+ //get the lightmap name
+ if (day_or_night) {
+ snprintf( TmpResRef, 9, "%sLM", map->WEDResRef);
+ } else {
+ snprintf( TmpResRef, 9, "%sLN", map->WEDResRef);
+ }
+
+ ResourceHolder<ImageMgr> lm(TmpResRef);
+ if (!lm) {
+ printf( "[AREImporter]: No lightmap available.\n" );
+ return false;
+ }
+
+ //alter the lightmap and the minimap (the tileset was already swapped)
+ map->ChangeTileMap(lm->GetImage(), sm?sm->GetSprite2D():NULL);
+ return true;
+}
+
+Map* AREImporter::GetMap(const char *ResRef, bool day_or_night)
+{
+ unsigned int i,x;
+
+ // if this area does not have extended night, force it to day mode
+ if (!(AreaFlags & AT_EXTENDED_NIGHT))
+ day_or_night = true;
+
+ Map* map = new Map();
+ if(!map) {
+ printf("Can't allocate map (out of memory).\n");
+ abort();
+ }
+ if (core->SaveAsOriginal) {
+ map->version = bigheader;
+ }
+
+ map->AreaFlags = AreaFlags;
+ map->Rain = WRain;
+ map->Snow = WSnow;
+ map->Fog = WFog;
+ map->Lightning = WLightning;
+ map->AreaType = AreaType;
+ map->DayNight = day_or_night;
+ strnlwrcpy( map->WEDResRef, WEDResRef, 8);
+ strnlwrcpy( map->Dream[0], Dream1, 8);
+ strnlwrcpy( map->Dream[1], Dream2, 8);
+
+ //we have to set this here because the actors will receive their
+ //current area setting here, areas' 'scriptname' is their name
+ map->SetScriptName( ResRef );
+ int idx = GetTrackString( ResRef );
+ if (idx>=0) {
+ map->SetTrackString(tracks[idx].text, tracks[idx].trackFlag, tracks[idx].difficulty);
+ } else {
+ map->SetTrackString((ieStrRef) -1, false, 0);
+ }
+
+ if (!core->IsAvailable( IE_WED_CLASS_ID )) {
+ printf( "[AREImporter]: No Tile Map Manager Available.\n" );
+ return NULL;
+ }
+ ieResRef TmpResRef;
+
+ if (day_or_night) {
+ memcpy( TmpResRef, WEDResRef, 9);
+ } else {
+ snprintf( TmpResRef, 9, "%sN", WEDResRef);
+ }
+
+ PluginHolder<TileMapMgr> tmm(IE_WED_CLASS_ID);
+ DataStream* wedfile = gamedata->GetResource( WEDResRef, IE_WED_CLASS_ID );
+ tmm->Open( wedfile );
+
+ //there was no tilemap set yet, so lets just send a NULL
+ TileMap* tm = tmm->GetTileMap(NULL);
+ if (!tm) {
+ printf( "[AREImporter]: No Tile Map Available.\n" );
+ return NULL;
+ }
+
+ // Small map for MapControl
+ // small map is *optional*!
+ ResourceHolder<ImageMgr> sm(TmpResRef);
+
+ //if the Script field is empty, the area name will be copied into it on first load
+ //this works only in the iwd branch of the games
+ if (!Script[0] && core->HasFeature(GF_FORCE_AREA_SCRIPT) ) {
+ memcpy(Script, ResRef, sizeof(ieResRef) );
+ }
+
+ if (Script[0]) {
+ //for some reason the area's script is run from the last slot
+ //at least one area script depends on this, if you need something
+ //more customisable, add a game flag
+ map->Scripts[MAX_SCRIPTS-1] = new GameScript( Script, map );
+ }
+
+ if (day_or_night) {
+ snprintf( TmpResRef, 9, "%sLM", WEDResRef);
+ } else {
+ snprintf( TmpResRef, 9, "%sLN", WEDResRef);
+ }
+
+ ResourceHolder<ImageMgr> lm(TmpResRef);
+ if (!lm) {
+ printf( "[AREImporter]: No lightmap available.\n" );
+ return NULL;
+ }
+
+ snprintf( TmpResRef, 9, "%sSR", WEDResRef);
+
+ ResourceHolder<ImageMgr> sr(TmpResRef);
+ if (!sr) {
+ printf( "[AREImporter]: No searchmap available.\n" );
+ return NULL;
+ }
+
+ snprintf( TmpResRef, 9, "%sHT", WEDResRef);
+
+ ResourceHolder<ImageMgr> hm(TmpResRef);
+ if (!hm) {
+ printf( "[AREImporter]: No heightmap available.\n" );
+ return NULL;
+ }
+
+ map->AddTileMap( tm, lm->GetImage(), sr->GetBitmap(), sm ? sm->GetSprite2D() : NULL, hm->GetBitmap() );
+
+ str->Seek( SongHeader, GEM_STREAM_START );
+ //5 is the number of song indices
+ for (i = 0; i < MAX_RESCOUNT; i++) {
+ str->ReadDword( map->SongHeader.SongList + i );
+ }
+ str->Seek( RestHeader + 32, GEM_STREAM_START );
+ for (i = 0; i < MAX_RESCOUNT; i++) {
+ str->ReadDword( map->RestHeader.Strref + i );
+ }
+ for (i = 0; i < MAX_RESCOUNT; i++) {
+ str->ReadResRef( map->RestHeader.CreResRef[i] );
+ }
+ str->ReadWord( &map->RestHeader.CreatureNum );
+ if( map->RestHeader.CreatureNum>MAX_RESCOUNT ) {
+ map->RestHeader.CreatureNum = MAX_RESCOUNT;
+ }
+ str->ReadWord( &map->RestHeader.Difficulty); //difficulty?
+ str->ReadDword( &map->RestHeader.sduration); //spawn duration
+ str->ReadWord( &map->RestHeader.rwdist); //random walk distance
+ str->ReadWord( &map->RestHeader.owdist); //other walk distance
+ str->ReadWord( &map->RestHeader.Maximum); //maximum number of creatures
+ str->ReadWord( &map->RestHeader.Enabled);
+ str->ReadWord( &map->RestHeader.DayChance );
+ str->ReadWord( &map->RestHeader.NightChance );
+
+ printf( "Loading regions\n" );
+ core->LoadProgress(70);
+ //Loading InfoPoints
+ for (i = 0; i < InfoPointsCount; i++) {
+ str->Seek( InfoPointsOffset + ( i * 0xC4 ), GEM_STREAM_START );
+ ieWord Type, VertexCount;
+ ieDword FirstVertex, Cursor, Flags;
+ ieWord TrapDetDiff, TrapRemDiff, Trapped, TrapDetected;
+ ieWord LaunchX, LaunchY;
+ ieWord PosX, PosY;
+ ieWord TalkX, TalkY;
+ ieVariable Name, Entrance;
+ ieResRef Script, KeyResRef, Destination;
+ ieResRef DialogResRef, WavResRef; //adopted pst specific fields
+ ieStrRef DialogName;
+ str->Read( Name, 32 );
+ Name[32] = 0;
+ str->ReadWord( &Type );
+ Region bbox;
+ ieWord tmp;
+ str->ReadWord( &tmp );
+ bbox.x = tmp;
+ str->ReadWord( &tmp );
+ bbox.y = tmp;
+ str->ReadWord( &tmp );
+ bbox.w = tmp - bbox.x;
+ str->ReadWord( &tmp );
+ bbox.h = tmp - bbox.y;
+ str->ReadWord( &VertexCount );
+ str->ReadDword( &FirstVertex );
+ ieDword tmp2;
+ str->ReadDword( &tmp2 );
+ str->ReadDword( &Cursor );
+ str->ReadResRef( Destination );
+ str->Read( Entrance, 32 );
+ Entrance[32] = 0;
+ str->ReadDword( &Flags );
+ ieStrRef StrRef;
+ str->ReadDword( &StrRef );
+ str->ReadWord( &TrapDetDiff );
+ str->ReadWord( &TrapRemDiff );
+ str->ReadWord( &Trapped );
+ str->ReadWord( &TrapDetected );
+ str->ReadWord( &LaunchX );
+ str->ReadWord( &LaunchY );
+ str->ReadResRef( KeyResRef );
+ str->ReadResRef( Script );
+ str->ReadWord( &PosX);
+ str->ReadWord( &PosY);
+ //maybe we have to store this
+ str->Seek( 36, GEM_CURRENT_POS );
+ str->ReadResRef( WavResRef );
+ str->ReadWord( &TalkX);
+ str->ReadWord( &TalkY);
+ str->ReadDword( &DialogName );
+ str->ReadResRef( DialogResRef );
+ char* string = core->GetString( StrRef );
+ str->Seek( VerticesOffset + ( FirstVertex * 4 ), GEM_STREAM_START );
+ Point* points = ( Point* ) malloc( VertexCount*sizeof( Point ) );
+ for (x = 0; x < VertexCount; x++) {
+ str->ReadWord( (ieWord*) &points[x].x );
+ str->ReadWord( (ieWord*) &points[x].y );
+ }
+ Gem_Polygon* poly = new Gem_Polygon( points, VertexCount, &bbox);
+ free( points );
+ InfoPoint* ip = tm->AddInfoPoint( Name, Type, poly );
+ ip->TrapDetectionDiff = TrapDetDiff;
+ ip->TrapRemovalDiff = TrapRemDiff;
+ ip->Trapped = Trapped;
+ ip->TrapDetected = TrapDetected;
+ ip->TrapLaunch.x = LaunchX;
+ ip->TrapLaunch.y = LaunchY;
+ // translate door cursor on infopoint to correct cursor
+ if (Cursor == IE_CURSOR_DOOR) Cursor = IE_CURSOR_PASS;
+ ip->Cursor = Cursor;
+ ip->overHeadText = string;
+ ip->StrRef = StrRef; //we need this when saving area
+ ip->textDisplaying = 0;
+ ip->timeStartDisplaying = 0;
+ ip->SetMap(map);
+ ip->Flags = Flags;
+ ip->UsePoint.x = PosX;
+ ip->UsePoint.y = PosY;
+ //FIXME: PST doesn't use this field
+ if (ip->Flags&TRAP_USEPOINT) {
+ ip->Pos = ip->UsePoint;
+ } else {
+ ip->Pos.x = bbox.x + ( bbox.w / 2 );
+ ip->Pos.y = bbox.y + ( bbox.h / 2 );
+ }
+ memcpy( ip->Destination, Destination, sizeof(Destination) );
+ memcpy( ip->EntranceName, Entrance, sizeof(Entrance) );
+ memcpy( ip->KeyResRef, KeyResRef, sizeof(KeyResRef) );
+
+ //these appear only in PST, but we could support them everywhere
+ ip->TalkPos.x=TalkX;
+ ip->TalkPos.y=TalkY;
+ ip->DialogName=DialogName;
+ ip->SetDialog(DialogResRef);
+ ip->SetEnter(WavResRef);
+
+ if (Script[0]) {
+ ip->Scripts[0] = new GameScript( Script, ip );
+ } else {
+ ip->Scripts[0] = NULL;
+ }
+ }
+
+ printf( "Loading containers\n" );
+ //Loading Containers
+ for (i = 0; i < ContainersCount; i++) {
+ str->Seek( ContainersOffset + ( i * 0xC0 ), GEM_STREAM_START );
+ ieVariable Name;
+ ieWord Type, LockDiff;
+ ieDword Flags;
+ ieWord TrapDetDiff, TrapRemDiff, Trapped, TrapDetected;
+ ieWord XPos, YPos;
+ ieWord LaunchX, LaunchY;
+ ieDword ItemIndex, ItemCount;
+ ieResRef KeyResRef;
+ ieStrRef OpenFail;
+
+ str->Read( Name, 32 );
+ Name[32] = 0;
+ str->ReadWord( &XPos );
+ str->ReadWord( &YPos );
+ str->ReadWord( &Type );
+ str->ReadWord( &LockDiff );
+ str->ReadDword( &Flags );
+ str->ReadWord( &TrapDetDiff );
+ str->ReadWord( &TrapRemDiff );
+ str->ReadWord( &Trapped );
+ str->ReadWord( &TrapDetected );
+ str->ReadWord( &LaunchX );
+ str->ReadWord( &LaunchY );
+ Region bbox;
+ ieWord tmp;
+ str->ReadWord( &tmp );
+ bbox.x = tmp;
+ str->ReadWord( &tmp );
+ bbox.y = tmp;
+ str->ReadWord( &tmp );
+ bbox.w = tmp - bbox.x;
+ str->ReadWord( &tmp );
+ bbox.h = tmp - bbox.y;
+ str->ReadDword( &ItemIndex );
+ str->ReadDword( &ItemCount );
+ str->ReadResRef( Script );
+ ieDword firstIndex;
+ ieWord vertCount, unknown;
+ str->ReadDword( &firstIndex );
+ //the vertex count is only 16 bits, there is a weird flag
+ //after it, which is usually 0, but sometimes set to 1
+ str->ReadWord( &vertCount );
+ str->ReadWord( &unknown );
+ //str->Read( Name, 32 );
+ str->Seek( 32, GEM_CURRENT_POS);
+ str->ReadResRef( KeyResRef);
+ str->Seek( 4, GEM_CURRENT_POS);
+ str->ReadDword( &OpenFail );
+
+ str->Seek( VerticesOffset + ( firstIndex * 4 ), GEM_STREAM_START );
+ Point* points = ( Point* ) malloc( vertCount*sizeof( Point ) );
+ for (x = 0; x < vertCount; x++) {
+ ieWord tmp;
+ str->ReadWord( &tmp );
+ points[x].x = tmp;
+ str->ReadWord( &tmp );
+ points[x].y = tmp;
+ }
+ if (vertCount == 0 && bbox.w == 0 && bbox.h == 0) {
+ /* piles have no polygons and no bounding box in some areas,
+ * but bg2 gives them this bounding box at first load,
+ * should we specifically check for Type==IE_CONTAINER_PILE? */
+ bbox.x = XPos - 7;
+ bbox.y = YPos - 5;
+ bbox.w = 16;
+ bbox.h = 12;
+ }
+ Gem_Polygon* poly = new Gem_Polygon( points, vertCount, &bbox );
+ free( points );
+ Container* c = map->AddContainer( Name, Type, poly );
+ //c->SetMap(map);
+ c->Pos.x = XPos;
+ c->Pos.y = YPos;
+ c->LockDifficulty = LockDiff;
+ c->Flags = Flags;
+ c->TrapDetectionDiff = TrapDetDiff;
+ c->TrapRemovalDiff = TrapRemDiff;
+ c->Trapped = Trapped;
+ c->TrapDetected = TrapDetected;
+ c->TrapLaunch.x = LaunchX;
+ c->TrapLaunch.y = LaunchY;
+ //reading items into a container
+ str->Seek( ItemsOffset+( ItemIndex * 0x14 ), GEM_STREAM_START);
+ while(ItemCount--) {
+ //cannot add directly to inventory (ground piles)
+ c->AddItem( core->ReadItem(str));
+ }
+ //update item flags (like movable flag)
+ c->inventory.CalculateWeight();
+
+ if (Type==IE_CONTAINER_PILE)
+ Script[0]=0;
+
+ if (Script[0]) {
+ c->Scripts[0] = new GameScript( Script, c );
+ } else {
+ c->Scripts[0] = NULL;
+ }
+ strnlwrcpy(c->KeyResRef, KeyResRef, 8);
+ if (!OpenFail) OpenFail = (ieStrRef)-1; // rewrite 0 to -1
+ c->OpenFail = OpenFail;
+ }
+
+ printf( "Loading doors\n" );
+ //Loading Doors
+ for (i = 0; i < DoorsCount; i++) {
+ str->Seek( DoorsOffset + ( i * 0xc8 ), GEM_STREAM_START );
+ int count;
+ ieDword Flags;
+ ieDword OpenFirstVertex, ClosedFirstVertex;
+ ieDword OpenFirstImpeded, ClosedFirstImpeded;
+ ieWord OpenVerticesCount, ClosedVerticesCount;
+ ieWord OpenImpededCount, ClosedImpededCount;
+ ieVariable LongName, LinkedInfo;
+ ieResRef ShortName;
+ ieWord minX, maxX, minY, maxY;
+ ieDword cursor;
+ ieResRef KeyResRef, Script;
+ ieWord TrapDetect, TrapRemoval;
+ ieWord Trapped, TrapDetected;
+ ieWord LaunchX, LaunchY;
+ ieDword DiscoveryDiff, LockRemoval;
+ Region BBClosed, BBOpen;
+ ieStrRef OpenStrRef;
+ ieStrRef NameStrRef;
+ ieResRef Dialog;
+ ieDword Unknown54;
+
+ str->Read( LongName, 32 );
+ LongName[32] = 0;
+ str->ReadResRef( ShortName );
+ str->ReadDword( &Flags );
+ str->ReadDword( &OpenFirstVertex );
+ str->ReadWord( &OpenVerticesCount );
+ str->ReadWord( &ClosedVerticesCount );
+ str->ReadDword( &ClosedFirstVertex );
+ str->ReadWord( &minX );
+ str->ReadWord( &minY );
+ str->ReadWord( &maxX );
+ str->ReadWord( &maxY );
+ BBOpen.x = minX;
+ BBOpen.y = minY;
+ BBOpen.w = maxX - minX;
+ BBOpen.h = maxY - minY;
+ str->ReadWord( &minX );
+ str->ReadWord( &minY );
+ str->ReadWord( &maxX );
+ str->ReadWord( &maxY );
+ BBClosed.x = minX;
+ BBClosed.y = minY;
+ BBClosed.w = maxX - minX;
+ BBClosed.h = maxY - minY;
+ str->ReadDword( &OpenFirstImpeded );
+ str->ReadWord( &OpenImpededCount );
+ str->ReadWord( &ClosedImpededCount );
+ str->ReadDword( &ClosedFirstImpeded );
+ str->ReadDword( &Unknown54);
+ ieResRef OpenResRef, CloseResRef;
+ str->ReadResRef( OpenResRef );
+ str->ReadResRef( CloseResRef );
+ str->ReadDword( &cursor );
+ str->ReadWord( &TrapDetect );
+ str->ReadWord( &TrapRemoval );
+ str->ReadWord( &Trapped );
+ str->ReadWord( &TrapDetected );
+ str->ReadWord( &LaunchX );
+ str->ReadWord( &LaunchY );
+ str->ReadResRef( KeyResRef );
+ str->ReadResRef( Script );
+ str->ReadDword( &DiscoveryDiff );
+ str->ReadDword( &LockRemoval );
+ Point toOpen[2];
+ str->ReadWord( &minX );
+ toOpen[0].x = minX;
+ str->ReadWord( &minY );
+ toOpen[0].y = minY;
+ str->ReadWord( &maxX );
+ toOpen[1].x = maxX;
+ str->ReadWord( &maxY );
+ toOpen[1].y = maxY;
+ str->ReadDword( &OpenStrRef);
+ if (core->HasFeature(GF_AUTOMAP_INI) ) {
+ str->Read( LinkedInfo, 24);
+ LinkedInfo[24] = 0; // LinkedInfo unused in pst anyway?
+ } else {
+ str->Read( LinkedInfo, 32);
+ }
+ str->ReadDword( &NameStrRef);
+ str->ReadResRef( Dialog );
+ if (core->HasFeature(GF_AUTOMAP_INI) ) {
+ // maybe this is important? but seems not
+ str->Seek( 8, GEM_CURRENT_POS );
+ }
+
+ //Reading Open Polygon
+ str->Seek( VerticesOffset + ( OpenFirstVertex * 4 ), GEM_STREAM_START );
+ Point* points = ( Point* )
+ malloc( OpenVerticesCount*sizeof( Point ) );
+ for (x = 0; x < OpenVerticesCount; x++) {
+ str->ReadWord( &minX );
+ points[x].x = minX;
+ str->ReadWord( &minY );
+ points[x].y = minY;
+ }
+ Gem_Polygon* open = new Gem_Polygon( points, OpenVerticesCount, &BBOpen );
+ free( points );
+
+ //Reading Closed Polygon
+ str->Seek( VerticesOffset + ( ClosedFirstVertex * 4 ),
+ GEM_STREAM_START );
+ points = ( Point * ) malloc( ClosedVerticesCount * sizeof( Point ) );
+ for (x = 0; x < ClosedVerticesCount; x++) {
+ str->ReadWord( &minX );
+ points[x].x = minX;
+ str->ReadWord( &minY );
+ points[x].y = minY;
+ }
+ Gem_Polygon* closed = new Gem_Polygon( points, ClosedVerticesCount, &BBClosed );
+ free( points );
+
+ //Getting Door Information from the WED File
+ bool BaseClosed;
+ unsigned short * indices = tmm->GetDoorIndices( ShortName, &count, BaseClosed );
+ if (core->HasFeature(GF_REVERSE_DOOR)) {
+ BaseClosed = !BaseClosed;
+ }
+
+ Door* door;
+ door = tm->AddDoor( ShortName, LongName, Flags, BaseClosed,
+ indices, count, open, closed );
+
+ tmm->SetupClosedDoor(door->closed_wg_index, door->closed_wg_count);
+ tmm->SetupOpenDoor(door->open_wg_index, door->open_wg_count);
+
+ //Reading Open Impeded blocks
+ str->Seek( VerticesOffset + ( OpenFirstImpeded * 4 ),
+ GEM_STREAM_START );
+ points = ( Point * ) malloc( OpenImpededCount * sizeof( Point ) );
+ for (x = 0; x < OpenImpededCount; x++) {
+ str->ReadWord( &minX );
+ points[x].x = minX;
+ str->ReadWord( &minY );
+ points[x].y = minY;
+ }
+ door->open_ib = points;
+ door->oibcount = OpenImpededCount;
+
+ //Reading Closed Impeded blocks
+ str->Seek( VerticesOffset + ( ClosedFirstImpeded * 4 ),
+ GEM_STREAM_START );
+ points = ( Point * ) malloc( ClosedImpededCount * sizeof( Point ) );
+ for (x = 0; x < ClosedImpededCount; x++) {
+ str->ReadWord( &minX );
+ points[x].x = minX;
+ str->ReadWord( &minY );
+ points[x].y = minY;
+ }
+ door->closed_ib = points;
+ door->cibcount = ClosedImpededCount;
+ door->SetMap(map);
+
+ door->Unknown54 = Unknown54;
+ door->TrapDetectionDiff = TrapDetect;
+ door->TrapRemovalDiff = TrapRemoval;
+ door->Trapped = Trapped;
+ door->TrapDetected = TrapDetected;
+ door->TrapLaunch.x = LaunchX;
+ door->TrapLaunch.y = LaunchY;
+
+ door->Cursor = cursor;
+ memcpy( door->KeyResRef, KeyResRef, sizeof(KeyResRef) );
+ if (Script[0]) {
+ door->Scripts[0] = new GameScript( Script, door );
+ } else {
+ door->Scripts[0] = NULL;
+ }
+
+ door->toOpen[0] = toOpen[0];
+ door->toOpen[1] = toOpen[1];
+ //Leave the default sound untouched
+ if (OpenResRef[0])
+ memcpy( door->OpenSound, OpenResRef, sizeof(OpenResRef) );
+ else {
+ if (Flags & DOOR_HIDDEN)
+ memcpy( door->OpenSound, Sounds[DEF_HOPEN], 9 );
+ else
+ memcpy( door->OpenSound, Sounds[DEF_OPEN], 9 );
+ }
+ if (CloseResRef[0])
+ memcpy( door->CloseSound, CloseResRef, sizeof(CloseResRef) );
+ else {
+ if (Flags & DOOR_HIDDEN)
+ memcpy( door->CloseSound, Sounds[DEF_HCLOSE], 9 );
+ else
+ memcpy( door->CloseSound, Sounds[DEF_CLOSE], 9 );
+ }
+ door->DiscoveryDiff=DiscoveryDiff;
+ door->LockDifficulty=LockRemoval;
+ if (!OpenStrRef) OpenStrRef = (ieStrRef)-1; // rewrite 0 to -1
+ door->OpenStrRef=OpenStrRef;
+ strnspccpy(door->LinkedInfo, LinkedInfo, 32);
+ //these 2 fields are not sure
+ door->NameStrRef=NameStrRef;
+ door->SetDialog(Dialog);
+ }
+
+ printf( "Loading spawnpoints\n" );
+ //Loading SpawnPoints
+ for (i = 0; i < SpawnCount; i++) {
+ str->Seek( SpawnOffset + (i*0xc8), GEM_STREAM_START );
+ ieVariable Name;
+ ieWord XPos, YPos;
+ ieWord Count, Difficulty, Frequency, Method;
+ ieWord Maximum, Enabled;
+ ieResRef creatures[MAX_RESCOUNT];
+ ieWord DayChance, NightChance;
+ ieDword Schedule;
+ ieDword sduration;
+ ieWord rwdist, owdist;
+
+ str->Read( Name, 32 );
+ Name[32] = 0;
+ str->ReadWord( &XPos );
+ str->ReadWord( &YPos );
+ for (unsigned int j = 0;j < MAX_RESCOUNT; j++) {
+ str->ReadResRef( creatures[j] );
+ }
+ str->ReadWord( &Count);
+ str->ReadWord( &Difficulty);
+ str->ReadWord( &Frequency );
+ str->ReadWord( &Method);
+ str->ReadDword( &sduration); //time to live for spawns
+ str->ReadWord( &rwdist); //random walk distance (0 is unlimited)
+ str->ReadWord( &owdist); //other walk distance (inactive in all engines?)
+ str->ReadWord( &Maximum);
+ str->ReadWord( &Enabled);
+ str->ReadDword( &Schedule);
+ str->ReadWord( &DayChance);
+ str->ReadWord( &NightChance);
+
+ Spawn *sp = map->AddSpawn(Name, XPos, YPos, creatures, Count);
+ sp->Difficulty = Difficulty;
+ //this value is used in a division, better make it nonzero now
+ //this will fix any old gemrb saves vs. the original engine
+ if (!Frequency) {
+ Frequency = 1;
+ }
+ sp->Frequency = Frequency;
+ sp->Method = Method;
+ sp->sduration = sduration;
+ sp->rwdist = rwdist;
+ sp->owdist = owdist;
+ sp->Maximum = Maximum;
+ sp->Enabled = Enabled;
+ sp->appearance = Schedule;
+ sp->DayChance = DayChance;
+ sp->NightChance = NightChance;
+ //the rest is not read, we seek for every record
+ }
+
+ core->LoadProgress(75);
+ printf( "Loading actors\n" );
+ //Loading Actors
+ str->Seek( ActorOffset, GEM_STREAM_START );
+ if (!core->IsAvailable( IE_CRE_CLASS_ID )) {
+ printf( "[AREImporter]: No Actor Manager Available, skipping actors\n" );
+ } else {
+ PluginHolder<ActorMgr> actmgr(IE_CRE_CLASS_ID);
+ for (i = 0; i < ActorCount; i++) {
+ ieVariable DefaultName;
+ ieResRef CreResRef;
+ ieDword TalkCount;
+ ieDword Orientation, Schedule, RemovalTime;
+ ieWord XPos, YPos, XDes, YDes;
+ ieResRef Dialog;
+ ieResRef Scripts[8]; //the original order
+ ieDword Flags;
+ str->Read( DefaultName, 32);
+ DefaultName[32]=0;
+ str->ReadWord( &XPos );
+ str->ReadWord( &YPos );
+ str->ReadWord( &XDes );
+ str->ReadWord( &YDes );
+ str->ReadDword( &Flags );
+ str->Seek( 8, GEM_CURRENT_POS );
+ str->ReadDword( &Orientation );
+ str->ReadDword( &RemovalTime );
+ str->Seek( 4, GEM_CURRENT_POS );
+ str->ReadDword( &Schedule );
+ str->ReadDword( &TalkCount );
+ str->ReadResRef( Dialog );
+ //TODO: script order
+ memset(Scripts,0,sizeof(Scripts));
+
+ str->ReadResRef( Scripts[SCR_OVERRIDE] );
+ str->ReadResRef( Scripts[SCR_CLASS] );
+ str->ReadResRef( Scripts[SCR_RACE] );
+ str->ReadResRef( Scripts[SCR_GENERAL] );
+ str->ReadResRef( Scripts[SCR_DEFAULT] );
+ str->ReadResRef( Scripts[SCR_SPECIFICS] );
+ str->ReadResRef( CreResRef );
+ DataStream* crefile;
+ Actor *ab;
+ ieDword CreOffset, CreSize;
+ str->ReadDword( &CreOffset );
+ str->ReadDword( &CreSize );
+ //TODO: iwd2 script?
+ str->ReadResRef( Scripts[SCR_AREA] );
+ str->Seek( 120, GEM_CURRENT_POS );
+ //not iwd2, this field is garbage
+ if (!core->HasFeature(GF_IWD2_SCRIPTNAME)) {
+ Scripts[SCR_AREA][0]=0;
+ }
+ //actually, Flags&1 signs that the creature
+ //is not loaded yet, so !(Flags&1) means it is embedded
+ if (CreOffset != 0 && !(Flags&1) ) {
+ CachedFileStream *fs = new CachedFileStream( (CachedFileStream *) str, CreOffset, CreSize, true);
+ crefile = (DataStream *) fs;
+ } else {
+ crefile = gamedata->GetResource( CreResRef, IE_CRE_CLASS_ID );
+ }
+ if(!actmgr->Open( crefile, true )) {
+ printf("Couldn't read actor: %s!\n", CreResRef);
+ continue;
+ }
+ ab = actmgr->GetActor(0);
+ if(!ab)
+ continue;
+ map->AddActor(ab);
+ ab->Pos.x = XPos;
+ ab->Pos.y = YPos;
+ ab->Destination.x = XDes;
+ ab->Destination.y = YDes;
+ //copying the scripting name into the actor
+ //if the CreatureAreaFlag was set to 8
+ if ((Flags&AF_NAME_OVERRIDE) || (core->HasFeature(GF_IWD2_SCRIPTNAME)) ) {
+ ab->SetScriptName(DefaultName);
+ }
+
+ if (Dialog[0]) {
+ ab->SetDialog(Dialog);
+ }
+ for (int j=0;j<8;j++) {
+ if (Scripts[j][0]) {
+ ab->SetScript(Scripts[j],j);
+ }
+ }
+ ab->SetOrientation( Orientation,0 );
+ ab->appearance = Schedule;
+ ab->TalkCount = TalkCount;
+ // TODO: remove corpse at removal time?
+ ab->RemovalTime = RemovalTime;
+ ab->RefreshEffects(NULL);
+ }
+ }
+
+ core->LoadProgress(90);
+ printf( "Loading animations\n" );
+ //Loading Animations
+ str->Seek( AnimOffset, GEM_STREAM_START );
+ if (!core->IsAvailable( IE_BAM_CLASS_ID )) {
+ printf( "[AREImporter]: No Animation Manager Available, skipping animations\n" );
+ } else {
+ for (i = 0; i < AnimCount; i++) {
+ AreaAnimation* anim = new AreaAnimation();
+ str->Read(anim->Name, 32);
+ ieWord animX, animY;
+ str->ReadWord( &animX );
+ str->ReadWord( &animY );
+ anim->Pos.x=animX;
+ anim->Pos.y=animY;
+ str->ReadDword( &anim->appearance );
+ str->ReadResRef( anim->BAM );
+ str->ReadWord( &anim->sequence );
+ str->ReadWord( &anim->frame );
+ str->ReadDword( &anim->Flags );
+ str->ReadWordSigned( &anim->height );
+ str->ReadWord( &anim->transparency );
+ str->ReadWord( &anim->unknown3c ); //not completely understood, if not 0, sequence is started
+ str->Read( &anim->startchance,1 );
+ if (anim->startchance<=0) {
+ anim->startchance=100; //percentage of starting a cycle
+ }
+ str->Read( &anim->skipcycle,1 ); //how many cycles are skipped (100% skippage)
+ str->ReadResRef( anim->PaletteRef );
+ str->ReadDword( &anim->unknown48 );
+
+ //set up the animation, it cannot be done here
+ //because a StaticSequence action can change
+ //it later
+ map->AddAnimation( anim );
+ //the animation was safely transferred to internal memory
+ delete anim;
+ }
+ }
+
+ printf( "Loading entrances\n" );
+ //Loading Entrances
+ str->Seek( EntrancesOffset, GEM_STREAM_START );
+ for (i = 0; i < EntrancesCount; i++) {
+ ieVariable Name;
+ ieWord XPos, YPos, Face;
+ str->Read( Name, 32 );
+ Name[32] = 0;
+ str->ReadWord( &XPos );
+ str->ReadWord( &YPos );
+ str->ReadWord( &Face );
+ str->Seek( 66, GEM_CURRENT_POS );
+ map->AddEntrance( Name, XPos, YPos, Face );
+ }
+
+ printf( "Loading variables\n" );
+ map->locals->LoadInitialValues(ResRef);
+ //Loading Variables
+ str->Seek( VariablesOffset, GEM_STREAM_START );
+ for (i = 0; i < VariablesCount; i++) {
+ ieVariable Name;
+ ieDword Value;
+ str->Read( Name, 32 );
+ Name[32] = 0;
+ str->Seek( 8, GEM_CURRENT_POS );
+ str->ReadDword( &Value );
+ str->Seek( 40, GEM_CURRENT_POS );
+ map->locals->SetAt( Name, Value );
+ }
+
+ printf( "Loading ambients\n" );
+ str->Seek( AmbiOffset, GEM_STREAM_START );
+ for (i = 0; i < AmbiCount; i++) {
+ int j;
+ ieResRef sounds[MAX_RESCOUNT];
+ ieWord tmpWord;
+
+ Ambient *ambi = new Ambient();
+ str->Read( &ambi->name, 32 );
+ str->ReadWord( &tmpWord );
+ ambi->origin.x = tmpWord;
+ str->ReadWord( &tmpWord );
+ ambi->origin.y = tmpWord;
+ str->ReadWord( &ambi->radius );
+ str->ReadWord( &ambi->height );
+ str->Seek( 6, GEM_CURRENT_POS );
+ str->ReadWord( &ambi->gain );
+ for (j = 0;j < MAX_RESCOUNT; j++) {
+ str->ReadResRef( sounds[j] );
+ }
+ str->ReadWord( &tmpWord );
+ str->Seek( 2, GEM_CURRENT_POS );
+ str->ReadDword( &ambi->interval );
+ str->ReadDword( &ambi->perset );
+ // schedule bits
+ str->ReadDword( &ambi->appearance );
+ str->ReadDword( &ambi->flags );
+ str->Seek( 64, GEM_CURRENT_POS );
+ //this is a physical limit
+ if (tmpWord>MAX_RESCOUNT) {
+ tmpWord=MAX_RESCOUNT;
+ }
+ for (j = 0; j < tmpWord; j++) {
+ char *sound = (char *) malloc(9);
+ memcpy(sound, sounds[j], 9);
+ ambi->sounds.push_back(sound);
+ }
+ map->AddAmbient(ambi);
+ }
+
+ printf( "Loading automap notes\n" );
+ str->Seek( NoteOffset, GEM_STREAM_START );
+
+ //this feature exists in all blackisle games but not in bioware games
+ if (core->HasFeature(GF_SPAWN_INI)) {
+ map->LoadIniSpawn();
+ }
+
+ Point point;
+ ieDword color;
+ char *text;
+
+ //Don't bother with autonote.ini if the area has autonotes (ie. it is a saved area)
+ int pst = core->HasFeature( GF_AUTOMAP_INI );
+ if (pst && !NoteCount) {
+ if( !INInote ) {
+ ReadAutonoteINI();
+ }
+ //add autonote.ini entries
+ if( INInote ) {
+ color = 1; //read only note
+ int count = INInote->GetKeyAsInt( map->GetScriptName(), "count", 0);
+ while (count) {
+ char key[32];
+ int value;
+ sprintf(key, "text%d",count);
+ value = INInote->GetKeyAsInt( map->GetScriptName(), key, 0);
+ text = core->GetString(value);
+ sprintf(key, "xPos%d",count);
+ value = INInote->GetKeyAsInt( map->GetScriptName(), key, 0);
+ point.x = value;
+ sprintf(key, "yPos%d",count);
+ value = INInote->GetKeyAsInt( map->GetScriptName(), key, 0);
+ point.y = value;
+ map->AddMapNote( point, color, text, 0);
+ count--;
+ }
+ }
+ }
+ for (i = 0; i < NoteCount; i++) {
+ ieStrRef strref = 0;
+
+ if (pst) {
+ ieDword px,py;
+
+ str->ReadDword(&px);
+ str->ReadDword(&py);
+ point.x=px;
+ point.y=py;
+ text = (char *) malloc( 500 );
+ str->Read(text, 500 );
+ text[499] = 0;
+ str->ReadDword(&color); //readonly == 1
+ str->Seek(20, GEM_CURRENT_POS);
+ //+1 for the terminating zero!!!
+ text = (char *) realloc( text, strlen(text)+1 );
+ }
+ else {
+ ieWord px,py;
+
+ str->ReadWord( &px );
+ str->ReadWord( &py );
+ point.x=px;
+ point.y=py;
+ str->ReadDword( &strref );
+ str->ReadWord( &px );
+ str->ReadWord( &py );
+ color=py;
+ str->Seek( 40, GEM_CURRENT_POS );
+ text = core->GetString( strref,0 );
+ }
+ map->AddMapNote( point, color, text, strref );
+ }
+
+ //this is a ToB feature (saves the unexploded projectiles)
+ printf( "Loading traps\n" );
+ for (i = 0; i < TrapCount; i++) {
+ ieResRef TrapResRef;
+ ieDword TrapEffOffset;
+ ieWord TrapSize, ProID;
+ ieWord X,Y;
+ ieDword Ticks;
+ ieWord Unknown;
+ ieByte PartyID;
+ ieByte Owner;
+
+ str->Seek( TrapOffset + ( i * 0x1c ), GEM_STREAM_START );
+
+ str->ReadResRef( TrapResRef );
+ str->ReadDword( &TrapEffOffset );
+ str->ReadWord( &TrapSize );
+ str->ReadWord( &ProID );
+ str->ReadDword( &Ticks );
+ str->ReadWord( &X );
+ str->ReadWord( &Y );
+ str->ReadWord( &Unknown );
+ str->Read( &PartyID,1 );
+ str->Read( &Owner,1 );
+ int TrapEffectCount = TrapSize/0x108;
+ if(TrapEffectCount*0x108!=TrapSize) {
+ printMessage("AREImporter", " ", LIGHT_RED);
+ printf("TrapEffectSize in game: %d != %d. Clearing it\n", TrapSize, TrapEffectCount*0x108);
+ continue;
+ }
+ //The projectile is always created, the worst that can happen
+ //is a dummy projectile
+ //The projectile ID is 214 for TRAPSNAR
+ //It is off by one compared to projectl.ids, but the same as missile.ids
+ Projectile *pro = core->GetProjectileServer()->GetProjectileByIndex(ProID-1);
+
+ //This could be wrong on msvc7 with its separate memory managers
+ EffectQueue *fxqueue = new EffectQueue();
+ CachedFileStream *fs = new CachedFileStream( (CachedFileStream *) str, TrapEffOffset, TrapSize, true);
+
+ ReadEffects((DataStream *) fs,fxqueue, TrapEffectCount);
+ Actor * caster = core->GetGame()->FindPC(PartyID);
+ pro->SetEffects(fxqueue);
+ if (caster) {
+ //FIXME: i don't know the level info
+ pro->SetCaster(caster->GetGlobalID(), 10);
+ }
+ Point pos(X,Y);
+ map->AddProjectile( pro, pos, pos);
+ }
+
+ printf( "Loading tiles\n" );
+ //Loading Tiled objects (if any)
+ str->Seek( TileOffset, GEM_STREAM_START );
+ for (i = 0; i < TileCount; i++) {
+ ieVariable Name;
+ ieResRef ID;
+ ieDword Flags;
+ ieDword OpenIndex, OpenCount;
+ ieDword ClosedIndex, ClosedCount;
+ str->Read( Name, 32 );
+ Name[32] = 0;
+ str->ReadResRef( ID );
+ str->ReadDword( &Flags );
+ str->ReadDword( &OpenIndex );
+ str->ReadDword( &OpenCount );
+ str->ReadDword( &ClosedIndex );
+ str->ReadDword( &ClosedCount );
+ str->Seek( 48, GEM_CURRENT_POS );
+ //absolutely no idea where these 'tile indices' are stored
+ //are they tileset tiles or impeded block tiles
+ map->TMap->AddTile( ID, Name, Flags, NULL,0, NULL, 0 );
+ }
+
+ printf( "Loading explored bitmap\n" );
+ i = map->GetExploredMapSize();
+ if (ExploredBitmapSize==i) {
+ map->ExploredBitmap = (ieByte *) malloc(i);
+ str->Seek( ExploredBitmapOffset, GEM_STREAM_START );
+ str->Read( map->ExploredBitmap, i );
+ }
+ else {
+ if( ExploredBitmapSize ) {
+ printMessage("AREImporter", " ", LIGHT_RED);
+ printf("ExploredBitmapSize in game: %d != %d. Clearing it\n", ExploredBitmapSize, i);
+ }
+ ExploredBitmapSize = i;
+ map->ExploredBitmap = (ieByte *) calloc(i, 1);
+ }
+ map->VisibleBitmap = (ieByte *) calloc(i, 1);
+
+ printf( "Loading wallgroups\n");
+ map->SetWallGroups( tmm->GetPolygonsCount(),tmm->GetWallGroups() );
+ //setting up doors
+ for (i=0;i<DoorsCount;i++) {
+ Door *door = tm->GetDoor(i);
+ door->SetDoorOpen(door->IsOpen(), false, 0);
+ }
+ return map;
+}
+
+void AREImporter::ReadEffects(DataStream *ds, EffectQueue *fxqueue, ieDword EffectsCount)
+{
+ unsigned int i;
+
+ PluginHolder<EffectMgr> eM(IE_EFF_CLASS_ID);
+ eM->Open( ds, true );
+
+ for (i = 0; i < EffectsCount; i++) {
+ Effect fx;
+
+ eM->GetEffectV20( &fx );
+ // NOTE: AddEffect() allocates a new effect
+ fxqueue->AddEffect( &fx );
+ }
+}
+
+int AREImporter::GetStoredFileSize(Map *map)
+{
+ unsigned int i;
+ int headersize = map->version+0x11c;
+ ActorOffset = headersize;
+
+ //get only saved actors (no familiars or partymembers)
+ //summons?
+ ActorCount = (ieWord) map->GetActorCount(false);
+ headersize += ActorCount * 0x110;
+
+ PluginHolder<ActorMgr> am(IE_CRE_CLASS_ID);
+ EmbeddedCreOffset = headersize;
+
+ for (i=0;i<ActorCount;i++) {
+ headersize += am->GetStoredFileSize(map->GetActor(i, false) );
+ }
+
+ InfoPointsOffset = headersize;
+
+ InfoPointsCount = (ieWord) map->TMap->GetInfoPointCount();
+ headersize += InfoPointsCount * 0xc4;
+ SpawnOffset = headersize;
+
+ SpawnCount = (ieDword) map->GetSpawnCount();
+ headersize += SpawnCount * 0xc8;
+ EntrancesOffset = headersize;
+
+ EntrancesCount = (ieDword) map->GetEntranceCount();
+ headersize += EntrancesCount * 0x68;
+ ContainersOffset = headersize;
+
+ //this one removes empty heaps and counts items, should be before
+ //getting ContainersCount
+ ItemsCount = (ieDword) map->ConsolidateContainers();
+ ContainersCount = (ieDword) map->TMap->GetContainerCount();
+ headersize += ContainersCount * 0xc0;
+ ItemsOffset = headersize;
+ headersize += ItemsCount * 0x14;
+ DoorsOffset = headersize;
+
+ DoorsCount = (ieDword) map->TMap->GetDoorCount();
+ headersize += DoorsCount * 0xc8;
+ VerticesOffset = headersize;
+
+ VerticesCount = 0;
+ for(i=0;i<InfoPointsCount;i++) {
+ InfoPoint *ip=map->TMap->GetInfoPoint(i);
+ VerticesCount+=ip->outline->count;
+ }
+ for(i=0;i<ContainersCount;i++) {
+ Container *c=map->TMap->GetContainer(i);
+ VerticesCount+=c->outline->count;
+ }
+ for(i=0;i<DoorsCount;i++) {
+ Door *d=map->TMap->GetDoor(i);
+ VerticesCount+=d->open->count+d->closed->count+d->oibcount+d->cibcount;
+ }
+ headersize += VerticesCount * 4;
+ AmbiOffset = headersize;
+
+ AmbiCount = (ieDword) map->GetAmbientCount();
+ headersize += AmbiCount * 0xd4;
+ VariablesOffset = headersize;
+
+ VariablesCount = (ieDword) map->locals->GetCount();
+ headersize += VariablesCount * 0x54;
+ AnimOffset = headersize;
+
+ AnimCount = (ieDword) map->GetAnimationCount();
+ headersize += AnimCount * 0x4c;
+ TileOffset = headersize;
+
+ TileCount = (ieDword) map->TMap->GetTileCount();
+ headersize += TileCount * 0x6c;
+ ExploredBitmapOffset = headersize;
+
+ ExploredBitmapSize = map->GetExploredMapSize();
+ headersize += ExploredBitmapSize;
+ EffectOffset = headersize;
+
+ TrapCount = (ieDword) map->GetTrapCount(piter);
+ for(i=0;i<TrapCount;i++) {
+ Projectile *pro = map->GetNextTrap(piter);
+ if (pro) {
+ EffectQueue *fxqueue = pro->GetEffects();
+ if (fxqueue) {
+ headersize += fxqueue->GetSavedEffectsCount() * 0x108;
+ }
+ }
+ }
+
+ TrapOffset = headersize;
+ headersize += TrapCount * 0x1c;
+ NoteOffset = headersize;
+
+ int pst = core->HasFeature( GF_AUTOMAP_INI );
+ NoteCount = (ieDword) map->GetMapNoteCount();
+ headersize += NoteCount * (pst?0x214: 0x34);
+ SongHeader = headersize;
+
+ headersize += 0x90;
+ RestHeader = headersize;
+
+ headersize += 0xe4;
+ return headersize;
+}
+
+int AREImporter::PutHeader(DataStream *stream, Map *map)
+{
+ char Signature[56];
+ ieDword tmpDword = 0;
+ ieWord tmpWord = 0;
+ int pst = core->HasFeature( GF_AUTOMAP_INI );
+
+ memcpy( Signature, "AREAV1.0", 8);
+ if (map->version==16) {
+ Signature[5]='9';
+ Signature[7]='1';
+ }
+ stream->Write( Signature, 8);
+ stream->WriteResRef( map->WEDResRef);
+ stream->WriteDword( &core->GetGame()->GameTime ); //lastsaved
+ stream->WriteDword( &map->AreaFlags);
+
+ memset(Signature, 0, sizeof(Signature)); //8 bytes 0
+ stream->Write( Signature, 8); //northref
+ stream->WriteDword( &tmpDword);
+ stream->Write( Signature, 8); //westref
+ stream->WriteDword( &tmpDword);
+ stream->Write( Signature, 8); //southref
+ stream->WriteDword( &tmpDword);
+ stream->Write( Signature, 8); //eastref
+ stream->WriteDword( &tmpDword);
+
+ stream->WriteWord( &map->AreaType);
+ stream->WriteWord( &map->Rain);
+ stream->WriteWord( &map->Snow);
+ stream->WriteWord( &map->Fog);
+ stream->WriteWord( &map->Lightning);
+ stream->WriteWord( &tmpWord);
+
+ if (map->version==16) { //writing 16 bytes of 0's
+ stream->Write( Signature, 8);
+ stream->Write( Signature, 8);
+ }
+
+ stream->WriteDword( &ActorOffset);
+ stream->WriteWord( &ActorCount);
+ stream->WriteWord( &InfoPointsCount );
+ stream->WriteDword( &InfoPointsOffset );
+ stream->WriteDword( &SpawnOffset );
+ stream->WriteDword( &SpawnCount );
+ stream->WriteDword( &EntrancesOffset );
+ stream->WriteDword( &EntrancesCount );
+ stream->WriteDword( &ContainersOffset );
+ stream->WriteWord( &ContainersCount );
+ stream->WriteWord( &ItemsCount );
+ stream->WriteDword( &ItemsOffset );
+ stream->WriteDword( &VerticesOffset );
+ stream->WriteWord( &VerticesCount );
+ stream->WriteWord( &AmbiCount );
+ stream->WriteDword( &AmbiOffset );
+ stream->WriteDword( &VariablesOffset );
+ stream->WriteDword( &VariablesCount );
+ stream->WriteDword( &tmpDword);
+
+ //the saved area script is in the last script slot!
+ GameScript *s = map->Scripts[MAX_SCRIPTS-1];
+ if (s) {
+ stream->WriteResRef( s->GetName() );
+ } else {
+ stream->Write( Signature, 8);
+ }
+ stream->WriteDword( &ExploredBitmapSize);
+ stream->WriteDword( &ExploredBitmapOffset);
+ stream->WriteDword( &DoorsCount );
+ stream->WriteDword( &DoorsOffset );
+ stream->WriteDword( &AnimCount );
+ stream->WriteDword( &AnimOffset );
+ stream->WriteDword( &TileCount);
+ stream->WriteDword( &TileOffset);
+ stream->WriteDword( &SongHeader);
+ stream->WriteDword( &RestHeader);
+ //an empty dword for pst
+ int i;
+ if (pst) {
+ tmpDword = 0xffffffff;
+ stream->WriteDword( &tmpDword);
+ i=52;
+ } else {
+ i=56;
+ }
+ stream->WriteDword( &NoteOffset );
+ stream->WriteDword( &NoteCount );
+ stream->WriteDword( &TrapOffset );
+ stream->WriteDword( &TrapCount );
+ stream->WriteResRef( map->Dream[0] );
+ stream->WriteResRef( map->Dream[1] );
+ //usually 56 empty bytes (but pst used up 4 elsewhere)
+ stream->Write( Signature, i);
+ return 0;
+}
+
+int AREImporter::PutDoors( DataStream *stream, Map *map, ieDword &VertIndex)
+{
+ char filling[8];
+ ieWord tmpWord = 0;
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<DoorsCount;i++) {
+ Door *d = map->TMap->GetDoor(i);
+
+ stream->Write( d->GetScriptName(), 32);
+ stream->WriteResRef( d->ID);
+ stream->WriteDword( &d->Flags);
+ stream->WriteDword( &VertIndex);
+ tmpWord = (ieWord) d->open->count;
+ stream->WriteWord( &tmpWord);
+ VertIndex += tmpWord;
+ tmpWord = (ieWord) d->closed->count;
+ stream->WriteWord( &tmpWord);
+ stream->WriteDword( &VertIndex);
+ VertIndex += tmpWord;
+ //open bounding box
+ tmpWord = (ieWord) d->open->BBox.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) d->open->BBox.y;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) (d->open->BBox.x+d->open->BBox.w);
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) (d->open->BBox.y+d->open->BBox.h);
+ stream->WriteWord( &tmpWord);
+ //closed bounding box
+ tmpWord = (ieWord) d->closed->BBox.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) d->closed->BBox.y;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) (d->closed->BBox.x+d->closed->BBox.w);
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) (d->closed->BBox.y+d->closed->BBox.h);
+ stream->WriteWord( &tmpWord);
+ //open and closed impeded blocks
+ stream->WriteDword( &VertIndex);
+ tmpWord = (ieWord) d->oibcount;
+ stream->WriteWord( &tmpWord);
+ VertIndex += tmpWord;
+ tmpWord = (ieWord) d->cibcount;
+ stream->WriteWord( &tmpWord);
+ stream->WriteDword( &VertIndex);
+ VertIndex += tmpWord;
+ stream->WriteDword( &d->Unknown54);
+ stream->WriteResRef( d->OpenSound);
+ stream->WriteResRef( d->CloseSound);
+ stream->WriteDword( &d->Cursor);
+ stream->WriteWord( &d->TrapDetectionDiff);
+ stream->WriteWord( &d->TrapRemovalDiff);
+ stream->WriteWord( &d->Trapped);
+ stream->WriteWord( &d->TrapDetected);
+ tmpWord = (ieWord) d->TrapLaunch.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) d->TrapLaunch.y;
+ stream->WriteWord( &tmpWord);
+ stream->WriteResRef( d->KeyResRef);
+ GameScript *s = d->Scripts[0];
+ if (s) {
+ stream->WriteResRef( s->GetName() );
+ } else {
+ stream->Write( filling, 8);
+ }
+ stream->WriteDword( &d->DiscoveryDiff);
+ //lock difficulty field
+ stream->WriteDword( &d->LockDifficulty);
+ //opening locations
+ tmpWord = (ieWord) d->toOpen[0].x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) d->toOpen[0].y;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) d->toOpen[1].x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) d->toOpen[1].y;
+ stream->WriteWord( &tmpWord);
+ stream->WriteDword( &d->OpenStrRef);
+ if (core->HasFeature(GF_AUTOMAP_INI) ) {
+ stream->Write( d->LinkedInfo, 24);
+ } else {
+ stream->Write( d->LinkedInfo, 32);
+ }
+ stream->WriteDword( &d->NameStrRef);
+ stream->WriteResRef( d->GetDialog());
+ if (core->HasFeature(GF_AUTOMAP_INI) ) {
+ stream->Write( filling, 8);
+ }
+ }
+ return 0;
+}
+
+int AREImporter::PutPoints( DataStream *stream, Point *p, unsigned int count)
+{
+ ieWord tmpWord;
+ unsigned int j;
+
+ for(j=0;j<count;j++) {
+ tmpWord = p[j].x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = p[j].y;
+ stream->WriteWord( &tmpWord);
+ }
+ return 0;
+}
+
+int AREImporter::PutVertices( DataStream *stream, Map *map)
+{
+ unsigned int i;
+
+ //regions
+ for(i=0;i<InfoPointsCount;i++) {
+ InfoPoint *ip = map->TMap->GetInfoPoint(i);
+ PutPoints(stream, ip->outline->points, ip->outline->count);
+ }
+ //containers
+ for(i=0;i<ContainersCount;i++) {
+ Container *c = map->TMap->GetContainer(i);
+ PutPoints(stream, c->outline->points, c->outline->count);
+ }
+ //doors
+ for(i=0;i<DoorsCount;i++) {
+ Door *d = map->TMap->GetDoor(i);
+ PutPoints(stream, d->open->points, d->open->count);
+ PutPoints(stream, d->closed->points, d->closed->count);
+ PutPoints(stream, d->open_ib, d->oibcount);
+ PutPoints(stream, d->closed_ib, d->cibcount);
+ }
+ return 0;
+}
+
+int AREImporter::PutItems( DataStream *stream, Map *map)
+{
+ for (unsigned int i=0;i<ContainersCount;i++) {
+ Container *c = map->TMap->GetContainer(i);
+
+ for(int j=0;j<c->inventory.GetSlotCount();j++) {
+ CREItem *ci = c->inventory.GetSlotItem(j);
+
+ stream->WriteResRef( ci->ItemResRef);
+ stream->WriteWord( &ci->Expired);
+ stream->WriteWord( &ci->Usages[0]);
+ stream->WriteWord( &ci->Usages[1]);
+ stream->WriteWord( &ci->Usages[2]);
+ stream->WriteDword( &ci->Flags);
+ }
+ }
+ return 0;
+}
+
+int AREImporter::PutContainers( DataStream *stream, Map *map, ieDword &VertIndex)
+{
+ char filling[56];
+ ieDword ItemIndex = 0;
+ ieDword tmpDword;
+ ieWord tmpWord;
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<ContainersCount;i++) {
+ Container *c = map->TMap->GetContainer(i);
+
+ //this is the editor name
+ stream->Write( c->GetScriptName(), 32);
+ tmpWord = (ieWord) c->Pos.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) c->Pos.y;
+ stream->WriteWord( &tmpWord);
+ stream->WriteWord( &c->Type);
+ stream->WriteWord( &c->LockDifficulty);
+ stream->WriteDword( &c->Flags);
+ stream->WriteWord( &c->TrapDetectionDiff);
+ stream->WriteWord( &c->TrapRemovalDiff);
+ stream->WriteWord( &c->Trapped);
+ stream->WriteWord( &c->TrapDetected);
+ tmpWord = (ieWord) c->TrapLaunch.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) c->TrapLaunch.y;
+ stream->WriteWord( &tmpWord);
+ //outline bounding box
+ tmpWord = (ieWord) c->outline->BBox.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) c->outline->BBox.y;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) (c->outline->BBox.x + c->outline->BBox.w);
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) (c->outline->BBox.y + c->outline->BBox.h);
+ stream->WriteWord( &tmpWord);
+ //item index and offset
+ tmpDword = c->inventory.GetSlotCount();
+ stream->WriteDword( &ItemIndex);
+ stream->WriteDword( &tmpDword);
+ ItemIndex +=tmpDword;
+ GameScript *s = c->Scripts[0];
+ if (s) {
+ stream->WriteResRef( s->GetName() );
+ } else {
+ stream->Write( filling, 8);
+ }
+ //outline polygon index and count
+ tmpWord = c->outline->count;
+ stream->WriteDword( &VertIndex);
+ stream->WriteWord( &tmpWord);
+ VertIndex +=tmpWord;
+ tmpWord = 0;
+ stream->WriteWord( &tmpWord); //vertex count is made short
+ //this is the real scripting name
+ stream->Write( c->GetScriptName(), 32);
+ stream->WriteResRef( c->KeyResRef);
+ stream->WriteDword( &tmpDword); //unknown80
+ stream->WriteDword( &c->OpenFail);
+ stream->Write( filling, 56); //unknown or unused stuff
+ }
+ return 0;
+}
+
+int AREImporter::PutRegions( DataStream *stream, Map *map, ieDword &VertIndex)
+{
+ ieDword tmpDword = 0;
+ ieWord tmpWord;
+ char filling[36];
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<InfoPointsCount;i++) {
+ InfoPoint *ip = map->TMap->GetInfoPoint(i);
+
+ stream->Write( ip->GetScriptName(), 32);
+ //this is a hack, we abuse a coincidence
+ //ST_PROXIMITY = 1, ST_TRIGGER = 2, ST_TRAVEL = 3
+ //translates to trap = 0, info = 1, travel = 2
+ tmpWord = ((ieWord) ip->Type) - 1;
+ stream->WriteWord( &tmpWord);
+ //outline bounding box
+ tmpWord = (ieWord) ip->outline->BBox.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) ip->outline->BBox.y;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) (ip->outline->BBox.x + ip->outline->BBox.w);
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) (ip->outline->BBox.y + ip->outline->BBox.h);
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) ip->outline->count;
+ stream->WriteWord( &tmpWord);
+ stream->WriteDword( &VertIndex);
+ VertIndex += tmpWord;
+ stream->WriteDword( &tmpDword); //unknown30
+ stream->WriteDword( &ip->Cursor);
+ stream->WriteResRef( ip->Destination);
+ stream->Write( ip->EntranceName, 32);
+ stream->WriteDword( &ip->Flags);
+ stream->WriteDword( &ip->StrRef);
+ stream->WriteWord( &ip->TrapDetectionDiff);
+ stream->WriteWord( &ip->TrapRemovalDiff);
+ stream->WriteWord( &ip->Trapped); //unknown???
+ stream->WriteWord( &ip->TrapDetected);
+ tmpWord = (ieWord) ip->TrapLaunch.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) ip->TrapLaunch.y;
+ stream->WriteWord( &tmpWord);
+ stream->WriteResRef( ip->KeyResRef);
+ GameScript *s = ip->Scripts[0];
+ if (s) {
+ stream->WriteResRef( s->GetName() );
+ } else {
+ stream->Write( filling, 8);
+ }
+ tmpWord = (ieWord) ip->UsePoint.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) ip->UsePoint.y;
+ stream->WriteWord( &tmpWord);
+ stream->Write( filling, 36); //unknown
+ //these are probably only in PST
+ stream->WriteResRef( ip->EnterWav);
+ tmpWord = (ieWord) ip->TalkPos.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) ip->TalkPos.y;
+ stream->WriteWord( &tmpWord);
+ stream->WriteDword( &ip->DialogName);
+ stream->WriteResRef( ip->GetDialog());
+ }
+ return 0;
+}
+
+int AREImporter::PutSpawns( DataStream *stream, Map *map)
+{
+ ieWord tmpWord;
+ char filling[56];
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<SpawnCount;i++) {
+ Spawn *sp = map->GetSpawn(i);
+
+ stream->Write( sp->Name, 32);
+ tmpWord = (ieWord) sp->Pos.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) sp->Pos.y;
+ stream->WriteWord( &tmpWord);
+ tmpWord = sp->GetCreatureCount();
+ int j;
+ for (j = 0;j < tmpWord; j++) {
+ stream->WriteResRef( sp->Creatures[j] );
+ }
+ while( j++<MAX_RESCOUNT) {
+ stream->Write( filling, 8);
+ }
+ stream->WriteWord( &tmpWord );
+ stream->WriteWord( &sp->Difficulty);
+ stream->WriteWord( &sp->Frequency);
+ stream->WriteWord( &sp->Method);
+ stream->WriteDword( &sp->sduration); //spawn duration
+ stream->WriteWord( &sp->rwdist); //random walk distance
+ stream->WriteWord( &sp->owdist); //other walk distance
+ stream->WriteWord( &sp->Maximum);
+ stream->WriteWord( &sp->Enabled);
+ stream->WriteDword( &sp->appearance);
+ stream->WriteWord( &sp->DayChance);
+ stream->WriteWord( &sp->NightChance);
+ stream->Write( filling, 56); //most likely unused crap
+ }
+ return 0;
+}
+
+void AREImporter::PutScript(DataStream *stream, Actor *ac, unsigned int index)
+{
+ char filling[8];
+
+ GameScript *s = ac->Scripts[index];
+ if (s) {
+ stream->WriteResRef( s->GetName() );
+ } else {
+ memset(filling,0,sizeof(filling));
+ stream->Write( filling, 8);
+ }
+}
+
+int AREImporter::PutActors( DataStream *stream, Map *map)
+{
+ ieDword tmpDword = 0;
+ ieWord tmpWord;
+ ieDword CreatureOffset = EmbeddedCreOffset;
+ char filling[120];
+ unsigned int i;
+
+ PluginHolder<ActorMgr> am(IE_CRE_CLASS_ID);
+ memset(filling,0,sizeof(filling) );
+ for (i=0;i<ActorCount;i++) {
+ Actor *ac = map->GetActor(i, false);
+
+ stream->Write( ac->GetScriptName(), 32);
+ tmpWord = (ieWord) ac->Pos.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) ac->Pos.y;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) ac->Destination.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) ac->Destination.y;
+ stream->WriteWord( &tmpWord);
+
+ stream->WriteDword( &tmpDword); //used fields flag always 0 for saved areas
+ stream->WriteDword( &tmpDword); //unknown2c
+ stream->WriteDword( &tmpDword); //actor animation, unused
+ tmpWord = ac->GetOrientation();
+ stream->WriteWord( &tmpWord);
+ tmpWord = 0;
+ stream->WriteWord( &tmpWord); //unknown
+ stream->WriteDword( &ac->RemovalTime);
+ stream->WriteDword( &tmpDword); //more unknowns
+ stream->WriteDword( &ac->appearance);
+ stream->WriteDword( &ac->TalkCount);
+ stream->WriteResRef( ac->GetDialog());
+ PutScript(stream, ac, SCR_OVERRIDE);
+ PutScript(stream, ac, SCR_CLASS);
+ PutScript(stream, ac, SCR_RACE);
+ PutScript(stream, ac, SCR_GENERAL);
+ PutScript(stream, ac, SCR_DEFAULT);
+ PutScript(stream, ac, SCR_SPECIFICS);
+ //creature reference is empty because we are embedding it
+ //the original engine used a '*'
+ stream->Write( filling, 8);
+ stream->WriteDword( &CreatureOffset);
+ ieDword CreatureSize = am->GetStoredFileSize(ac);
+ stream->WriteDword( &CreatureSize);
+ CreatureOffset += CreatureSize;
+ PutScript(stream, ac, SCR_AREA);
+ stream->Write( filling, 120);
+ }
+
+ CreatureOffset = EmbeddedCreOffset;
+ for (i=0;i<ActorCount;i++) {
+ assert(stream->GetPos() == CreatureOffset);
+ Actor *ac = map->GetActor(i, false);
+
+ //reconstructing offsets again
+ CreatureOffset += am->GetStoredFileSize(ac);
+ am->PutActor( stream, ac);
+ }
+ assert(stream->GetPos() == CreatureOffset);
+
+ return 0;
+}
+
+int AREImporter::PutAnimations( DataStream *stream, Map *map)
+{
+ ieWord tmpWord;
+
+ aniIterator iter = map->GetFirstAnimation();
+ while(AreaAnimation *an = map->GetNextAnimation(iter) ) {
+ stream->Write( an->Name, 32);
+ tmpWord = (ieWord) an->Pos.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) an->Pos.y;
+ stream->WriteWord( &tmpWord);
+ stream->WriteDword( &an->appearance);
+ stream->WriteResRef( an->BAM);
+ stream->WriteWord( &an->sequence);
+ stream->WriteWord( &an->frame);
+ stream->WriteDword( &an->Flags);
+ stream->WriteWord( (ieWord *) &an->height);
+ stream->WriteWord( &an->transparency);
+ stream->WriteWord( &an->unknown3c); //animation already played?
+ stream->Write( &an->startchance,1);
+ stream->Write( &an->skipcycle,1);
+ stream->WriteResRef( an->PaletteRef);
+ stream->WriteDword( &an->unknown48);//seems utterly unused
+ }
+ return 0;
+}
+
+int AREImporter::PutEntrances( DataStream *stream, Map *map)
+{
+ ieWord tmpWord;
+ char filling[66];
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<EntrancesCount;i++) {
+ Entrance *e = map->GetEntrance(i);
+
+ stream->Write( e->Name, 32);
+ tmpWord = (ieWord) e->Pos.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) e->Pos.y;
+ stream->WriteWord( &tmpWord);
+ stream->WriteWord( &e->Face);
+ //a large empty piece of crap
+ stream->Write( filling, 66);
+ }
+ return 0;
+}
+
+int AREImporter::PutVariables( DataStream *stream, Map *map)
+{
+ char filling[40];
+ Variables::iterator pos=NULL;
+ const char *name;
+ ieDword value;
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<VariablesCount;i++) {
+ pos=map->locals->GetNextAssoc( pos, name, value);
+ //name isn't necessarily 32 bytes long, so we play safe
+ strncpy(filling, name, 32);
+ stream->Write( filling, 40);
+ //clearing up after the strncpy so we'll write 0's next
+ memset(filling,0,sizeof(filling) );
+ stream->WriteDword( &value);
+ //40 bytes of empty crap
+ stream->Write( filling, 40);
+ }
+ return 0;
+}
+
+int AREImporter::PutAmbients( DataStream *stream, Map *map)
+{
+ char filling[64];
+ ieWord tmpWord;
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<AmbiCount;i++) {
+ Ambient *am = map->GetAmbient(i);
+ stream->Write( am->name, 32 );
+ tmpWord = (ieWord) am->origin.x;
+ stream->WriteWord( &tmpWord );
+ tmpWord = (ieWord) am->origin.y;
+ stream->WriteWord( &tmpWord );
+ stream->WriteWord( &am->radius );
+ stream->WriteWord( &am->height );
+ stream->Write( filling, 6 );
+ stream->WriteWord( &am->gain );
+ tmpWord = (ieWord) am->sounds.size();
+ int j;
+ for (j = 0;j < tmpWord; j++) {
+ stream->WriteResRef( am->sounds[j] );
+ }
+ while( j++<MAX_RESCOUNT) {
+ stream->Write( filling, 8);
+ }
+ stream->WriteWord( &tmpWord );
+ stream->Write( filling, 2 );
+ stream->WriteDword( &am->interval );
+ stream->WriteDword( &am->perset );
+ stream->WriteDword( &am->appearance );
+ stream->WriteDword( &am->flags );
+ stream->Write( filling, 64);
+ }
+ return 0;
+}
+
+int AREImporter::PutMapnotes( DataStream *stream, Map *map)
+{
+ char filling[8];
+ ieDword tmpDword;
+ ieWord tmpWord;
+
+ //different format
+ int pst = core->HasFeature( GF_AUTOMAP_INI );
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<NoteCount;i++) {
+ MapNote *mn = map->GetMapNote(i);
+ int x;
+
+ if (pst) {
+ tmpDword = (ieWord) mn->Pos.x;
+ stream->WriteDword( &tmpDword );
+ tmpDword = (ieDword) mn->Pos.y;
+ stream->WriteDword( &tmpDword );
+ unsigned int len = (unsigned int) strlen(mn->text);
+ if (len>500) len=500;
+ stream->Write( mn->text, len);
+ x = 500-len;
+ for (int j=0;j<x/8;j++) {
+ stream->Write( filling, 8);
+ }
+ x = x%8;
+ if (x) {
+ stream->Write( filling, x);
+ }
+ stream->WriteWord( &mn->color);
+ stream->WriteWord( &tmpWord);
+ for (x=0;x<5;x++) { //5 empty dwords
+ stream->Write( filling, 4);
+ }
+ } else {
+ tmpWord = (ieWord) mn->Pos.x;
+ stream->WriteWord( &tmpWord );
+ tmpWord = (ieWord) mn->Pos.y;
+ stream->WriteWord( &tmpWord );
+ //update custom strref
+ core->UpdateString( mn->strref, mn->text);
+ tmpDword = mn->strref;
+ stream->WriteDword( &tmpDword);
+ stream->WriteWord( &tmpWord );
+ stream->WriteWord( &mn->color );
+ tmpDword = 1;
+ stream->WriteDword( &tmpDword );
+ for (x=0;x<9;x++) { //9 empty dwords
+ stream->Write( filling, 4);
+ }
+ }
+ }
+ return 0;
+}
+
+int AREImporter::PutEffects( DataStream *stream, EffectQueue *fxqueue)
+{
+ PluginHolder<EffectMgr> eM(IE_EFF_CLASS_ID);
+ assert(eM != NULL);
+
+ std::list< Effect* >::const_iterator f=fxqueue->GetFirstEffect();
+ ieDword EffectsCount = fxqueue->GetSavedEffectsCount();
+ for(unsigned int i=0;i<EffectsCount;i++) {
+ const Effect *fx = fxqueue->GetNextSavedEffect(f);
+
+ assert(fx!=NULL);
+
+ eM->PutEffectV2(stream, fx);
+ }
+ return 0;
+}
+
+int AREImporter::PutTraps( DataStream *stream, Map *map)
+{
+ ieDword Offset;
+ ieDword tmpDword;
+ ieWord tmpWord;
+ ieByte tmpByte;
+ ieResRef name;
+ ieWord type = 0;
+ Point dest(0,0);
+
+ Offset = EffectOffset;
+ ieDword i = map->GetTrapCount(piter);
+ while(i--) {
+ tmpWord = 0;
+ Projectile *pro = map->GetNextTrap(piter);
+ if (pro) {
+ //The projectile ID is based on missile.ids which is
+ //off by one compared to projectl.ids
+ type = pro->GetType()+1;
+ dest = pro->GetDestination();
+ strnuprcpy(name, pro->GetName(), 8);
+ EffectQueue *fxqueue = pro->GetEffects();
+ if (fxqueue) {
+ tmpWord = fxqueue->GetSavedEffectsCount();
+ }
+ ieDword ID = pro->GetCaster();
+ Actor *actor = map->GetActorByGlobalID(ID);
+ //0xff if not in party
+ //party slot if in party
+ if (actor) tmpByte = (ieByte) (actor->InParty-1);
+ else tmpByte = 0xff;
+ }
+
+ stream->WriteResRef( name );
+ stream->WriteDword( &Offset );
+ //size of fxqueue;
+ assert(tmpWord<256);
+ tmpWord *= 0x108;
+ Offset += tmpWord;
+ stream->WriteWord( &tmpWord ); //size in bytes
+ stream->WriteWord( &type ); //missile.ids
+ tmpDword = 0;
+ stream->WriteDword( &tmpDword );//unknown field
+ tmpWord = (ieWord) dest.x;
+ stream->WriteWord( &tmpWord );
+ tmpWord = (ieWord) dest.y;
+ stream->WriteWord( &tmpWord );
+ tmpWord = 0;
+ stream->WriteWord( &tmpWord ); //unknown field
+ stream->Write( &tmpByte,1 ); //unknown field
+ stream->Write( &tmpByte,1 ); //InParty flag
+ }
+ return 0;
+}
+
+int AREImporter::PutExplored( DataStream *stream, Map *map)
+{
+ stream->Write( map->ExploredBitmap, ExploredBitmapSize);
+ return 0;
+}
+
+int AREImporter::PutTiles( DataStream * stream, Map * map)
+{
+ char filling[48];
+ ieDword tmpDword = 0;
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<TileCount;i++) {
+ TileObject *am = map->TMap->GetTile(i);
+ stream->Write( am->Name, 32 );
+ stream->WriteResRef( am->Tileset );
+ stream->WriteDword( &am->Flags);
+ stream->WriteDword( &am->opencount);
+ //can't write tiles, otherwise now we should write a tile index
+ stream->WriteDword( &tmpDword);
+ stream->WriteDword( &am->closedcount);
+ //can't write tiles otherwise now we should write a tile index
+ stream->WriteDword( &tmpDword);
+ stream->Write( filling, 48);
+ }
+ return 0;
+}
+
+int AREImporter::PutSongHeader( DataStream *stream, Map *map)
+{
+ int i;
+ char filling[8];
+ ieDword tmpDword = 0;
+
+ memset(filling,0,sizeof(filling) );
+ for(i=0;i<MAX_RESCOUNT;i++) {
+ stream->WriteDword( &map->SongHeader.SongList[i]);
+ }
+ //day
+ stream->Write( filling,8);
+ stream->Write( filling,8);
+ stream->WriteDword( &tmpDword);
+ //night
+ stream->Write( filling,8);
+ stream->Write( filling,8);
+ stream->WriteDword( &tmpDword);
+ //song flag
+ stream->WriteDword( &tmpDword);
+ //lots of empty crap (15x4)
+ for(i=0;i<15;i++) {
+ stream->WriteDword( &tmpDword);
+ }
+ return 0;
+}
+
+int AREImporter::PutRestHeader( DataStream *stream, Map *map)
+{
+ int i;
+ ieDword tmpDword = 0;
+
+ char filling[32];
+ memset(filling,0,sizeof(filling) );
+ stream->Write( filling, 32); //empty label
+ for(i=0;i<MAX_RESCOUNT;i++) {
+ stream->WriteDword( &map->RestHeader.Strref[i]);
+ }
+ for(i=0;i<MAX_RESCOUNT;i++) {
+ stream->WriteResRef( map->RestHeader.CreResRef[i]);
+ }
+ stream->WriteWord( &map->RestHeader.CreatureNum);
+ stream->WriteWord( &map->RestHeader.Difficulty);
+ stream->WriteDword( &map->RestHeader.sduration);
+ stream->WriteWord( &map->RestHeader.rwdist);
+ stream->WriteWord( &map->RestHeader.owdist);
+ stream->WriteWord( &map->RestHeader.Maximum);
+ stream->WriteWord( &map->RestHeader.Enabled);
+ stream->WriteWord( &map->RestHeader.DayChance);
+ stream->WriteWord( &map->RestHeader.NightChance);
+ for(i=0;i<14;i++) {
+ stream->WriteDword( &tmpDword);
+ }
+ return 0;
+}
+
+/* no saving of tiled objects, are they used anywhere? */
+int AREImporter::PutArea(DataStream *stream, Map *map)
+{
+ ieDword VertIndex = 0;
+ int ret;
+
+ if (!stream || !map) {
+ return -1;
+ }
+
+ ret = PutHeader( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutActors( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutRegions( stream, map, VertIndex);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutSpawns( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutEntrances( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutContainers( stream, map, VertIndex);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutItems( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutDoors( stream, map, VertIndex);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutVertices( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutAmbients( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutVariables( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutAnimations( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutTiles( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutExplored( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ieDword i = map->GetTrapCount(piter);
+ while(i--) {
+ Projectile *trap = map->GetNextTrap(piter);
+ if (!trap) {
+ continue;
+ }
+
+ EffectQueue *fxqueue = trap->GetEffects();
+
+ if (!fxqueue) {
+ continue;
+ }
+
+ ret = PutEffects( stream, fxqueue);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ ret = PutTraps( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutMapnotes( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutSongHeader( stream, map);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutRestHeader( stream, map);
+
+ return ret;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x145B60F0, "ARE File Importer")
+PLUGIN_CLASS(IE_ARE_CLASS_ID, AREImporter)
+PLUGIN_CLEANUP(ReleaseMemory)
+END_PLUGIN()
diff --git a/gemrb/plugins/AREImporter/AREImporter.h b/gemrb/plugins/AREImporter/AREImporter.h
new file mode 100644
index 0000000..db80154
--- /dev/null
+++ b/gemrb/plugins/AREImporter/AREImporter.h
@@ -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.
+ *
+ *
+ */
+
+#ifndef AREIMPORTER_H
+#define AREIMPORTER_H
+
+#include "MapMgr.h"
+
+class Animation;
+class AnimationFactory;
+
+class AREImporter : public MapMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ int bigheader;
+ ieResRef WEDResRef;
+ ieDword LastSave;
+ ieDword AreaFlags;
+ ieWord AreaType, WRain, WSnow, WFog, WLightning, WUnknown;
+ ieDword ActorOffset, EmbeddedCreOffset, AnimOffset, AnimCount;
+ ieDword VerticesOffset;
+ ieDword DoorsCount, DoorsOffset;
+ ieDword ExploredBitmapSize, ExploredBitmapOffset;
+ ieDword EntrancesOffset, EntrancesCount;
+ ieDword SongHeader, RestHeader;
+ ieWord ActorCount, VerticesCount, AmbiCount;
+ ieWord ContainersCount, InfoPointsCount, ItemsCount;
+ ieDword VariablesCount;
+ ieDword ContainersOffset, InfoPointsOffset, ItemsOffset;
+ ieDword AmbiOffset, VariablesOffset;
+ ieDword SpawnOffset, SpawnCount;
+ ieDword TileOffset, TileCount;
+ ieDword NoteOffset, NoteCount;
+ ieDword TrapOffset, TrapCount; //only in ToB?
+ proIterator piter; //iterator for saving projectiles
+ ieDword EffectOffset;
+ ieResRef Script;
+ ieResRef Dream1, Dream2; //only in ToB
+public:
+ AREImporter(void);
+ ~AREImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ bool ChangeMap(Map *map, bool day_or_night);
+ Map* GetMap(const char* ResRef, bool day_or_night);
+ int GetStoredFileSize(Map *map);
+ /* stores an area in the Cache (swaps it out) */
+ int PutArea(DataStream *stream, Map *map);
+private:
+ void ReadEffects(DataStream *ds, EffectQueue *fx, ieDword EffectsCount);
+ CREItem* GetItem();
+ int PutHeader(DataStream *stream, Map *map);
+ int PutPoints(DataStream *stream, Point *p, unsigned int count);
+ int PutDoors(DataStream *stream, Map *map, ieDword &VertIndex);
+ int PutItems(DataStream *stream, Map *map);
+ int PutContainers(DataStream *stream, Map *map, ieDword &VertIndex);
+ int PutRegions(DataStream *stream, Map *map, ieDword &VertIndex);
+ int PutVertices(DataStream *stream, Map *map);
+ int PutSpawns(DataStream *stream, Map *map);
+ void PutScript(DataStream *stream, Actor *ac, unsigned int index);
+ int PutActors(DataStream *stream, Map *map);
+ int PutAnimations(DataStream *stream, Map *map);
+ int PutEntrances(DataStream *stream, Map *map);
+ int PutVariables(DataStream *stream, Map *map);
+ int PutAmbients(DataStream *stream, Map *map);
+ int PutMapnotes(DataStream *stream, Map *map);
+ int PutEffects( DataStream *stream, EffectQueue *fxqueue);
+ int PutTraps(DataStream *stream, Map *map);
+ int PutExplored(DataStream *stream, Map *map);
+ int PutTiles(DataStream *stream, Map *map);
+ int PutRestHeader(DataStream *stream, Map *map);
+ int PutSongHeader(DataStream *stream, Map *map);
+};
+
+#endif
diff --git a/gemrb/plugins/AREImporter/CMakeLists.txt b/gemrb/plugins/AREImporter/CMakeLists.txt
new file mode 100644
index 0000000..9eea480
--- /dev/null
+++ b/gemrb/plugins/AREImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (AREImporter AREImporter.cpp)
diff --git a/gemrb/plugins/AREImporter/Makefile.am b/gemrb/plugins/AREImporter/Makefile.am
new file mode 100644
index 0000000..ab14cea
--- /dev/null
+++ b/gemrb/plugins/AREImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = AREImporter.la
+AREImporter_la_LDFLAGS = -module -avoid-version -shared
+AREImporter_la_SOURCES = AREImporter.cpp AREImporter.h
diff --git a/gemrb/plugins/BAMImporter/BAMImporter.cpp b/gemrb/plugins/BAMImporter/BAMImporter.cpp
new file mode 100644
index 0000000..8531158
--- /dev/null
+++ b/gemrb/plugins/BAMImporter/BAMImporter.cpp
@@ -0,0 +1,395 @@
+/* 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.
+ *
+ *
+ */
+
+#if defined(__HAIKU__)
+#include <unistd.h>
+#endif
+
+#ifdef ANDROID
+#include "swab.h"
+#endif
+
+#include "BAMImporter.h"
+
+#include "win32def.h"
+
+#include "Compressor.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "Palette.h"
+#include "Video.h"
+#include "System/FileStream.h"
+
+BAMImporter::BAMImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+ frames = NULL;
+ cycles = NULL;
+ palette = NULL;
+ FramesCount = 0;
+ CyclesCount = 0;
+}
+
+BAMImporter::~BAMImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+ delete[] frames;
+ delete[] cycles;
+ gamedata->FreePalette(palette);
+}
+
+bool BAMImporter::Open(DataStream* stream, bool autoFree)
+{
+ unsigned int i;
+
+ if (stream == NULL) {
+ return false;
+ }
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ delete[] frames;
+ delete[] cycles;
+ gamedata->FreePalette(palette);
+
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "BAMCV1 ", 8 ) == 0) {
+ //Check if Decompressed file has already been Cached
+ char cpath[_MAX_PATH];
+ strcpy( cpath, core->CachePath );
+ strcat( cpath, stream->filename );
+ FILE* exist_in_cache = fopen( cpath, "rb" );
+ if (exist_in_cache) {
+ //File was previously cached, using local copy
+ if (autoFree) {
+ delete( str );
+ }
+ fclose( exist_in_cache );
+ FileStream* s = new FileStream();
+ s->Open( cpath );
+ str = s;
+ str->Read( Signature, 8 );
+ } else {
+ //No file found in Cache, Decompressing and storing for further use
+ str->Seek( 4, GEM_CURRENT_POS );
+
+ if (!core->IsAvailable( PLUGIN_COMPRESSION_ZLIB )) {
+ printf( "No Compression Manager Available.\nCannot Load Compressed Bam File.\n" );
+ return false;
+ }
+ FILE* newfile = fopen( cpath, "wb" );
+ if (!newfile) {
+ printMessage("BAMImporter", " ", RED);
+ printf( "Cannot write %s.\n", cpath );
+ return false;
+ }
+ PluginHolder<Compressor> comp(PLUGIN_COMPRESSION_ZLIB);
+ comp->Decompress( newfile, str );
+ fclose( newfile );
+ if (autoFree)
+ delete( str );
+ FileStream* s = new FileStream();
+ s->Open( cpath );
+ str = s;
+ str->Read( Signature, 8 );
+ }
+ }
+ if (strncmp( Signature, "BAM V1 ", 8 ) != 0) {
+ return false;
+ }
+ str->ReadWord( &FramesCount );
+ str->Read( &CyclesCount, 1 );
+ str->Read( &CompressedColorIndex, 1 );
+ str->ReadDword( &FramesOffset );
+ str->ReadDword( &PaletteOffset );
+ str->ReadDword( &FLTOffset );
+ str->Seek( FramesOffset, GEM_STREAM_START );
+ frames = new FrameEntry[FramesCount];
+ DataStart = str->Size();
+ for (i = 0; i < FramesCount; i++) {
+ str->ReadWord( &frames[i].Width );
+ str->ReadWord( &frames[i].Height );
+ str->ReadWord( &frames[i].XPos );
+ str->ReadWord( &frames[i].YPos );
+ str->ReadDword( &frames[i].FrameData );
+ if ((frames[i].FrameData & 0x7FFFFFFF) < DataStart)
+ DataStart = (frames[i].FrameData & 0x7FFFFFFF);
+ }
+ cycles = new CycleEntry[CyclesCount];
+ for (i = 0; i < CyclesCount; i++) {
+ str->ReadWord( &cycles[i].FramesCount );
+ str->ReadWord( &cycles[i].FirstFrame );
+ }
+ str->Seek( PaletteOffset, GEM_STREAM_START );
+ palette = new Palette();
+ // no need to switch this
+ for (i = 0; i < 256; i++) {
+ RevColor rc;
+ str->Read( &rc, 4 );
+ palette->col[i].r = rc.r;
+ palette->col[i].g = rc.g;
+ palette->col[i].b = rc.b;
+ palette->col[i].a = rc.a;
+ }
+
+ return true;
+}
+
+int BAMImporter::GetCycleSize(unsigned char Cycle)
+{
+ if(Cycle >= CyclesCount ) {
+ return -1;
+ }
+ return cycles[Cycle].FramesCount;
+}
+
+Sprite2D* BAMImporter::GetFrameInternal(unsigned short findex, unsigned char mode,
+ bool BAMsprite, const unsigned char* data,
+ AnimationFactory* datasrc)
+{
+ Sprite2D* spr = 0;
+
+ if (BAMsprite) {
+ bool RLECompressed = (frames[findex].FrameData & 0x80000000) == 0;
+
+ assert(data);
+ const unsigned char* framedata = data;
+ framedata += (frames[findex].FrameData & 0x7FFFFFFF) - DataStart;
+ if (RLECompressed) {
+ spr = core->GetVideoDriver()->CreateSpriteBAM8(
+ frames[findex].Width, frames[findex].Height,
+ true, framedata, datasrc, palette, CompressedColorIndex);
+ } else {
+ spr = core->GetVideoDriver()->CreateSpriteBAM8(
+ frames[findex].Width, frames[findex].Height, false,
+ framedata, datasrc, palette, CompressedColorIndex );
+ }
+ } else {
+ void* pixels = GetFramePixels(findex);
+ spr = core->GetVideoDriver()->CreateSprite8(
+ frames[findex].Width, frames[findex].Height, 8,
+ pixels, palette->col, true, 0 );
+ }
+
+ spr->XPos = (ieWordSigned)frames[findex].XPos;
+ spr->YPos = (ieWordSigned)frames[findex].YPos;
+ if (mode == IE_SHADED) {
+ // CHECKME: is this ever used? Should we modify the sprite's palette
+ // without creating a local copy for this sprite?
+ Palette* pal = spr->GetPalette();
+ pal->CreateShadedAlphaChannel();
+ pal->Release();
+ }
+ return spr;
+}
+
+void* BAMImporter::GetFramePixels(unsigned short findex)
+{
+ if (findex >= FramesCount) {
+ findex = cycles[0].FirstFrame;
+ }
+ str->Seek( ( frames[findex].FrameData & 0x7FFFFFFF ), GEM_STREAM_START );
+ unsigned long pixelcount = frames[findex].Height * frames[findex].Width;
+ void* pixels = malloc( pixelcount );
+ bool RLECompressed = ( ( frames[findex].FrameData & 0x80000000 ) == 0 );
+ if (RLECompressed) {
+ //if RLE Compressed
+ unsigned long RLESize;
+ RLESize = ( unsigned long )
+ ( frames[findex].Width * frames[findex].Height * 3 ) / 2 + 1;
+ //without partial reads, we should be careful
+ unsigned long remains = str->Remains();
+ if (RLESize > remains) {
+ RLESize = remains;
+ }
+ unsigned char* inpix;
+ inpix = (unsigned char*)malloc( RLESize );
+ if (str->Read( inpix, RLESize ) == GEM_ERROR) {
+ free( inpix );
+ return NULL;
+ }
+ unsigned char * p = inpix;
+ unsigned char * Buffer = (unsigned char*)pixels;
+ unsigned int i = 0;
+ while (i < pixelcount) {
+ if (*p == CompressedColorIndex) {
+ p++;
+ // FIXME: Czech HOW has apparently broken frame
+ // #141 in REALMS.BAM. Maybe we should put
+ // this condition to #ifdef BROKEN_xx ?
+ // Or maybe rather put correct REALMS.BAM
+ // into override/ dir?
+ if (i + ( *p ) + 1 > pixelcount) {
+ memset( &Buffer[i], CompressedColorIndex, pixelcount - i );
+ printf ("Broken frame %d\n", findex);
+ } else {
+ memset( &Buffer[i], CompressedColorIndex, ( *p ) + 1 );
+ }
+ i += *p;
+ } else
+ Buffer[i] = *p;
+ p++;
+ i++;
+ }
+ free( inpix );
+ } else {
+ str->Read( pixels, pixelcount );
+ }
+ return pixels;
+}
+
+ieWord * BAMImporter::CacheFLT(unsigned int &count)
+{
+ int i;
+
+ count = 0;
+ for (i = 0; i < CyclesCount; i++) {
+ unsigned int tmp = cycles[i].FirstFrame + cycles[i].FramesCount;
+ if (tmp > count) {
+ count = tmp;
+ }
+ }
+ ieWord * FLT = ( ieWord * ) calloc( count, sizeof(ieWord) );
+ str->Seek( FLTOffset, GEM_STREAM_START );
+ str->Read( FLT, count * sizeof(ieWord) );
+ if( DataStream::IsEndianSwitch() ) {
+ //msvc likes it as char *
+ swab( (char*) FLT, (char*) FLT, count * sizeof(ieWord) );
+ }
+ return FLT;
+}
+
+AnimationFactory* BAMImporter::GetAnimationFactory(const char* ResRef, unsigned char mode)
+{
+ unsigned int i, count;
+ AnimationFactory* af = new AnimationFactory( ResRef );
+ ieWord *FLT = CacheFLT( count );
+
+ bool videoBAMsupport = core->GetVideoDriver()->SupportsBAMSprites();
+ unsigned char* data = NULL;
+
+ if (videoBAMsupport) {
+ str->Seek( DataStart, GEM_STREAM_START );
+ unsigned long length = str->Remains();
+ if (length == 0) return af;
+ //data = new unsigned char[length];
+ data = (unsigned char *) malloc(length);
+ str->Read( data, length );
+ af->SetFrameData(data);
+ }
+
+ for (i = 0; i < FramesCount; ++i) {
+ Sprite2D* frame = GetFrameInternal(i, mode, videoBAMsupport, data, af);
+ assert(!videoBAMsupport || frame->BAM);
+ af->AddFrame(frame);
+ }
+ for (i = 0; i < CyclesCount; ++i) {
+ af->AddCycle( cycles[i] );
+ }
+ af->LoadFLT ( FLT, count );
+ free (FLT);
+ return af;
+}
+
+/** This function will load the Animation as a Font */
+Font* BAMImporter::GetFont()
+{
+ unsigned int i;
+
+ int w = 0, h = 0;
+ unsigned int Count;
+
+ ieWord *FLT = CacheFLT(Count);
+
+ // Numeric fonts have all frames in single cycle
+ if (CyclesCount > 1) {
+ Count = CyclesCount;
+ } else {
+ Count = FramesCount;
+ }
+
+ for (i = 0; i < Count; i++) {
+ unsigned int index;
+ if (CyclesCount > 1) {
+ index = FLT[cycles[i].FirstFrame];
+ if (index >= FramesCount)
+ continue;
+ } else {
+ index = i;
+ }
+
+ w = w + frames[index].Width;
+ if (frames[index].Height > h)
+ h = frames[index].Height;
+ }
+
+ Font* fnt = new Font( w, h, palette );
+ for (i = 0; i < Count; i++) {
+ unsigned int index;
+ if (CyclesCount > 1) {
+ index = FLT[cycles[i].FirstFrame];
+ if (index >= FramesCount) {
+ fnt->AddChar( NULL, 0, 0, 0, 0 );
+ continue;
+ }
+ } else {
+ index = i;
+ }
+
+ unsigned char* pixels = (unsigned char*)GetFramePixels( index );
+ if( !pixels) {
+ fnt->AddChar( NULL, 0, 0, 0, 0 );
+ continue;
+ }
+ fnt->AddChar( pixels, frames[index].Width,
+ frames[index].Height,
+ frames[index].XPos,
+ frames[index].YPos );
+ free( pixels );
+ }
+ free( FLT );
+
+ fnt->FinalizeSprite( true, 0 );
+
+ return fnt;
+}
+/** Debug Function: Returns the Global Animation Palette as a Sprite2D Object.
+If the Global Animation Palette is NULL, returns NULL. */
+Sprite2D* BAMImporter::GetPalette()
+{
+ unsigned char * pixels = ( unsigned char * ) malloc( 256 );
+ unsigned char * p = pixels;
+ for (int i = 0; i < 256; i++) {
+ *p++ = ( unsigned char ) i;
+ }
+ return core->GetVideoDriver()->CreateSprite8( 16, 16, 8, pixels, palette->col, false );
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x3AD6427A, "BAM File Importer")
+PLUGIN_CLASS(IE_BAM_CLASS_ID, BAMImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/BAMImporter/BAMImporter.h b/gemrb/plugins/BAMImporter/BAMImporter.h
new file mode 100644
index 0000000..be59c35
--- /dev/null
+++ b/gemrb/plugins/BAMImporter/BAMImporter.h
@@ -0,0 +1,90 @@
+/* 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 BAMIMPORTER_H
+#define BAMIMPORTER_H
+
+#include "AnimationMgr.h"
+
+#include "RGBAColor.h"
+#include "globals.h"
+
+struct FrameEntry {
+ ieWord Width;
+ ieWord Height;
+ ieWord XPos;
+ ieWord YPos;
+ ieDword FrameData;
+};
+
+class Palette;
+
+class BAMImporter : public AnimationMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ FrameEntry* frames;
+ CycleEntry* cycles;
+ ieWord FramesCount;
+ ieByte CyclesCount;
+ Palette* palette;
+ ieByte CompressedColorIndex;
+ ieDword FramesOffset, PaletteOffset, FLTOffset;
+ unsigned long DataStart;
+private:
+ Sprite2D* GetFrameInternal(unsigned short findex, unsigned char mode,
+ bool BAMsprite, const unsigned char* data,
+ AnimationFactory* datasrc);
+ void* GetFramePixels(unsigned short findex);
+ ieWord * CacheFLT(unsigned int &count);
+public:
+ BAMImporter(void);
+ ~BAMImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ int GetCycleSize(unsigned char Cycle);
+ AnimationFactory* GetAnimationFactory(const char* ResRef,
+ unsigned char mode = IE_NORMAL);
+ /** This function will load the Animation as a Font */
+ Font* GetFont();
+ /** Debug Function: Returns the Global Animation Palette as a Sprite2D Object.
+ If the Global Animation Palette is NULL, returns NULL. */
+ Sprite2D* GetPalette();
+
+ /** Gets a Pixel Index from the Image, unused */
+ unsigned int GetPixelIndex(unsigned int /*x*/, unsigned int /*y*/)
+ {
+ return 0;
+ }
+ /** Gets a Pixel from the Image, unused */
+ Color GetPixel(unsigned int /*x*/, unsigned int /*y*/)
+ {
+ Color null = {
+ 0x00, 0x00, 0x00, 0x00
+ };
+ return null;
+ }
+public:
+ int GetCycleCount()
+ {
+ return CyclesCount;
+ }
+};
+
+#endif
diff --git a/gemrb/plugins/BAMImporter/CMakeLists.txt b/gemrb/plugins/BAMImporter/CMakeLists.txt
new file mode 100644
index 0000000..1152b8e
--- /dev/null
+++ b/gemrb/plugins/BAMImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (BAMImporter BAMImporter.cpp)
diff --git a/gemrb/plugins/BAMImporter/Makefile.am b/gemrb/plugins/BAMImporter/Makefile.am
new file mode 100644
index 0000000..8907c33
--- /dev/null
+++ b/gemrb/plugins/BAMImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = BAMImporter.la
+BAMImporter_la_LDFLAGS = -module -avoid-version -shared
+BAMImporter_la_SOURCES = BAMImporter.cpp BAMImporter.h
diff --git a/gemrb/plugins/BIFImporter/BIFImporter.cpp b/gemrb/plugins/BIFImporter/BIFImporter.cpp
new file mode 100644
index 0000000..ccee859
--- /dev/null
+++ b/gemrb/plugins/BIFImporter/BIFImporter.cpp
@@ -0,0 +1,348 @@
+/* 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 "BIFImporter.h"
+
+#include "win32def.h"
+
+#include "Compressor.h"
+#include "Interface.h"
+#include "System/CachedFileStream.h"
+#include "System/FileStream.h"
+
+BIFImporter::BIFImporter(void)
+{
+ stream = NULL;
+ fentries = NULL;
+ tentries = NULL;
+}
+
+BIFImporter::~BIFImporter(void)
+{
+ if (stream) {
+ delete( stream );
+ }
+ if (fentries) {
+ delete[] fentries;
+ }
+ if (tentries) {
+ delete[] tentries;
+ }
+}
+
+int BIFImporter::DecompressSaveGame(DataStream *compressed)
+{
+ char Signature[8];
+ compressed->Read( Signature, 8 );
+ if (strncmp( Signature, "SAV V1.0", 8 ) ) {
+ return GEM_ERROR;
+ }
+ int All = compressed->Remains();
+ int Current;
+ if (!All) return GEM_ERROR;
+ do {
+ ieDword fnlen, complen, declen;
+ compressed->ReadDword( &fnlen );
+ char* fname = ( char* ) malloc( fnlen );
+ compressed->Read( fname, fnlen );
+ strlwr(fname);
+ compressed->ReadDword( &declen );
+ compressed->ReadDword( &complen );
+ PathJoin( path, core->CachePath, fname, NULL );
+ printf( "Decompressing %s\n",fname );
+ free( fname );
+ if (!core->IsAvailable( PLUGIN_COMPRESSION_ZLIB ))
+ return GEM_ERROR;
+ FILE *in_cache = fopen( path, "wb" );
+ if (!in_cache) {
+ printMessage("BIFImporter", " ", RED);
+ printf( "Cannot write %s.\n", path );
+ return GEM_ERROR;
+ }
+ PluginHolder<Compressor> comp(PLUGIN_COMPRESSION_ZLIB);
+ if (comp->Decompress( in_cache, compressed, complen ) != GEM_OK) {
+ return GEM_ERROR;
+ }
+ fclose( in_cache );
+ Current = compressed->Remains();
+ //starting at 20% going up to 70%
+ core->LoadProgress( 20+(All-Current)*50/All );
+ }
+ while(Current);
+ return GEM_OK;
+}
+
+//this one can create .sav files only
+int BIFImporter::CreateArchive(DataStream *compressed)
+{
+ if (stream) {
+ delete( stream );
+ stream = NULL;
+ }
+ if (!compressed) {
+ return GEM_ERROR;
+ }
+ char Signature[8];
+
+ memcpy(Signature,"SAV V1.0",8);
+ compressed->Write(Signature, 8);
+
+ return GEM_OK;
+}
+
+int BIFImporter::AddToSaveGame(DataStream *str, DataStream *uncompressed)
+{
+ ieDword fnlen, declen, complen;
+
+ fnlen = strlen(uncompressed->filename)+1;
+ declen = uncompressed->Size();
+ str->WriteDword( &fnlen);
+ str->Write( uncompressed->filename, fnlen);
+ str->WriteDword( &declen);
+ //baaah, we dump output right in the stream, we get the compressed length
+ //only after the compressed data was written
+ complen = 0xcdcdcdcd; //placeholder
+ unsigned long Pos = str->GetPos(); //storing the stream position
+ str->WriteDword( &complen);
+
+ PluginHolder<Compressor> comp(PLUGIN_COMPRESSION_ZLIB);
+ comp->Compress( str, uncompressed );
+
+ //writing compressed length (calculated)
+ unsigned long Pos2 = str->GetPos();
+ complen = Pos2-Pos-sizeof(ieDword); //calculating the compressed stream size
+ str->Seek(Pos, GEM_STREAM_START); //going back to the placeholder
+ str->WriteDword( &complen); //updating size
+ str->Seek(Pos2, GEM_STREAM_START);//resuming work
+ return GEM_OK;
+}
+
+int BIFImporter::OpenArchive(const char* filename)
+{
+ if (stream) {
+ delete( stream );
+ stream = NULL;
+ }
+ FILE* in_cache = fopen( filename, "rb" );
+ if( !in_cache) {
+ return GEM_ERROR;
+ }
+ char Signature[8];
+ if (fread( &Signature, 1, 8, in_cache ) != 8) {
+ fclose ( in_cache );
+ return GEM_ERROR;
+ }
+ fclose( in_cache );
+ //normal bif, not in cache
+ if (strncmp( Signature, "BIFFV1 ", 8 ) == 0) {
+ stream = new CachedFileStream( filename );
+ stream->Read( Signature, 8 );
+ strcpy( path, filename );
+ ReadBIF();
+ return GEM_OK;
+ }
+ //not found as normal bif
+ //checking compression type
+ FileStream* compressed = new FileStream();
+ compressed->Open( filename, true );
+ compressed->Read( Signature, 8 );
+ if (strncmp( Signature, "BIF V1.0", 8 ) == 0) {
+ ieDword fnlen, complen, declen;
+ compressed->ReadDword( &fnlen );
+ char* fname = ( char* ) malloc( fnlen );
+ compressed->Read( fname, fnlen );
+ strlwr(fname);
+ compressed->ReadDword( &declen );
+ compressed->ReadDword( &complen );
+ PathJoin( path, core->CachePath, fname, NULL );
+ free( fname );
+ in_cache = fopen( path, "rb" );
+ if (in_cache) {
+ //printf("Found in Cache\n");
+ fclose( in_cache );
+ delete( compressed );
+ stream = new CachedFileStream( path );
+ stream->Read( Signature, 8 );
+ if (strncmp( Signature, "BIFFV1 ", 8 ) == 0)
+ ReadBIF();
+ else
+ return GEM_ERROR;
+ return GEM_OK;
+ }
+ printf( "Decompressing\n" );
+ if (!core->IsAvailable( PLUGIN_COMPRESSION_ZLIB )) {
+ printMessage("BIFImporter", "No Compression Manager Available.", RED);
+ return GEM_ERROR;
+ }
+ in_cache = fopen( path, "wb" );
+ if (!in_cache) {
+ printMessage("BIFImporter", " ", RED);
+ printf( "Cannot write %s.\n", path );
+ return GEM_ERROR;
+ }
+ PluginHolder<Compressor> comp(PLUGIN_COMPRESSION_ZLIB);
+ if (comp->Decompress( in_cache, compressed, complen ) != GEM_OK) {
+ return GEM_ERROR;
+ }
+ fclose( in_cache );
+ delete( compressed );
+ stream = new CachedFileStream( path );
+ stream->Read( Signature, 8 );
+ if (strncmp( Signature, "BIFFV1 ", 8 ) == 0)
+ ReadBIF();
+ else
+ return GEM_ERROR;
+ return GEM_OK;
+ }
+
+ if (strncmp( Signature, "BIFCV1.0", 8 ) == 0) {
+ //printf("'BIFCV1.0' Compressed File Found\n");
+ PathJoin( path, core->CachePath, compressed->filename, NULL );
+ in_cache = fopen( path, "rb" );
+ if (in_cache) {
+ //printf("Found in Cache\n");
+ fclose( in_cache );
+ delete( compressed );
+ stream = new CachedFileStream( path );
+ stream->Read( Signature, 8 );
+ if (strncmp( Signature, "BIFFV1 ", 8 ) == 0) {
+ ReadBIF();
+ } else
+ return GEM_ERROR;
+ return GEM_OK;
+ }
+ printf( "Decompressing\n" );
+ if (!core->IsAvailable( PLUGIN_COMPRESSION_ZLIB ))
+ return GEM_ERROR;
+ PluginHolder<Compressor> comp(PLUGIN_COMPRESSION_ZLIB);
+ ieDword unCompBifSize;
+ compressed->ReadDword( &unCompBifSize );
+ printf( "\nDecompressing file: [..........]" );
+ fflush(stdout);
+ in_cache = fopen( path, "wb" );
+ if (!in_cache) {
+ printMessage("BIFImporter", " ", RED);
+ printf( "Cannot write %s.\n", path );
+ return GEM_ERROR;
+ }
+ ieDword finalsize = 0;
+ int laststep = 0;
+ while (finalsize < unCompBifSize) {
+ ieDword complen, declen;
+ compressed->ReadDword( &declen );
+ compressed->ReadDword( &complen );
+ if (comp->Decompress( in_cache, compressed, complen ) != GEM_OK) {
+ return GEM_ERROR;
+ }
+ finalsize = ftell( in_cache );
+ if (( int ) ( finalsize * ( 10.0 / unCompBifSize ) ) != laststep) {
+ laststep++;
+ printf( "\b\b\b\b\b\b\b\b\b\b\b" );
+ int l;
+
+ for (l = 0; l < laststep; l++)
+ printf( "|" );
+ for (; l < 10; l++)//l starts from laststep
+ printf( "." );
+ printf( "]" );
+ fflush(stdout);
+ }
+ }
+ printf( "\n" );
+ fclose( in_cache );
+ delete( compressed );
+ stream = new CachedFileStream( path );
+ stream->Read( Signature, 8 );
+ if (strncmp( Signature, "BIFFV1 ", 8 ) == 0)
+ ReadBIF();
+ else
+ return GEM_ERROR;
+ return GEM_OK;
+ }
+ delete (compressed);
+ return GEM_ERROR;
+}
+
+DataStream* BIFImporter::GetStream(unsigned long Resource, unsigned long Type)
+{
+ if (Type == IE_TIS_CLASS_ID) {
+ unsigned int srcResLoc = Resource & 0xFC000;
+ for (unsigned int i = 0; i < tentcount; i++) {
+ if (( tentries[i].resLocator & 0xFC000 ) == srcResLoc) {
+ return new CachedFileStream( stream, tentries[i].dataOffset,
+ tentries[i].tileSize * tentries[i].tilesCount );
+ }
+ }
+ } else {
+ ieDword srcResLoc = Resource & 0x3FFF;
+ for (ieDword i = 0; i < fentcount; i++) {
+ if (( fentries[i].resLocator & 0x3FFF ) == srcResLoc) {
+ return new CachedFileStream( stream, fentries[i].dataOffset,
+ fentries[i].fileSize );
+ }
+ }
+ }
+ return NULL;
+}
+
+void BIFImporter::ReadBIF(void)
+{
+ ieDword foffset;
+ stream->ReadDword( &fentcount );
+ stream->ReadDword( &tentcount );
+ stream->ReadDword( &foffset );
+ stream->Seek( foffset, GEM_STREAM_START );
+ fentries = new FileEntry[fentcount];
+ tentries = new TileEntry[tentcount];
+ if (!fentries || !tentries) {
+ if (fentries) {
+ delete fentries;
+ fentries = NULL;
+ }
+ if (tentries) {
+ delete tentries;
+ tentries = NULL;
+ }
+ return;
+ }
+ unsigned int i;
+
+ for (i=0;i<fentcount;i++) {
+ stream->ReadDword( &fentries[i].resLocator);
+ stream->ReadDword( &fentries[i].dataOffset);
+ stream->ReadDword( &fentries[i].fileSize);
+ stream->ReadWord( &fentries[i].type);
+ stream->ReadWord( &fentries[i].u1);
+ }
+ for (i=0;i<tentcount;i++) {
+ stream->ReadDword( &tentries[i].resLocator);
+ stream->ReadDword( &tentries[i].dataOffset);
+ stream->ReadDword( &tentries[i].tilesCount);
+ stream->ReadDword( &tentries[i].tileSize);
+ stream->ReadWord( &tentries[i].type);
+ stream->ReadWord( &tentries[i].u1);
+ }
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xC7F133C, "BIF File Importer")
+PLUGIN_CLASS(IE_BIF_CLASS_ID, BIFImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/BIFImporter/BIFImporter.h b/gemrb/plugins/BIFImporter/BIFImporter.h
new file mode 100644
index 0000000..eaabc32
--- /dev/null
+++ b/gemrb/plugins/BIFImporter/BIFImporter.h
@@ -0,0 +1,66 @@
+/* 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 BIFIMPORTER_H
+#define BIFIMPORTER_H
+
+#include "ArchiveImporter.h"
+
+#include "globals.h"
+
+#include "System/CachedFileStream.h"
+
+struct FileEntry {
+ ieDword resLocator;
+ ieDword dataOffset;
+ ieDword fileSize;
+ ieWord type;
+ ieWord u1; //Unknown Field
+};
+
+struct TileEntry {
+ ieDword resLocator;
+ ieDword dataOffset;
+ ieDword tilesCount;
+ ieDword tileSize; //named tilesize so it isn't confused
+ ieWord type;
+ ieWord u1; //Unknown Field
+};
+
+class BIFImporter : public ArchiveImporter {
+private:
+ char path[_MAX_PATH];
+ FileEntry* fentries;
+ TileEntry* tentries;
+ ieDword fentcount, tentcount;
+ CachedFileStream* stream;
+public:
+ BIFImporter(void);
+ ~BIFImporter(void);
+ int DecompressSaveGame(DataStream *compressed);
+ int AddToSaveGame(DataStream *str, DataStream *uncompressed);
+ int OpenArchive(const char* filename);
+ int CreateArchive(DataStream *compressed);
+ DataStream* GetStream(unsigned long Resource, unsigned long Type);
+private:
+ void ReadBIF(void);
+};
+
+#endif
diff --git a/gemrb/plugins/BIFImporter/CMakeLists.txt b/gemrb/plugins/BIFImporter/CMakeLists.txt
new file mode 100644
index 0000000..11cb1d8
--- /dev/null
+++ b/gemrb/plugins/BIFImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (BIFImporter BIFImporter.cpp)
diff --git a/gemrb/plugins/BIFImporter/Makefile.am b/gemrb/plugins/BIFImporter/Makefile.am
new file mode 100644
index 0000000..f738ff7
--- /dev/null
+++ b/gemrb/plugins/BIFImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = BIFImporter.la
+BIFImporter_la_LDFLAGS = -module -avoid-version -shared
+BIFImporter_la_SOURCES = BIFImporter.cpp BIFImporter.h
diff --git a/gemrb/plugins/BIKPlayer/BIKPlayer.cpp b/gemrb/plugins/BIKPlayer/BIKPlayer.cpp
new file mode 100644
index 0000000..5a7da6e
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/BIKPlayer.cpp
@@ -0,0 +1,1617 @@
+/* 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.
+ *
+ *
+ */
+
+/*
+ * code derived from FFMPeg
+ * @author Michael Niedermayer <michaelni at gmx.at>
+ *
+ * code derived from Bink Audio decoder
+ * Copyright (c) 2007-2009 Peter Ross (pross at xvid.org)
+ * Copyright (c) 2009 Daniel Verkamp (daniel at drv.nu)
+ *
+ * code derived from Bink video decoder
+ * Copyright (c) 2009 Konstantin Shishkov
+*/
+
+#if defined(__HAIKU__)
+#include <unistd.h>
+#endif
+
+#include "BIKPlayer.h"
+
+#include "rational.h"
+#include "binkdata.h"
+
+#include "ie_types.h"
+
+#include "Audio.h"
+#include "Variables.h"
+#include "Video.h"
+
+#include <cassert>
+#include <cstdio>
+
+static int g_truecolor;
+static ieDword *cbAtFrame = NULL;
+static ieDword *strRef = NULL;
+
+static const int ff_wma_critical_freqs[25] = {
+ 100, 200, 300, 400, 510, 630, 770, 920,
+ 1080, 1270, 1480, 1720, 2000, 2320, 2700, 3150,
+ 3700, 4400, 5300, 6400, 7700, 9500, 12000, 15500,
+ 24500,
+};
+
+BIKPlayer::BIKPlayer(void)
+{
+ video = core->GetVideoDriver();
+ inbuff = NULL;
+ maxRow = 0;
+ rowCount = 0;
+ frameCount = 0;
+ //force initialisation of static tables
+ memset(bink_trees, 0, sizeof(bink_trees));
+ memset(table, 0, sizeof(table));
+}
+
+BIKPlayer::~BIKPlayer(void)
+{
+ av_freep((void **) &inbuff);
+}
+
+void BIKPlayer::av_set_pts_info(AVRational &time_base, unsigned int pts_num, unsigned int pts_den)
+{
+ //pts_wrap_bits, if needed, is always 64
+ if(av_reduce(time_base.num, time_base.den, pts_num, pts_den, INT_MAX)) {
+ //bla bla, something didn't work
+ }
+
+ if(!time_base.num || !time_base.den)
+ time_base.num = time_base.den = 0;
+}
+
+int BIKPlayer::ReadHeader()
+{
+ str->Seek(0,GEM_STREAM_START);
+ str->Read( header.signature, BIK_SIGNATURE_LEN );
+ str->ReadDword(&header.filesize);
+ header.filesize += 8;
+ str->ReadDword(&header.framecount);
+
+ if (header.framecount > 1000000) {
+ return -1;
+ }
+
+ str->ReadDword(&header.maxframesize);
+ if (header.maxframesize > header.filesize) {
+ return -1;
+ }
+
+ str->Seek(4,GEM_CURRENT_POS);
+
+ str->ReadDword(&header.width);
+ str->ReadDword(&header.height);
+
+ ieDword fps_num, fps_den;
+
+ str->ReadDword(&fps_num);
+ str->ReadDword(&fps_den);
+
+ if (fps_num == 0 || fps_den == 0) {
+ //av_log(s, AV_LOG_ERROR, "invalid header: invalid fps (%d/%d)\n", fps_num, fps_den);
+ return -1;
+ }
+ //also sets pts_wrap_bits to 64
+ av_set_pts_info(v_timebase, fps_den, fps_num);
+
+ str->Seek(4,GEM_CURRENT_POS);
+ str->ReadDword(&header.tracks);
+
+ //we handle only single tracks, is this a problem with multi language iwd2?
+ if (header.tracks > 1) {
+ return -1;
+ }
+
+ if (header.tracks) {
+ str->Seek(4 * header.tracks,GEM_CURRENT_POS);
+ //make sure we use one track, if more needed, rewrite this part
+ assert(header.tracks==1);
+
+ str->ReadWord(&header.samplerate);
+ //also sets pts_wrap_bits to 64
+ //av_set_pts_info(s_timebase, 1, header.samplerate); //unused, we simply use header.samplerate
+ str->ReadWord(&header.audioflag);
+
+ str->Seek(4 * header.tracks,GEM_CURRENT_POS);
+ }
+
+ /* build frame index table */
+ ieDword pos, next_pos;
+ int keyframe;
+
+ str->ReadDword(&pos);
+ keyframe = pos & 1;
+ pos &= ~1;
+
+ frames.reserve(header.framecount);
+ for (unsigned int i = 0; i < header.framecount; i++) {
+ if (i == header.framecount - 1) {
+ next_pos = header.filesize;
+ } else {
+ str->ReadDword(&next_pos);
+ }
+ if (next_pos <= pos) {
+ // av_log(s, AV_LOG_ERROR, "invalid frame index table\n");
+ return -1;
+ }
+ //offset, size, keyframe
+ binkframe frame;
+
+ //the order of instructions is important here!
+ frame.pos=pos;
+ frame.keyframe=keyframe;
+ pos = next_pos&~1;
+ keyframe = next_pos&1;
+ frame.size=pos-frame.pos;
+ //sanity hack, we might as well just go belly up and refuse playing
+ if (frame.size>header.maxframesize) {
+ frame.size = header.maxframesize;
+ }
+
+ frames.push_back(frame);
+
+ }
+ inbuff = (ieByte *) av_malloc(header.maxframesize);
+ if (!inbuff) {
+ return -2;
+ }
+
+ str->Seek(4, GEM_CURRENT_POS);
+ return 0;
+}
+
+bool BIKPlayer::Open(DataStream* stream)
+{
+ validVideo = false;
+ str = stream;
+
+ str->Read( &header.signature, BIK_SIGNATURE_LEN );
+ if (memcmp( header.signature, BIK_SIGNATURE_DATA, 4 ) == 0) {
+ validVideo = ReadHeader()==0;
+ return validVideo;
+ }
+ return false;
+}
+
+void BIKPlayer::CallBackAtFrames(ieDword cnt, ieDword *arg, ieDword *arg2 )
+{
+ maxRow = cnt;
+ frameCount = 0;
+ rowCount = 0;
+ cbAtFrame = arg;
+ strRef = arg2;
+}
+
+int BIKPlayer::Play()
+{
+ if (!validVideo) {
+ return 0;
+ }
+ //Start Movie Playback
+ frameCount = 0;
+ int ret = doPlay( );
+
+ EndAudio();
+ EndVideo();
+ av_freep((void **) &inbuff);
+ return ret;
+}
+
+//this code could be in the movieplayer parent class
+void static get_current_time(long &sec, long &usec) {
+#ifdef _WIN32
+ DWORD time;
+ time = GetTickCount();
+
+ sec = time / 1000;
+ usec = (time % 1000) * 1000;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ sec = tv.tv_sec;
+ usec = tv.tv_usec;
+#endif
+}
+
+void BIKPlayer::timer_start()
+{
+ get_current_time(timer_last_sec, timer_last_usec);
+}
+
+void BIKPlayer::timer_wait()
+{
+ long sec, usec;
+ get_current_time(sec, usec);
+
+ while (sec > timer_last_sec) {
+ usec += 1000000;
+ timer_last_sec++;
+ }
+
+ //quick hack, we should rather use the rational time base as ffmpeg
+ frame_wait = v_timebase.num*1000000/v_timebase.den;
+ while (usec - timer_last_usec > (long)frame_wait) {
+ usec -= frame_wait;
+ video_frameskip++;
+ }
+
+ long to_sleep = frame_wait - (usec - timer_last_usec);
+#ifdef _WIN32
+ Sleep(to_sleep / 1000);
+#else
+ usleep(to_sleep);
+#endif
+
+ timer_start();
+}
+
+bool BIKPlayer::next_frame()
+{
+ if (timer_last_sec) {
+ timer_wait();
+ }
+ if(frameCount>=header.framecount) {
+ return false;
+ }
+ binkframe frame = frames[frameCount++];
+ str->Seek(frame.pos, GEM_STREAM_START);
+ ieDword audframesize;
+ str->ReadDword(&audframesize);
+ frame.size = str->Read( inbuff, frame.size - 4 );
+ if (DecodeAudioFrame(inbuff, audframesize)) {
+ //buggy frame, we stop immediately
+ //return false;
+ }
+ if (DecodeVideoFrame(inbuff+audframesize, frame.size-audframesize)) {
+ //buggy frame, we stop immediately
+ return false;
+ }
+ if (!timer_last_sec) {
+ timer_start();
+ }
+ return true;
+}
+
+int BIKPlayer::doPlay()
+{
+ int done = 0;
+
+ //bink is always truecolor
+ g_truecolor = 1;
+
+ frame_wait = 0;
+ timer_last_sec = 0;
+ video_frameskip = 0;
+
+ if (sound_init( core->GetAudioDrv()->CanPlay())) {
+ //sound couldn't be initialized
+ return 1;
+ }
+
+ //last parameter is to enable YUV overlay
+ outputwidth = (int) header.width;
+ outputheight= (int) header.height;
+ video->InitMovieScreen(outputwidth,outputheight, true);
+
+ if (video_init(outputwidth,outputheight)) {
+ return 2;
+ }
+
+ while (!done && next_frame()) {
+ done = video->PollMovieEvents();
+ }
+
+ return 0;
+}
+
+unsigned int BIKPlayer::fileRead(unsigned int pos, void* buf, unsigned int count)
+{
+ str->Seek(pos, GEM_STREAM_START);
+ return str->Read( buf, count );
+}
+
+void BIKPlayer::showFrame(unsigned char** buf, unsigned int *strides, unsigned int bufw,
+ unsigned int bufh, unsigned int w, unsigned int h, unsigned int dstx, unsigned int dsty)
+{
+ ieDword titleref = 0;
+
+ if (cbAtFrame && strRef) {
+ frameCount ++;
+ if ((rowCount<maxRow) && (frameCount >= cbAtFrame[rowCount]) ) {
+ rowCount++;
+ }
+ //draw subtitle here
+ if (rowCount) {
+ titleref = strRef[rowCount-1];
+ }
+ }
+ video->showYUVFrame(buf,strides,bufw,bufh,w,h,dstx,dsty, titleref);
+}
+
+int BIKPlayer::setAudioStream()
+{
+ ieDword volume;
+ core->GetDictionary()->Lookup( "Volume Movie", volume) ;
+ int source = core->GetAudioDrv()->SetupNewStream(0, 0, 0, volume, false, false);
+ return source;
+}
+
+void BIKPlayer::freeAudioStream(int stream)
+{
+ if (stream > -1)
+ core->GetAudioDrv()->ReleaseStream(stream, true);
+}
+
+void BIKPlayer::queueBuffer(int stream, unsigned short bits, int channels, short* memory, int size, int samplerate)
+{
+ if (stream > -1)
+ core->GetAudioDrv()->QueueBuffer(stream, bits, channels, memory, size, samplerate);
+}
+
+
+/**
+ * @file libavcodec/binkaudio.c
+ *
+ * Technical details here:
+ * http://wiki.multimedia.cx/index.php?title=Bink_Audio
+ */
+
+int BIKPlayer::sound_init(bool need_init)
+{
+ int sample_rate = header.samplerate;
+ int sample_rate_half;
+ unsigned int i;
+ int frame_len_bits;
+ int ret;
+
+ if(need_init) {
+ s_stream = setAudioStream();
+ } else {
+ s_stream = -1;
+ return 0;
+ }
+
+ if(s_stream<0) {
+ return 0;
+ }
+
+ if(header.audioflag&BINK_AUD_STEREO) {
+ header.channels=2;
+ }
+
+ /* determine frame length */
+ if (sample_rate < 22050) {
+ frame_len_bits = 9;
+ } else if (sample_rate < 44100) {
+ frame_len_bits = 10;
+ } else {
+ frame_len_bits = 11;
+ }
+ //audio frame length
+ s_frame_len = 1 << frame_len_bits;
+
+ if (header.channels > MAX_CHANNELS) {
+ //av_log(s->avctx, AV_LOG_ERROR, "too many channels: %d\n", s->channels);
+ return -1;
+ }
+
+ if (header.audioflag&BINK_AUD_USEDCT) {
+ s_channels = header.channels;
+ } else {
+ // audio is already interleaved for the RDFT format variant
+ sample_rate *= header.channels;
+ s_frame_len *= header.channels;
+ s_channels = 1;
+ if (header.channels == 2)
+ frame_len_bits++;
+ }
+
+ s_overlap_len = s_frame_len / 16;
+ s_block_size = (s_frame_len - s_overlap_len) * s_channels;
+ sample_rate_half = (sample_rate + 1) / 2;
+ s_root = (float) (2.0 / sqrt((float) s_frame_len));
+
+ /* calculate number of bands */
+ for (s_num_bands = 1; s_num_bands < 25; s_num_bands++) {
+ if (sample_rate_half <= ff_wma_critical_freqs[s_num_bands - 1]) {
+ break;
+ }
+ }
+
+ s_bands = (unsigned int *) av_malloc((s_num_bands + 1) * sizeof(*s_bands));
+ if (!s_bands) {
+ return -2;
+ }
+
+ /* populate bands data */
+ s_bands[0] = 1;
+ for (i = 1; i < s_num_bands; i++)
+ s_bands[i] = ff_wma_critical_freqs[i - 1] * (s_frame_len / 2) / sample_rate_half;
+ s_bands[s_num_bands] = s_frame_len / 2;
+
+ s_first = 1;
+
+ for (i = 0; i < s_channels; i++)
+ s_coeffs_ptr[i] = s_coeffs + i * s_frame_len;
+
+ if (header.audioflag&BINK_AUD_USEDCT)
+ ret = ff_dct_init(&s_trans.dct, frame_len_bits, 1);
+ else
+ ret = ff_rdft_init(&s_trans.rdft, frame_len_bits, IRIDFT);
+
+ return ret;
+}
+
+void BIKPlayer::ff_init_scantable(ScanTable *st, const uint8_t *src_scantable){
+ int i,j;
+ int end;
+
+ st->scantable= src_scantable;
+
+ for(i=0; i<64; i++){
+ j = src_scantable[i];
+ st->permutated[i] = j;
+ }
+
+ end=-1;
+ for(i=0; i<64; i++){
+ j = st->permutated[i];
+ if(j>end) end=j;
+ st->raster_end[i]= end;
+ }
+}
+
+int BIKPlayer::video_init(int w, int h)
+{
+ int bw, bh, blocks;
+ int i;
+
+ if (!bink_trees[15].table) {
+ for (i = 0; i < 16; i++) {
+ const int maxbits = bink_tree_lens[i][15];
+ bink_trees[i].table = table + i*128;
+ bink_trees[i].table_allocated = 1 << maxbits;
+ bink_trees[i].init_vlc(maxbits, 16, bink_tree_lens[i], 1, 1,
+ bink_tree_bits[i], 1, 1, INIT_VLC_LE);
+ }
+ }
+
+ memset(&c_pic,0, sizeof(AVFrame));
+ memset(&c_last,0, sizeof(AVFrame));
+
+ if (w<(signed) header.width || h<(signed) header.height) {
+ //movie dimensions are higher than available screen
+ return 1;
+ }
+
+ ff_init_scantable(&c_scantable, bink_scan);
+
+ bw = (header.width + 7) >> 3;
+ bh = (header.height + 7) >> 3;
+ blocks = bw * bh;
+
+ for (i = 0; i < BINK_NB_SRC; i++) {
+ c_bundle[i].data = (uint8_t *) av_malloc(blocks * 64);
+ //not enough memory
+ if(!c_bundle[i].data) {
+ return 2;
+ }
+ c_bundle[i].data_end = (uint8_t *) c_bundle[i].data + blocks * 64;
+ }
+
+ return 0;
+}
+
+int BIKPlayer::EndAudio()
+{
+ freeAudioStream(s_stream);
+ av_freep((void **) &s_bands);
+ if (header.audioflag&BINK_AUD_USEDCT)
+ ff_dct_end(&s_trans.dct);
+ else
+ ff_rdft_end(&s_trans.rdft);
+ return 0;
+}
+
+static inline void release_buffer(AVFrame *p)
+{
+ int i;
+
+ for(i=0;i<3;i++) {
+ av_freep((void **) &p->data[i]);
+ }
+}
+
+static inline void ff_fill_linesize(AVFrame *picture, int width)
+{
+ memset(picture->linesize, 0, sizeof(picture->linesize));
+ int w2 = (width + (1 << 1) - 1) >> 1;
+ picture->linesize[0] = width;
+ picture->linesize[1] = w2;
+ picture->linesize[2] = w2;
+}
+
+static inline void get_buffer(AVFrame *p, int width, int height)
+{
+ ff_fill_linesize(p, width);
+ for(int plane=0;plane<3;plane++) {
+ p->data[plane] = (uint8_t *) av_malloc(p->linesize[plane]*height);
+ }
+}
+
+int BIKPlayer::EndVideo()
+{
+ int i;
+
+ release_buffer(&c_pic);
+ release_buffer(&c_last);
+ for (i = 0; i < BINK_NB_SRC; i++) {
+ av_freep((void **) &c_bundle[i].data);
+ }
+ video->DrawMovieSubtitle(0);
+ return 0;
+}
+static const uint8_t rle_length_tab[16] = {
+ 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32, 64
+};
+
+const uint8_t ff_log2_tab[256]={
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+
+static inline av_const int av_log2(unsigned int v)
+{
+ int n = 0;
+ if (v & 0xffff0000) {
+ v >>= 16;
+ n += 16;
+ }
+ if (v & 0xff00) {
+ v >>= 8;
+ n += 8;
+ }
+ n += ff_log2_tab[v];
+
+ return n;
+}
+
+static inline int float_to_int16_one(const float *src){
+ float f = *src;
+ // clamp the values to the range of an int16.
+ if (f > 32767.0)
+ return 32767;
+ else if (f < -32768.0)
+ return -32768;
+ return (int32_t) (f);
+}
+
+static void ff_float_to_int16_interleave_c(int16_t *dst, const float **src, long len, int channels){
+ int i;
+ if(channels==2) {
+ for(i=0; i<len; i++) {
+ dst[2*i] = float_to_int16_one(src[0]+i);
+ dst[2*i+1] = float_to_int16_one(src[1]+i);
+ }
+ return;
+ }
+ //one channel
+ for(i=0; i<len; i++) {
+ dst[i] = float_to_int16_one(src[0]+i);
+ }
+}
+
+/**
+ * Decode Bink Audio block
+ * @param[out] out Output buffer (must contain s->block_size elements)
+ */
+void BIKPlayer::DecodeBlock(short *out)
+{
+ unsigned int ch, i, j, k;
+ float q, quant[25];
+ int width, coeff;
+
+ if (header.audioflag&BINK_AUD_USEDCT) {
+ s_gb.skip_bits(2);
+ }
+
+ for (ch = 0; ch < s_channels; ch++) {
+ FFTSample *coeffs = s_coeffs_ptr[ch];
+ q = 0.0;
+ coeffs[0] = s_gb.get_float() * s_root;
+ coeffs[1] = s_gb.get_float() * s_root;
+
+ for (i = 0; i < s_num_bands; i++) {
+ int value = s_gb.get_bits(8);
+ quant[i] = (float) pow(10.0, FFMIN(value, 95) * 0.066399999) * s_root;
+ }
+
+ // find band (k)
+ for (k = 0; s_bands[k] * 2 < 2; k++) {
+ q = quant[k];
+ }
+
+ // parse coefficients
+ i = 2;
+ while (i < s_frame_len) {
+ if (s_gb.get_bits(1)) {
+ j = i + rle_length_tab[s_gb.get_bits(4)] * 8;
+ } else {
+ j = i + 8;
+ }
+
+ if (j > s_frame_len)
+ j = s_frame_len;
+
+ width = s_gb.get_bits(4);
+ if (width == 0) {
+ memset(coeffs + i, 0, (j - i) * sizeof(*coeffs));
+ i = j;
+ while (s_bands[k] * 2 < i)
+ q = quant[k++];
+ } else {
+ while (i < j) {
+ if (s_bands[k] * 2 == i)
+ q = quant[k++];
+ coeff = s_gb.get_bits(width);
+ if (coeff) {
+ if (s_gb.get_bits(1))
+ coeffs[i] = -q * coeff;
+ else
+ coeffs[i] = q * coeff;
+ } else {
+ coeffs[i] = 0.0;
+ }
+ i++;
+ }
+ }
+ }
+
+ if (header.audioflag&BINK_AUD_USEDCT) {
+ coeffs[0] /= 0.5;
+ ff_dct_calc (&s_trans.dct, coeffs);
+ for (i = 0; i < s_frame_len; i++)
+ coeffs[i] *= s_frame_len / 2;
+ } else
+ ff_rdft_calc(&s_trans.rdft, coeffs);
+ }
+
+ ff_float_to_int16_interleave_c(out, (const float **)s_coeffs_ptr, s_frame_len, s_channels);
+
+ if (!s_first) {
+ unsigned int count = s_overlap_len * s_channels;
+ int shift = av_log2(count);
+ for (i = 0; i < count; i++) {
+ out[i] = (s_previous[i] * (count - i) + out[i] * i) >> shift;
+ }
+ }
+
+ memcpy(s_previous, out + s_block_size, s_overlap_len * s_channels * sizeof(*out));
+
+ s_first = 0;
+}
+
+//audio samples
+int BIKPlayer::DecodeAudioFrame(void *data, int data_size)
+{
+ int bits = data_size*8;
+ s_gb.init_get_bits((uint8_t *) data, bits);
+
+ unsigned int reported_size = s_gb.get_bits_long(32);
+ ieWordSigned *samples = (ieWordSigned *) calloc(reported_size+s_block_size,1);
+ if (!samples) {
+ return -1;
+ }
+
+ ieWordSigned *outbuf = samples;
+ ieWordSigned *samples_end = samples+reported_size/sizeof(ieWordSigned);
+
+ //s_block_size is in sample units
+ while (s_gb.get_bits_count() < bits && outbuf + s_block_size <= samples_end) {
+ DecodeBlock(outbuf);
+ outbuf += s_block_size;
+ s_gb.get_bits_align32();
+ }
+
+ unsigned int ret = (unsigned int) ((uint8_t*)outbuf - (uint8_t*)samples);
+
+ //sample format is signed 16 bit integers
+ //ret is a better value here as it provides almost perfect sound.
+ //Original ffmpeg code produces worse results with reported_size.
+ //Ideally ret == reported_size
+ queueBuffer(s_stream, 16, s_channels, samples, ret, header.samplerate);
+
+ free(samples);
+ return reported_size!=ret;
+}
+
+/**
+ * Reads 8x8 block of DCT coefficients.
+ *
+ * @param gb context for reading bits
+ * @param block place for storing coefficients
+ * @param scan scan order table
+ * @return 0 for success, negative value in other cases
+ */
+int BIKPlayer::read_dct_coeffs(DCTELEM block[64], const uint8_t *scan, bool is_intra)
+{
+ int mode_list[128];
+ int i, t, mask, bits, ccoef, mode;
+ int list_start = 64, list_end = 64, list_pos;
+ int coef_count = 0;
+ int coef_idx[64];
+ int quant_idx;
+ const uint32_t* quant;
+
+ mode_list[list_end++] = ( 4 << 2) | 0;
+ mode_list[list_end++] = (24 << 2) | 0;
+ mode_list[list_end++] = (44 << 2) | 0;
+ mode_list[list_end++] = ( 1 << 2) | 3;
+ mode_list[list_end++] = ( 2 << 2) | 3;
+ mode_list[list_end++] = ( 3 << 2) | 3;
+
+ bits = v_gb.get_bits(4) - 1;
+ for (mask = 1 << bits; bits >= 0; mask >>= 1, bits--) {
+ list_pos = list_start;
+ while (list_pos < list_end) {
+ if (!mode_list[list_pos] || !v_gb.get_bits(1)) {
+ list_pos++;
+ continue;
+ }
+ ccoef = mode_list[list_pos] >> 2;
+ mode = mode_list[list_pos] & 3;
+ switch (mode) {
+ case 0:
+ case 2:
+ if (!mode) {
+ mode_list[list_pos] = ((ccoef + 4) << 2) | 1;
+ } else {
+ mode_list[list_pos++] = 0;
+ }
+ for (i = 0; i < 4; i++, ccoef++) {
+ if (v_gb.get_bits(1)) {
+ mode_list[--list_start] = (ccoef << 2) | 3;
+ } else {
+ int t;
+ if (!bits) {
+ t = v_gb.get_bits(1) ? -1 : 1;
+ } else {
+ t = v_gb.get_bits(bits) | mask;
+ if (v_gb.get_bits(1)) {
+ t = -t;
+ }
+ }
+ block[scan[ccoef]] = t;
+ coef_idx[coef_count++] = ccoef;
+ }
+ }
+ break;
+ case 1:
+ mode_list[list_pos] = (ccoef << 2) | 2;
+ for (i = 0; i < 3; i++) {
+ ccoef += 4;
+ mode_list[list_end++] = (ccoef << 2) | 2;
+ }
+ break;
+ case 3:
+ if (!bits) {
+ t = v_gb.get_bits(1) ? -1 : 1;
+ } else {
+ t = v_gb.get_bits(bits) | mask;
+ if (v_gb.get_bits(1))
+ t = -t;
+ }
+ block[scan[ccoef]] = t;
+ coef_idx[coef_count++] = ccoef;
+ mode_list[list_pos++] = 0;
+ break;
+ }
+ }
+ }
+
+ quant_idx = v_gb.get_bits(4);
+ quant = is_intra ? bink_intra_quant[quant_idx]
+ : bink_inter_quant[quant_idx];
+ block[0] = (block[0] * quant[0]) >> 11;
+ for (i = 0; i < coef_count; i++) {
+ int idx = coef_idx[i];
+ block[scan[idx]] = (block[scan[idx]] * quant[idx]) >> 11;
+ }
+
+ return 0;
+}
+
+/**
+ * Reads 8x8 block with residue after motion compensation.
+ *
+ * @param gb context for reading bits
+ * @param block place to store read data
+ * @param masks_count number of masks to decode
+ * @return 0 on success, negative value in other cases
+ */
+int BIKPlayer::read_residue(DCTELEM block[64], int masks_count)
+{
+ int mode_list[128];
+ int i, mask, ccoef, mode;
+ int list_start = 64, list_end = 64, list_pos;
+ int nz_coeff[64];
+ int nz_coeff_count = 0;
+
+ mode_list[list_end++] = ( 4 << 2) | 0;
+ mode_list[list_end++] = (24 << 2) | 0;
+ mode_list[list_end++] = (44 << 2) | 0;
+ mode_list[list_end++] = ( 0 << 2) | 2;
+
+ for (mask = 1 << v_gb.get_bits(3); mask; mask >>= 1) {
+ for (i = 0; i < nz_coeff_count; i++) {
+ if (!v_gb.get_bits(1))
+ continue;
+ if (block[nz_coeff[i]] < 0)
+ block[nz_coeff[i]] -= mask;
+ else
+ block[nz_coeff[i]] += mask;
+ masks_count--;
+ if (masks_count < 0)
+ return 0;
+ }
+ list_pos = list_start;
+ while (list_pos < list_end) {
+ if (!mode_list[list_pos] || !v_gb.get_bits(1)) {
+ list_pos++;
+ continue;
+ }
+ ccoef = mode_list[list_pos] >> 2;
+ mode = mode_list[list_pos] & 3;
+ switch (mode) {
+ case 0:
+ case 2:
+ if (!mode) {
+ mode_list[list_pos] = ((ccoef + 4) << 2) | 1;
+ } else {
+ mode_list[list_pos++] = 0;
+ }
+ for (i = 0; i < 4; i++, ccoef++) {
+ if (v_gb.get_bits(1)) {
+ mode_list[--list_start] = (ccoef << 2) | 3;
+ } else {
+ nz_coeff[nz_coeff_count++] = bink_scan[ccoef];
+ block[bink_scan[ccoef]] = v_gb.get_bits(1) ? -mask : mask;
+ masks_count--;
+ if (masks_count < 0) {
+ return 0;
+ }
+ }
+ }
+ break;
+ case 1:
+ mode_list[list_pos] = (ccoef << 2) | 2;
+ for (i = 0; i < 3; i++) {
+ ccoef += 4;
+ mode_list[list_end++] = (ccoef << 2) | 2;
+ }
+ break;
+ case 3:
+ nz_coeff[nz_coeff_count++] = bink_scan[ccoef];
+ block[bink_scan[ccoef]] = v_gb.get_bits(1) ? -mask : mask;
+ mode_list[list_pos++] = 0;
+ masks_count--;
+ if (masks_count < 0)
+ return 0;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Prepares bundle for decoding data.
+ *
+ * @param gb context for reading bits
+ * @param c decoder context
+ * @param bundle_num number of the bundle to initialize
+ */
+void BIKPlayer::read_bundle(int bundle_num)
+{
+ int i;
+
+ if (bundle_num == BINK_SRC_COLORS) {
+ for (i = 0; i < 16; i++)
+ v_gb.read_tree(&c_col_high[i]);
+ c_col_lastval = 0;
+ }
+ if (bundle_num != BINK_SRC_INTRA_DC && bundle_num != BINK_SRC_INTER_DC)
+ v_gb.read_tree(&c_bundle[bundle_num].tree);
+ c_bundle[bundle_num].cur_dec =
+ c_bundle[bundle_num].cur_ptr = c_bundle[bundle_num].data;
+}
+
+/**
+ * Initializes length length in all bundles.
+ *
+ * @param c decoder context
+ * @param width plane width
+ * @param bw plane width in 8x8 blocks
+ */
+void BIKPlayer::init_lengths(int width, int bw)
+{
+ c_bundle[BINK_SRC_BLOCK_TYPES].len = av_log2((width >> 3) + 511) + 1;
+
+ c_bundle[BINK_SRC_SUB_BLOCK_TYPES].len = av_log2((width >> 4) + 511) + 1;
+
+ c_bundle[BINK_SRC_COLORS].len = av_log2((width >> 3)*64 + 511) + 1;
+
+ c_bundle[BINK_SRC_INTRA_DC].len =
+ c_bundle[BINK_SRC_INTER_DC].len =
+ c_bundle[BINK_SRC_X_OFF].len =
+ c_bundle[BINK_SRC_Y_OFF].len = av_log2((width >> 3) + 511) + 1;
+
+ c_bundle[BINK_SRC_PATTERN].len = av_log2((bw << 3) + 511) + 1;
+
+ c_bundle[BINK_SRC_RUN].len = av_log2((width >> 3)*48 + 511) + 1;
+}
+
+#define CHECK_READ_VAL(gb, b, t) \
+ if (!b->cur_dec || (b->cur_dec > b->cur_ptr)) \
+ return 0; \
+ t = gb.get_bits(b->len); \
+ if (!t) { \
+ b->cur_dec = NULL; \
+ return 0; \
+ }
+
+int BIKPlayer::get_vlc2(int16_t (*table)[2], int bits, int max_depth)
+{
+ int code;
+ int n, index, nb_bits;
+
+ index= v_gb.peek_bits(bits);
+ code = table[index][0];
+ n = table[index][1];
+
+ if(max_depth > 1 && n < 0){
+ v_gb.skip_bits(bits);
+
+ nb_bits = -n;
+
+ index= v_gb.peek_bits(nb_bits) + code;
+ code = table[index][0];
+ n = table[index][1];
+ if(max_depth > 2 && n < 0){
+ v_gb.skip_bits(nb_bits);
+
+ nb_bits = -n;
+
+ index= v_gb.get_bits(nb_bits) + code;
+ code = table[index][0];
+ n = table[index][1];
+ }
+ }
+ v_gb.skip_bits(n);
+ return code;
+}
+
+#define GET_HUFF(tree) \
+ (tree).syms[get_vlc2(bink_trees[(tree).vlc_num].table, \
+ bink_trees[(tree).vlc_num].bits, 1)]
+
+int BIKPlayer::read_runs(Bundle *b)
+{
+ int i, t, v;
+
+ CHECK_READ_VAL(v_gb, b, t);
+ if (v_gb.get_bits(1)) {
+ v = v_gb.get_bits(4);
+ if (b->cur_dec + t > b->data_end) {
+ return -1;
+ }
+ memset(b->cur_dec, v, t);
+ b->cur_dec += t;
+ } else {
+ for (i = 0; i < t; i++) {
+ v = GET_HUFF(b->tree);
+ *b->cur_dec++ = v;
+ }
+ }
+ return 0;
+}
+
+int BIKPlayer::read_motion_values(Bundle *b)
+{
+ int i, t, v;
+
+ CHECK_READ_VAL(v_gb, b, t);
+ if (v_gb.get_bits(1)) {
+ v = v_gb.get_bits(4);
+ if (v && v_gb.get_bits(1)) {
+ v = -v;
+ }
+ if (b->cur_dec + t > b->data_end) {
+ return -1;
+ }
+ memset(b->cur_dec, v, t);
+ b->cur_dec += t;
+ } else {
+ for (i = 0; i < t; i++) {
+ v = GET_HUFF(b->tree);
+ if (v && v_gb.get_bits(1)) {
+ v = -v;
+ }
+ *b->cur_dec++ = v;
+ }
+ }
+ return 0;
+}
+
+const uint8_t bink_rlelens[4] = { 4, 8, 12, 32 };
+
+int BIKPlayer::read_block_types(Bundle *b)
+{
+ int i, t, v;
+ int last = 0;
+
+ CHECK_READ_VAL(v_gb, b, t);
+ if (v_gb.get_bits(1)) {
+ v = v_gb.get_bits(4);
+ memset(b->cur_dec, v, t);
+ b->cur_dec += t;
+ } else {
+ for (i = 0; i < t; i++) {
+ v = GET_HUFF(b->tree);
+ if (v < 12) {
+ last = v;
+ *b->cur_dec++ = v;
+ } else {
+ int run = bink_rlelens[v - 12];
+
+ memset(b->cur_dec, last, run);
+ b->cur_dec += run;
+ i += run - 1;
+ }
+ }
+ }
+ return 0;
+}
+
+int BIKPlayer::read_patterns(Bundle *b)
+{
+ int i, t, v;
+
+ CHECK_READ_VAL(v_gb, b, t);
+ for (i = 0; i < t; i++) {
+ v = GET_HUFF(b->tree);
+ v |= GET_HUFF(b->tree) << 4;
+ *b->cur_dec++ = v;
+ }
+
+ return 0;
+}
+
+int BIKPlayer::read_colors(Bundle *b)
+{
+ int i, t, v, v2;
+
+ CHECK_READ_VAL(v_gb, b, t);
+ if (v_gb.get_bits(1)) {
+ v2 = GET_HUFF(c_col_high[c_col_lastval]);
+ c_col_lastval = v2;
+ v = GET_HUFF(b->tree);
+ v = (v2 << 4) | v;
+ memset(b->cur_dec, v, t);
+ b->cur_dec += t;
+ } else {
+ for (i = 0; i < t; i++) {
+ v2 = GET_HUFF(c_col_high[c_col_lastval]);
+ c_col_lastval = v2;
+ v = GET_HUFF(b->tree);
+ v = (v2 << 4) | v;
+ *b->cur_dec++ = v;
+ }
+ }
+ return 0;
+}
+
+/** number of bits used to store first DC value in bundle */
+#define DC_START_BITS 11
+
+int BIKPlayer::read_dcs(Bundle *b, int start_bits, int has_sign)
+{
+ int i, j, len, len2, bsize, v, v2;
+ SET_INT_TYPE *dst = (SET_INT_TYPE*)b->cur_dec;
+ //int16_t *dst = (int16_t*)b->cur_dec;
+
+ CHECK_READ_VAL(v_gb, b, len);
+ if (has_sign) {
+ v = v_gb.get_bits(start_bits - 1);
+ if (v && v_gb.get_bits(1))
+ v = -v;
+ } else {
+ v = v_gb.get_bits(start_bits);
+ }
+ SET_INT_VALUE(dst, v);
+ //*dst++ = v;
+ len--;
+ for (i = 0; i < len; i += 8) {
+ len2 = FFMIN(len - i, 8);
+ bsize = v_gb.get_bits(4);
+ if (bsize) {
+ for (j = 0; j < len2; j++) {
+ v2 = v_gb.get_bits(bsize);
+ if (v2 && v_gb.get_bits(1)) {
+ v2 = -v2;
+ }
+ v += v2;
+ SET_INT_VALUE(dst, v);
+ //*dst++ = v;
+ if (v < -32768 || v > 32767) {
+ return -1;
+ }
+ }
+ } else {
+ for (j = 0; j < len2; j++) {
+ SET_INT_VALUE(dst, v);
+ //*dst++ = v;
+ }
+ }
+ }
+
+ b->cur_dec = (uint8_t*)dst;
+ return 0;
+}
+
+inline int BIKPlayer::get_value(int bundle)
+{
+ int16_t ret;
+
+ if (bundle < BINK_SRC_X_OFF || bundle == BINK_SRC_RUN) {
+ return *c_bundle[bundle].cur_ptr++;
+ }
+ if (bundle == BINK_SRC_X_OFF || bundle == BINK_SRC_Y_OFF) {
+ return (int8_t)*c_bundle[bundle].cur_ptr++;
+ }
+ GET_INT_VALUE(ret, c_bundle[bundle].cur_ptr);
+ //ret = *(int16_t*)c_bundle[bundle].cur_ptr;
+ //c_bundle[bundle].cur_ptr += 2;
+ return ret;
+}
+
+#define PUT2x2(dst, stride, x, y, pix) \
+ dst[(x)*2 + (y)*2 * stride] = \
+ dst[(x)*2 + 1 + (y)*2 * stride] = \
+ dst[(x)*2 + ((y)*2 + 1) * stride] = \
+ dst[(x)*2 + 1 + ((y)*2 + 1) * stride] = pix;
+
+static void get_pixels(DCTELEM *block, const uint8_t *pixels, int line_size)
+{
+ int i;
+
+ /* read the pixels */
+ for(i=0;i<8;i++) {
+ block[0] = pixels[0];
+ block[1] = pixels[1];
+ block[2] = pixels[2];
+ block[3] = pixels[3];
+ block[4] = pixels[4];
+ block[5] = pixels[5];
+ block[6] = pixels[6];
+ block[7] = pixels[7];
+ pixels += line_size;
+ block += 8;
+ }
+}
+
+static void put_pixels_nonclamped(const DCTELEM *block, uint8_t *pixels, int line_size)
+{
+ int i;
+ /* read the pixels */
+ for(i=0;i<8;i++) {
+ pixels[0] = block[0];
+ pixels[1] = block[1];
+ pixels[2] = block[2];
+ pixels[3] = block[3];
+ pixels[4] = block[4];
+ pixels[5] = block[5];
+ pixels[6] = block[6];
+ pixels[7] = block[7];
+ pixels += line_size;
+ block += 8;
+ }
+}
+
+static void add_pixels_nonclamped(const DCTELEM *block, uint8_t *pixels, int line_size)
+{
+ int i;
+
+ /* read the pixels */
+ for(i=0;i<8;i++) {
+ pixels[0] += block[0];
+ pixels[1] += block[1];
+ pixels[2] += block[2];
+ pixels[3] += block[3];
+ pixels[4] += block[4];
+ pixels[5] += block[5];
+ pixels[6] += block[6];
+ pixels[7] += block[7];
+ pixels += line_size;
+ block += 8;
+ }
+}
+
+static inline void copy_block(DCTELEM block[64], const uint8_t *src, uint8_t *dst, int stride)
+{
+ get_pixels(block, src, stride);
+ put_pixels_nonclamped(block, dst, stride);
+}
+
+#define clear_block(block) memset( (block), 0, sizeof(DCTELEM)*64);
+
+//This replaces the j_rev_dct module
+void bink_idct(DCTELEM *block)
+{
+ int i, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, tA, tB, tC;
+ int tblock[64];
+
+ for (i = 0; i < 8; i++) {
+ t0 = block[i+ 0] + block[i+32];
+ t1 = block[i+ 0] - block[i+32];
+ t2 = block[i+16] + block[i+48];
+ t3 = block[i+16] - block[i+48];
+ t3 = ((t3 * 0xB50) >> 11) - t2;
+
+ t4 = t0 - t2;
+ t5 = t0 + t2;
+ t6 = t1 + t3;
+ t7 = t1 - t3;
+
+ t0 = block[i+40] + block[i+24];
+ t1 = block[i+40] - block[i+24];
+ t2 = block[i+ 8] + block[i+56];
+ t3 = block[i+ 8] - block[i+56];
+
+ t8 = t2 + t0;
+ t9 = t3 + t1;
+ t9 = (0xEC8 * t9) >> 11;
+ tA = ((-0x14E8 * t1) >> 11) + t9 - t8;
+ tB = t2 - t0;
+ tB = ((0xB50 * tB) >> 11) - tA;
+ tC = ((0x8A9 * t3) >> 11) + tB - t9;
+
+ tblock[i+ 0] = t5 + t8;
+ tblock[i+56] = t5 - t8;
+ tblock[i+ 8] = t6 + tA;
+ tblock[i+48] = t6 - tA;
+ tblock[i+16] = t7 + tB;
+ tblock[i+40] = t7 - tB;
+ tblock[i+32] = t4 + tC;
+ tblock[i+24] = t4 - tC;
+ }
+
+ for (i = 0; i < 64; i += 8) {
+ t0 = tblock[i+0] + tblock[i+4];
+ t1 = tblock[i+0] - tblock[i+4];
+ t2 = tblock[i+2] + tblock[i+6];
+ t3 = tblock[i+2] - tblock[i+6];
+ t3 = ((t3 * 0xB50) >> 11) - t2;
+
+ t4 = t0 - t2;
+ t5 = t0 + t2;
+ t6 = t1 + t3;
+ t7 = t1 - t3;
+
+ t0 = tblock[i+5] + tblock[i+3];
+ t1 = tblock[i+5] - tblock[i+3];
+ t2 = tblock[i+1] + tblock[i+7];
+ t3 = tblock[i+1] - tblock[i+7];
+
+ t8 = t2 + t0;
+ t9 = t3 + t1;
+ t9 = (0xEC8 * t9) >> 11;
+ tA = ((-0x14E8 * t1) >> 11) + t9 - t8;
+ tB = t2 - t0;
+ tB = ((0xB50 * tB) >> 11) - tA;
+ tC = ((0x8A9 * t3) >> 11) + tB - t9;
+
+ block[i+0] = (t5 + t8 + 0x7F) >> 8;
+ block[i+7] = (t5 - t8 + 0x7F) >> 8;
+ block[i+1] = (t6 + tA + 0x7F) >> 8;
+ block[i+6] = (t6 - tA + 0x7F) >> 8;
+ block[i+2] = (t7 + tB + 0x7F) >> 8;
+ block[i+5] = (t7 - tB + 0x7F) >> 8;
+ block[i+4] = (t4 + tC + 0x7F) >> 8;
+ block[i+3] = (t4 - tC + 0x7F) >> 8;
+ }
+}
+
+static void idct_put(uint8_t *dest, int line_size, DCTELEM *block)
+{
+ bink_idct(block);
+ put_pixels_nonclamped(block, dest, line_size);
+}
+
+static void idct_add(uint8_t *dest, int line_size, DCTELEM *block)
+{
+ bink_idct(block);
+ add_pixels_nonclamped(block, dest, line_size);
+}
+
+int BIKPlayer::DecodeVideoFrame(void *data, int data_size)
+{
+ int blk, bw, bh;
+ int i, j, plane, bx, by;
+ uint8_t *dst, *prev;
+ int v, c1, c2;
+ const uint8_t *scan;
+ int xoff, yoff;
+#pragma pack(push,16)
+ DCTELEM block[64];
+#pragma pack(pop)
+
+ int bits = data_size*8;
+ v_gb.init_get_bits((uint8_t *) data, bits);
+ //this is compatible only with the BIKi version
+ v_gb.skip_bits(32);
+
+ get_buffer(&c_pic, header.width, header.height);
+ //plane order is YUV
+ for (plane = 0; plane < 3; plane++) {
+ const int stride = c_pic.linesize[plane];
+
+ bw = plane ? (header.width + 15) >> 4 : (header.width + 7) >> 3;
+ bh = plane ? (header.height + 15) >> 4 : (header.height + 7) >> 3;
+ if(plane) {
+ init_lengths(header.width >> 1, bw);
+ } else {
+ init_lengths(header.width, bw);
+ }
+
+ for (i = 0; i < BINK_NB_SRC; i++) {
+ read_bundle(i);
+ }
+
+ for (by = 0; by < bh; by++) {
+ if (read_block_types(&c_bundle[BINK_SRC_BLOCK_TYPES]) < 0)
+ return -1;
+ if (read_block_types(&c_bundle[BINK_SRC_SUB_BLOCK_TYPES]) < 0)
+ return -1;
+ if (read_colors(&c_bundle[BINK_SRC_COLORS]) < 0)
+ return -1;
+ if (read_patterns(&c_bundle[BINK_SRC_PATTERN]) < 0)
+ return -1;
+ if (read_motion_values(&c_bundle[BINK_SRC_X_OFF]) < 0)
+ return -1;
+ if (read_motion_values(&c_bundle[BINK_SRC_Y_OFF]) < 0)
+ return -1;
+ if (read_dcs(&c_bundle[BINK_SRC_INTRA_DC], DC_START_BITS, 0) < 0)
+ return -1;
+ if (read_dcs(&c_bundle[BINK_SRC_INTER_DC], DC_START_BITS, 1) < 0)
+ return -1;
+ if (read_runs(&c_bundle[BINK_SRC_RUN]) < 0)
+ return -1;
+
+ //why is this here?
+ if (by == bh)
+ break;
+
+ dst = c_pic.data[plane] + 8*by*stride;
+ prev = c_last.data[plane] + 8*by*stride;
+ for (bx = 0; bx < bw; bx++, dst += 8, prev += 8) {
+ blk = get_value(BINK_SRC_BLOCK_TYPES);
+ if ((by & 1) && (blk == SCALED_BLOCK) ) {
+ bx++;
+ dst += 8;
+ prev += 8;
+ continue;
+ }
+ switch (blk) {
+ case SKIP_BLOCK:
+ copy_block(block, prev, dst, stride);
+ break;
+ case SCALED_BLOCK:
+ blk = get_value(BINK_SRC_SUB_BLOCK_TYPES);
+ switch (blk) {
+ case RUN_BLOCK:
+ scan = bink_patterns[v_gb.get_bits(4)];
+ i = 0;
+ do {
+ int run = get_value(BINK_SRC_RUN) + 1;
+ if (v_gb.get_bits(1)) {
+ v = get_value(BINK_SRC_COLORS);
+ for (j = 0; j < run; j++) {
+ int pos = *scan++;
+ PUT2x2(dst, stride, pos & 7, pos >> 3, v);
+ }
+ } else {
+ for (j = 0; j < run; j++) {
+ int pos = *scan++;
+ PUT2x2(dst, stride, pos & 7, pos >> 3, get_value(BINK_SRC_COLORS));
+ }
+ }
+ i += run;
+ } while (i < 63);
+ if (i == 63) {
+ int pos = *scan++;
+ PUT2x2(dst, stride, pos & 7, pos >> 3, get_value(BINK_SRC_COLORS));
+ }
+ break;
+ case INTRA_BLOCK:
+ clear_block(block);
+ block[0] = get_value(BINK_SRC_INTRA_DC);
+ read_dct_coeffs(block, c_scantable.permutated,true);
+ bink_idct(block);
+ for (j = 0; j < 8; j++) {
+ for (i = 0; i < 8; i++) {
+ PUT2x2(dst, stride, i, j, block[i + j*8]);
+ }
+ }
+ break;
+ case FILL_BLOCK:
+ v = get_value(BINK_SRC_COLORS);
+ for (j = 0; j < 16; j++) {
+ memset(dst + j*stride, v, 16);
+ }
+ break;
+ case PATTERN_BLOCK:
+ c1 = get_value(BINK_SRC_COLORS);
+ c2 = get_value(BINK_SRC_COLORS);
+ for (i = 0; i < 8; i++) {
+ v = get_value(BINK_SRC_PATTERN);
+ for (j = 0; j < 8; j++, v >>= 1) {
+ PUT2x2(dst, stride, j, i, (v & 1) ? c2 : c1);
+ }
+ }
+ break;
+ case RAW_BLOCK:
+ for (j = 0; j < 8; j++) {
+ for (i = 0; i < 8; i++) {
+ PUT2x2(dst, stride, i, j, get_value(BINK_SRC_COLORS));
+ }
+ }
+ break;
+ default:
+ printf("Incorrect 16x16 block type!\n");
+ return -1;
+ }
+ bx++;
+ dst += 8;
+ prev += 8;
+ break;
+ case MOTION_BLOCK:
+ xoff = get_value(BINK_SRC_X_OFF);
+ yoff = get_value(BINK_SRC_Y_OFF);
+ copy_block(block, prev + xoff + yoff*stride, dst, stride);
+ break;
+ case RUN_BLOCK:
+ scan = bink_patterns[v_gb.get_bits(4)];
+ i = 0;
+ do {
+ int run = get_value(BINK_SRC_RUN) + 1;
+ if (v_gb.get_bits(1)) {
+ v = get_value(BINK_SRC_COLORS);
+ for (j = 0; j < run; j++) {
+ int pos = *scan++;
+ dst[(pos & 7) + (pos >> 3) * stride] = v;
+ }
+ } else {
+ for (j = 0; j < run; j++) {
+ int pos = *scan++;
+ dst[(pos & 7) + (pos >> 3) * stride] = get_value(BINK_SRC_COLORS);
+ }
+ }
+ i += run;
+ } while (i < 63);
+ if (i == 63) {
+ int pos = *scan++;
+ dst[(pos & 7) + (pos >> 3)*stride] = get_value(BINK_SRC_COLORS);
+ }
+ break;
+ case RESIDUE_BLOCK:
+ xoff = get_value(BINK_SRC_X_OFF);
+ yoff = get_value(BINK_SRC_Y_OFF);
+ copy_block(block, prev + xoff + yoff*stride, dst, stride);
+ clear_block(block);
+ v = v_gb.get_bits(7);
+ read_residue(block, v);
+ add_pixels_nonclamped(block, dst, stride);
+ break;
+ case INTRA_BLOCK:
+ clear_block(block);
+ block[0] = get_value(BINK_SRC_INTRA_DC);
+ read_dct_coeffs(block, c_scantable.permutated,true);
+ idct_put(dst, stride, block);
+ break;
+ case FILL_BLOCK:
+ v = get_value(BINK_SRC_COLORS);
+ for (i = 0; i < 8; i++) {
+ memset(dst + i*stride, v, 8);
+ }
+ break;
+ case INTER_BLOCK:
+ xoff = get_value(BINK_SRC_X_OFF);
+ yoff = get_value(BINK_SRC_Y_OFF);
+ copy_block(block, prev + xoff + yoff*stride, dst, stride);
+ clear_block(block);
+ block[0] = get_value(BINK_SRC_INTER_DC);
+ read_dct_coeffs(block, c_scantable.permutated,false);
+ idct_add(dst, stride, block);
+ break;
+ case PATTERN_BLOCK:
+ c1 = get_value(BINK_SRC_COLORS);
+ c2 = get_value(BINK_SRC_COLORS);
+ for (i = 0; i < 8; i++) {
+ v = get_value(BINK_SRC_PATTERN);
+ for (j = 0; j < 8; j++, v >>= 1) {
+ dst[i*stride+j] = (v & 1) ? c2 : c1;
+ }
+ }
+ break;
+ case RAW_BLOCK:
+ for (i = 0; i < 8; i++) {
+ memcpy(dst + i*stride, c_bundle[BINK_SRC_COLORS].cur_ptr + i*8, 8);
+ }
+ c_bundle[BINK_SRC_COLORS].cur_ptr += 64;
+ break;
+ default:
+ printf("Unknown block type!\n");
+ return -1;
+ }
+ }
+ }
+ v_gb.get_bits_align32();
+ }
+
+ if (video_frameskip) {
+ video_frameskip--;
+ video_skippedframes++;
+ } else {
+ unsigned int dest_x = (outputwidth - header.width) >> 1;
+ unsigned int dest_y = (outputheight - header.height) >> 1;
+ showFrame((ieByte **) c_pic.data, (unsigned int *) c_pic.linesize, header.width, header.height, header.width, header.height, dest_x, dest_y);
+ }
+
+ //release the old frame even when frame is skipped
+ release_buffer(&c_last);
+ memcpy(&c_last, &c_pic, sizeof(AVFrame));
+ memset(&c_pic, 0, sizeof(AVFrame));
+ return 0;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x316E2EDE, "BIK Video Player")
+PLUGIN_RESOURCE(BIKPlayer, ".mve")
+END_PLUGIN()
diff --git a/gemrb/plugins/BIKPlayer/BIKPlayer.h b/gemrb/plugins/BIKPlayer/BIKPlayer.h
new file mode 100644
index 0000000..baba425
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/BIKPlayer.h
@@ -0,0 +1,259 @@
+/* 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 BIKPLAYER_H
+#define BIKPLAYER_H
+
+#include "MoviePlayer.h"
+
+#include "globals.h"
+#include "win32def.h"
+
+#include "Interface.h"
+
+// FIXME: This has to be included last, since it defines int*_t, which causes
+// mingw g++ 4.5.0 to choke.
+#include "GetBitContext.h"
+#include "common.h"
+#include "dsputil.h"
+#include "rational.h"
+
+#define BIK_SIGNATURE_LEN 4
+#define BIK_SIGNATURE_DATA "BIKi"
+
+#define MAX_CHANNELS 2
+#define BINK_BLOCK_MAX_SIZE (MAX_CHANNELS << 11)
+
+#if defined(__arm__)
+#define SET_INT_TYPE uint8_t
+#define SET_INT_VALUE(ptr, value)\
+ *(ptr) = (value) && 0xff; \
+ (ptr)++; \
+ *(ptr) = ((value) >> 8) && 0xff; \
+ (ptr)++;
+#else
+#define SET_INT_TYPE int16_t
+#define SET_INT_VALUE(ptr, value)\
+ *(ptr)++ = (value);
+#endif
+
+#if defined(__arm__)
+#define GET_INT_VALUE(value, ptr)\
+ (value) = *(ptr); \
+ (ptr)++; \
+ (value) |= (*(ptr)) << 8; \
+ (ptr)++;
+#else
+#define GET_INT_VALUE(value, ptr)\
+ (value) = *(int16_t*)(ptr); \
+ (ptr) += 2;
+#endif
+
+enum BinkAudFlags {
+ BINK_AUD_16BITS = 0x4000, ///< prefer 16-bit output
+ BINK_AUD_STEREO = 0x2000,
+ BINK_AUD_USEDCT = 0x1000
+};
+
+/**
+ * IDs for different data types used in Bink video codec
+ */
+enum Sources {
+ BINK_SRC_BLOCK_TYPES = 0, ///< 8x8 block types
+ BINK_SRC_SUB_BLOCK_TYPES, ///< 16x16 block types (a subset of 8x8 block types)
+ BINK_SRC_COLORS, ///< pixel values used for different block types
+ BINK_SRC_PATTERN, ///< 8-bit values for 2-colour pattern fill
+ BINK_SRC_X_OFF, ///< X components of motion value
+ BINK_SRC_Y_OFF, ///< Y components of motion value
+ BINK_SRC_INTRA_DC, ///< DC values for intrablocks with DCT
+ BINK_SRC_INTER_DC, ///< DC values for intrablocks with DCT
+ BINK_SRC_RUN, ///< run lengths for special fill block
+
+ BINK_NB_SRC
+};
+
+/**
+ * Bink video block types
+ */
+enum BlockTypes {
+ SKIP_BLOCK = 0, ///< skipped block
+ SCALED_BLOCK, ///< block has size 16x16
+ MOTION_BLOCK, ///< block is copied from previous frame with some offset
+ RUN_BLOCK, ///< block is composed from runs of colours with custom scan order
+ RESIDUE_BLOCK, ///< motion block with some difference added
+ INTRA_BLOCK, ///< intra DCT block
+ FILL_BLOCK, ///< block is filled with single colour
+ INTER_BLOCK, ///< motion block with DCT applied to the difference
+ PATTERN_BLOCK, ///< block is filled with two colours following custom pattern
+ RAW_BLOCK ///< uncoded 8x8 block
+};
+
+typedef struct AVFrame {
+ /**
+ * pointer to the picture planes.
+ * This might be different from the first allocated byte
+ * - encoding:
+ * - decoding:
+ */
+ uint8_t *data[3];
+ int linesize[3];
+} AVFrame;
+
+typedef struct {
+ char signature[BIK_SIGNATURE_LEN];
+ ieDword filesize;
+ ieDword framecount;
+ ieDword maxframesize;
+ //ieDword framecount2; //unused by player
+ ieDword width;
+ ieDword height;
+ ieDword fps;
+ ieDword divider;
+ ieDword videoflag;
+ ieDword tracks; //BinkAudFlags
+ //optional if tracks == 1 (original had multiple tracks)
+ ieWord unknown2;
+ ieWord channels;
+ ieWord samplerate;
+ ieWord audioflag;
+ ieDword unknown4;
+} binkheader;
+
+typedef struct {
+ int keyframe;
+ ieDword pos;
+ ieDword size;
+} binkframe;
+
+typedef struct Bundle {
+ int len; ///< length of number of entries to decode (in bits)
+ Tree tree; ///< Huffman tree-related data
+ uint8_t *data; ///< buffer for decoded symbols
+ uint8_t *data_end; ///< buffer end
+ uint8_t *cur_dec; ///< pointer to the not yet decoded part of the buffer
+ uint8_t *cur_ptr; ///< pointer to the data that is not read from buffer yet
+} Bundle;
+
+class BIKPlayer : public MoviePlayer {
+private:
+ Video *video;
+ bool validVideo;
+ binkheader header;
+ std::vector<binkframe> frames;
+ ieByte *inbuff;
+
+ //subtitle and frame counting
+ ieDword maxRow;
+ ieDword rowCount;
+ ieDword frameCount;
+
+ //audio context (consider packing it in a struct)
+ unsigned int s_frame_len;
+ unsigned int s_channels;
+ int s_overlap_len;
+ int s_block_size;
+ unsigned int *s_bands;
+ float s_root;
+ unsigned int s_num_bands;
+ int s_first;
+ bool s_audio;
+ int s_stream; //openal stream handle
+
+#pragma pack(push,16)
+ FFTSample s_coeffs[BINK_BLOCK_MAX_SIZE];
+ short s_previous[BINK_BLOCK_MAX_SIZE / 16]; ///< coeffs from previous audio block
+#pragma pack(pop)
+
+ float *s_coeffs_ptr[MAX_CHANNELS]; ///< pointers to the coeffs arrays for float_to_int16_interleave
+
+ union {
+ RDFTContext rdft;
+ DCTContext dct;
+ } s_trans;
+ GetBitContext s_gb;
+
+ //video context (consider packing it in a struct)
+ AVRational v_timebase;
+ long timer_last_sec;
+ long timer_last_usec;
+ unsigned int frame_wait;
+ bool video_rendered_frame;
+ unsigned int video_frameskip;
+ bool done;
+ int outputwidth, outputheight;
+ unsigned int video_skippedframes;
+ //bink specific
+ ScanTable c_scantable;
+ Bundle c_bundle[BINK_NB_SRC]; ///< bundles for decoding all data types
+ Tree c_col_high[16]; ///< trees for decoding high nibble in "colours" data type
+ int c_col_lastval; ///< value of last decoded high nibble in "colours" data type
+
+ //huffman trees for video decoding
+ VLC bink_trees[16];
+ int16_t table[16 * 128][2];
+ GetBitContext v_gb;
+ AVFrame c_pic, c_last;
+
+private:
+ void timer_start();
+ void timer_wait();
+ void segment_video_play();
+ bool next_frame();
+ int doPlay();
+ unsigned int fileRead(unsigned int pos, void* buf, unsigned int count);
+ void showFrame(unsigned char** buf, unsigned int *strides, unsigned int bufw,
+ unsigned int bufh, unsigned int w, unsigned int h, unsigned int dstx,
+ unsigned int dsty);
+ int pollEvents();
+ int setAudioStream();
+ void freeAudioStream(int stream);
+ void queueBuffer(int stream, unsigned short bits,
+ int channels, short* memory, int size, int samplerate);
+ int sound_init(bool need_init);
+ void ff_init_scantable(ScanTable *st, const uint8_t *src_scantable);
+ int video_init(int w, int h);
+ void av_set_pts_info(AVRational &time_base, unsigned int pts_num, unsigned int pts_den);
+ int ReadHeader();
+ void DecodeBlock(short *out);
+ int DecodeAudioFrame(void *data, int data_size);
+ inline int get_value(int bundle);
+ int read_dct_coeffs(DCTELEM block[64], const uint8_t *scan, bool is_intra);
+ int read_residue(DCTELEM block[64], int masks_count);
+ int read_runs(Bundle *b);
+ int read_motion_values(Bundle *b);
+ int read_block_types(Bundle *b);
+ int read_patterns(Bundle *b);
+ int read_colors(Bundle *b);
+ int read_dcs(Bundle *b, int start_bits, int has_sign);
+ int get_vlc2(int16_t (*table)[2], int bits, int max_depth);
+ void read_bundle(int bundle_num);
+ void init_lengths(int width, int bw);
+ int DecodeVideoFrame(void *data, int data_size);
+ int EndAudio();
+ int EndVideo();
+public:
+ BIKPlayer(void);
+ ~BIKPlayer(void);
+ bool Open(DataStream* stream);
+ void CallBackAtFrames(ieDword cnt, ieDword *arg, ieDword *arg2);
+ int Play();
+};
+
+#endif
diff --git a/gemrb/plugins/BIKPlayer/CMakeLists.txt b/gemrb/plugins/BIKPlayer/CMakeLists.txt
new file mode 100644
index 0000000..6126d43
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN ( BIKPlayer BIKPlayer.cpp dct.cpp fft.cpp GetBitContext.cpp mem.cpp rational.cpp rdft.cpp )
diff --git a/gemrb/plugins/BIKPlayer/GetBitContext.cpp b/gemrb/plugins/BIKPlayer/GetBitContext.cpp
new file mode 100644
index 0000000..5ef416a
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/GetBitContext.cpp
@@ -0,0 +1,317 @@
+/* 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.
+ *
+ *
+ */
+
+// code derived from FFMPEG's libavcodec/get_bits.h
+// copyright (c) 2004 Michael Niedermayer <michaelni at gmx.at>
+// and binkaudio.cpp
+// Copyright (c) 2007-2009 Peter Ross (pross at xvid.org)
+// Copyright (c) 2009 Daniel Verkamp (daniel at drv.nu)
+
+#include <math.h>
+#include "GetBitContext.h"
+
+//don't return more than 25 bits this way
+unsigned int GetBitContext::get_bits(int n) {
+ register unsigned int tmp;
+
+ tmp = AV_RL32(buffer+(index>>3))>>(index&7);
+ index+=n;
+ return tmp& (0xffffffff>>(32-n));
+}
+
+//peek at the next <25 bits (without bumping the bit counter)
+unsigned int GetBitContext::peek_bits(int n) {
+ register unsigned int tmp;
+
+ tmp = AV_RL32(buffer+(index>>3))>>(index&7);
+ return tmp& (0xffffffff>>(32-n));
+}
+
+//i think, this get_bits_long could be simple get_bits (less than 25 bits taken)
+float GetBitContext::get_float()
+{
+ int power = get_bits(5);
+ float f = ldexpf((float) get_bits_long(23), power - 23);
+ if (get_bits(1))
+ f = -f;
+ return f;
+}
+
+void GetBitContext::get_bits_align32()
+{
+ int n = (-get_bits_count()) & 31;
+ if (n) skip_bits(n);
+}
+
+unsigned int GetBitContext::get_bits_long(int n)
+{
+ if(n<=17) return get_bits(n);
+ else {
+ int ret= get_bits(16);
+ return ret | (get_bits(n-16) << 16);
+ }
+}
+
+void GetBitContext::init_get_bits(const uint8_t *b, int bit_size)
+{
+ int buffer_size = (bit_size+7)>>3;
+ if(buffer_size < 0 || bit_size < 0) {
+ buffer_size = bit_size = 0;
+ buffer = NULL;
+ }
+
+ buffer = b;
+ size_in_bits = bit_size;
+ buffer_end = buffer + buffer_size;
+ index=0;
+}
+
+//bink video related from bink.c
+// * Copyright (c) 2009 Konstantin Shishkov
+
+/**
+ * Reads information about Huffman tree used to decode data.
+ *
+ * @param tree pointer for storing tree data
+ */
+void GetBitContext::read_tree(Tree *tree)
+{
+ uint8_t tmp1[16], tmp2[16], *in = tmp1, *out = tmp2;
+ int i, t, len;
+
+ tree->vlc_num = get_bits(4);
+ if (!tree->vlc_num) {
+ for (i = 0; i < 16; i++)
+ tree->syms[i] = i;
+ return;
+ }
+ if (get_bits(1)) {
+ len = get_bits(3);
+ memset(tmp1, 0, sizeof(tmp1));
+ for (i = 0; i <= len; i++) {
+ tree->syms[i] = get_bits(4);
+ tmp1[tree->syms[i]] = 1;
+ }
+ for (i = 0; i < 16; i++)
+ if (!tmp1[i])
+ tree->syms[++len] = i;
+ } else {
+ len = get_bits(2);
+ for (i = 0; i < 16; i++)
+ in[i] = i;
+ for (i = 0; i <= len; i++) {
+ int size = 1 << i;
+ for (t = 0; t < 16; t += size << 1)
+ merge(out + t, in + t, size);
+ FFSWAP(uint8_t*, in, out);
+ }
+ memcpy(tree->syms, in, 16);
+ }
+}
+
+/**
+ * Merges two consequent lists of equal size depending on bits read.
+ *
+ * @param gb context for reading bits
+ * @param dst buffer where merged list will be written to
+ * @param src pointer to the head of the first list (the second lists starts at src+size)
+ * @param size input lists size
+ */
+void GetBitContext::merge( uint8_t *dst, uint8_t *src, int size)
+{
+ uint8_t *src2 = src + size;
+ int size2 = size;
+
+ do {
+ if (!get_bits(1)) {
+ *dst++ = *src++;
+ size--;
+ } else {
+ *dst++ = *src2++;
+ size2--;
+ }
+ } while (size && size2);
+
+ while (size--)
+ *dst++ = *src++;
+ while (size2--)
+ *dst++ = *src2++;
+}
+
+
+void GetBitContext::debug(const char *prefix)
+{
+ printf("%s: %d\n", prefix, index);
+}
+
+//VLC specific code from bitstream.c
+//removed all non-static parts for simplicity
+
+int VLC::alloc_table(int size)
+{
+ int index;
+ index = table_size;
+ table_size += size;
+ if (table_size > table_allocated) {
+ abort(); //cant do anything, init_vlc() is used with too little memory
+ }
+ return index;
+}
+
+int VLC::build_table(int table_nb_bits, int nb_codes,
+ const void *bits, int bits_wrap, int bits_size,
+ const void *codes, int codes_wrap, int codes_size,
+ uint32_t code_prefix, int n_prefix, int flags)
+{
+ int i, j, k, n, table_size, table_index, nb, n1, index, symbol;
+ uint32_t code_prefix2;
+ uint32_t code;
+ int16_t (*p_table)[2];
+
+ table_size = 1 << table_nb_bits;
+ table_index = alloc_table(table_size);
+ if (table_index < 0)
+ return -1;
+ p_table = &table[table_index];
+
+ for(i=0;i<table_size;i++) {
+ p_table[i][1] = 0; //bits
+ p_table[i][0] = -1; //codes
+ }
+
+ /* first pass: map codes and compute auxillary table sizes */
+ for(i=0;i<nb_codes;i++) {
+ GET_DATA(n, bits, i, bits_wrap, bits_size);
+ GET_DATA(code, codes, i, codes_wrap, codes_size);
+ /* we accept tables with holes */
+ if (n <= 0)
+ continue;
+ symbol = i;
+ /* if code matches the prefix, it is in the table */
+ n -= n_prefix;
+ if(flags & INIT_VLC_LE)
+ code_prefix2= code & (n_prefix>=32 ? 0xffffffff : (1 << n_prefix)-1);
+ else
+ code_prefix2= code >> n;
+ if (n > 0 && code_prefix2 == code_prefix) {
+ if (n <= table_nb_bits) {
+ /* no need to add another table */
+ j = (code << (table_nb_bits - n)) & (table_size - 1);
+ nb = 1 << (table_nb_bits - n);
+ for(k=0;k<nb;k++) {
+ if(flags & INIT_VLC_LE)
+ j = (code >> n_prefix) + (k<<n);
+ if (p_table[j][1] /*bits*/ != 0) {
+ //av_log(NULL, AV_LOG_ERROR, "incorrect codes\n");
+ return -1;
+ }
+ p_table[j][1] = n; //bits
+ p_table[j][0] = symbol;
+ j++;
+ }
+ } else {
+ n -= table_nb_bits;
+ j = (code >> ((flags & INIT_VLC_LE) ? n_prefix : n)) & ((1 << table_nb_bits) - 1);
+ /* compute table size */
+ n1 = -p_table[j][1]; //bits
+ if (n > n1)
+ n1 = n;
+ p_table[j][1] = -n1; //bits
+ }
+ }
+ }
+
+ /* second pass : fill auxillary tables recursively */
+ for(i=0;i<table_size;i++) {
+ n = p_table[i][1]; //bits
+ if (n < 0) {
+ n = -n;
+ if (n > table_nb_bits) {
+ n = table_nb_bits;
+ p_table[i][1] = -n; //bits
+ }
+ index = build_table(n, nb_codes,
+ bits, bits_wrap, bits_size,
+ codes, codes_wrap, codes_size,
+ (flags & INIT_VLC_LE) ? (code_prefix | (i << n_prefix)) : ((code_prefix << table_nb_bits) | i),
+ n_prefix + table_nb_bits, flags);
+ if (index < 0)
+ return -1;
+ /* note: realloc has been done, so reload tables */
+ p_table = &table[table_index];
+ p_table[i][0] = index; //code
+ }
+ }
+ return table_index;
+}
+
+/* Build VLC decoding tables suitable for use with get_vlc().
+
+ 'nb_bits' set thee decoding table size (2^nb_bits) entries. The
+ bigger it is, the faster is the decoding. But it should not be too
+ big to save memory and L1 cache. '9' is a good compromise.
+
+ 'nb_codes' : number of vlcs codes
+
+ 'bits' : table which gives the size (in bits) of each vlc code.
+
+ 'codes' : table which gives the bit pattern of of each vlc code.
+
+ 'symbols' : table which gives the values to be returned from get_vlc().
+
+ 'xxx_wrap' : give the number of bytes between each entry of the
+ 'bits' or 'codes' tables.
+
+ 'xxx_size' : gives the number of bytes of each entry of the 'bits'
+ or 'codes' tables.
+
+ 'wrap' and 'size' allows to use any memory configuration and types
+ (byte/word/long) to store the 'bits', 'codes', and 'symbols' tables.
+
+ 'use_static' should be set to 1 for tables, which should be freed
+ with av_free_static(), 0 if free_vlc() will be used.
+*/
+int VLC::init_vlc(int nb_bits, int nb_codes,
+ const void *p_bits, int bits_wrap, int bits_size,
+ const void *codes, int codes_wrap, int codes_size,
+ int flags)
+{
+ bits = nb_bits;
+ if(table_size) {
+ if(table_size == table_allocated) {
+ return 0;
+ } else if(table_size) {
+ //trying to reallocate static table
+ return -1;
+ }
+ }
+
+ if (build_table(nb_bits, nb_codes, p_bits, bits_wrap, bits_size,
+ codes, codes_wrap, codes_size, 0, 0, flags) < 0) {
+ //no need of freeing this, it was allocated staticly
+ //av_freep((void **) &table);
+ return -1;
+ }
+ if(table_size != table_allocated) {
+ //av_log(NULL, AV_LOG_ERROR, "needed %d had %d\n", table_size, table_allocated);
+ }
+ return 0;
+}
+
diff --git a/gemrb/plugins/BIKPlayer/GetBitContext.h b/gemrb/plugins/BIKPlayer/GetBitContext.h
new file mode 100644
index 0000000..d056f37
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/GetBitContext.h
@@ -0,0 +1,107 @@
+/* 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.
+ *
+ *
+ */
+
+//code derived from FFMPEG
+//using code from get_bits.h, bitstream.c
+
+#include "common.h"
+
+#define INIT_VLC_LE 2
+#define INIT_VLC_USE_NEW_STATIC 4
+
+class VLC
+{
+public:
+ int bits;
+ int16_t (*table)[2]; ///< code, bits
+ int table_size, table_allocated;
+public:
+ int init_vlc(int nb_bits, int nb_codes,
+ const void *bits, int bits_wrap, int bits_size,
+ const void *codes, int codes_wrap, int codes_size,
+ int flags);
+private:
+ int alloc_table(int size);
+ int build_table(int table_nb_bits, int nb_codes,
+ const void *bits, int bits_wrap, int bits_size,
+ const void *codes, int codes_wrap, int codes_size,
+ uint32_t code_prefix, int n_prefix, int flags);
+
+};
+
+#if defined(__arm__)
+#define GET_DATA(v, table, i, wrap, size) \
+{\
+ const uint8_t *ptr = (const uint8_t *)table + i * wrap;\
+ v = 0;\
+ switch(size) {\
+ default:\
+ v |= *((const uint8_t *)ptr+3) << 24;\
+ v |= *((const uint8_t *)ptr+2) << 16;\
+ case 2:\
+ v |= *((const uint8_t *)ptr+1) << 8;\
+ case 1:\
+ v |= *(const uint8_t *)ptr;\
+ }\
+}
+#else
+#define GET_DATA(v, table, i, wrap, size) \
+{\
+ const uint8_t *ptr = (const uint8_t *)table + i * wrap;\
+ switch(size) {\
+ case 1:\
+ v = *(const uint8_t *)ptr;\
+ break;\
+ case 2:\
+ v = *(const uint16_t *)ptr;\
+ break;\
+ default:\
+ v = *(const uint32_t *)ptr;\
+ break;\
+ }\
+}
+#endif
+
+#define AV_RL32(x) \
+ ((((const uint8_t*)(x))[3] << 24) | \
+ (((const uint8_t*)(x))[2] << 16) | \
+ (((const uint8_t*)(x))[1] << 8) | \
+ ((const uint8_t*)(x))[0])
+
+class GetBitContext
+{
+public:
+ const uint8_t *buffer, *buffer_end;
+ int index;
+ int size_in_bits;
+public:
+ void debug(const char *prefix);
+ float get_float();
+ void skip_bits(int x) { index+=x; }
+ int get_bits_count() { return index; }
+ void get_bits_align32();
+ unsigned int get_bits(int x);
+ unsigned int peek_bits(int x);
+ unsigned int get_bits_long(int n);
+ void init_get_bits(const uint8_t *b, int bit_size);
+ void read_tree(Tree *tree);
+private:
+ void merge( uint8_t *dst, uint8_t *src, int size);
+};
diff --git a/gemrb/plugins/BIKPlayer/Makefile.am b/gemrb/plugins/BIKPlayer/Makefile.am
new file mode 100644
index 0000000..b5f35b4
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/Makefile.am
@@ -0,0 +1,16 @@
+plugin_LTLIBRARIES = BIKPlayer.la
+BIKPlayer_la_LDFLAGS = -module -avoid-version -shared
+BIKPlayer_la_SOURCES = \
+ BIKPlayer.cpp \
+ BIKPlayer.h \
+ GetBitContext.cpp \
+ GetBitContext.h \
+ dct.cpp \
+ fft.cpp \
+ rdft.cpp \
+ rational.cpp \
+ rational.h \
+ mem.cpp \
+ binkdata.h \
+ dsputil.h \
+ common.h
diff --git a/gemrb/plugins/BIKPlayer/binkdata.h b/gemrb/plugins/BIKPlayer/binkdata.h
new file mode 100644
index 0000000..f9013c8
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/binkdata.h
@@ -0,0 +1,613 @@
+/*
+ * Bink video decoder
+ * Copyright (C) 2009 Kostya Shishkov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_BINKDATA_H
+#define AVCODEC_BINKDATA_H
+
+/** Bink DCT and residue 8x8 block scan order */
+static const uint8_t bink_scan[64] = {
+ 0, 1, 8, 9, 2, 3, 10, 11,
+ 4, 5, 12, 13, 6, 7, 14, 15,
+ 20, 21, 28, 29, 22, 23, 30, 31,
+ 16, 17, 24, 25, 32, 33, 40, 41,
+ 34, 35, 42, 43, 48, 49, 56, 57,
+ 50, 51, 58, 59, 18, 19, 26, 27,
+ 36, 37, 44, 45, 38, 39, 46, 47,
+ 52, 53, 60, 61, 54, 55, 62, 63
+};
+
+static const uint8_t bink_tree_bits[16][16] = {
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ },
+ {
+ 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D,
+ 0x0F, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,
+ },
+ {
+ 0x00, 0x02, 0x01, 0x09, 0x05, 0x15, 0x0D, 0x1D,
+ 0x03, 0x13, 0x0B, 0x1B, 0x07, 0x17, 0x0F, 0x1F,
+ },
+ {
+ 0x00, 0x02, 0x06, 0x01, 0x09, 0x05, 0x0D, 0x1D,
+ 0x03, 0x13, 0x0B, 0x1B, 0x07, 0x17, 0x0F, 0x1F,
+ },
+ {
+ 0x00, 0x04, 0x02, 0x06, 0x01, 0x09, 0x05, 0x0D,
+ 0x03, 0x13, 0x0B, 0x1B, 0x07, 0x17, 0x0F, 0x1F,
+ },
+ {
+ 0x00, 0x04, 0x02, 0x0A, 0x06, 0x0E, 0x01, 0x09,
+ 0x05, 0x0D, 0x03, 0x0B, 0x07, 0x17, 0x0F, 0x1F,
+ },
+ {
+ 0x00, 0x02, 0x0A, 0x06, 0x0E, 0x01, 0x09, 0x05,
+ 0x0D, 0x03, 0x0B, 0x1B, 0x07, 0x17, 0x0F, 0x1F,
+ },
+ {
+ 0x00, 0x01, 0x05, 0x03, 0x13, 0x0B, 0x1B, 0x3B,
+ 0x07, 0x27, 0x17, 0x37, 0x0F, 0x2F, 0x1F, 0x3F,
+ },
+ {
+ 0x00, 0x01, 0x03, 0x13, 0x0B, 0x2B, 0x1B, 0x3B,
+ 0x07, 0x27, 0x17, 0x37, 0x0F, 0x2F, 0x1F, 0x3F,
+ },
+ {
+ 0x00, 0x01, 0x05, 0x0D, 0x03, 0x13, 0x0B, 0x1B,
+ 0x07, 0x27, 0x17, 0x37, 0x0F, 0x2F, 0x1F, 0x3F,
+ },
+ {
+ 0x00, 0x02, 0x01, 0x05, 0x0D, 0x03, 0x13, 0x0B,
+ 0x1B, 0x07, 0x17, 0x37, 0x0F, 0x2F, 0x1F, 0x3F,
+ },
+ {
+ 0x00, 0x01, 0x09, 0x05, 0x0D, 0x03, 0x13, 0x0B,
+ 0x1B, 0x07, 0x17, 0x37, 0x0F, 0x2F, 0x1F, 0x3F,
+ },
+ {
+ 0x00, 0x02, 0x01, 0x03, 0x13, 0x0B, 0x1B, 0x3B,
+ 0x07, 0x27, 0x17, 0x37, 0x0F, 0x2F, 0x1F, 0x3F,
+ },
+ {
+ 0x00, 0x01, 0x05, 0x03, 0x07, 0x27, 0x17, 0x37,
+ 0x0F, 0x4F, 0x2F, 0x6F, 0x1F, 0x5F, 0x3F, 0x7F,
+ },
+ {
+ 0x00, 0x01, 0x05, 0x03, 0x07, 0x17, 0x37, 0x77,
+ 0x0F, 0x4F, 0x2F, 0x6F, 0x1F, 0x5F, 0x3F, 0x7F,
+ },
+ {
+ 0x00, 0x02, 0x01, 0x05, 0x03, 0x07, 0x27, 0x17,
+ 0x37, 0x0F, 0x2F, 0x6F, 0x1F, 0x5F, 0x3F, 0x7F,
+ },
+};
+
+static const uint8_t bink_tree_lens[16][16] = {
+ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },
+ { 1, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 },
+ { 2, 2, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 },
+ { 2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 },
+ { 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5 },
+ { 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5 },
+ { 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5 },
+ { 1, 3, 3, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 1, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 1, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 2, 2, 3, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6 },
+ { 1, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6 },
+ { 2, 2, 2, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
+ { 1, 3, 3, 3, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7 },
+ { 1, 3, 3, 3, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 },
+ { 2, 2, 3, 3, 3, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7 },
+};
+
+static const uint8_t bink_patterns[16][64] = {
+ {
+ 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38,
+ 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01,
+ 0x02, 0x0A, 0x12, 0x1A, 0x22, 0x2A, 0x32, 0x3A,
+ 0x3B, 0x33, 0x2B, 0x23, 0x1B, 0x13, 0x0B, 0x03,
+ 0x04, 0x0C, 0x14, 0x1C, 0x24, 0x2C, 0x34, 0x3C,
+ 0x3D, 0x35, 0x2D, 0x25, 0x1D, 0x15, 0x0D, 0x05,
+ 0x06, 0x0E, 0x16, 0x1E, 0x26, 0x2E, 0x36, 0x3E,
+ 0x3F, 0x37, 0x2F, 0x27, 0x1F, 0x17, 0x0F, 0x07,
+ },
+ {
+ 0x3B, 0x3A, 0x39, 0x38, 0x30, 0x31, 0x32, 0x33,
+ 0x2B, 0x2A, 0x29, 0x28, 0x20, 0x21, 0x22, 0x23,
+ 0x1B, 0x1A, 0x19, 0x18, 0x10, 0x11, 0x12, 0x13,
+ 0x0B, 0x0A, 0x09, 0x08, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x0F, 0x0E, 0x0D, 0x0C,
+ 0x14, 0x15, 0x16, 0x17, 0x1F, 0x1E, 0x1D, 0x1C,
+ 0x24, 0x25, 0x26, 0x27, 0x2F, 0x2E, 0x2D, 0x2C,
+ 0x34, 0x35, 0x36, 0x37, 0x3F, 0x3E, 0x3D, 0x3C,
+ },
+ {
+ 0x19, 0x11, 0x12, 0x1A, 0x1B, 0x13, 0x0B, 0x03,
+ 0x02, 0x0A, 0x09, 0x01, 0x00, 0x08, 0x10, 0x18,
+ 0x20, 0x28, 0x30, 0x38, 0x39, 0x31, 0x29, 0x2A,
+ 0x32, 0x3A, 0x3B, 0x33, 0x2B, 0x23, 0x22, 0x21,
+ 0x1D, 0x15, 0x16, 0x1E, 0x1F, 0x17, 0x0F, 0x07,
+ 0x06, 0x0E, 0x0D, 0x05, 0x04, 0x0C, 0x14, 0x1C,
+ 0x24, 0x2C, 0x34, 0x3C, 0x3D, 0x35, 0x2D, 0x2E,
+ 0x36, 0x3E, 0x3F, 0x37, 0x2F, 0x27, 0x26, 0x25,
+ },
+ {
+ 0x03, 0x0B, 0x02, 0x0A, 0x01, 0x09, 0x00, 0x08,
+ 0x10, 0x18, 0x11, 0x19, 0x12, 0x1A, 0x13, 0x1B,
+ 0x23, 0x2B, 0x22, 0x2A, 0x21, 0x29, 0x20, 0x28,
+ 0x30, 0x38, 0x31, 0x39, 0x32, 0x3A, 0x33, 0x3B,
+ 0x3C, 0x34, 0x3D, 0x35, 0x3E, 0x36, 0x3F, 0x37,
+ 0x2F, 0x27, 0x2E, 0x26, 0x2D, 0x25, 0x2C, 0x24,
+ 0x1C, 0x14, 0x1D, 0x15, 0x1E, 0x16, 0x1F, 0x17,
+ 0x0F, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04,
+ },
+ {
+ 0x18, 0x19, 0x10, 0x11, 0x08, 0x09, 0x00, 0x01,
+ 0x02, 0x03, 0x0A, 0x0B, 0x12, 0x13, 0x1A, 0x1B,
+ 0x1C, 0x1D, 0x14, 0x15, 0x0C, 0x0D, 0x04, 0x05,
+ 0x06, 0x07, 0x0E, 0x0F, 0x16, 0x17, 0x1E, 0x1F,
+ 0x27, 0x26, 0x2F, 0x2E, 0x37, 0x36, 0x3F, 0x3E,
+ 0x3D, 0x3C, 0x35, 0x34, 0x2D, 0x2C, 0x25, 0x24,
+ 0x23, 0x22, 0x2B, 0x2A, 0x33, 0x32, 0x3B, 0x3A,
+ 0x39, 0x38, 0x31, 0x30, 0x29, 0x28, 0x21, 0x20,
+ },
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0A, 0x0B,
+ 0x10, 0x11, 0x12, 0x13, 0x18, 0x19, 0x1A, 0x1B,
+ 0x20, 0x21, 0x22, 0x23, 0x28, 0x29, 0x2A, 0x2B,
+ 0x30, 0x31, 0x32, 0x33, 0x38, 0x39, 0x3A, 0x3B,
+ 0x04, 0x05, 0x06, 0x07, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x14, 0x15, 0x16, 0x17, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x24, 0x25, 0x26, 0x27, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x34, 0x35, 0x36, 0x37, 0x3C, 0x3D, 0x3E, 0x3F,
+ },
+ {
+ 0x06, 0x07, 0x0F, 0x0E, 0x0D, 0x05, 0x0C, 0x04,
+ 0x03, 0x0B, 0x02, 0x0A, 0x09, 0x01, 0x00, 0x08,
+ 0x10, 0x18, 0x11, 0x19, 0x12, 0x1A, 0x13, 0x1B,
+ 0x14, 0x1C, 0x15, 0x1D, 0x16, 0x1E, 0x17, 0x1F,
+ 0x27, 0x2F, 0x26, 0x2E, 0x25, 0x2D, 0x24, 0x2C,
+ 0x23, 0x2B, 0x22, 0x2A, 0x21, 0x29, 0x20, 0x28,
+ 0x31, 0x30, 0x38, 0x39, 0x3A, 0x32, 0x3B, 0x33,
+ 0x3C, 0x34, 0x3D, 0x35, 0x36, 0x37, 0x3F, 0x3E,
+ },
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38,
+ },
+ {
+ 0x00, 0x08, 0x09, 0x01, 0x02, 0x03, 0x0B, 0x0A,
+ 0x12, 0x13, 0x1B, 0x1A, 0x19, 0x11, 0x10, 0x18,
+ 0x20, 0x28, 0x29, 0x21, 0x22, 0x23, 0x2B, 0x2A,
+ 0x32, 0x31, 0x30, 0x38, 0x39, 0x3A, 0x3B, 0x33,
+ 0x34, 0x3C, 0x3D, 0x3E, 0x3F, 0x37, 0x36, 0x35,
+ 0x2D, 0x2C, 0x24, 0x25, 0x26, 0x2E, 0x2F, 0x27,
+ 0x1F, 0x17, 0x16, 0x1E, 0x1D, 0x1C, 0x14, 0x15,
+ 0x0D, 0x0C, 0x04, 0x05, 0x06, 0x0E, 0x0F, 0x07,
+ },
+ {
+ 0x18, 0x19, 0x10, 0x11, 0x08, 0x09, 0x00, 0x01,
+ 0x02, 0x03, 0x0A, 0x0B, 0x12, 0x13, 0x1A, 0x1B,
+ 0x1C, 0x1D, 0x14, 0x15, 0x0C, 0x0D, 0x04, 0x05,
+ 0x06, 0x07, 0x0E, 0x0F, 0x16, 0x17, 0x1E, 0x1F,
+ 0x26, 0x27, 0x2E, 0x2F, 0x36, 0x37, 0x3E, 0x3F,
+ 0x3C, 0x3D, 0x34, 0x35, 0x2C, 0x2D, 0x24, 0x25,
+ 0x22, 0x23, 0x2A, 0x2B, 0x32, 0x33, 0x3A, 0x3B,
+ 0x38, 0x39, 0x30, 0x31, 0x28, 0x29, 0x20, 0x21,
+ },
+ {
+ 0x00, 0x08, 0x01, 0x09, 0x02, 0x0A, 0x03, 0x0B,
+ 0x13, 0x1B, 0x12, 0x1A, 0x11, 0x19, 0x10, 0x18,
+ 0x20, 0x28, 0x21, 0x29, 0x22, 0x2A, 0x23, 0x2B,
+ 0x33, 0x3B, 0x32, 0x3A, 0x31, 0x39, 0x30, 0x38,
+ 0x3C, 0x34, 0x3D, 0x35, 0x3E, 0x36, 0x3F, 0x37,
+ 0x2F, 0x27, 0x2E, 0x26, 0x2D, 0x25, 0x2C, 0x24,
+ 0x1F, 0x17, 0x1E, 0x16, 0x1D, 0x15, 0x1C, 0x14,
+ 0x0C, 0x04, 0x0D, 0x05, 0x0E, 0x06, 0x0F, 0x07,
+ },
+ {
+ 0x00, 0x08, 0x10, 0x18, 0x19, 0x1A, 0x1B, 0x13,
+ 0x0B, 0x03, 0x02, 0x01, 0x09, 0x11, 0x12, 0x0A,
+ 0x04, 0x0C, 0x14, 0x1C, 0x1D, 0x1E, 0x1F, 0x17,
+ 0x0F, 0x07, 0x06, 0x05, 0x0D, 0x15, 0x16, 0x0E,
+ 0x24, 0x2C, 0x34, 0x3C, 0x3D, 0x3E, 0x3F, 0x37,
+ 0x2F, 0x27, 0x26, 0x25, 0x2D, 0x35, 0x36, 0x2E,
+ 0x20, 0x28, 0x30, 0x38, 0x39, 0x3A, 0x3B, 0x33,
+ 0x2B, 0x23, 0x22, 0x21, 0x29, 0x31, 0x32, 0x2A,
+ },
+ {
+ 0x00, 0x08, 0x09, 0x01, 0x02, 0x03, 0x0B, 0x0A,
+ 0x13, 0x1B, 0x1A, 0x12, 0x11, 0x10, 0x18, 0x19,
+ 0x21, 0x20, 0x28, 0x29, 0x2A, 0x22, 0x23, 0x2B,
+ 0x33, 0x3B, 0x3A, 0x32, 0x31, 0x39, 0x38, 0x30,
+ 0x34, 0x3C, 0x3D, 0x35, 0x36, 0x3E, 0x3F, 0x37,
+ 0x2F, 0x27, 0x26, 0x2E, 0x2D, 0x2C, 0x24, 0x25,
+ 0x1D, 0x1C, 0x14, 0x15, 0x16, 0x1E, 0x1F, 0x17,
+ 0x0E, 0x0F, 0x07, 0x06, 0x05, 0x0D, 0x0C, 0x04,
+ },
+ {
+ 0x18, 0x10, 0x08, 0x00, 0x01, 0x02, 0x03, 0x0B,
+ 0x13, 0x1B, 0x1A, 0x19, 0x11, 0x0A, 0x09, 0x12,
+ 0x1C, 0x14, 0x0C, 0x04, 0x05, 0x06, 0x07, 0x0F,
+ 0x17, 0x1F, 0x1E, 0x1D, 0x15, 0x0E, 0x0D, 0x16,
+ 0x3C, 0x34, 0x2C, 0x24, 0x25, 0x26, 0x27, 0x2F,
+ 0x37, 0x3F, 0x3E, 0x3D, 0x35, 0x2E, 0x2D, 0x36,
+ 0x38, 0x30, 0x28, 0x20, 0x21, 0x22, 0x23, 0x2B,
+ 0x33, 0x3B, 0x3A, 0x39, 0x31, 0x2A, 0x29, 0x32,
+ },
+ {
+ 0x00, 0x08, 0x09, 0x01, 0x02, 0x0A, 0x12, 0x11,
+ 0x10, 0x18, 0x19, 0x1A, 0x1B, 0x13, 0x0B, 0x03,
+ 0x07, 0x06, 0x0E, 0x0F, 0x17, 0x16, 0x15, 0x0D,
+ 0x05, 0x04, 0x0C, 0x14, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x3F, 0x3E, 0x36, 0x37, 0x2F, 0x2E, 0x2D, 0x35,
+ 0x3D, 0x3C, 0x34, 0x2C, 0x24, 0x25, 0x26, 0x27,
+ 0x38, 0x30, 0x31, 0x39, 0x3A, 0x32, 0x2A, 0x29,
+ 0x28, 0x20, 0x21, 0x22, 0x23, 0x2B, 0x33, 0x3B,
+ },
+ {
+ 0x00, 0x01, 0x08, 0x09, 0x10, 0x11, 0x18, 0x19,
+ 0x20, 0x21, 0x28, 0x29, 0x30, 0x31, 0x38, 0x39,
+ 0x3A, 0x3B, 0x32, 0x33, 0x2A, 0x2B, 0x22, 0x23,
+ 0x1A, 0x1B, 0x12, 0x13, 0x0A, 0x0B, 0x02, 0x03,
+ 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
+ 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x3C, 0x3D,
+ 0x3E, 0x3F, 0x36, 0x37, 0x2E, 0x2F, 0x26, 0x27,
+ 0x1E, 0x1F, 0x16, 0x17, 0x0E, 0x0F, 0x06, 0x07,
+ }
+};
+
+static const uint32_t bink_intra_quant[16][64] = {
+{
+ 0x010000, 0x016315, 0x01E83D, 0x02A535, 0x014E7B, 0x016577, 0x02F1E6, 0x02724C,
+ 0x010000, 0x00EEDA, 0x024102, 0x017F9B, 0x00BE80, 0x00611E, 0x01083C, 0x00A552,
+ 0x021F88, 0x01DC53, 0x027FAD, 0x01F697, 0x014819, 0x00A743, 0x015A31, 0x009688,
+ 0x02346F, 0x030EE5, 0x01FBFA, 0x02C096, 0x01D000, 0x028396, 0x019247, 0x01F9AA,
+ 0x02346F, 0x01FBFA, 0x01DC53, 0x0231B8, 0x012F12, 0x01E06C, 0x00CB10, 0x0119A8,
+ 0x01C48C, 0x019748, 0x014E86, 0x0122AF, 0x02C628, 0x027F20, 0x0297B5, 0x023F32,
+ 0x025000, 0x01AB6B, 0x01D122, 0x0159B3, 0x012669, 0x008D43, 0x00EE1F, 0x0075ED,
+ 0x01490C, 0x010288, 0x00F735, 0x00EF51, 0x00E0F1, 0x0072AD, 0x00A4D8, 0x006517,
+},
+{
+ 0x015555, 0x01D971, 0x028AFC, 0x0386F1, 0x01BDF9, 0x01DC9F, 0x03ED33, 0x034311,
+ 0x015555, 0x013E78, 0x030158, 0x01FF7A, 0x00FE00, 0x00817D, 0x01604F, 0x00DC6D,
+ 0x02D4B5, 0x027B19, 0x0354E7, 0x029E1F, 0x01B577, 0x00DF04, 0x01CD96, 0x00C8B6,
+ 0x02F095, 0x0413DC, 0x02A54E, 0x03AB73, 0x026AAB, 0x035A1E, 0x02185E, 0x02A238,
+ 0x02F095, 0x02A54E, 0x027B19, 0x02ECF5, 0x019418, 0x028090, 0x010EC0, 0x01778A,
+ 0x025B66, 0x021F0B, 0x01BE09, 0x018394, 0x03B2E0, 0x03542A, 0x0374F1, 0x02FEEE,
+ 0x031555, 0x0239E4, 0x026C2D, 0x01CCEE, 0x01888C, 0x00BC59, 0x013D7E, 0x009D3C,
+ 0x01B6BB, 0x0158B5, 0x01499C, 0x013F17, 0x012BEC, 0x0098E6, 0x00DBCB, 0x0086C9,
+},
+{
+ 0x01AAAB, 0x024FCE, 0x032DBB, 0x0468AD, 0x022D78, 0x0253C7, 0x04E87F, 0x0413D5,
+ 0x01AAAB, 0x018E16, 0x03C1AE, 0x027F58, 0x013D80, 0x00A1DC, 0x01B863, 0x011388,
+ 0x0389E2, 0x0319DF, 0x042A21, 0x0345A7, 0x0222D4, 0x0116C5, 0x0240FC, 0x00FAE3,
+ 0x03ACBA, 0x0518D3, 0x034EA1, 0x04964F, 0x030555, 0x0430A5, 0x029E76, 0x034AC5,
+ 0x03ACBA, 0x034EA1, 0x0319DF, 0x03A833, 0x01F91E, 0x0320B4, 0x015270, 0x01D56D,
+ 0x02F23F, 0x02A6CE, 0x022D8B, 0x01E479, 0x049F98, 0x042935, 0x04522D, 0x03BEA9,
+ 0x03DAAB, 0x02C85D, 0x030738, 0x02402A, 0x01EAAF, 0x00EB6F, 0x018CDE, 0x00C48A,
+ 0x022469, 0x01AEE2, 0x019C02, 0x018EDD, 0x0176E7, 0x00BF20, 0x0112BE, 0x00A87B,
+},
+{
+ 0x020000, 0x02C62A, 0x03D07A, 0x054A69, 0x029CF6, 0x02CAEF, 0x05E3CC, 0x04E499,
+ 0x020000, 0x01DDB4, 0x048204, 0x02FF36, 0x017D01, 0x00C23C, 0x021077, 0x014AA3,
+ 0x043F0F, 0x03B8A6, 0x04FF5A, 0x03ED2E, 0x029032, 0x014E86, 0x02B461, 0x012D11,
+ 0x0468DF, 0x061DCA, 0x03F7F5, 0x05812C, 0x03A000, 0x05072C, 0x03248D, 0x03F353,
+ 0x0468DF, 0x03F7F5, 0x03B8A6, 0x046370, 0x025E24, 0x03C0D8, 0x019620, 0x02334F,
+ 0x038919, 0x032E91, 0x029D0D, 0x02455E, 0x058C50, 0x04FE3F, 0x052F69, 0x047E65,
+ 0x04A000, 0x0356D6, 0x03A243, 0x02B365, 0x024CD2, 0x011A85, 0x01DC3E, 0x00EBD9,
+ 0x029218, 0x020510, 0x01EE69, 0x01DEA2, 0x01C1E2, 0x00E559, 0x0149B0, 0x00CA2D,
+},
+{
+ 0x02AAAB, 0x03B2E3, 0x0515F8, 0x070DE2, 0x037BF2, 0x03B93E, 0x07DA65, 0x068621,
+ 0x02AAAB, 0x027CF0, 0x0602B1, 0x03FEF3, 0x01FC01, 0x0102FA, 0x02C09F, 0x01B8DA,
+ 0x05A96A, 0x04F632, 0x06A9CE, 0x053C3E, 0x036AED, 0x01BE09, 0x039B2D, 0x01916B,
+ 0x05E129, 0x0827B8, 0x054A9C, 0x0756E5, 0x04D555, 0x06B43B, 0x0430BC, 0x05446F,
+ 0x05E129, 0x054A9C, 0x04F632, 0x05D9EB, 0x032830, 0x050121, 0x021D80, 0x02EF14,
+ 0x04B6CC, 0x043E16, 0x037C11, 0x030728, 0x0765C0, 0x06A855, 0x06E9E2, 0x05FDDB,
+ 0x062AAB, 0x0473C8, 0x04D85A, 0x0399DC, 0x031118, 0x0178B2, 0x027AFD, 0x013A77,
+ 0x036D76, 0x02B16A, 0x029337, 0x027E2E, 0x0257D8, 0x0131CC, 0x01B796, 0x010D91,
+},
+{
+ 0x038000, 0x04DACA, 0x06ACD5, 0x094238, 0x0492AE, 0x04E322, 0x0A4EA5, 0x08900C,
+ 0x038000, 0x0343FB, 0x07E388, 0x053E9F, 0x029AC1, 0x0153E8, 0x039CD0, 0x02429E,
+ 0x076E5B, 0x068322, 0x08BEDE, 0x06DF11, 0x047C57, 0x02496B, 0x04BBAB, 0x020EDD,
+ 0x07B786, 0x0AB421, 0x06F1ED, 0x09A20D, 0x065800, 0x08CC8E, 0x057FF7, 0x06E9D2,
+ 0x07B786, 0x06F1ED, 0x068322, 0x07AE04, 0x0424BF, 0x06917B, 0x02C6B8, 0x03D9CB,
+ 0x062FEB, 0x05917D, 0x0492D7, 0x03F964, 0x09B58C, 0x08BCEF, 0x0912F8, 0x07DD30,
+ 0x081800, 0x05D7F7, 0x065BF6, 0x04B9F1, 0x040670, 0x01EE69, 0x03416C, 0x019CBC,
+ 0x047FAA, 0x0388DC, 0x036138, 0x03459C, 0x03134C, 0x01915C, 0x0240F5, 0x0161CF,
+},
+{
+ 0x040000, 0x058C54, 0x07A0F4, 0x0A94D3, 0x0539EC, 0x0595DD, 0x0BC798, 0x09C932,
+ 0x040000, 0x03BB68, 0x090409, 0x05FE6D, 0x02FA01, 0x018477, 0x0420EE, 0x029547,
+ 0x087E1F, 0x07714C, 0x09FEB5, 0x07DA5D, 0x052064, 0x029D0D, 0x0568C3, 0x025A21,
+ 0x08D1BE, 0x0C3B94, 0x07EFEA, 0x0B0258, 0x074000, 0x0A0E59, 0x06491A, 0x07E6A7,
+ 0x08D1BE, 0x07EFEA, 0x07714C, 0x08C6E0, 0x04BC48, 0x0781B1, 0x032C3F, 0x04669F,
+ 0x071232, 0x065D22, 0x053A1A, 0x048ABC, 0x0B18A0, 0x09FC7F, 0x0A5ED3, 0x08FCC9,
+ 0x094000, 0x06ADAC, 0x074487, 0x0566CA, 0x0499A5, 0x02350B, 0x03B87B, 0x01D7B3,
+ 0x052430, 0x040A20, 0x03DCD3, 0x03BD45, 0x0383C5, 0x01CAB3, 0x029361, 0x01945A,
+},
+{
+ 0x050000, 0x06EF69, 0x098931, 0x0D3A07, 0x068867, 0x06FB55, 0x0EB97E, 0x0C3B7E,
+ 0x050000, 0x04AA42, 0x0B450B, 0x077E08, 0x03B881, 0x01E595, 0x05292A, 0x033A99,
+ 0x0A9DA7, 0x094D9F, 0x0C7E62, 0x09D0F4, 0x06687D, 0x034450, 0x06C2F4, 0x02F0AA,
+ 0x0B062D, 0x0F4A78, 0x09EBE4, 0x0DC2EE, 0x091000, 0x0C91EF, 0x07DB61, 0x09E050,
+ 0x0B062D, 0x09EBE4, 0x094D9F, 0x0AF898, 0x05EB59, 0x09621D, 0x03F74F, 0x058046,
+ 0x08D6BE, 0x07F46A, 0x0688A0, 0x05AD6B, 0x0DDEC8, 0x0C7B9F, 0x0CF687, 0x0B3BFB,
+ 0x0B9000, 0x085917, 0x0915A8, 0x06C07D, 0x05C00E, 0x02C24D, 0x04A69A, 0x024D9F,
+ 0x066D3C, 0x050CA7, 0x04D407, 0x04AC96, 0x0464B6, 0x023D5F, 0x033839, 0x01F971,
+},
+{
+ 0x060000, 0x08527E, 0x0B716E, 0x0FDF3C, 0x07D6E1, 0x0860CC, 0x11AB63, 0x0EADCB,
+ 0x060000, 0x05991C, 0x0D860D, 0x08FDA3, 0x047702, 0x0246B3, 0x063165, 0x03DFEA,
+ 0x0CBD2E, 0x0B29F1, 0x0EFE0F, 0x0BC78B, 0x07B096, 0x03EB93, 0x081D24, 0x038732,
+ 0x0D3A9C, 0x12595D, 0x0BE7DF, 0x108384, 0x0AE000, 0x0F1585, 0x096DA8, 0x0BD9FA,
+ 0x0D3A9C, 0x0BE7DF, 0x0B29F1, 0x0D2A50, 0x071A6B, 0x0B4289, 0x04C25F, 0x0699EE,
+ 0x0A9B4A, 0x098BB2, 0x07D727, 0x06D01A, 0x10A4F0, 0x0EFABE, 0x0F8E3C, 0x0D7B2E,
+ 0x0DE000, 0x0A0482, 0x0AE6CA, 0x081A2F, 0x06E677, 0x034F90, 0x0594B9, 0x02C38C,
+ 0x07B649, 0x060F2F, 0x05CB3C, 0x059BE7, 0x0545A7, 0x02B00C, 0x03DD11, 0x025E87,
+},
+{
+ 0x080000, 0x0B18A8, 0x0F41E8, 0x1529A5, 0x0A73D7, 0x0B2BBB, 0x178F2F, 0x139264,
+ 0x080000, 0x0776CF, 0x120812, 0x0BFCD9, 0x05F402, 0x0308EF, 0x0841DC, 0x052A8E,
+ 0x10FC3E, 0x0EE297, 0x13FD69, 0x0FB4B9, 0x0A40C8, 0x053A1A, 0x0AD186, 0x04B442,
+ 0x11A37B, 0x187727, 0x0FDFD4, 0x1604B0, 0x0E8000, 0x141CB1, 0x0C9235, 0x0FCD4D,
+ 0x11A37B, 0x0FDFD4, 0x0EE297, 0x118DC0, 0x09788F, 0x0F0362, 0x06587F, 0x08CD3D,
+ 0x0E2463, 0x0CBA43, 0x0A7434, 0x091577, 0x163140, 0x13F8FE, 0x14BDA5, 0x11F992,
+ 0x128000, 0x0D5B58, 0x0E890D, 0x0ACD94, 0x093349, 0x046A15, 0x0770F7, 0x03AF65,
+ 0x0A4861, 0x08143F, 0x07B9A6, 0x077A89, 0x070789, 0x039565, 0x0526C2, 0x0328B4,
+},
+{
+ 0x0C0000, 0x10A4FD, 0x16E2DB, 0x1FBE78, 0x0FADC3, 0x10C198, 0x2356C7, 0x1D5B96,
+ 0x0C0000, 0x0B3237, 0x1B0C1A, 0x11FB46, 0x08EE03, 0x048D66, 0x0C62CA, 0x07BFD5,
+ 0x197A5D, 0x1653E3, 0x1DFC1E, 0x178F16, 0x0F612C, 0x07D727, 0x103A49, 0x070E64,
+ 0x1A7539, 0x24B2BB, 0x17CFBD, 0x210709, 0x15C000, 0x1E2B0A, 0x12DB4F, 0x17B3F4,
+ 0x1A7539, 0x17CFBD, 0x1653E3, 0x1A54A0, 0x0E34D7, 0x168513, 0x0984BE, 0x0D33DC,
+ 0x153695, 0x131765, 0x0FAE4E, 0x0DA033, 0x2149E1, 0x1DF57D, 0x1F1C78, 0x1AF65B,
+ 0x1BC000, 0x140904, 0x15CD94, 0x10345E, 0x0DCCEE, 0x069F20, 0x0B2972, 0x058718,
+ 0x0F6C91, 0x0C1E5E, 0x0B9678, 0x0B37CE, 0x0A8B4E, 0x056018, 0x07BA22, 0x04BD0E,
+},
+{
+ 0x110000, 0x179466, 0x206C0C, 0x2CF87F, 0x16362A, 0x17BCED, 0x321044, 0x299714,
+ 0x110000, 0x0FDC79, 0x265125, 0x19794E, 0x0CA685, 0x0672FB, 0x118BF4, 0x0AFA6D,
+ 0x241804, 0x1FA181, 0x2A7A80, 0x21600A, 0x15C9A9, 0x0B1B77, 0x16FD3C, 0x09FF0D,
+ 0x257B66, 0x33FD33, 0x21BBA2, 0x2EC9F7, 0x1ED000, 0x2ABCF9, 0x1AB6B0, 0x219444,
+ 0x257B66, 0x21BBA2, 0x1FA181, 0x254D38, 0x142030, 0x1FE730, 0x0D7C0E, 0x12B423,
+ 0x1E0D52, 0x1B0BCF, 0x1636EE, 0x134D9E, 0x2F28A9, 0x2A711B, 0x2C12FF, 0x263256,
+ 0x275000, 0x1C621B, 0x1EE33C, 0x16F4DB, 0x138CFB, 0x09616E, 0x0FD00C, 0x07D4B7,
+ 0x15D9CE, 0x112B06, 0x106A80, 0x0FE464, 0x0EF004, 0x079D77, 0x0AF25B, 0x06B67F,
+},
+{
+ 0x160000, 0x1E83CF, 0x29F53D, 0x3A3286, 0x1CBE90, 0x1EB842, 0x40C9C2, 0x35D293,
+ 0x160000, 0x1486BA, 0x319630, 0x20F756, 0x105F06, 0x085891, 0x16B51E, 0x0E3506,
+ 0x2EB5AA, 0x28EF20, 0x36F8E1, 0x2B30FE, 0x1C3225, 0x0E5FC7, 0x1DC030, 0x0CEFB7,
+ 0x308193, 0x4347AC, 0x2BA786, 0x3C8CE5, 0x27E000, 0x374EE7, 0x229212, 0x2B7494,
+ 0x308193, 0x2BA786, 0x28EF20, 0x3045D0, 0x1A0B89, 0x29494D, 0x11735D, 0x183469,
+ 0x26E410, 0x230039, 0x1CBF8F, 0x18FB09, 0x3D0771, 0x36ECBA, 0x390986, 0x316E52,
+ 0x32E000, 0x24BB33, 0x27F8E4, 0x1DB557, 0x194D09, 0x0C23BB, 0x1476A6, 0x0A2256,
+ 0x1C470A, 0x1637AD, 0x153E87, 0x1490FA, 0x1354B9, 0x09DAD6, 0x0E2A94, 0x08AFF0,
+},
+{
+ 0x1C0000, 0x26D64D, 0x3566AA, 0x4A11C2, 0x249572, 0x27190E, 0x527525, 0x44805E,
+ 0x1C0000, 0x1A1FD6, 0x3F1C3E, 0x29F4F9, 0x14D607, 0x0A9F44, 0x1CE683, 0x1214F0,
+ 0x3B72D9, 0x341911, 0x45F6F0, 0x36F889, 0x23E2BB, 0x124B5B, 0x25DD54, 0x1076E9,
+ 0x3DBC30, 0x55A109, 0x378F64, 0x4D1069, 0x32C000, 0x46646C, 0x2BFFB9, 0x374E8E,
+ 0x3DBC30, 0x378F64, 0x341911, 0x3D7020, 0x2125F5, 0x348BD6, 0x1635BC, 0x1ECE57,
+ 0x317F5B, 0x2C8BEB, 0x2496B6, 0x1FCB22, 0x4DAC61, 0x45E778, 0x4897C2, 0x3EE97F,
+ 0x40C000, 0x2EBFB5, 0x32DFAE, 0x25CF86, 0x203380, 0x0F734B, 0x1A0B5F, 0x0CE5E2,
+ 0x23FD53, 0x1C46DC, 0x1B09C4, 0x1A2CE1, 0x189A60, 0x0C8AE2, 0x1207A5, 0x0B0E77,
+},
+{
+ 0x220000, 0x2F28CC, 0x40D818, 0x59F0FE, 0x2C6C53, 0x2F79DA, 0x642089, 0x532E29,
+ 0x220000, 0x1FB8F1, 0x4CA24B, 0x32F29C, 0x194D09, 0x0CE5F7, 0x2317E8, 0x15F4DB,
+ 0x483007, 0x3F4303, 0x54F4FF, 0x42C014, 0x2B9351, 0x1636EE, 0x2DFA79, 0x13FE1A,
+ 0x4AF6CC, 0x67FA67, 0x437743, 0x5D93EE, 0x3DA000, 0x5579F1, 0x356D61, 0x432888,
+ 0x4AF6CC, 0x437743, 0x3F4303, 0x4A9A70, 0x284060, 0x3FCE60, 0x1AF81B, 0x256845,
+ 0x3C1AA5, 0x36179D, 0x2C6DDD, 0x269B3C, 0x5E5152, 0x54E237, 0x5825FE, 0x4C64AD,
+ 0x4EA000, 0x38C437, 0x3DC678, 0x2DE9B5, 0x2719F7, 0x12C2DB, 0x1FA018, 0x0FA96E,
+ 0x2BB39B, 0x22560C, 0x20D500, 0x1FC8C8, 0x1DE007, 0x0F3AEE, 0x15E4B7, 0x0D6CFE,
+},
+{
+ 0x2C0000, 0x3D079E, 0x53EA79, 0x74650C, 0x397D20, 0x3D7083, 0x819383, 0x6BA525,
+ 0x2C0000, 0x290D75, 0x632C61, 0x41EEAC, 0x20BE0C, 0x10B121, 0x2D6A3B, 0x1C6A0C,
+ 0x5D6B54, 0x51DE40, 0x6DF1C2, 0x5661FB, 0x38644B, 0x1CBF8F, 0x3B8060, 0x19DF6D,
+ 0x610326, 0x868F57, 0x574F0B, 0x7919CA, 0x4FC000, 0x6E9DCE, 0x452423, 0x56E928,
+ 0x610326, 0x574F0B, 0x51DE40, 0x608BA0, 0x341713, 0x52929A, 0x22E6BA, 0x3068D2,
+ 0x4DC821, 0x460071, 0x397F1E, 0x31F611, 0x7A0EE2, 0x6DD974, 0x72130C, 0x62DCA3,
+ 0x65C000, 0x497665, 0x4FF1C9, 0x3B6AAE, 0x329A12, 0x184776, 0x28ED4D, 0x1444AC,
+ 0x388E14, 0x2C6F5A, 0x2A7D0F, 0x2921F4, 0x26A973, 0x13B5AD, 0x1C5528, 0x115FDF,
+},
+};
+
+static const uint32_t bink_inter_quant[16][64] = {
+{
+ 0x010000, 0x017946, 0x01A5A9, 0x0248DC, 0x016363, 0x0152A7, 0x0243EC, 0x0209EA,
+ 0x012000, 0x00E248, 0x01BBDA, 0x015CBC, 0x00A486, 0x0053E0, 0x00F036, 0x008095,
+ 0x01B701, 0x016959, 0x01B0B9, 0x0153FD, 0x00F8E7, 0x007EE4, 0x00EA30, 0x007763,
+ 0x01B701, 0x0260EB, 0x019DE9, 0x023E1B, 0x017000, 0x01FE6E, 0x012DB5, 0x01A27B,
+ 0x01E0D1, 0x01B0B9, 0x018A33, 0x01718D, 0x00D87A, 0x014449, 0x007B9A, 0x00AB71,
+ 0x013178, 0x0112EA, 0x00AD08, 0x009BB9, 0x023D97, 0x020437, 0x021CCC, 0x01E6B4,
+ 0x018000, 0x012DB5, 0x0146D9, 0x0100CE, 0x00CFD2, 0x006E5C, 0x00B0E4, 0x005A2D,
+ 0x00E9CC, 0x00B7B1, 0x00846F, 0x006B85, 0x008337, 0x0042E5, 0x004A10, 0x002831,
+},
+{
+ 0x015555, 0x01F708, 0x023237, 0x030BD0, 0x01D9D9, 0x01C389, 0x03053B, 0x02B7E3,
+ 0x018000, 0x012DB5, 0x024FCE, 0x01D0FA, 0x00DB5D, 0x006FD5, 0x014048, 0x00AB71,
+ 0x024957, 0x01E1CC, 0x0240F7, 0x01C551, 0x014BDE, 0x00A92F, 0x013840, 0x009F2F,
+ 0x024957, 0x032BE4, 0x0227E1, 0x02FD7A, 0x01EAAB, 0x02A893, 0x019247, 0x022DF9,
+ 0x028116, 0x0240F7, 0x020D99, 0x01ECBC, 0x0120A3, 0x01B061, 0x00A4CE, 0x00E497,
+ 0x01974B, 0x016E8E, 0x00E6B5, 0x00CFA2, 0x02FCC9, 0x02B04A, 0x02D110, 0x0288F1,
+ 0x020000, 0x019247, 0x01B3CC, 0x015668, 0x011518, 0x009325, 0x00EBDA, 0x00783D,
+ 0x0137BB, 0x00F4ED, 0x00B093, 0x008F5C, 0x00AEF4, 0x005931, 0x0062BF, 0x003597,
+},
+{
+ 0x01AAAB, 0x0274CB, 0x02BEC4, 0x03CEC4, 0x02504F, 0x02346C, 0x03C689, 0x0365DC,
+ 0x01E000, 0x017922, 0x02E3C1, 0x024539, 0x011235, 0x008BCA, 0x01905A, 0x00D64D,
+ 0x02DBAD, 0x025A40, 0x02D134, 0x0236A5, 0x019ED6, 0x00D37B, 0x018650, 0x00C6FB,
+ 0x02DBAD, 0x03F6DD, 0x02B1D9, 0x03BCD8, 0x026555, 0x0352B8, 0x01F6D8, 0x02B977,
+ 0x03215C, 0x02D134, 0x029100, 0x0267EB, 0x0168CC, 0x021C7A, 0x00CE01, 0x011DBD,
+ 0x01FD1E, 0x01CA31, 0x012062, 0x01038A, 0x03BBFB, 0x035C5C, 0x038554, 0x032B2D,
+ 0x028000, 0x01F6D8, 0x0220C0, 0x01AC02, 0x015A5E, 0x00B7EF, 0x0126D1, 0x00964C,
+ 0x0185A9, 0x013228, 0x00DCB8, 0x00B333, 0x00DAB2, 0x006F7D, 0x007B6F, 0x0042FC,
+},
+{
+ 0x020000, 0x02F28D, 0x034B52, 0x0491B8, 0x02C6C5, 0x02A54E, 0x0487D8, 0x0413D5,
+ 0x024000, 0x01C48F, 0x0377B5, 0x02B977, 0x01490C, 0x00A7BF, 0x01E06C, 0x01012A,
+ 0x036E03, 0x02D2B3, 0x036172, 0x02A7FA, 0x01F1CE, 0x00FDC7, 0x01D460, 0x00EEC7,
+ 0x036E03, 0x04C1D6, 0x033BD1, 0x047C37, 0x02E000, 0x03FCDD, 0x025B6A, 0x0344F5,
+ 0x03C1A1, 0x036172, 0x031466, 0x02E31B, 0x01B0F5, 0x028892, 0x00F735, 0x0156E2,
+ 0x0262F1, 0x0225D5, 0x015A10, 0x013772, 0x047B2D, 0x04086E, 0x043998, 0x03CD69,
+ 0x030000, 0x025B6A, 0x028DB3, 0x02019B, 0x019FA3, 0x00DCB8, 0x0161C7, 0x00B45B,
+ 0x01D398, 0x016F63, 0x0108DD, 0x00D70A, 0x01066F, 0x0085C9, 0x00941F, 0x005062,
+},
+{
+ 0x02AAAB, 0x03EE11, 0x04646D, 0x0617A0, 0x03B3B2, 0x038713, 0x060A75, 0x056FC6,
+ 0x030000, 0x025B6A, 0x049F9B, 0x03A1F4, 0x01B6BB, 0x00DFAA, 0x028090, 0x0156E2,
+ 0x0492AE, 0x03C399, 0x0481ED, 0x038AA2, 0x0297BD, 0x01525F, 0x027080, 0x013E5E,
+ 0x0492AE, 0x0657C8, 0x044FC1, 0x05FAF4, 0x03D555, 0x055126, 0x03248D, 0x045BF2,
+ 0x05022D, 0x0481ED, 0x041B33, 0x03D979, 0x024147, 0x0360C3, 0x01499C, 0x01C92E,
+ 0x032E96, 0x02DD1C, 0x01CD6A, 0x019F43, 0x05F991, 0x056093, 0x05A220, 0x0511E1,
+ 0x040000, 0x03248D, 0x036799, 0x02ACCF, 0x022A2F, 0x01264B, 0x01D7B5, 0x00F079,
+ 0x026F75, 0x01E9D9, 0x016127, 0x011EB8, 0x015DE9, 0x00B262, 0x00C57F, 0x006B2D,
+},
+{
+ 0x038000, 0x052876, 0x05C3CF, 0x07FF02, 0x04DBD9, 0x04A148, 0x07EDBA, 0x0722B4,
+ 0x03F000, 0x0317FB, 0x06117C, 0x04C491, 0x023FD5, 0x01258F, 0x0348BD, 0x01C209,
+ 0x060085, 0x04F0B9, 0x05EA87, 0x04A5F5, 0x036728, 0x01BC1C, 0x0333A8, 0x01A1DB,
+ 0x060085, 0x085336, 0x05A8AE, 0x07D960, 0x050800, 0x06FA82, 0x041FF9, 0x05B8AE,
+ 0x0692DA, 0x05EA87, 0x0563B2, 0x050D6E, 0x02F5AD, 0x046F00, 0x01B09C, 0x02580C,
+ 0x042D25, 0x03C235, 0x025D9B, 0x022108, 0x07D78F, 0x070EC1, 0x0764CA, 0x06A777,
+ 0x054000, 0x041FF9, 0x0477F9, 0x0382D0, 0x02D75E, 0x018242, 0x026B1D, 0x013B9F,
+ 0x03324A, 0x0282ED, 0x01CF83, 0x017851, 0x01CB42, 0x00EA21, 0x010336, 0x008CAC,
+},
+{
+ 0x040000, 0x05E519, 0x0696A4, 0x092370, 0x058D8A, 0x054A9C, 0x090FB0, 0x0827AA,
+ 0x048000, 0x03891F, 0x06EF69, 0x0572EE, 0x029218, 0x014F7E, 0x03C0D8, 0x020254,
+ 0x06DC05, 0x05A565, 0x06C2E4, 0x054FF3, 0x03E39B, 0x01FB8E, 0x03A8C0, 0x01DD8D,
+ 0x06DC05, 0x0983AC, 0x0677A2, 0x08F86E, 0x05C000, 0x07F9B9, 0x04B6D4, 0x0689EB,
+ 0x078343, 0x06C2E4, 0x0628CC, 0x05C635, 0x0361EA, 0x051124, 0x01EE69, 0x02ADC5,
+ 0x04C5E1, 0x044BAA, 0x02B41F, 0x026EE5, 0x08F65A, 0x0810DD, 0x087330, 0x079AD1,
+ 0x060000, 0x04B6D4, 0x051B65, 0x040337, 0x033F47, 0x01B970, 0x02C38F, 0x0168B6,
+ 0x03A730, 0x02DEC6, 0x0211BA, 0x01AE14, 0x020CDD, 0x010B93, 0x01283E, 0x00A0C4,
+},
+{
+ 0x050000, 0x075E60, 0x083C4D, 0x0B6C4C, 0x06F0ED, 0x069D43, 0x0B539C, 0x0A3194,
+ 0x05A000, 0x046B67, 0x08AB44, 0x06CFAA, 0x03369E, 0x01A35E, 0x04B10F, 0x0282E8,
+ 0x089307, 0x070EBF, 0x08739C, 0x06A3F0, 0x04DC82, 0x027A72, 0x0492F0, 0x0254F0,
+ 0x089307, 0x0BE497, 0x08158B, 0x0B3689, 0x073000, 0x09F827, 0x05E489, 0x082C66,
+ 0x096413, 0x08739C, 0x07B2FF, 0x0737C2, 0x043A64, 0x06556D, 0x026A04, 0x035936,
+ 0x05F75A, 0x055E94, 0x036127, 0x030A9E, 0x0B33F1, 0x0A1514, 0x0A8FFC, 0x098186,
+ 0x078000, 0x05E489, 0x06623F, 0x050405, 0x040F19, 0x0227CC, 0x037473, 0x01C2E3,
+ 0x0490FC, 0x039677, 0x029629, 0x021999, 0x029015, 0x014E78, 0x01724E, 0x00C8F5,
+},
+{
+ 0x060000, 0x08D7A6, 0x09E1F6, 0x0DB528, 0x085450, 0x07EFEA, 0x0D9788, 0x0C3B7E,
+ 0x06C000, 0x054DAE, 0x0A671E, 0x082C66, 0x03DB24, 0x01F73E, 0x05A145, 0x03037D,
+ 0x0A4A08, 0x087818, 0x0A2455, 0x07F7ED, 0x05D569, 0x02F955, 0x057D20, 0x02CC54,
+ 0x0A4A08, 0x0E4582, 0x09B373, 0x0D74A5, 0x08A000, 0x0BF696, 0x07123E, 0x09CEE0,
+ 0x0B44E4, 0x0A2455, 0x093D32, 0x08A950, 0x0512DF, 0x0799B6, 0x02E59E, 0x0404A7,
+ 0x0728D2, 0x06717F, 0x040E2F, 0x03A657, 0x0D7187, 0x0C194B, 0x0CACC8, 0x0B683A,
+ 0x090000, 0x07123E, 0x07A918, 0x0604D2, 0x04DEEA, 0x029629, 0x042556, 0x021D11,
+ 0x057AC8, 0x044E28, 0x031A97, 0x02851E, 0x03134C, 0x01915C, 0x01BC5D, 0x00F126,
+},
+{
+ 0x080000, 0x0BCA33, 0x0D2D48, 0x1246E0, 0x0B1B15, 0x0A9538, 0x121F5F, 0x104F53,
+ 0x090000, 0x07123E, 0x0DDED2, 0x0AE5DD, 0x052430, 0x029EFD, 0x0781B1, 0x0404A7,
+ 0x0DB80B, 0x0B4ACB, 0x0D85C7, 0x0A9FE7, 0x07C736, 0x03F71D, 0x075180, 0x03BB1A,
+ 0x0DB80B, 0x130757, 0x0CEF44, 0x11F0DC, 0x0B8000, 0x0FF372, 0x096DA8, 0x0D13D6,
+ 0x0F0686, 0x0D85C7, 0x0C5198, 0x0B8C6A, 0x06C3D4, 0x0A2248, 0x03DCD3, 0x055B8A,
+ 0x098BC3, 0x089754, 0x05683E, 0x04DDC9, 0x11ECB4, 0x1021B9, 0x10E661, 0x0F35A3,
+ 0x0C0000, 0x096DA8, 0x0A36CB, 0x08066E, 0x067E8E, 0x0372E1, 0x05871E, 0x02D16B,
+ 0x074E60, 0x05BD8B, 0x042374, 0x035C28, 0x0419BB, 0x021726, 0x02507C, 0x014188,
+},
+{
+ 0x0C0000, 0x11AF4C, 0x13C3EC, 0x1B6A50, 0x10A89F, 0x0FDFD4, 0x1B2F0F, 0x1876FD,
+ 0x0D8000, 0x0A9B5D, 0x14CE3C, 0x1058CB, 0x07B649, 0x03EE7B, 0x0B4289, 0x0606FB,
+ 0x149410, 0x10F030, 0x1448AB, 0x0FEFDA, 0x0BAAD2, 0x05F2AB, 0x0AFA40, 0x0598A7,
+ 0x149410, 0x1C8B03, 0x1366E6, 0x1AE949, 0x114000, 0x17ED2B, 0x0E247C, 0x139DC1,
+ 0x1689C8, 0x1448AB, 0x127A63, 0x11529F, 0x0A25BE, 0x0F336D, 0x05CB3C, 0x08094E,
+ 0x0E51A4, 0x0CE2FE, 0x081C5D, 0x074CAE, 0x1AE30E, 0x183296, 0x195991, 0x16D074,
+ 0x120000, 0x0E247C, 0x0F5230, 0x0C09A5, 0x09BDD5, 0x052C51, 0x084AAC, 0x043A21,
+ 0x0AF590, 0x089C51, 0x06352E, 0x050A3B, 0x062698, 0x0322B9, 0x0378BA, 0x01E24D,
+},
+{
+ 0x110000, 0x190DAC, 0x1C0039, 0x26D69C, 0x17998C, 0x167D16, 0x2682AB, 0x22A891,
+ 0x132000, 0x0F06C3, 0x1D797F, 0x172876, 0x0AECE7, 0x0591D9, 0x0FF398, 0x0889E3,
+ 0x1D2717, 0x17FEEF, 0x1CBC47, 0x1693CA, 0x108754, 0x086D1D, 0x0F8D30, 0x07ED98,
+ 0x1D2717, 0x286F9A, 0x1B7C71, 0x261FD3, 0x187000, 0x21E552, 0x140904, 0x1BCA27,
+ 0x1FEDDC, 0x1CBC47, 0x1A2D62, 0x188A62, 0x0E6022, 0x1588DA, 0x083540, 0x0B6284,
+ 0x1448FE, 0x124192, 0x0B7D84, 0x0A574B, 0x2616FF, 0x2247AA, 0x23E98D, 0x2051FA,
+ 0x198000, 0x140904, 0x15B46F, 0x110DAA, 0x0DCCEE, 0x07541E, 0x0BBF1F, 0x05FD04,
+ 0x0F868B, 0x0C32C8, 0x08CB57, 0x0723D4, 0x08B6AD, 0x047130, 0x04EB08, 0x02AB42,
+},
+{
+ 0x160000, 0x206C0C, 0x243C86, 0x3242E8, 0x1E8A79, 0x1D1A59, 0x31D646, 0x2CDA25,
+ 0x18C000, 0x13722A, 0x2624C3, 0x1DF820, 0x0E2385, 0x073537, 0x14A4A7, 0x0B0CCC,
+ 0x25BA1D, 0x1F0DAE, 0x252FE4, 0x1D37BB, 0x1563D6, 0x0AE78E, 0x142021, 0x0A4288,
+ 0x25BA1D, 0x345430, 0x2391FB, 0x31565C, 0x1FA000, 0x2BDD7A, 0x19ED8D, 0x23F68C,
+ 0x2951EF, 0x252FE4, 0x21E061, 0x1FC224, 0x129A87, 0x1BDE47, 0x0A9F44, 0x0EBBBA,
+ 0x1A4058, 0x17A026, 0x0EDEAB, 0x0D61E9, 0x314AEF, 0x2C5CBE, 0x2E798A, 0x29D380,
+ 0x210000, 0x19ED8D, 0x1C16AE, 0x1611AE, 0x11DC06, 0x097BEA, 0x0F3391, 0x07BFE7,
+ 0x141787, 0x0FC93E, 0x0B617F, 0x093D6D, 0x0B46C1, 0x05BFA8, 0x065D55, 0x037437,
+},
+{
+ 0x1C0000, 0x2943B2, 0x2E1E7C, 0x3FF810, 0x26DEC9, 0x250A43, 0x3F6DCE, 0x3915A3,
+ 0x1F8000, 0x18BFD8, 0x308BE1, 0x262485, 0x11FEA9, 0x092C75, 0x1A45EB, 0x0E1049,
+ 0x300425, 0x2785C6, 0x2F5439, 0x252FA8, 0x1B393F, 0x0DE0E4, 0x199D41, 0x0D0EDC,
+ 0x300425, 0x4299B2, 0x2D456E, 0x3ECB00, 0x284000, 0x37D40F, 0x20FFCB, 0x2DC56D,
+ 0x3496D3, 0x2F5439, 0x2B1D93, 0x286B74, 0x17AD66, 0x2377FE, 0x0D84E2, 0x12C062,
+ 0x21692A, 0x1E11A5, 0x12ECDA, 0x110840, 0x3EBC76, 0x387608, 0x3B2652, 0x353BBA,
+ 0x2A0000, 0x20FFCB, 0x23BFC6, 0x1C1681, 0x16BAF1, 0x0C1213, 0x1358E8, 0x09DCF8,
+ 0x19924F, 0x141767, 0x0E7C16, 0x0BC28A, 0x0E5A0D, 0x075104, 0x0819B2, 0x04655D,
+},
+{
+ 0x220000, 0x321B58, 0x380072, 0x4DAD38, 0x2F3318, 0x2CFA2D, 0x4D0556, 0x455122,
+ 0x264000, 0x1E0D86, 0x3AF2FE, 0x2E50EB, 0x15D9CE, 0x0B23B2, 0x1FE730, 0x1113C7,
+ 0x3A4E2D, 0x2FFDDF, 0x39788E, 0x2D2795, 0x210EA8, 0x10DA39, 0x1F1A61, 0x0FDB2F,
+ 0x3A4E2D, 0x50DF33, 0x36F8E1, 0x4C3FA5, 0x30E000, 0x43CAA5, 0x281209, 0x37944D,
+ 0x3FDBB7, 0x39788E, 0x345AC4, 0x3114C3, 0x1CC044, 0x2B11B4, 0x106A80, 0x16C509,
+ 0x2891FC, 0x248324, 0x16FB08, 0x14AE97, 0x4C2DFD, 0x448F54, 0x47D31B, 0x40A3F5,
+ 0x330000, 0x281209, 0x2B68DF, 0x221B53, 0x1B99DB, 0x0EA83B, 0x177E3E, 0x0BFA09,
+ 0x1F0D17, 0x18658F, 0x1196AE, 0x0E47A8, 0x116D5A, 0x08E260, 0x09D60F, 0x055684,
+},
+{
+ 0x2C0000, 0x40D818, 0x48790C, 0x6485D0, 0x3D14F2, 0x3A34B2, 0x63AC8D, 0x59B44A,
+ 0x318000, 0x26E454, 0x4C4986, 0x3BF03F, 0x1C470A, 0x0E6A6E, 0x29494D, 0x161998,
+ 0x4B743A, 0x3E1B5C, 0x4A5FC7, 0x3A6F75, 0x2AC7AC, 0x15CF1D, 0x284041, 0x148510,
+ 0x4B743A, 0x68A861, 0x4723F6, 0x62ACB8, 0x3F4000, 0x57BAF3, 0x33DB1A, 0x47ED19,
+ 0x52A3DE, 0x4A5FC7, 0x43C0C2, 0x3F8448, 0x25350D, 0x37BC8E, 0x153E87, 0x1D7775,
+ 0x3480B0, 0x2F404C, 0x1DBD56, 0x1AC3D2, 0x6295DE, 0x58B97B, 0x5CF313, 0x53A701,
+ 0x420000, 0x33DB1A, 0x382D5C, 0x2C235D, 0x23B80D, 0x12F7D4, 0x1E6723, 0x0F7FCF,
+ 0x282F0E, 0x1F927D, 0x16C2FF, 0x127AD9, 0x168D83, 0x0B7F50, 0x0CBAAA, 0x06E86E,
+},
+};
+
+
+#endif /* AVCODEC_BINKDATA_H */
diff --git a/gemrb/plugins/BIKPlayer/common.h b/gemrb/plugins/BIKPlayer/common.h
new file mode 100644
index 0000000..edc732b
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/common.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.
+ *
+ *
+ * Code derived from FFMPeg/libavutil/common.h
+ * @author Michael Niedermayer <michaelni at gmx.at>
+ */
+
+#ifndef AVUTIL_COMMON_H
+#define AVUTIL_COMMON_H
+
+#include "win32def.h"
+#include "globals.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define av_const
+#define av_cold
+#define av_flatten
+#define attribute_deprecated
+#define av_unused
+#define av_uninit(x) x
+#define av_always_inline inline
+
+#define uint8_t unsigned char
+#define uint16_t unsigned short
+#define uint32_t unsigned int
+#define uint64_t __int64
+
+#define int8_t signed char
+#define int16_t signed short
+#define int32_t signed int
+#define int64_t signed __int64
+
+#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
+#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
+#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0)
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 /* pi */
+#endif
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
+#endif
+
+void *av_malloc(unsigned int size);
+void av_freep(void **ptr);
+
+/**
+ * data needed to decode 4-bit Huffman-coded value
+ */
+typedef struct Tree {
+ int vlc_num; ///< tree number (in bink_trees[])
+ uint8_t syms[16]; ///< leaf value to symbol mapping
+} Tree;
+
+#endif /* AVUTIL_COMMON_H */
diff --git a/gemrb/plugins/BIKPlayer/dct.cpp b/gemrb/plugins/BIKPlayer/dct.cpp
new file mode 100644
index 0000000..95d210f
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/dct.cpp
@@ -0,0 +1,97 @@
+/*
+ * (I)DCT Transforms
+ * Copyright (c) 2009 Peter Ross (pross at xvid.org)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file libavcodec/dct.c
+ * (Inverse) Discrete Cosine Transforms
+ */
+
+#define _USE_MATH_DEFINES
+
+#include <math.h>
+#include "dsputil.h"
+
+av_cold int ff_dct_init(DCTContext *s, int nbits, int inverse)
+{
+ int n = 1 << nbits;
+
+ s->nbits = nbits;
+ s->inverse = inverse;
+
+ s->data = (struct FFTComplex *) av_malloc(sizeof(FFTComplex) * 2 * n);
+ if (!s->data)
+ return -1;
+
+ if (ff_fft_init(&s->fft, nbits+1, inverse) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void ff_dct_calc_c(DCTContext *s, FFTSample *data)
+{
+ int n = 1<<s->nbits;
+ int i;
+
+#define ROTATE(i,n) (-M_PI*((n)-0.5f)*(i)/(n))
+ if (s->inverse) {
+ for(i=0; i < n; i++) {
+ s->data[i].re = (float) (2 * data[i] * cos(ROTATE(i,n)));
+ s->data[i].im = (float) (2 * data[i] * sin(ROTATE(i,n)));
+ }
+ s->data[n].re = 0;
+ s->data[n].im = 0;
+ for(i=0; i<n-1; i++) {
+ s->data[n+i+1].re = (float) (-2 * data[n - (i+1)] * cos(ROTATE(n+i+1,n)));
+ s->data[n+i+1].im = (float) (-2 * data[n - (i+1)] * sin(ROTATE(n+i+1,n)));
+ }
+ }else{
+ for(i=0; i < n; i++) {
+ s->data[i].re = data[n - (i+1)];
+ s->data[i].im = 0;
+ s->data[n+i].re = data[i];
+ s->data[n+i].im = 0;
+ }
+ }
+
+ ff_fft_permute(&s->fft, s->data);
+ ff_fft_calc(&s->fft, s->data);
+
+ if (s->inverse) {
+ for(i=0; i < n; i++)
+ data[i] = s->data[n-(i+1)].re / (2 * n);
+ }else {
+ for(i=0; i < n; i++)
+ data[i] = (float) (s->data[i].re / (2 * cos(ROTATE(i,n))));
+ }
+#undef ROTATE
+}
+
+void ff_dct_calc(DCTContext *s, FFTSample *data)
+{
+ ff_dct_calc_c(s, data);
+}
+
+av_cold void ff_dct_end(DCTContext *s)
+{
+ ff_fft_end(&s->fft);
+ av_freep((void **) &s->data);
+}
diff --git a/gemrb/plugins/BIKPlayer/dsputil.h b/gemrb/plugins/BIKPlayer/dsputil.h
new file mode 100644
index 0000000..84161fa
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/dsputil.h
@@ -0,0 +1,230 @@
+/*
+ * DSP utils
+ * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
+ * Copyright (c) 2002-2004 Michael Niedermayer <michaelni at gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file libavcodec/dsputil.h
+ * DSP utils.
+ * note, many functions in here may use MMX which trashes the FPU state, it is
+ * absolutely necessary to call emms_c() between dsp & float/double code
+ */
+
+#ifndef AVCODEC_DSPUTIL_H
+#define AVCODEC_DSPUTIL_H
+
+#include "common.h"
+
+/* dct code */
+typedef short DCTELEM;
+typedef int DWTELEM;
+typedef short IDWTELEM;
+
+/**
+ * Scantable.
+ */
+typedef struct ScanTable{
+ const uint8_t *scantable;
+ uint8_t permutated[64];
+ uint8_t raster_end[64];
+} ScanTable;
+
+void ff_init_scantable(uint8_t *, ScanTable *st, const uint8_t *src_scantable);
+
+/**
+ * Empty mmx state.
+ * this must be called between any dsp function and float/double code.
+ * for example sin(); dsp->idct_put(); emms_c(); cos()
+ */
+#define emms_c()
+
+/* should be defined by architectures supporting
+ one or more MultiMedia extension */
+int mm_support(void);
+extern int mm_flags;
+
+#define DECLARE_ALIGNED_16(t, v) DECLARE_ALIGNED(16, t, v)
+#define DECLARE_ALIGNED_8(t, v) DECLARE_ALIGNED(8, t, v)
+
+#define mm_flags 0
+#define mm_support() 0
+
+#ifndef STRIDE_ALIGN
+# define STRIDE_ALIGN 8
+#endif
+
+/* FFT computation */
+
+/* NOTE: soon integer code will be added, so you must use the
+ FFTSample type */
+typedef float FFTSample;
+
+struct MDCTContext;
+
+typedef struct FFTComplex {
+ FFTSample re, im;
+} FFTComplex;
+
+typedef struct FFTContext {
+ int nbits;
+ int inverse;
+ uint16_t *revtab;
+ FFTComplex *exptab;
+ FFTComplex *exptab1; /* only used by SSE code */
+ FFTComplex *tmp_buf;
+ void (*fft_permute)(struct FFTContext *s, FFTComplex *z);
+ void (*fft_calc)(struct FFTContext *s, FFTComplex *z);
+ int split_radix;
+ int permutation;
+#define FF_MDCT_PERM_NONE 0
+#define FF_MDCT_PERM_INTERLEAVE 1
+} FFTContext;
+
+extern FFTSample* const ff_cos_tabs[17];
+
+#define COSTABLE_CONST
+#define SINTABLE_CONST
+
+#define COSTABLE(size) COSTABLE_CONST FFTSample ff_cos_##size[size/2]
+
+#define SINTABLE(size) SINTABLE_CONST FFTSample ff_sin_##size[size/2]
+
+extern COSTABLE(16);
+extern COSTABLE(32);
+extern COSTABLE(64);
+extern COSTABLE(128);
+extern COSTABLE(256);
+extern COSTABLE(512);
+extern COSTABLE(1024);
+extern COSTABLE(2048);
+extern COSTABLE(4096);
+extern COSTABLE(8192);
+extern COSTABLE(16384);
+extern COSTABLE(32768);
+extern COSTABLE(65536);
+
+/**
+ * Initializes the cosine table in ff_cos_tabs[index]
+ * \param index index in ff_cos_tabs array of the table to initialize
+ */
+void ff_init_ff_cos_tabs(int index);
+
+extern SINTABLE(16);
+extern SINTABLE(32);
+extern SINTABLE(64);
+extern SINTABLE(128);
+extern SINTABLE(256);
+extern SINTABLE(512);
+extern SINTABLE(1024);
+extern SINTABLE(2048);
+extern SINTABLE(4096);
+extern SINTABLE(8192);
+extern SINTABLE(16384);
+extern SINTABLE(32768);
+extern SINTABLE(65536);
+
+/**
+ * Sets up a complex FFT.
+ * @param nbits log2 of the length of the input array
+ * @param inverse if 0 perform the forward transform, if 1 perform the inverse
+ */
+int ff_fft_init(FFTContext *s, int nbits, int inverse);
+void ff_fft_permute_c(FFTContext *s, FFTComplex *z);
+void ff_fft_calc_c(FFTContext *s, FFTComplex *z);
+
+/**
+ * Do the permutation needed BEFORE calling ff_fft_calc().
+ */
+static inline void ff_fft_permute(FFTContext *s, FFTComplex *z)
+{
+ s->fft_permute(s, z);
+}
+/**
+ * Do a complex FFT with the parameters defined in ff_fft_init(). The
+ * input data must be permuted before. No 1.0/sqrt(n) normalization is done.
+ */
+static inline void ff_fft_calc(FFTContext *s, FFTComplex *z)
+{
+ s->fft_calc(s, z);
+}
+void ff_fft_end(FFTContext *s);
+
+/**
+ * Generate a sine window.
+ * @param window pointer to half window
+ * @param n size of half window
+ */
+void ff_sine_window_init(float *window, int n);
+extern float ff_sine_128 [ 128];
+extern float ff_sine_256 [ 256];
+extern float ff_sine_512 [ 512];
+extern float ff_sine_1024[1024];
+extern float ff_sine_2048[2048];
+extern float ff_sine_4096[4096];
+extern float * const ff_sine_windows[6];
+
+/* Real Discrete Fourier Transform */
+
+enum RDFTransformType {
+ RDFT,
+ IRDFT,
+ RIDFT,
+ IRIDFT
+};
+
+typedef struct {
+ int nbits;
+ int inverse;
+ int sign_convention;
+
+ /* pre/post rotation tables */
+ FFTSample *tcos;
+ FFTSample *tsin;
+ FFTContext fft;
+} RDFTContext;
+
+/**
+ * Sets up a real FFT.
+ * @param nbits log2 of the length of the input array
+ * @param trans the type of transform
+ */
+int ff_rdft_init(RDFTContext *s, int nbits, enum RDFTransformType trans);
+void ff_rdft_calc(RDFTContext *s, FFTSample *data);
+void ff_rdft_end(RDFTContext *s);
+
+/* Discrete Cosine Transform */
+
+typedef struct {
+ int nbits;
+ int inverse;
+ FFTComplex *data;
+ FFTContext fft;
+} DCTContext;
+
+/**
+ * Sets up (Inverse)DCT.
+ * @param nbits log2 of the length of the input array
+ * @param inverse >0 forward transform, <0 inverse transform
+ */
+int ff_dct_init(DCTContext *s, int nbits, int inverse);
+void ff_dct_calc(DCTContext *s, FFTSample *data);
+void ff_dct_end(DCTContext *s);
+
+#endif /* AVCODEC_DSPUTIL_H */
diff --git a/gemrb/plugins/BIKPlayer/fft.cpp b/gemrb/plugins/BIKPlayer/fft.cpp
new file mode 100644
index 0000000..31bf639
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/fft.cpp
@@ -0,0 +1,358 @@
+/*
+ * FFT/IFFT transforms
+ * Copyright (c) 2008 Loren Merritt
+ * Copyright (c) 2002 Fabrice Bellard
+ * Partly based on libdjbfft by D. J. Bernstein
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file libavcodec/fft.c
+ * FFT/IFFT transforms.
+ */
+
+#include <math.h>
+#include "dsputil.h"
+
+/* cos(2*pi*x/n) for 0<=x<=n/4, followed by its reverse */
+COSTABLE(16);
+COSTABLE(32);
+COSTABLE(64);
+COSTABLE(128);
+COSTABLE(256);
+COSTABLE(512);
+COSTABLE(1024);
+COSTABLE(2048);
+COSTABLE(4096);
+COSTABLE(8192);
+COSTABLE(16384);
+COSTABLE(32768);
+COSTABLE(65536);
+
+COSTABLE_CONST FFTSample * const ff_cos_tabs[] = {
+ NULL, NULL, NULL, NULL,
+ ff_cos_16, ff_cos_32, ff_cos_64, ff_cos_128, ff_cos_256, ff_cos_512, ff_cos_1024,
+ ff_cos_2048, ff_cos_4096, ff_cos_8192, ff_cos_16384, ff_cos_32768, ff_cos_65536,
+};
+
+static int split_radix_permutation(int i, int n, int inverse)
+{
+ int m;
+ if(n <= 2) return i&1;
+ m = n >> 1;
+ if(!(i&m)) return split_radix_permutation(i, m, inverse)*2;
+ m >>= 1;
+ if(inverse == !(i&m)) return split_radix_permutation(i, m, inverse)*4 + 1;
+ else return split_radix_permutation(i, m, inverse)*4 - 1;
+}
+
+av_cold void ff_init_ff_cos_tabs(int index)
+{
+ int i;
+ int m = 1<<index;
+ double freq = 2*M_PI/m;
+ FFTSample *tab = ff_cos_tabs[index];
+ for(i=0; i<=m/4; i++)
+ tab[i] = (float) cos(i*freq);
+ for(i=1; i<m/4; i++)
+ tab[m/2-i] = tab[i];
+}
+
+av_cold int ff_fft_init(FFTContext *s, int nbits, int inverse)
+{
+ int i, j, m, n;
+ float alpha, c1, s1, s2;
+ //int av_unused has_vectors;
+
+ if (nbits < 2 || nbits > 16)
+ goto fail;
+ s->nbits = nbits;
+ n = 1 << nbits;
+
+ s->tmp_buf = NULL;
+ s->exptab = (struct FFTComplex *) av_malloc((n / 2) * sizeof(FFTComplex));
+ if (!s->exptab)
+ goto fail;
+ s->revtab = (unsigned short *) av_malloc(n * sizeof(uint16_t));
+ if (!s->revtab)
+ goto fail;
+ s->inverse = inverse;
+
+ s2 = (float) (inverse ? 1.0 : -1.0);
+
+ s->fft_permute = ff_fft_permute_c;
+ s->fft_calc = ff_fft_calc_c;
+ s->exptab1 = NULL;
+ s->split_radix = 1;
+
+ if (s->split_radix) {
+ for(j=4; j<=nbits; j++) {
+ ff_init_ff_cos_tabs(j);
+ }
+ for(i=0; i<n; i++)
+ s->revtab[-split_radix_permutation(i, n, s->inverse) & (n-1)] = i;
+ s->tmp_buf = (struct FFTComplex *) av_malloc(n * sizeof(FFTComplex));
+ if(!s->tmp_buf) {
+ goto fail;
+ }
+
+ } else {
+ int np, nblocks, np2, l;
+ FFTComplex *q;
+
+ for(i=0; i<(n/2); i++) {
+ alpha = (float) (2 * M_PI * (float)i / (float)n);
+ c1 = (float) cos(alpha);
+ s1 = (float) (sin(alpha) * s2);
+ s->exptab[i].re = c1;
+ s->exptab[i].im = s1;
+ }
+
+ np = 1 << nbits;
+ nblocks = np >> 3;
+ np2 = np >> 1;
+ s->exptab1 = (struct FFTComplex *) av_malloc(np * 2 * sizeof(FFTComplex));
+ if (!s->exptab1)
+ goto fail;
+ q = s->exptab1;
+ do {
+ for(l = 0; l < np2; l += 2 * nblocks) {
+ *q++ = s->exptab[l];
+ *q++ = s->exptab[l + nblocks];
+
+ q->re = -s->exptab[l].im;
+ q->im = s->exptab[l].re;
+ q++;
+ q->re = -s->exptab[l + nblocks].im;
+ q->im = s->exptab[l + nblocks].re;
+ q++;
+ }
+ nblocks = nblocks >> 1;
+ } while (nblocks != 0);
+ av_freep((void **) &s->exptab);
+
+ /* compute bit reverse table */
+ for(i=0;i<n;i++) {
+ m=0;
+ for(j=0;j<nbits;j++) {
+ m |= ((i >> j) & 1) << (nbits-j-1);
+ }
+ s->revtab[i]=m;
+ }
+ }
+
+ return 0;
+ fail:
+ av_freep((void **) &s->revtab);
+ av_freep((void **) &s->exptab);
+ av_freep((void **) &s->exptab1);
+ av_freep((void **) &s->tmp_buf);
+ return -1;
+}
+
+void ff_fft_permute_c(FFTContext *s, FFTComplex *z)
+{
+ int j, k, np;
+ FFTComplex tmp;
+ const uint16_t *revtab = s->revtab;
+ np = 1 << s->nbits;
+
+ if (s->tmp_buf) {
+ /* TODO: handle split-radix permute in a more optimal way, probably in-place */
+ for(j=0;j<np;j++) s->tmp_buf[revtab[j]] = z[j];
+ memcpy(z, s->tmp_buf, np * sizeof(FFTComplex));
+ return;
+ }
+
+ /* reverse */
+ for(j=0;j<np;j++) {
+ k = revtab[j];
+ if (k < j) {
+ tmp = z[k];
+ z[k] = z[j];
+ z[j] = tmp;
+ }
+ }
+}
+
+av_cold void ff_fft_end(FFTContext *s)
+{
+ av_freep((void **) &s->revtab);
+ av_freep((void **) &s->exptab);
+ av_freep((void **) &s->exptab1);
+ av_freep((void **) &s->tmp_buf);
+}
+
+#define sqrthalf (float)M_SQRT1_2
+
+#define BF(x,y,a,b) {\
+ x = a - b;\
+ y = a + b;\
+}
+
+#define BUTTERFLIES(a0,a1,a2,a3) {\
+ BF(t3, t5, t5, t1);\
+ BF(a2.re, a0.re, a0.re, t5);\
+ BF(a3.im, a1.im, a1.im, t3);\
+ BF(t4, t6, t2, t6);\
+ BF(a3.re, a1.re, a1.re, t4);\
+ BF(a2.im, a0.im, a0.im, t6);\
+}
+
+// force loading all the inputs before storing any.
+// this is slightly slower for small data, but avoids store->load aliasing
+// for addresses separated by large powers of 2.
+#define BUTTERFLIES_BIG(a0,a1,a2,a3) {\
+ FFTSample r0=a0.re, i0=a0.im, r1=a1.re, i1=a1.im;\
+ BF(t3, t5, t5, t1);\
+ BF(a2.re, a0.re, r0, t5);\
+ BF(a3.im, a1.im, i1, t3);\
+ BF(t4, t6, t2, t6);\
+ BF(a3.re, a1.re, r1, t4);\
+ BF(a2.im, a0.im, i0, t6);\
+}
+
+#define TRANSFORM(a0,a1,a2,a3,wre,wim) {\
+ t1 = a2.re * wre + a2.im * wim;\
+ t2 = a2.im * wre - a2.re * wim;\
+ t5 = a3.re * wre - a3.im * wim;\
+ t6 = a3.im * wre + a3.re * wim;\
+ BUTTERFLIES(a0,a1,a2,a3)\
+}
+
+#define TRANSFORM_ZERO(a0,a1,a2,a3) {\
+ t1 = a2.re;\
+ t2 = a2.im;\
+ t5 = a3.re;\
+ t6 = a3.im;\
+ BUTTERFLIES(a0,a1,a2,a3)\
+}
+
+/* z[0...8n-1], w[1...2n-1] */
+#define PASS(name)\
+static void name(FFTComplex *z, const FFTSample *wre, unsigned int n)\
+{\
+ FFTSample t1, t2, t3, t4, t5, t6;\
+ int o1 = 2*n;\
+ int o2 = 4*n;\
+ int o3 = 6*n;\
+ const FFTSample *wim = wre+o1;\
+ n--;\
+\
+ TRANSFORM_ZERO(z[0],z[o1],z[o2],z[o3]);\
+ TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]);\
+ do {\
+ z += 2;\
+ wre += 2;\
+ wim -= 2;\
+ TRANSFORM(z[0],z[o1],z[o2],z[o3],wre[0],wim[0]);\
+ TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]);\
+ } while(--n);\
+}
+
+PASS(pass)
+#undef BUTTERFLIES
+#define BUTTERFLIES BUTTERFLIES_BIG
+PASS(pass_big)
+
+#define DECL_FFT(n,n2,n4)\
+static void fft##n(FFTComplex *z)\
+{\
+ fft##n2(z);\
+ fft##n4(z+n4*2);\
+ fft##n4(z+n4*3);\
+ pass(z,ff_cos_##n,n4/2);\
+}
+
+static void fft4(FFTComplex *z)
+{
+ FFTSample t1, t2, t3, t4, t5, t6, t7, t8;
+
+ BF(t3, t1, z[0].re, z[1].re);
+ BF(t8, t6, z[3].re, z[2].re);
+ BF(z[2].re, z[0].re, t1, t6);
+ BF(t4, t2, z[0].im, z[1].im);
+ BF(t7, t5, z[2].im, z[3].im);
+ BF(z[3].im, z[1].im, t4, t8);
+ BF(z[3].re, z[1].re, t3, t7);
+ BF(z[2].im, z[0].im, t2, t5);
+}
+
+static void fft8(FFTComplex *z)
+{
+ FFTSample t1, t2, t3, t4, t5, t6, t7, t8;
+
+ fft4(z);
+
+ BF(t1, z[5].re, z[4].re, -z[5].re);
+ BF(t2, z[5].im, z[4].im, -z[5].im);
+ BF(t3, z[7].re, z[6].re, -z[7].re);
+ BF(t4, z[7].im, z[6].im, -z[7].im);
+ BF(t8, t1, t3, t1);
+ BF(t7, t2, t2, t4);
+ BF(z[4].re, z[0].re, z[0].re, t1);
+ BF(z[4].im, z[0].im, z[0].im, t2);
+ BF(z[6].re, z[2].re, z[2].re, t7);
+ BF(z[6].im, z[2].im, z[2].im, t8);
+
+ TRANSFORM(z[1],z[3],z[5],z[7],sqrthalf,sqrthalf);
+}
+
+#if !CONFIG_SMALL
+static void fft16(FFTComplex *z)
+{
+ FFTSample t1, t2, t3, t4, t5, t6;
+
+ fft8(z);
+ fft4(z+8);
+ fft4(z+12);
+
+ TRANSFORM_ZERO(z[0],z[4],z[8],z[12]);
+ TRANSFORM(z[2],z[6],z[10],z[14],sqrthalf,sqrthalf);
+ TRANSFORM(z[1],z[5],z[9],z[13],ff_cos_16[1],ff_cos_16[3]);
+ TRANSFORM(z[3],z[7],z[11],z[15],ff_cos_16[3],ff_cos_16[1]);
+}
+#else
+DECL_FFT(16,8,4)
+#endif
+DECL_FFT(32,16,8)
+DECL_FFT(64,32,16)
+DECL_FFT(128,64,32)
+DECL_FFT(256,128,64)
+DECL_FFT(512,256,128)
+#if !CONFIG_SMALL
+#define pass pass_big
+#endif
+DECL_FFT(1024,512,256)
+DECL_FFT(2048,1024,512)
+DECL_FFT(4096,2048,1024)
+DECL_FFT(8192,4096,2048)
+DECL_FFT(16384,8192,4096)
+DECL_FFT(32768,16384,8192)
+DECL_FFT(65536,32768,16384)
+
+static void (* const fft_dispatch[])(FFTComplex*) = {
+ fft4, fft8, fft16, fft32, fft64, fft128, fft256, fft512, fft1024,
+ fft2048, fft4096, fft8192, fft16384, fft32768, fft65536,
+};
+
+void ff_fft_calc_c(FFTContext *s, FFTComplex *z)
+{
+ fft_dispatch[s->nbits-2](z);
+}
+
diff --git a/gemrb/plugins/BIKPlayer/mem.cpp b/gemrb/plugins/BIKPlayer/mem.cpp
new file mode 100644
index 0000000..cbebe47
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/mem.cpp
@@ -0,0 +1,68 @@
+/*
+ * default memory allocator for libavutil
+ * Copyright (c) 2002 Fabrice Bellard
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file libavutil/mem.c
+ * default memory allocator for libavutil
+ */
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include "common.h"
+
+/* You can redefine av_malloc and av_free in your project to use your
+ memory allocator. You do not need to suppress this file because the
+ linker will do it automatically. */
+
+void *av_malloc(unsigned int size)
+{
+ void *ptr = NULL;
+ long diff;
+
+ /* let's disallow possible ambiguous cases */
+ if(size > (INT_MAX-16) )
+ return NULL;
+
+ ptr = malloc(size+16);
+ if(!ptr)
+ return ptr;
+ diff= ((-(long)ptr - 1)&15) + 1;
+ ptr = (char*)ptr + diff;
+ ((char*)ptr)[-1]= diff;
+ return ptr;
+}
+
+void av_free(void *ptr)
+{
+ /* XXX: this test should not be needed on most libcs */
+ if (ptr)
+ free((char*)ptr - ((char*)ptr)[-1]);
+}
+
+void av_freep(void **arg)
+{
+ av_free(*arg);
+ *arg = NULL;
+}
diff --git a/gemrb/plugins/BIKPlayer/rational.cpp b/gemrb/plugins/BIKPlayer/rational.cpp
new file mode 100644
index 0000000..d389568
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/rational.cpp
@@ -0,0 +1,83 @@
+/*
+ * rational numbers
+ * Copyright (c) 2003 Michael Niedermayer <michaelni at gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file libavutil/rational.c
+ * rational numbers
+ * @author Michael Niedermayer <michaelni at gmx.at>
+ */
+
+#include <assert.h>
+#include <limits.h>
+
+#include "common.h"
+#include "rational.h"
+
+int64_t av_gcd(int64_t a, int64_t b){
+ if(b) return av_gcd(b, a%b);
+ return a;
+}
+
+int av_reduce(int &dst_num, int &dst_den, int64_t num, int64_t den, int64_t max) {
+ AVRational a0={0,1}, a1={1,0};
+ int sign= (num<0) ^ (den<0);
+ int64_t gcd= av_gcd(FFABS(num), FFABS(den));
+
+ if(gcd){
+ num = FFABS(num)/gcd;
+ den = FFABS(den)/gcd;
+ }
+ if(num<=max && den<=max){
+ a1.num = num;
+ a1.den = den;
+ den=0;
+ }
+
+ while(den){
+ uint64_t x = num / den;
+ int64_t next_den= num - den*x;
+ int64_t a2n= x*a1.num + a0.num;
+ int64_t a2d= x*a1.den + a0.den;
+
+ if(a2n > max || a2d > max){
+ if(a1.num) x= (max - a0.num) / a1.num;
+ if(a1.den) x= FFMIN(x, (max - a0.den) / a1.den);
+
+ if (den*(2*x*a1.den + a0.den) > num*a1.den) {
+ a1.den =x*a1.den + a0.den;
+ a1.num =x*a1.num + a0.num;
+ }
+ break;
+ }
+
+ a0 = a1;
+ a1.den = a2d;
+ a1.num = a2n;
+ num = den;
+ den = next_den;
+ }
+ assert(av_gcd(a1.num, a1.den) <= 1U);
+
+ dst_num = sign ? -a1.num : a1.num;
+ dst_den = a1.den;
+
+ return den==0;
+}
diff --git a/gemrb/plugins/BIKPlayer/rational.h b/gemrb/plugins/BIKPlayer/rational.h
new file mode 100644
index 0000000..2d38645
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/rational.h
@@ -0,0 +1,126 @@
+/*
+ * rational numbers
+ * Copyright (c) 2003 Michael Niedermayer <michaelni at gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file libavutil/rational.h
+ * rational numbers
+ * @author Michael Niedermayer <michaelni at gmx.at>
+ */
+
+#ifndef AVUTIL_RATIONAL_H
+#define AVUTIL_RATIONAL_H
+
+/**
+ * rational number numerator/denominator
+ */
+typedef struct AVRational{
+ int num; ///< numerator
+ int den; ///< denominator
+} AVRational;
+
+/**
+ * Compares two rationals.
+ * @param a first rational
+ * @param b second rational
+ * @return 0 if a==b, 1 if a>b and -1 if a<b
+ */
+static inline int av_cmp_q(AVRational a, AVRational b){
+ const int64_t tmp= a.num * (int64_t)b.den - b.num * (int64_t)a.den;
+
+ if(tmp) return (int) ((tmp>>63)|1);
+ else return 0;
+}
+
+/**
+ * Converts rational to double.
+ * @param a rational to convert
+ * @return (double) a
+ */
+static inline double av_q2d(AVRational a){
+ return a.num / (double) a.den;
+}
+
+/**
+ * Reduces a fraction.
+ * This is useful for framerate calculations.
+ * @param dst_num destination numerator
+ * @param dst_den destination denominator
+ * @param num source numerator
+ * @param den source denominator
+ * @param max the maximum allowed for dst_num & dst_den
+ * @return 1 if exact, 0 otherwise
+ */
+int av_reduce(int &dst_num, int &dst_den, int64_t num, int64_t den, int64_t max);
+
+/**
+ * Multiplies two rationals.
+ * @param b first rational
+ * @param c second rational
+ * @return b*c
+ */
+AVRational av_mul_q(AVRational b, AVRational c) av_const;
+
+/**
+ * Divides one rational by another.
+ * @param b first rational
+ * @param c second rational
+ * @return b/c
+ */
+AVRational av_div_q(AVRational b, AVRational c) av_const;
+
+/**
+ * Adds two rationals.
+ * @param b first rational
+ * @param c second rational
+ * @return b+c
+ */
+AVRational av_add_q(AVRational b, AVRational c) av_const;
+
+/**
+ * Subtracts one rational from another.
+ * @param b first rational
+ * @param c second rational
+ * @return b-c
+ */
+AVRational av_sub_q(AVRational b, AVRational c) av_const;
+
+/**
+ * Converts a double precision floating point number to a rational.
+ * @param d double to convert
+ * @param max the maximum allowed numerator and denominator
+ * @return (AVRational) d
+ */
+AVRational av_d2q(double d, int max) av_const;
+
+/**
+ * @return 1 if q1 is nearer to q than q2, -1 if q2 is nearer
+ * than q1, 0 if they have the same distance.
+ */
+int av_nearer_q(AVRational q, AVRational q1, AVRational q2);
+
+/**
+ * Finds the nearest value in q_list to q.
+ * @param q_list an array of rationals terminated by {0, 0}
+ * @return the index of the nearest value found in the array
+ */
+int av_find_nearest_q_idx(AVRational q, const AVRational* q_list);
+
+#endif /* AVUTIL_RATIONAL_H */
diff --git a/gemrb/plugins/BIKPlayer/rdft.cpp b/gemrb/plugins/BIKPlayer/rdft.cpp
new file mode 100644
index 0000000..8b15ecf
--- /dev/null
+++ b/gemrb/plugins/BIKPlayer/rdft.cpp
@@ -0,0 +1,130 @@
+/*
+ * (I)RDFT transforms
+ * Copyright (c) 2009 Alex Converse <alex dot converse at gmail dot com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <math.h>
+#include "dsputil.h"
+
+/**
+ * @file libavcodec/rdft.c
+ * (Inverse) Real Discrete Fourier Transforms.
+ */
+
+/* sin(2*pi*x/n) for 0<=x<n/4, followed by n/2<=x<3n/4 */
+SINTABLE(16);
+SINTABLE(32);
+SINTABLE(64);
+SINTABLE(128);
+SINTABLE(256);
+SINTABLE(512);
+SINTABLE(1024);
+SINTABLE(2048);
+SINTABLE(4096);
+SINTABLE(8192);
+SINTABLE(16384);
+SINTABLE(32768);
+SINTABLE(65536);
+
+SINTABLE_CONST FFTSample * const ff_sin_tabs[] = {
+ NULL, NULL, NULL, NULL,
+ ff_sin_16, ff_sin_32, ff_sin_64, ff_sin_128, ff_sin_256, ff_sin_512, ff_sin_1024,
+ ff_sin_2048, ff_sin_4096, ff_sin_8192, ff_sin_16384, ff_sin_32768, ff_sin_65536,
+};
+
+av_cold int ff_rdft_init(RDFTContext *s, int nbits, enum RDFTransformType trans)
+{
+ int n = 1 << nbits;
+ int i;
+ const double theta = (trans == RDFT || trans == IRIDFT ? -1 : 1)*2*M_PI/n;
+
+ s->nbits = nbits;
+ s->inverse = trans == IRDFT || trans == IRIDFT;
+ s->sign_convention = trans == RIDFT || trans == IRIDFT ? 1 : -1;
+
+ if (nbits < 4 || nbits > 16)
+ return -1;
+
+ if (ff_fft_init(&s->fft, nbits-1, trans == IRDFT || trans == RIDFT) < 0)
+ return -1;
+
+ ff_init_ff_cos_tabs(nbits);
+ s->tcos = ff_cos_tabs[nbits];
+ s->tsin = ff_sin_tabs[nbits]+(trans == RDFT || trans == IRIDFT)*(n>>2);
+ for (i = 0; i < (n>>2); i++) {
+ s->tsin[i] = (float) sin(i*theta);
+ }
+ return 0;
+}
+
+/** Map one real FFT into two parallel real even and odd FFTs. Then interleave
+ * the two real FFTs into one complex FFT. Unmangle the results.
+ * ref: http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM
+ */
+void ff_rdft_calc_c(RDFTContext* s, FFTSample* data)
+{
+ int i, i1, i2;
+ FFTComplex ev, od;
+ const int n = 1 << s->nbits;
+ const float k1 = 0.5;
+ const float k2 = (float) (0.5 - s->inverse);
+ const FFTSample *tcos = s->tcos;
+ const FFTSample *tsin = s->tsin;
+
+ if (!s->inverse) {
+ ff_fft_permute(&s->fft, (FFTComplex*)data);
+ ff_fft_calc(&s->fft, (FFTComplex*)data);
+ }
+ /* i=0 is a special case because of packing, the DC term is real, so we
+ are going to throw the N/2 term (also real) in with it. */
+ ev.re = data[0];
+ data[0] = ev.re+data[1];
+ data[1] = ev.re-data[1];
+ for (i = 1; i < (n>>2); i++) {
+ i1 = 2*i;
+ i2 = n-i1;
+ /* Separate even and odd FFTs */
+ ev.re = k1*(data[i1 ]+data[i2 ]);
+ od.im = -k2*(data[i1 ]-data[i2 ]);
+ ev.im = k1*(data[i1+1]-data[i2+1]);
+ od.re = k2*(data[i1+1]+data[i2+1]);
+ /* Apply twiddle factors to the odd FFT and add to the even FFT */
+ data[i1 ] = ev.re + od.re*tcos[i] - od.im*tsin[i];
+ data[i1+1] = ev.im + od.im*tcos[i] + od.re*tsin[i];
+ data[i2 ] = ev.re - od.re*tcos[i] + od.im*tsin[i];
+ data[i2+1] = -ev.im + od.im*tcos[i] + od.re*tsin[i];
+ }
+ data[2*i+1]=s->sign_convention*data[2*i+1];
+ if (s->inverse) {
+ data[0] *= k1;
+ data[1] *= k1;
+ ff_fft_permute(&s->fft, (FFTComplex*)data);
+ ff_fft_calc(&s->fft, (FFTComplex*)data);
+ }
+}
+
+void ff_rdft_calc(RDFTContext *s, FFTSample *data)
+{
+ ff_rdft_calc_c(s, data);
+}
+
+av_cold void ff_rdft_end(RDFTContext *s)
+{
+ ff_fft_end(&s->fft);
+}
diff --git a/gemrb/plugins/BMPImporter/BMPImporter.cpp b/gemrb/plugins/BMPImporter/BMPImporter.cpp
new file mode 100644
index 0000000..284aa00
--- /dev/null
+++ b/gemrb/plugins/BMPImporter/BMPImporter.cpp
@@ -0,0 +1,295 @@
+/* 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 "BMPImporter.h"
+
+#include "RGBAColor.h"
+#include "win32def.h"
+
+#include "Interface.h"
+#include "Video.h"
+
+#define BMP_HEADER_SIZE 54
+
+static ieDword red_mask = 0x00ff0000;
+static ieDword green_mask = 0x0000ff00;
+static ieDword blue_mask = 0x000000ff;
+
+BMPImporter::BMPImporter(void)
+{
+ Palette = NULL;
+ pixels = NULL;
+ if (DataStream::IsEndianSwitch()) {
+ red_mask = 0x000000ff;
+ green_mask = 0x0000ff00;
+ blue_mask = 0x00ff0000;
+ }
+}
+
+BMPImporter::~BMPImporter(void)
+{
+ free( Palette );
+ free( pixels );
+}
+
+bool BMPImporter::Open(DataStream* stream)
+{
+ str = stream;
+ //we release the previous pixel data
+ free( pixels );
+ pixels = NULL;
+ free( Palette );
+ Palette = NULL;
+
+ //BITMAPFILEHEADER
+ char Signature[2];
+ ieDword FileSize, DataOffset;
+
+ str->Read( Signature, 2 );
+ if (strncmp( Signature, "BM", 2 ) != 0) {
+ printMessage( "BMPImporter","Not a valid BMP File.\n",LIGHT_RED);
+ return false;
+ }
+ str->ReadDword( &FileSize );
+ str->Seek( 4, GEM_CURRENT_POS );
+ str->ReadDword( &DataOffset );
+
+ //BITMAPINFOHEADER
+
+ str->ReadDword( &Size );
+ //some IE palettes are of a different format (OS/2 BMP)!
+ if (Size < 24) {
+ printMessage( "BMPImporter","OS/2 Bitmap, not supported.\n", LIGHT_RED);
+ return false;
+ }
+ str->ReadDword( &Width );
+ str->ReadDword( &Height );
+ str->ReadWord( &Planes );
+ str->ReadWord( &BitCount );
+ str->ReadDword( &Compression );
+ str->ReadDword( &ImageSize );
+ //24 = the already read bytes 3x4+2x2+2x4
+ //this is normally 16
+ str->Seek( Size-24, GEM_CURRENT_POS );
+ //str->ReadDword(&Hres );
+ //str->ReadDword(&Vres );
+ //str->ReadDword(&ColorsUsed );
+ //str->ReadDword(&ColorsImportant );
+ if (Compression != 0) {
+ printMessage( "BMPImporter"," ", LIGHT_RED);
+ printf( "Compressed %d-bits Image, not supported.\n", BitCount );
+ return false;
+ }
+ //COLORTABLE
+ Palette = NULL;
+ if (BitCount <= 8) {
+ if (BitCount == 8)
+ NumColors = 256;
+ else
+ NumColors = 16;
+ Palette = ( Color * ) malloc( 4 * NumColors );
+ for (unsigned int i = 0; i < NumColors; i++) {
+ str->Read( &Palette[i].b, 1 );
+ str->Read( &Palette[i].g, 1 );
+ str->Read( &Palette[i].r, 1 );
+ str->Read( &Palette[i].a, 1 );
+ }
+ }
+ str->Seek( DataOffset, GEM_STREAM_START );
+ //no idea if we have to swap this or not
+ //RASTERDATA
+ switch (BitCount) {
+ case 32:
+ PaddedRowLength = Width * 4;
+ break;
+
+ case 24:
+ PaddedRowLength = Width * 3;
+ break;
+
+ case 16:
+ PaddedRowLength = Width * 2;
+ break;
+
+ case 8:
+ PaddedRowLength = Width;
+ break;
+
+ case 4:
+ PaddedRowLength = ( Width >> 1 );
+ break;
+ default:
+ printMessage( "BMPImporter"," ", LIGHT_RED);
+ printf( "BitCount %d is not supported.\n", BitCount );
+ return false;
+ }
+ //if(BitCount!=4)
+ //{
+ if (PaddedRowLength & 3) {
+ PaddedRowLength += 4 - ( PaddedRowLength & 3 );
+ }
+ //}
+ void* rpixels = malloc( PaddedRowLength* Height );
+ str->Read( rpixels, PaddedRowLength * Height );
+ if (BitCount == 32) {
+ //convert to 24 bits on the fly
+ int size = Width * Height * 3;
+ pixels = malloc( size );
+ unsigned char * dest = ( unsigned char * ) pixels;
+ dest += size;
+ unsigned char * src = ( unsigned char * ) rpixels;
+ for (int i = Height; i; i--) {
+ dest -= ( Width * 3 );
+ for (unsigned int j=0;j<Width;j++) {
+ dest[j*3]=src[j*4];
+ dest[j*3+1]=src[j*4+1];
+ dest[j*3+2]=src[j*4+2];
+ }
+ src += PaddedRowLength;
+ }
+ BitCount = 24;
+ } else if (BitCount == 24) {
+ int size = Width * Height * 3;
+ pixels = malloc( size );
+ unsigned char * dest = ( unsigned char * ) pixels;
+ dest += size;
+ unsigned char * src = ( unsigned char * ) rpixels;
+ for (int i = Height; i; i--) {
+ dest -= ( Width * 3 );
+ memcpy( dest, src, Width * 3 );
+ src += PaddedRowLength;
+ }
+ } else if (BitCount == 8) {
+ pixels = malloc( Width * Height );
+ unsigned char * dest = ( unsigned char * ) pixels;
+ dest += Height * Width;
+ unsigned char * src = ( unsigned char * ) rpixels;
+ for (int i = Height; i; i--) {
+ dest -= Width;
+ memcpy( dest, src, Width );
+ src += PaddedRowLength;
+ }
+ } else if (BitCount == 4) {
+ Read4To8(rpixels);
+ }
+ free( rpixels );
+ return true;
+}
+
+void BMPImporter::Read8To8(void *rpixels)
+{
+ pixels = malloc( Width * Height );
+ unsigned char * dest = ( unsigned char * ) pixels;
+ dest += Height * Width;
+ unsigned char * src = ( unsigned char * ) rpixels;
+ for (int i = Height; i; i--) {
+ dest -= Width;
+ memcpy( dest, src, Width );
+ src += PaddedRowLength;
+ }
+}
+
+void BMPImporter::Read4To8(void *rpixels)
+{
+ BitCount = 8;
+ pixels = malloc( Width * Height );
+ unsigned char * dest = ( unsigned char * ) pixels;
+ dest += Height * Width;
+ unsigned char * src = ( unsigned char * ) rpixels;
+ for (int i = Height; i; i--) {
+ dest -= Width;
+ for (unsigned int j=0;j<Width;j++) {
+ if (!(j&1)) {
+ dest[j] = ((unsigned) src[j/2])>>4;
+ } else {
+ dest[j] = src[j/2]&15;
+ }
+ }
+ src += PaddedRowLength;
+ }
+}
+
+Sprite2D* BMPImporter::GetSprite2D()
+{
+ Sprite2D* spr = NULL;
+ if (BitCount == 24) {
+ void* p = malloc( Width * Height * 3 );
+ memcpy( p, pixels, Width * Height * 3 );
+ spr = core->GetVideoDriver()->CreateSprite( Width, Height, 24,
+ red_mask, green_mask, blue_mask, 0x00000000, p,
+ true, green_mask );
+ } else if (BitCount == 8) {
+ void* p = malloc( Width* Height );
+ memcpy( p, pixels, Width * Height );
+ spr = core->GetVideoDriver()->CreateSprite8( Width, Height, NumColors==16?4:8,
+ p, Palette, true, 0 );
+ }
+ return spr;
+}
+
+void BMPImporter::GetPalette(int colors, Color* pal)
+{
+ if (BitCount > 8) {
+ ImageMgr::GetPalette(colors, pal);
+ return;
+ }
+
+ for (int i = 0; i < colors; i++) {
+ pal[i].r = Palette[i%NumColors].r;
+ pal[i].g = Palette[i%NumColors].g;
+ pal[i].b = Palette[i%NumColors].b;
+ pal[i].a = 0xff;
+ }
+}
+
+Bitmap* BMPImporter::GetBitmap()
+{
+ Bitmap *data = new Bitmap(Width,Height);
+
+ unsigned char *p = ( unsigned char * ) pixels;
+ switch (BitCount) {
+ case 8:
+ unsigned int y;
+ for (y = 0; y < Height; y++) {
+ for (unsigned int x = 0; x < Width; x++) {
+ data->SetAt(x,y,p[y*Width + x]);
+ }
+ }
+ break;
+ case 24:
+ printMessage("BMPImporter", "Don't know how to handle 24bit bitmap from ", WHITE);
+ printf( "%s...", str->filename );
+ printStatus( "ERROR", LIGHT_RED );
+ for (y = 0; y < Height; y++) {
+ for (unsigned int x = 0; x < Width; x++) {
+ data->SetAt(x,y,p[3*(y*Width + x)]);
+ }
+ }
+ break;
+ }
+
+ return data;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xD768B1, "BMP File Reader")
+PLUGIN_IE_RESOURCE(BMPImporter, ".bmp", (ieWord)IE_BMP_CLASS_ID)
+END_PLUGIN()
diff --git a/gemrb/plugins/BMPImporter/BMPImporter.h b/gemrb/plugins/BMPImporter/BMPImporter.h
new file mode 100644
index 0000000..2470a35
--- /dev/null
+++ b/gemrb/plugins/BMPImporter/BMPImporter.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.
+ *
+ *
+ */
+
+#ifndef BMPIMP_H
+#define BMPIMP_H
+
+#include "ImageMgr.h"
+
+class BMPImporter : public ImageMgr {
+private:
+ //BITMAPINFOHEADER
+ ieDword Size, Width, Height, Compression, ImageSize, ColorsUsed, ColorsImportant;
+ ieWord Planes, BitCount;
+
+ //COLORTABLE
+ ieDword NumColors;
+ Color* Palette;
+
+ //RASTERDATA
+ void* pixels;
+
+ //OTHER
+ unsigned int PaddedRowLength;
+public:
+ BMPImporter(void);
+ ~BMPImporter(void);
+ bool Open(DataStream* stream);
+ Sprite2D* GetSprite2D();
+ virtual Bitmap* GetBitmap();
+ void GetPalette(int colors, Color* pal);
+
+ int GetWidth() { return (int) Width; }
+ int GetHeight() { return (int) Height; }
+private:
+ void Read8To8(void *rpixels);
+ void Read4To8(void *rpixels);
+};
+
+#endif
diff --git a/gemrb/plugins/BMPImporter/CMakeLists.txt b/gemrb/plugins/BMPImporter/CMakeLists.txt
new file mode 100644
index 0000000..0738b20
--- /dev/null
+++ b/gemrb/plugins/BMPImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (BMPImporter BMPImporter.cpp)
diff --git a/gemrb/plugins/BMPImporter/Makefile.am b/gemrb/plugins/BMPImporter/Makefile.am
new file mode 100644
index 0000000..a49f778
--- /dev/null
+++ b/gemrb/plugins/BMPImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = BMPImporter.la
+BMPImporter_la_LDFLAGS = -module -avoid-version -shared
+BMPImporter_la_SOURCES = BMPImporter.cpp BMPImporter.h
diff --git a/gemrb/plugins/BMPWriter/BMPWriter.cpp b/gemrb/plugins/BMPWriter/BMPWriter.cpp
new file mode 100644
index 0000000..a89ad5d
--- /dev/null
+++ b/gemrb/plugins/BMPWriter/BMPWriter.cpp
@@ -0,0 +1,77 @@
+#include "BMPWriter.h"
+
+#include "Interface.h"
+#include "Video.h"
+
+#include <cstring>
+
+#define BMP_HEADER_SIZE 54 //FIXME: duplicate
+
+#define GET_SCANLINE_LENGTH(width, bitsperpixel) (((width)*(bitsperpixel)+7)/8)
+
+BMPWriter::BMPWriter()
+{
+}
+
+BMPWriter::~BMPWriter()
+{
+}
+
+void BMPWriter::PutImage(DataStream *output, Sprite2D *spr)
+{
+ ieDword tmpDword;
+ ieWord tmpWord;
+
+ // FIXME
+ ieDword Width = spr->Width;
+ ieDword Height = spr->Height;
+ char filling[3] = {'B','M'};
+ ieDword PaddedRowLength = GET_SCANLINE_LENGTH(Width,24);
+ int stuff = (4-(PaddedRowLength&3))&3; // rounding it up to 4 bytes boundary
+ PaddedRowLength+=stuff;
+ ieDword fullsize = PaddedRowLength*Height;
+
+ //always save in truecolor (24 bit), no palette
+ output->Write( filling, 2);
+ tmpDword = fullsize+BMP_HEADER_SIZE; // FileSize
+ output->WriteDword( &tmpDword);
+ tmpDword = 0;
+ output->WriteDword( &tmpDword); // ??
+ tmpDword = BMP_HEADER_SIZE; // DataOffset
+ output->WriteDword( &tmpDword);
+ tmpDword = 40; // Size
+ output->WriteDword( &tmpDword);
+ output->WriteDword( &Width);
+ output->WriteDword( &Height);
+ tmpWord = 1; // Planes
+ output->WriteWord( &tmpWord);
+ tmpWord = 24; //24 bits // BitCount
+ output->WriteWord( &tmpWord);
+ tmpDword = 0; // Compression
+ output->WriteDword( &tmpDword);
+ output->WriteDword( &tmpDword); // ImageSize
+ output->WriteDword( &tmpDword);
+ output->WriteDword( &tmpDword);
+ output->WriteDword( &tmpDword);
+ output->WriteDword( &tmpDword);
+
+ memset( filling,0,sizeof(filling) );
+ for (unsigned int y=0;y<Height;y++) {
+ for (unsigned int x=0;x<Width;x++) {
+ Color c = spr->GetPixel(x,Height-y-1);
+
+ output->Write( &c.b, 1);
+ output->Write( &c.g, 1);
+ output->Write( &c.r, 1);
+ }
+ output->Write( filling, stuff);
+ }
+
+ core->GetVideoDriver()->FreeSprite(spr);
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xD48051E, "BMP File Writer")
+PLUGIN_CLASS(PLUGIN_IMAGE_WRITER_BMP, BMPWriter)
+END_PLUGIN()
diff --git a/gemrb/plugins/BMPWriter/BMPWriter.h b/gemrb/plugins/BMPWriter/BMPWriter.h
new file mode 100644
index 0000000..a8c3ce8
--- /dev/null
+++ b/gemrb/plugins/BMPWriter/BMPWriter.h
@@ -0,0 +1,9 @@
+#include "ImageWriter.h"
+
+class BMPWriter : public ImageWriter {
+public:
+ BMPWriter(void);
+ ~BMPWriter(void);
+
+ void PutImage(DataStream *output, Sprite2D *sprite);
+};
diff --git a/gemrb/plugins/BMPWriter/CMakeLists.txt b/gemrb/plugins/BMPWriter/CMakeLists.txt
new file mode 100644
index 0000000..f435043
--- /dev/null
+++ b/gemrb/plugins/BMPWriter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (BMPWriter BMPWriter.cpp)
diff --git a/gemrb/plugins/BMPWriter/Makefile.am b/gemrb/plugins/BMPWriter/Makefile.am
new file mode 100644
index 0000000..0adb461
--- /dev/null
+++ b/gemrb/plugins/BMPWriter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = BMPWriter.la
+BMPWriter_la_LDFLAGS = -module -avoid-version -shared
+BMPWriter_la_SOURCES = BMPWriter.cpp BMPWriter.h
diff --git a/gemrb/plugins/CHUImporter/CHUImporter.cpp b/gemrb/plugins/CHUImporter/CHUImporter.cpp
new file mode 100644
index 0000000..4d9609d
--- /dev/null
+++ b/gemrb/plugins/CHUImporter/CHUImporter.cpp
@@ -0,0 +1,531 @@
+/* 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 "CHUImporter.h"
+
+#include "RGBAColor.h"
+#include "win32def.h"
+
+#include "AnimationFactory.h"
+#include "GameData.h"
+#include "ImageMgr.h"
+#include "Interface.h"
+#include "GUI/Button.h"
+#include "GUI/Label.h"
+#include "GUI/Progressbar.h"
+#include "GUI/ScrollBar.h"
+#include "GUI/Slider.h"
+#include "GUI/TextArea.h"
+#include "GUI/TextEdit.h"
+
+CHUImporter::CHUImporter()
+{
+ str = NULL;
+ autoFree = false;
+}
+
+CHUImporter::~CHUImporter()
+{
+ if (autoFree) {
+ delete str;
+ }
+}
+
+/** This function loads all available windows from the 'stream' parameter. */
+bool CHUImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (this->autoFree) {
+ delete str;
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "CHUIV1 ", 8 ) != 0) {
+ printMessage( "CHUImporter","Not a Valid CHU File\n",LIGHT_RED );
+ return false;
+ }
+ str->ReadDword( &WindowCount );
+ str->ReadDword( &CTOffset );
+ str->ReadDword( &WEOffset );
+ return true;
+}
+
+/** Returns the i-th window in the Previously Loaded Stream */
+Window* CHUImporter::GetWindow(unsigned int wid)
+{
+ ieWord WindowID, XPos, YPos, Width, Height, BackGround;
+ ieWord ControlsCount, FirstControl;
+ ieResRef MosFile;
+ unsigned int i;
+
+ bool found = false;
+ for (unsigned int c = 0; c < WindowCount; c++) {
+ str->Seek( WEOffset + ( 0x1c * c ), GEM_STREAM_START );
+ str->ReadWord( &WindowID );
+ if (WindowID == wid) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return NULL;
+ }
+ str->Seek( 2, GEM_CURRENT_POS );
+ str->ReadWord( &XPos );
+ str->ReadWord( &YPos );
+ str->ReadWord( &Width );
+ str->ReadWord( &Height );
+ str->ReadWord( &BackGround );
+ str->ReadWord( &ControlsCount );
+ str->ReadResRef( MosFile );
+ str->ReadWord( &FirstControl );
+
+ Window* win = new Window( WindowID, XPos, YPos, Width, Height );
+ if (BackGround == 1) {
+ ResourceHolder<ImageMgr> mos(MosFile);
+ if (mos != NULL) {
+ win->SetBackGround( mos->GetSprite2D(), true );
+ } else
+ printMessage( "CHUImporter","Cannot Load BackGround, skipping\n",YELLOW );
+ }
+ if (!core->IsAvailable( IE_BAM_CLASS_ID )) {
+ printMessage( "CHUImporter","No BAM Importer Available, skipping controls\n",LIGHT_RED );
+ return win;
+ }
+ for (i = 0; i < ControlsCount; i++) {
+ str->Seek( CTOffset + ( ( FirstControl + i ) * 8 ), GEM_STREAM_START );
+ ieDword COffset, CLength, ControlID;
+ ieWord XPos, YPos, Width, Height;
+ ieByte ControlType, temp;
+ str->ReadDword( &COffset );
+ str->ReadDword( &CLength );
+ str->Seek( COffset, GEM_STREAM_START );
+ str->ReadDword( &ControlID );
+ str->ReadWord( &XPos );
+ str->ReadWord( &YPos );
+ str->ReadWord( &Width );
+ str->ReadWord( &Height );
+ str->Read( &ControlType, 1 );
+ str->Read( &temp, 1 );
+ switch (ControlType) {
+ case IE_GUI_BUTTON:
+ {
+ //Button
+ Button* btn = new Button( );
+ btn->ControlID = ControlID;
+ btn->XPos = XPos;
+ btn->YPos = YPos;
+ btn->Width = Width;
+ btn->Height = Height;
+ btn->ControlType = ControlType;
+ ieResRef BAMFile;
+ ieByte Cycle, tmp;
+ ieDword Flags;
+ ieByte UnpressedIndex, x1;
+ ieByte PressedIndex, x2;
+ ieByte SelectedIndex, y1;
+ ieByte DisabledIndex, y2;
+ str->ReadResRef( BAMFile );
+ str->Read( &Cycle, 1 );
+ str->Read( &tmp, 1 );
+ Flags = ((ieDword) tmp)<<8;
+ str->Read( &UnpressedIndex, 1 );
+ str->Read( &x1, 1 );
+ str->Read( &PressedIndex, 1 );
+ str->Read( &x2, 1 );
+ str->Read( &SelectedIndex, 1 );
+ str->Read( &y1, 1 );
+ str->Read( &DisabledIndex, 1 );
+ str->Read( &y2, 1 );
+ btn->Owner = win;
+ /** Justification comes from the .chu, other bits are set by script */
+ if (!Width) {
+ btn->SetFlags(IE_GUI_BUTTON_NO_IMAGE, BM_OR);
+ }
+ if (core->HasFeature(GF_UPPER_BUTTON_TEXT)) {
+ btn->SetFlags(IE_GUI_BUTTON_CAPS, BM_OR);
+ }
+
+ btn->SetFlags( Flags, BM_OR );
+ if (Flags & IE_GUI_BUTTON_ANCHOR) {
+ btn->SetAnchor(x1 | (x2<<8), y1 | (y2<<8));
+ }
+
+ if (strnicmp( BAMFile, "guictrl\0", 8 ) == 0) {
+ if (UnpressedIndex == 0) {
+ //printMessage("CHUImporter", "Special Button Control, Skipping Image Loading\n",GREEN );
+ win->AddControl( btn );
+ break;
+ }
+ }
+ AnimationFactory* bam = ( AnimationFactory* )
+ gamedata->GetFactoryResource( BAMFile,
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (!bam ) {
+ printMessage( "CHUImporter","Cannot Load Button Images, skipping control\n",LIGHT_RED );
+ /* IceWind Dale 2 has fake BAM ResRefs for some Buttons,
+ this will handle bad ResRefs */
+ win->AddControl( btn );
+ break;
+ }
+ /** Cycle is only a byte for buttons */
+ Sprite2D* tspr = bam->GetFrame( UnpressedIndex, (unsigned char) Cycle );
+ btn->SetImage( IE_GUI_BUTTON_UNPRESSED, tspr );
+ tspr = bam->GetFrame( PressedIndex, Cycle );
+ btn->SetImage( IE_GUI_BUTTON_PRESSED, tspr );
+ //ignorebuttonframes is a terrible hack
+ if (core->HasFeature( GF_IGNORE_BUTTON_FRAMES) ) {
+ if (bam->GetCycleSize(Cycle) == 4 )
+ SelectedIndex=2;
+ }
+ tspr = bam->GetFrame( SelectedIndex, (unsigned char) Cycle );
+ btn->SetImage( IE_GUI_BUTTON_SELECTED, tspr );
+ if (core->HasFeature( GF_IGNORE_BUTTON_FRAMES) ) {
+ if (bam->GetCycleSize( (unsigned char) Cycle) == 4 )
+ DisabledIndex=3;
+ }
+ tspr = bam->GetFrame( DisabledIndex, (unsigned char) Cycle );
+ btn->SetImage( IE_GUI_BUTTON_DISABLED, tspr );
+ win->AddControl( btn );
+ }
+ break;
+
+ case IE_GUI_PROGRESSBAR:
+ {
+ //GemRB specific, progressbar
+ ieResRef MOSFile, MOSFile2;
+ ieResRef BAMFile;
+ ieWord KnobXPos, KnobYPos;
+ ieWord CapXPos, CapYPos;
+ ieWord KnobStepsCount;
+ ieWord Cycle;
+
+ str->ReadResRef( MOSFile );
+ str->ReadResRef( MOSFile2 );
+ str->ReadResRef( BAMFile );
+ str->ReadWord( &KnobStepsCount );
+ str->ReadWord( &Cycle );
+ str->ReadWord( &KnobXPos );
+ str->ReadWord( &KnobYPos );
+ str->ReadWord( &CapXPos );
+ str->ReadWord( &CapYPos );
+ Progressbar* pbar = new Progressbar(KnobStepsCount, true );
+ pbar->ControlID = ControlID;
+ pbar->XPos = XPos;
+ pbar->YPos = YPos;
+ pbar->ControlType = ControlType;
+ pbar->Width = Width;
+ pbar->Height = Height;
+ pbar->SetSliderPos( KnobXPos, KnobYPos, CapXPos, CapYPos );
+
+ Sprite2D* img = NULL;
+ Sprite2D* img2 = NULL;
+ if ( MOSFile[0] ) {
+ ResourceHolder<ImageMgr> mos(MOSFile);
+ img = mos->GetSprite2D();
+ }
+ if ( MOSFile2[0] ) {
+ ResourceHolder<ImageMgr> mos(MOSFile2);
+ img2 = mos->GetSprite2D();
+ }
+
+ pbar->SetImage( img, img2 );
+ if( KnobStepsCount ) {
+ /* getting the bam */
+ AnimationFactory *af = (AnimationFactory *)
+ gamedata->GetFactoryResource(BAMFile, IE_BAM_CLASS_ID );
+ /* Getting the Cycle of the bam */
+ pbar->SetAnimation(af->GetCycle( Cycle & 0xff ) );
+ }
+ else {
+ ResourceHolder<ImageMgr> mos(BAMFile);
+ Sprite2D* img3 = mos->GetSprite2D();
+ pbar->SetBarCap( img3 );
+ }
+ win->AddControl( pbar );
+ }
+ break;
+ case IE_GUI_SLIDER:
+ {
+ //Slider
+ ieResRef MOSFile, BAMFile;
+ ieWord Cycle, Knob, GrabbedKnob;
+ ieWord KnobXPos, KnobYPos, KnobStep, KnobStepsCount;
+ str->ReadResRef( MOSFile );
+ str->ReadResRef( BAMFile );
+ str->ReadWord( &Cycle );
+ str->ReadWord( &Knob );
+ str->ReadWord( &GrabbedKnob );
+ str->ReadWord( &KnobXPos );
+ str->ReadWord( &KnobYPos );
+ str->ReadWord( &KnobStep );
+ str->ReadWord( &KnobStepsCount );
+ Slider* sldr = new Slider( KnobXPos, KnobYPos, KnobStep, KnobStepsCount, true );
+ sldr->ControlID = ControlID;
+ sldr->XPos = XPos;
+ sldr->YPos = YPos;
+ sldr->ControlType = ControlType;
+ sldr->Width = Width;
+ sldr->Height = Height;
+ ResourceHolder<ImageMgr> mos(MOSFile);
+ Sprite2D* img = mos->GetSprite2D();
+ sldr->SetImage( IE_GUI_SLIDER_BACKGROUND, img);
+
+ AnimationFactory* bam = ( AnimationFactory* )
+ gamedata->GetFactoryResource( BAMFile,
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if( bam ) {
+ img = bam->GetFrame( Knob, 0 );
+ sldr->SetImage( IE_GUI_SLIDER_KNOB, img );
+ img = bam->GetFrame( GrabbedKnob, 0 );
+ sldr->SetImage( IE_GUI_SLIDER_GRABBEDKNOB, img );
+ }
+ else {
+ sldr->SetState(IE_GUI_SLIDER_BACKGROUND);
+ }
+ win->AddControl( sldr );
+ }
+ break;
+
+ case IE_GUI_EDIT:
+ {
+ //Text Edit
+ ieResRef BGMos;
+ ieResRef FontResRef, CursorResRef;
+ ieWord maxInput;
+ ieWord CurCycle, CurFrame;
+ ieWord PosX, PosY;
+ ieWord Pos2X, Pos2Y;
+ ieVariable Initial;
+
+ str->ReadResRef( BGMos );
+ //These are two more MOS resrefs, probably unused
+ str->Seek( 16, GEM_CURRENT_POS );
+ str->ReadResRef( CursorResRef );
+ str->ReadWord( &CurCycle );
+ str->ReadWord( &CurFrame );
+ str->ReadWord( &PosX );
+ str->ReadWord( &PosY );
+ //FIXME: I still don't know what to do with this point
+ //Contrary to forum posts, it is definitely not a scrollbar ID
+ str->ReadWord( &Pos2X );
+ str->ReadWord( &Pos2Y );
+ str->ReadResRef( FontResRef );
+ //this field is still unknown or unused
+ str->Seek( 2, GEM_CURRENT_POS );
+ //This is really a text field, but apparently the original engine
+ //always writes it over, and never uses it
+ str->Read( Initial, 32 );
+ Initial[32]=0;
+ str->ReadWord( &maxInput );
+ Font* fnt = core->GetFont( FontResRef );
+
+ AnimationFactory* bam = ( AnimationFactory* )
+ gamedata->GetFactoryResource( CursorResRef,
+ IE_BAM_CLASS_ID,
+ IE_NORMAL );
+ Sprite2D *cursor = NULL;
+ if (bam) {
+ cursor = bam->GetFrame( CurCycle, CurFrame );
+ }
+
+ ResourceHolder<ImageMgr> mos(BGMos);
+ Sprite2D *img = NULL;
+ if(mos) {
+ img = mos->GetSprite2D();
+ }
+
+ TextEdit* te = new TextEdit( maxInput, PosX, PosY );
+ te->ControlID = ControlID;
+ te->XPos = XPos;
+ te->YPos = YPos;
+ te->Width = Width;
+ te->Height = Height;
+ te->ControlType = ControlType;
+ te->SetFont( fnt );
+ te->SetCursor( cursor );
+ te->SetBackGround( img );
+ //The original engine always seems to ignore this textfield
+ //te->SetText (Initial );
+ win->AddControl( te );
+ }
+ break;
+
+ case IE_GUI_TEXTAREA:
+ {
+ //Text Area
+ ieResRef FontResRef, InitResRef;
+ Color fore, init, back;
+ ieWord SBID;
+ str->ReadResRef( FontResRef );
+ str->ReadResRef( InitResRef );
+ Font* fnt = core->GetFont( FontResRef );
+ Font* ini = core->GetFont( InitResRef );
+ str->Read( &fore, 4 );
+ str->Read( &init, 4 );
+ str->Read( &back, 4 );
+ str->ReadWord( &SBID );
+ TextArea* ta = new TextArea( fore, init, back );
+ ta->ControlID = ControlID;
+ ta->XPos = XPos;
+ ta->YPos = YPos;
+ ta->Width = Width;
+ ta->Height = Height;
+ ta->ControlType = ControlType;
+ ta->SetFonts( ini, fnt );
+ win->AddControl( ta );
+ if (SBID != 0xffff)
+ win->Link( SBID, ( unsigned short ) ControlID );
+ }
+ break;
+
+ case IE_GUI_LABEL:
+ {
+ //Label
+ ieResRef FontResRef;
+ ieStrRef StrRef;
+ RevColor fore, back;
+ ieWord alignment;
+ str->ReadDword( &StrRef );
+ str->ReadResRef( FontResRef );
+ Font* fnt = core->GetFont( FontResRef );
+ str->Read( &fore, 4 );
+ str->Read( &back, 4 );
+ str->ReadWord( &alignment );
+ Label* lab = new Label( fnt );
+ lab->ControlID = ControlID;
+ lab->XPos = XPos;
+ lab->YPos = YPos;
+ lab->Width = Width;
+ lab->Height = Height;
+ lab->ControlType = ControlType;
+ char* str = core->GetString( StrRef );
+ lab->SetText( str );
+ core->FreeString( str );
+ if (alignment & 1) {
+ lab->useRGB = true;
+ Color f, b;
+ f.r = fore.b;
+ f.g = fore.g;
+ f.b = fore.r;
+ f.a = 0;
+ b.r = back.b;
+ b.g = back.g;
+ b.b = back.r;
+ b.a = 0;
+ lab->SetColor( f, b );
+ }
+ int align = IE_FONT_ALIGN_CENTER;
+ if (( alignment & 0x10 ) != 0) {
+ align = IE_FONT_ALIGN_RIGHT;
+ goto endvertical;
+ }
+ if (( alignment & 0x04 ) != 0) {
+ goto endvertical;
+ }
+ if (( alignment & 0x08 ) != 0) {
+ align = IE_FONT_ALIGN_LEFT;
+ goto endvertical;
+ }
+endvertical:
+ if (( alignment & 0x20 ) != 0) {
+ align |= IE_FONT_ALIGN_TOP;
+ goto endalign;
+ }
+ if (( alignment & 0x80 ) != 0) {
+ align |= IE_FONT_ALIGN_BOTTOM;
+ } else {
+ align |= IE_FONT_ALIGN_MIDDLE;
+ }
+endalign:
+ lab->SetAlignment( align );
+ win->AddControl( lab );
+ }
+ break;
+
+ case IE_GUI_SCROLLBAR:
+ {
+ //ScrollBar
+ ieResRef BAMResRef;
+ ieWord Cycle, Trough, Slider, TAID;
+ ieWord UpUnPressed, UpPressed;
+ ieWord DownUnPressed, DownPressed;
+
+ str->ReadResRef( BAMResRef );
+ str->ReadWord( &Cycle );
+ str->ReadWord( &UpUnPressed );
+ str->ReadWord( &UpPressed );
+ str->ReadWord( &DownUnPressed );
+ str->ReadWord( &DownPressed );
+ str->ReadWord( &Trough );
+ str->ReadWord( &Slider );
+ str->ReadWord( &TAID );
+ ScrollBar* sbar = new ScrollBar();
+ sbar->ControlID = ControlID;
+ sbar->XPos = XPos;
+ sbar->YPos = YPos;
+ sbar->Width = Width;
+ sbar->Height = Height;
+ sbar->ControlType = ControlType;
+
+ AnimationFactory* bam = ( AnimationFactory* )
+ gamedata->GetFactoryResource( BAMResRef,
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (bam) {
+ sbar->SetImage( IE_GUI_SCROLLBAR_UP_UNPRESSED,
+ bam->GetFrame( UpUnPressed, Cycle ) );
+ sbar->SetImage( IE_GUI_SCROLLBAR_UP_PRESSED,
+ bam->GetFrame( UpPressed, Cycle ) );
+ sbar->SetImage( IE_GUI_SCROLLBAR_DOWN_UNPRESSED,
+ bam->GetFrame( DownUnPressed, Cycle ) );
+ sbar->SetImage( IE_GUI_SCROLLBAR_DOWN_PRESSED,
+ bam->GetFrame( DownPressed, Cycle ) );
+ sbar->SetImage( IE_GUI_SCROLLBAR_TROUGH,
+ bam->GetFrame( Trough, Cycle ) );
+ sbar->SetImage( IE_GUI_SCROLLBAR_SLIDER,
+ bam->GetFrame( Slider, Cycle ) );
+ }
+ win->AddControl( sbar );
+ if (TAID != 0xffff)
+ win->Link( ( unsigned short ) ControlID, TAID );
+ }
+ break;
+
+ default:
+ printMessage( "CHUImporter","Control Not Supported\n",LIGHT_RED );
+ }
+ }
+ return win;
+}
+/** Returns the number of available windows */
+unsigned int CHUImporter::GetWindowsCount()
+{
+ return WindowCount;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x23A7F6CA, "CHU File Importer")
+PLUGIN_CLASS(IE_CHU_CLASS_ID, CHUImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/CHUImporter/CHUImporter.h b/gemrb/plugins/CHUImporter/CHUImporter.h
new file mode 100644
index 0000000..512bb73
--- /dev/null
+++ b/gemrb/plugins/CHUImporter/CHUImporter.h
@@ -0,0 +1,48 @@
+/* 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 CHUIMPORTER_H
+#define CHUIMPORTER_H
+
+#include "WindowMgr.h"
+
+#include "System/DataStream.h"
+
+/**CHU File Importer Class
+ *@author GemRB Developement Team
+ */
+
+class CHUImporter : public WindowMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ ieDword WindowCount, CTOffset, WEOffset;
+public:
+ CHUImporter();
+ ~CHUImporter();
+ /** Returns the number of available windows */
+ unsigned int GetWindowsCount();
+ /** Returns the i-th window in the Previously Loaded Stream */
+ Window* GetWindow(unsigned int i);
+ /** This function loads all available windows from the 'stream' parameter. */
+ bool Open(DataStream* stream, bool autoFree = true);
+};
+
+#endif
diff --git a/gemrb/plugins/CHUImporter/CMakeLists.txt b/gemrb/plugins/CHUImporter/CMakeLists.txt
new file mode 100644
index 0000000..c76f9fd
--- /dev/null
+++ b/gemrb/plugins/CHUImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (CHUImporter CHUImporter.cpp)
diff --git a/gemrb/plugins/CHUImporter/Makefile.am b/gemrb/plugins/CHUImporter/Makefile.am
new file mode 100644
index 0000000..09ec5d0
--- /dev/null
+++ b/gemrb/plugins/CHUImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = CHUImporter.la
+CHUImporter_la_LDFLAGS = -module -avoid-version -shared
+CHUImporter_la_SOURCES = CHUImporter.cpp CHUImporter.h
diff --git a/gemrb/plugins/CMakeLists.txt b/gemrb/plugins/CMakeLists.txt
new file mode 100644
index 0000000..6c48216
--- /dev/null
+++ b/gemrb/plugins/CMakeLists.txt
@@ -0,0 +1,45 @@
+ADD_SUBDIRECTORY( 2DAImporter )
+ADD_SUBDIRECTORY( ACMReader )
+ADD_SUBDIRECTORY( AREImporter )
+ADD_SUBDIRECTORY( BAMImporter )
+ADD_SUBDIRECTORY( BIFImporter )
+ADD_SUBDIRECTORY( BIKPlayer )
+ADD_SUBDIRECTORY( BMPImporter )
+ADD_SUBDIRECTORY( BMPWriter )
+ADD_SUBDIRECTORY( CHUImporter )
+ADD_SUBDIRECTORY( CREImporter )
+ADD_SUBDIRECTORY( DLGImporter )
+ADD_SUBDIRECTORY( DirectoryImporter )
+ADD_SUBDIRECTORY( EFFImporter )
+ADD_SUBDIRECTORY( FXOpcodes )
+ADD_SUBDIRECTORY( GAMImporter )
+ADD_SUBDIRECTORY( GUIScript )
+ADD_SUBDIRECTORY( IDSImporter )
+ADD_SUBDIRECTORY( INIImporter )
+ADD_SUBDIRECTORY( ITMImporter )
+ADD_SUBDIRECTORY( IWDOpcodes )
+ADD_SUBDIRECTORY( KEYImporter )
+ADD_SUBDIRECTORY( MOSImporter )
+ADD_SUBDIRECTORY( MUSImporter )
+ADD_SUBDIRECTORY( MVEPlayer )
+ADD_SUBDIRECTORY( NullSound )
+ADD_SUBDIRECTORY( OGGReader )
+ADD_SUBDIRECTORY( OpenALAudio )
+ADD_SUBDIRECTORY( PLTImporter )
+ADD_SUBDIRECTORY( PNGImporter )
+ADD_SUBDIRECTORY( PROImporter )
+ADD_SUBDIRECTORY( PSTOpcodes )
+ADD_SUBDIRECTORY( SDLAudio )
+ADD_SUBDIRECTORY( SDLVideo )
+ADD_SUBDIRECTORY( SPLImporter )
+ADD_SUBDIRECTORY( STOImporter )
+ADD_SUBDIRECTORY( TISImporter )
+ADD_SUBDIRECTORY( TLKImporter )
+ADD_SUBDIRECTORY( WAVReader )
+ADD_SUBDIRECTORY( WEDImporter )
+ADD_SUBDIRECTORY( WMPImporter )
+ADD_SUBDIRECTORY( ZLibManager )
+
+if (STATIC_LINK)
+ SET(plugins ${plugins} PARENT_SCOPE)
+endif (STATIC_LINK)
diff --git a/gemrb/plugins/CREImporter/CMakeLists.txt b/gemrb/plugins/CREImporter/CMakeLists.txt
new file mode 100644
index 0000000..d4c324d
--- /dev/null
+++ b/gemrb/plugins/CREImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (CREImporter CREImporter.cpp)
diff --git a/gemrb/plugins/CREImporter/CREImporter.cpp b/gemrb/plugins/CREImporter/CREImporter.cpp
new file mode 100644
index 0000000..a28e301
--- /dev/null
+++ b/gemrb/plugins/CREImporter/CREImporter.cpp
@@ -0,0 +1,2961 @@
+/* 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 "CREImporter.h"
+
+#include "ie_stats.h"
+#include "win32def.h"
+
+#include "EffectMgr.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "GameScript/GameScript.h"
+
+#include <cassert>
+
+#define MAXCOLOR 12
+typedef unsigned char ColorSet[MAXCOLOR];
+
+static int RandColor=-1;
+static int RandRows=-1;
+static ColorSet* randcolors=NULL;
+static int MagicBit;
+
+static void Initializer()
+{
+ MagicBit = core->HasFeature(GF_MAGICBIT);
+}
+
+//one column, these don't have a level
+static ieResRef* innlist; //IE_IWD2_SPELL_INNATE
+static int inncount=-1;
+static ieResRef* snglist; //IE_IWD2_SPELL_SONG
+static int sngcount=-1;
+static ieResRef* shplist; //IE_IWD2_SPELL_SHAPE
+static int shpcount=-1;
+
+struct LevelAndKit
+{
+ int level;
+ int kit;
+};
+
+class SpellEntry
+{
+public:
+ ~SpellEntry();
+ SpellEntry();
+ const ieResRef *FindSpell(int level, int kit) const;
+ bool Equals(const char *spl) const;
+ void SetSpell(const char *spl);
+ void AddLevel(int level, int kit);
+private:
+ ieResRef spell;
+ LevelAndKit *levels;
+ int count;
+};
+
+SpellEntry::SpellEntry()
+{
+ levels = NULL;
+ spell[0]=0;
+ count = 0;
+}
+
+SpellEntry::~SpellEntry()
+{
+ free(levels);
+ levels = NULL;
+}
+
+const ieResRef *SpellEntry::FindSpell(int level, int kit) const
+{
+ int i = count;
+ while(i--) {
+ if (levels[i].level==level && levels[i].kit==kit) {
+ return &spell;
+ }
+ }
+ return NULL;
+}
+
+bool SpellEntry::Equals(const char *spl) const
+{
+ return !strnicmp(spell, spl, sizeof(ieResRef));
+}
+
+void SpellEntry::SetSpell(const char *spl)
+{
+ strnlwrcpy(spell, spl, 8);
+}
+
+void SpellEntry::AddLevel(int level, int kit)
+{
+ if(!level) {
+ return;
+ }
+
+ level--;
+ for(int i=0;i<count;i++) {
+ if(levels[i].kit==kit && levels[i].level==level) {
+ return;
+ }
+ }
+ levels = (LevelAndKit *) realloc(levels, sizeof(LevelAndKit) * (count+1) );
+ levels[count].kit=kit;
+ levels[count].level=level;
+ count++;
+}
+
+static SpellEntry* spllist=NULL;
+static int splcount=-1;
+static SpellEntry* domlist=NULL;
+static int domcount=-1;
+static SpellEntry* maglist=NULL;
+static int magcount=-1;
+
+int ResolveSpellName(ieResRef name, int level, ieIWD2SpellType type)
+{
+ int i;
+
+ if (level>=MAX_SPELL_LEVEL) {
+ return -1;
+ }
+ switch(type)
+ {
+ case IE_IWD2_SPELL_INNATE:
+ for(i=0;i<inncount;i++) {
+ if(!strnicmp(name, innlist[i], 8) ) {
+ return i;
+ }
+ }
+ break;
+ case IE_IWD2_SPELL_SONG:
+ for(i=0;i<sngcount;i++) {
+ if(!strnicmp(name, snglist[i], 8) ) {
+ return i;
+ }
+ }
+ break;
+ case IE_IWD2_SPELL_SHAPE:
+ for(i=0;i<shpcount;i++) {
+ if(!strnicmp(name, shplist[i], 8) ) {
+ return i;
+ }
+ }
+ break;
+ case IE_IWD2_SPELL_DOMAIN:
+ return -1;
+ default:
+ return -1;
+ }
+ return -1;
+}
+
+//input: index, level, type, kit
+const ieResRef *ResolveSpellIndex(int index, int level, ieIWD2SpellType type, int kit)
+{
+ const ieResRef *ret;
+
+ if (level>=MAX_SPELL_LEVEL) {
+ return NULL;
+ }
+
+ switch(type)
+ {
+ case IE_IWD2_SPELL_INNATE:
+ if (index>=inncount) {
+ return NULL;
+ }
+ return &innlist[index];
+ case IE_IWD2_SPELL_SONG:
+ if (index>=sngcount) {
+ return NULL;
+ }
+ return &snglist[index];
+ case IE_IWD2_SPELL_SHAPE:
+ if (index>=shpcount) {
+ return NULL;
+ }
+ return &shplist[index];
+ case IE_IWD2_SPELL_DOMAIN:
+ if (index>=domcount) {
+ return NULL;
+ }
+ return domlist[index].FindSpell(level, kit);
+ case IE_IWD2_SPELL_WIZARD:
+ if (index>=magcount) {
+ break;
+ }
+ //if it is a specialist spell, return it now
+ ret = maglist[index].FindSpell(level, kit);
+ if ( ret) {
+ return ret;
+ }
+ //fall through
+ default:
+ kit = -1;
+ //comes later
+ break;
+ }
+
+ ret = spllist[index].FindSpell(level, type);
+ if ( !ret || (kit==-1) ) {
+ return ret;
+ }
+
+ int i;
+
+ for(i=0;i<magcount;i++) {
+ if (maglist[i].Equals(*ret)) {
+ return maglist[i].FindSpell(level, kit);
+ }
+ }
+ return NULL;
+}
+
+void ReleaseMemoryCRE()
+{
+ if (randcolors) {
+ delete [] randcolors;
+ randcolors = NULL;
+ }
+ RandColor = -1;
+
+ if (spllist) {
+ delete [] spllist;
+ spllist = NULL;
+ }
+ splcount = -1;
+
+ if (domlist) {
+ delete [] domlist;
+ domlist = NULL;
+ }
+ domcount = -1;
+
+ if (maglist) {
+ delete [] maglist;
+ maglist = NULL;
+ }
+ magcount = -1;
+
+ if (innlist) {
+ free(innlist);
+ innlist = NULL;
+ }
+ inncount = -1;
+
+ if(snglist) {
+ free(snglist);
+ snglist = NULL;
+ }
+ sngcount = -1;
+
+ if(shplist) {
+ free(shplist);
+ shplist = NULL;
+ }
+ shpcount = -1;
+}
+
+static ieResRef *GetSpellTable(const ieResRef tableresref, int &count)
+{
+ count = 0;
+ AutoTable tab(tableresref);
+ if (!tab)
+ return 0;
+
+ int column = tab->GetColumnCount()-1;
+ if (column<0) {
+ return 0;
+ }
+
+ count = tab->GetRowCount();
+ ieResRef *reslist = (ieResRef *) malloc (sizeof(ieResRef) * count);
+ for(int i = 0; i<count;i++) {
+ strnlwrcpy(reslist[i], tab->QueryField(i, column), 8);
+ }
+ return reslist;
+}
+
+static SpellEntry *GetKitSpell(const ieResRef tableresref, int &count)
+{
+ count = 0;
+ AutoTable tab(tableresref);
+ if (!tab)
+ return 0;
+
+ int column = tab->GetColumnCount()-1;
+ if (column<1) {
+ return 0;
+ }
+
+ count = tab->GetRowCount();
+ SpellEntry *reslist = new SpellEntry[count];
+ for(int i = 0;i<count;i++) {
+ reslist[i].SetSpell(tab->QueryField(i,column));
+ for(int col=0;col<column;col++) {
+ reslist[i].AddLevel(atoi(tab->QueryField(i,col)), col);
+ }
+ }
+ return reslist;
+}
+
+static void InitSpellbook()
+{
+ if (splcount!=-1) {
+ return;
+ }
+
+ if (core->HasFeature(GF_HAS_SPELLLIST)) {
+ innlist = GetSpellTable("listinnt", inncount);
+ snglist = GetSpellTable("listsong", sngcount);
+ shplist = GetSpellTable("listshap", shpcount);
+ spllist = GetKitSpell("listspll", splcount);
+ maglist = GetKitSpell("listmage", magcount);
+ domlist = GetKitSpell("listdomn", domcount);
+ }
+}
+
+CREImporter::CREImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+ TotSCEFF = 0xff;
+ CREVersion = 0xff;
+ InitSpellbook();
+}
+
+CREImporter::~CREImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+}
+
+bool CREImporter::Open(DataStream* stream, bool aF)
+{
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ str = stream;
+ autoFree = aF;
+ if (stream == NULL) {
+ return false;
+ }
+ char Signature[8];
+ str->Read( Signature, 8 );
+ IsCharacter = false;
+ if (strncmp( Signature, "CHR ",4) == 0) {
+ IsCharacter = true;
+ //skips chr signature, reads cre signature
+ if (!SeekCreHeader(Signature)) {
+ return false;
+ }
+ } else {
+ CREOffset = 0;
+ }
+ if (strncmp( Signature, "CRE V1.0", 8 ) == 0) {
+ CREVersion = IE_CRE_V1_0;
+ return true;
+ }
+ if (strncmp( Signature, "CRE V1.2", 8 ) == 0) {
+ CREVersion = IE_CRE_V1_2;
+ return true;
+ }
+ if (strncmp( Signature, "CRE V2.2", 8 ) == 0) {
+ CREVersion = IE_CRE_V2_2;
+ return true;
+ }
+ if (strncmp( Signature, "CRE V9.0", 8 ) == 0) {
+ CREVersion = IE_CRE_V9_0;
+ return true;
+ }
+ if (strncmp( Signature, "CRE V0.0", 8 ) == 0) {
+ CREVersion = IE_CRE_GEMRB;
+ return true;
+ }
+
+ printMessage( "CREImporter"," ",LIGHT_RED);
+ printf("Not a CRE File or File Version not supported: %8.8s\n", Signature );
+ return false;
+}
+
+void CREImporter::SetupSlotCounts()
+{
+ switch (CREVersion) {
+ case IE_CRE_V1_2: //pst
+ QWPCount=4;
+ QSPCount=3;
+ QITCount=5;
+ break;
+ case IE_CRE_GEMRB: //own
+ QWPCount=8;
+ QSPCount=9;
+ QITCount=5;
+ break;
+ case IE_CRE_V2_2: //iwd2
+ QWPCount=8;
+ QSPCount=9;
+ QITCount=3;
+ break;
+ default: //others
+ QWPCount=4;
+ QSPCount=3;
+ QITCount=3;
+ break;
+ }
+}
+
+void CREImporter::WriteChrHeader(DataStream *stream, Actor *act)
+{
+ char Signature[8];
+ ieVariable name;
+ ieDword tmpDword, CRESize;
+ ieWord tmpWord;
+ ieByte tmpByte;
+
+ CRESize = GetStoredFileSize (act);
+ switch (CREVersion) {
+ case IE_CRE_V9_0: //iwd/HoW
+ memcpy(Signature, "CHR V1.0",8);
+ tmpDword = 0x64; //headersize
+ TotSCEFF = 1;
+ break;
+ case IE_CRE_V1_0: //bg1
+ memcpy(Signature, "CHR V1.0",8);
+ tmpDword = 0x64; //headersize
+ TotSCEFF = 0;
+ break;
+ case IE_CRE_V1_1: //bg2 (fake)
+ memcpy(Signature, "CHR V2.0",8);
+ tmpDword = 0x64; //headersize
+ TotSCEFF = 1;
+ break;
+ case IE_CRE_V1_2: //pst
+ memcpy(Signature, "CHR V1.2",8);
+ tmpDword = 0x68; //headersize
+ TotSCEFF = 0;
+ break;
+ case IE_CRE_V2_2: //iwd2
+ memcpy(Signature, "CHR V2.2",8);
+ tmpDword = 0x21c; //headersize
+ TotSCEFF = 1;
+ break;
+ case IE_CRE_GEMRB: //own format
+ memcpy(Signature, "CHR V0.0",8);
+ tmpDword = 0x1dc; //headersize (iwd2-9x8+8)
+ TotSCEFF = 1;
+ break;
+ default:
+ printMessage("CREImporter","Unknown CHR version!\n",LIGHT_RED);
+ return;
+ }
+ stream->Write( Signature, 8);
+ memset( Signature,0,sizeof(Signature));
+ memset( name,0,sizeof(name));
+ strncpy( name, act->GetName(0), sizeof(name) );
+ stream->Write( name, 32);
+
+ stream->WriteDword( &tmpDword); //cre offset (chr header size)
+ stream->WriteDword( &CRESize); //cre size
+
+ SetupSlotCounts();
+ int i;
+ for (i=0;i<QWPCount;i++) {
+ tmpWord = act->PCStats->QuickWeaponSlots[i];
+ stream->WriteWord (&tmpWord);
+ }
+ for (i=0;i<QWPCount;i++) {
+ tmpWord = act->PCStats->QuickWeaponHeaders[i];
+ stream->WriteWord (&tmpWord);
+ }
+ for (i=0;i<QSPCount;i++) {
+ stream->WriteResRef (act->PCStats->QuickSpells[i]);
+ }
+ //This is 9 for IWD2 and GemRB
+ if (QSPCount==9) {
+ //NOTE: the gemrb internal format stores
+ //0xff or 0xfe in case of innates and bardsongs
+ //if IWD2 doesn't tolerate this, then this field
+ //should be sanitized for IWD2 (not for GemRB)
+ stream->Write( act->PCStats->QuickSpellClass,9);
+ tmpByte = 0;
+ stream->Write( &tmpByte, 1);
+ }
+ for (i=0;i<QITCount;i++) {
+ tmpWord = act->PCStats->QuickItemSlots[i];
+ stream->WriteWord (&tmpWord);
+ }
+ for (i=0;i<QITCount;i++) {
+ tmpWord = act->PCStats->QuickItemHeaders[i];
+ stream->WriteWord (&tmpWord);
+ }
+ switch (CREVersion) {
+ case IE_CRE_V2_2:
+ //gemrb format doesn't save these redundantly
+ for (i=0;i<QSPCount;i++) {
+ if (act->PCStats->QuickSpellClass[i]==0xff) {
+ stream->WriteResRef (act->PCStats->QuickSpells[i]);
+ } else {
+ //empty field
+ stream->Write( Signature, 8);
+ }
+ }
+ for (i=0;i<QSPCount;i++) {
+ if (act->PCStats->QuickSpellClass[i]==0xfe) {
+ stream->WriteResRef (act->PCStats->QuickSpells[i]);
+ } else {
+ //empty field
+ stream->Write( Signature, 8);
+ }
+ }
+ //fallthrough
+ case IE_CRE_GEMRB:
+ for (i=0;i<QSPCount;i++) {
+ tmpDword = act->PCStats->QSlots[i];
+ stream->WriteDword( &tmpDword);
+ }
+ for (i=0;i<13;i++) {
+ stream->WriteWord (&tmpWord);
+ }
+ stream->Write( act->PCStats->SoundFolder, 32);
+ stream->Write( act->PCStats->SoundSet, 8);
+ for (i=0;i<32;i++) {
+ stream->WriteDword( &tmpDword);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void CREImporter::ReadChrHeader(Actor *act)
+{
+ ieVariable name;
+ char Signature[8];
+ ieDword offset, size;
+ ieWord tmpWord;
+ ieByte tmpByte;
+
+ act->CreateStats();
+ str->Rewind();
+ str->Read (Signature, 8);
+ str->Read (name, 32);
+ name[32]=0;
+ if (name[0]) {
+ act->SetName( name, 0 ); //setting longname
+ }
+ str->ReadDword( &offset);
+ str->ReadDword( &size);
+ SetupSlotCounts();
+ int i;
+ for (i=0;i<QWPCount;i++) {
+ str->ReadWord (&tmpWord);
+ act->PCStats->QuickWeaponSlots[i]=tmpWord;
+ }
+ for (i=0;i<QWPCount;i++) {
+ str->ReadWord (&tmpWord);
+ act->PCStats->QuickWeaponHeaders[i]=tmpWord;
+ }
+ for (i=0;i<QSPCount;i++) {
+ str->ReadResRef (act->PCStats->QuickSpells[i]);
+ }
+ if (QSPCount==9) {
+ str->Read (act->PCStats->QuickSpellClass,9);
+ str->Read (&tmpByte, 1);
+ }
+ for (i=0;i<QITCount;i++) {
+ str->ReadWord (&tmpWord);
+ act->PCStats->QuickItemSlots[i]=tmpWord;
+ }
+ for (i=0;i<QITCount;i++) {
+ str->ReadWord (&tmpWord);
+ act->PCStats->QuickItemHeaders[i]=tmpWord;
+ }
+ //here comes the version specific read
+}
+
+bool CREImporter::SeekCreHeader(char *Signature)
+{
+ str->Seek(32, GEM_CURRENT_POS);
+ str->ReadDword( &CREOffset );
+ str->Seek(CREOffset, GEM_STREAM_START);
+ str->Read( Signature, 8);
+ return true;
+}
+
+CREMemorizedSpell* CREImporter::GetMemorizedSpell()
+{
+ CREMemorizedSpell* spl = new CREMemorizedSpell();
+
+ str->ReadResRef( spl->SpellResRef );
+ str->ReadDword( &spl->Flags );
+
+ return spl;
+}
+
+CREKnownSpell* CREImporter::GetKnownSpell()
+{
+ CREKnownSpell* spl = new CREKnownSpell();
+
+ str->ReadResRef( spl->SpellResRef );
+ str->ReadWord( &spl->Level );
+ str->ReadWord( &spl->Type );
+
+ return spl;
+}
+
+void CREImporter::ReadScript(Actor *act, int ScriptLevel)
+{
+ ieResRef aScript;
+ str->ReadResRef( aScript );
+ act->SetScript( aScript, ScriptLevel, act->InParty!=0);
+}
+
+CRESpellMemorization* CREImporter::GetSpellMemorization(Actor *act)
+{
+ ieWord Level, Type, Number, Number2;
+
+ str->ReadWord( &Level );
+ str->ReadWord( &Number );
+ str->ReadWord( &Number2 );
+ str->ReadWord( &Type );
+ str->ReadDword( &MemorizedIndex );
+ str->ReadDword( &MemorizedCount );
+
+ CRESpellMemorization* spl = act->spellbook.GetSpellMemorization(Type, Level);
+ assert(spl && spl->Number == 0 && spl->Number2 == 0); // unused
+ spl->Number = Number;
+ spl->Number2 = Number;
+
+ return spl;
+}
+
+void CREImporter::SetupColor(ieDword &stat)
+{
+ if (RandColor==-1) {
+ RandColor=0;
+ RandRows=0;
+ AutoTable rndcol("randcolr");
+ if (rndcol) {
+ RandColor = rndcol->GetColumnCount();
+ RandRows = rndcol->GetRowCount();
+ if (RandRows>MAXCOLOR) RandRows=MAXCOLOR;
+ }
+ if (RandRows>1 && RandColor>0) {
+ randcolors = new ColorSet[RandColor];
+ int cols = RandColor;
+ while(cols--)
+ {
+ for (int i=0;i<RandRows;i++) {
+ randcolors[cols][i]=atoi( rndcol->QueryField( i, cols ) );
+ }
+ randcolors[cols][0]-=200;
+ }
+ }
+ else {
+ RandColor=0;
+ }
+ }
+
+ if (stat<200) return;
+ if (RandColor>0) {
+ stat-=200;
+ //assuming an ordered list, so looking in the middle first
+ int i;
+ for (i=(int) stat;i>=0;i--) {
+ if (randcolors[i][0]==stat) {
+ stat=randcolors[i][ rand()%RandRows + 1];
+ return;
+ }
+ }
+ for (i=(int) stat+1;i<RandColor;i++) {
+ if (randcolors[i][0]==stat) {
+ stat=randcolors[i][ rand()%RandRows + 1];
+ return;
+ }
+ }
+ }
+}
+
+void CREImporter::ReadDialog(Actor *act)
+{
+ ieResRef Dialog;
+
+ str->ReadResRef(Dialog);
+ //Hacking NONE to no error
+ if (strnicmp(Dialog,"NONE",8) == 0) {
+ Dialog[0]=0;
+ }
+ act->SetDialog(Dialog);
+}
+
+Actor* CREImporter::GetActor(unsigned char is_in_party)
+{
+ if (!str)
+ return NULL;
+ Actor* act = new Actor();
+ if (!act)
+ return NULL;
+ act->InParty = is_in_party;
+ str->ReadDword( &act->LongStrRef );
+ //Beetle name in IWD needs the allow zero flag
+ char* poi = core->GetString( act->LongStrRef, IE_STR_ALLOW_ZERO );
+ act->SetName( poi, 1 ); //setting longname
+ free( poi );
+ str->ReadDword( &act->ShortStrRef );
+ poi = core->GetString( act->ShortStrRef );
+ act->SetName( poi, 2 ); //setting shortname (for tooltips)
+ free( poi );
+ act->BaseStats[IE_VISUALRANGE] = 30; //this is just a hack
+ act->BaseStats[IE_DIALOGRANGE] = 15; //this is just a hack
+ str->ReadDword( &act->BaseStats[IE_MC_FLAGS] );
+ str->ReadDword( &act->BaseStats[IE_XPVALUE] );
+ str->ReadDword( &act->BaseStats[IE_XP] );
+ str->ReadDword( &act->BaseStats[IE_GOLD] );
+ str->ReadDword( &act->BaseStats[IE_STATE_ID] );
+ ieWord tmp;
+ ieWordSigned tmps;
+ str->ReadWordSigned( &tmps );
+ act->BaseStats[IE_HITPOINTS]=(ieDwordSigned)tmps;
+ str->ReadWord( &tmp );
+ act->BaseStats[IE_MAXHITPOINTS]=tmp;
+ str->ReadDword( &act->BaseStats[IE_ANIMATION_ID] );//animID is a dword
+ ieByte tmp2[7];
+ str->Read( tmp2, 7);
+ for (int i=0;i<7;i++) {
+ ieDword t = tmp2[i];
+ // apply RANDCOLR.2DA transformation
+ SetupColor(t);
+ t |= t << 8;
+ t |= t << 16;
+ act->BaseStats[IE_COLORS+i]=t;
+ }
+
+ str->Read( &TotSCEFF, 1 );
+ if (CREVersion==IE_CRE_V1_0 && TotSCEFF) {
+ CREVersion = IE_CRE_V1_1;
+ }
+ // saving in original version requires the original version
+ // otherwise it is set to 0 at construction time
+ if (core->SaveAsOriginal) {
+ act->version = CREVersion;
+ }
+ str->ReadResRef( act->SmallPortrait );
+ if (act->SmallPortrait[0]==0) {
+ memcpy(act->SmallPortrait, "NONE\0\0\0\0", 8);
+ }
+ str->ReadResRef( act->LargePortrait );
+ if (act->LargePortrait[0]==0) {
+ memcpy(act->LargePortrait, "NONE\0\0\0\0", 8);
+ }
+
+ unsigned int Inventory_Size;
+
+ switch(CREVersion) {
+ case IE_CRE_GEMRB:
+ Inventory_Size = GetActorGemRB(act);
+ break;
+ case IE_CRE_V1_2:
+ Inventory_Size=46;
+ GetActorPST(act);
+ break;
+ case IE_CRE_V1_1: //bg2 (fake version)
+ case IE_CRE_V1_0: //bg1 too
+ Inventory_Size=38;
+ GetActorBG(act);
+ break;
+ case IE_CRE_V2_2:
+ Inventory_Size=50;
+ GetActorIWD2(act);
+ break;
+ case IE_CRE_V9_0:
+ Inventory_Size=38;
+ GetActorIWD1(act);
+ break;
+ default:
+ Inventory_Size=0;
+ printMessage("CREImporter","Unknown creature signature: ", RED);
+ printf("%d\n", CREVersion);
+ abort();
+ }
+
+ // Read saved effects
+ if (core->IsAvailable(IE_EFF_CLASS_ID) ) {
+ ReadEffects( act );
+ } else {
+ printMessage("CREImporter", "Effect importer is unavailable!\n", RED);
+ }
+ // Reading inventory, spellbook, etc
+ ReadInventory( act, Inventory_Size );
+
+ if (IsCharacter) {
+ ReadChrHeader(act);
+ }
+
+ act->InitStatsOnLoad();
+
+ return act;
+}
+
+void CREImporter::GetActorPST(Actor *act)
+{
+ int i;
+ ieByte tmpByte;
+ ieWord tmpWord;
+
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_REPUTATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
+ str->ReadWord( &tmpWord );
+ //skipping a word
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ARMORCLASS]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TOHIT]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ tmpByte = tmpByte * 2;
+ if (tmpByte>10) tmpByte-=11;
+ act->BaseStats[IE_NUMBEROFATTACKS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSBREATH]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSSPELL]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
+ //this is used for unused prof points count
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_FREESLOTS]=tmpByte; //using another field than usually
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SETTRAPS]=tmpByte; //this is unused in pst
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LORE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LOCKPICKING]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_STEALTH]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TRAPS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_PICKPOCKET]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_FATIGUE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_INTOXICATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LUCK]=tmpByte;
+ for (i=0;i<21;i++) {
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_PROFICIENCYBASTARDSWORD+i]=tmpByte;
+ }
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TRACKING]=tmpByte;
+ str->Seek( 32, GEM_CURRENT_POS );
+ for (i=0;i<100;i++) {
+ str->ReadDword( &act->StrRefs[i] );
+ }
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LEVEL]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LEVEL2]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LEVEL3]=tmpByte;
+ //this is rumoured to be IE_SEX, but we use the gender field for this
+ str->Read( &tmpByte, 1 );
+ //skipping a byte
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_STR]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_STREXTRA]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_INT]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_WIS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_DEX]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_CON]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_CHR]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MORALE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MORALEBREAK]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_HATEDRACE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MORALERECOVERYTIME]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ //skipping a byte
+ str->ReadDword( &act->BaseStats[IE_KIT] );
+ ReadScript(act, SCR_OVERRIDE);
+ ReadScript(act, SCR_CLASS);
+ ReadScript(act, SCR_RACE);
+ ReadScript(act, SCR_GENERAL);
+ ReadScript(act, SCR_DEFAULT);
+
+ str->Seek( 36, GEM_CURRENT_POS );
+ //the overlays are not fully decoded yet
+ //they are a kind of effect block (like our vvclist)
+ str->ReadDword( &OverlayOffset );
+ str->ReadDword( &OverlayMemorySize );
+ str->ReadDword( &act->BaseStats[IE_XP_MAGE] ); // Exp for secondary class
+ str->ReadDword( &act->BaseStats[IE_XP_THIEF] ); // Exp for tertiary class
+ for (i = 0; i<10; i++) {
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_INTERNAL_0+i]=tmpWord;
+ }
+ //good, law, lady, murder
+ for (i=0;i<4;i++) {
+ str->Read( &tmpByte, 1);
+ act->DeathCounters[i]=(ieByteSigned) tmpByte;
+ }
+ ieVariable KillVar; //use this as needed
+ str->Read(KillVar,32);
+ KillVar[32]=0;
+ str->Seek( 3, GEM_CURRENT_POS ); // dialog radius, feet circle size???
+
+ str->Read( &tmpByte, 1 );
+
+ //str->ReadWord( &act->AppearanceFlags1 );
+ //str->ReadWord( &act->AppearanceFlags2 );
+ str->ReadDword( &act->AppearanceFlags );
+
+ for (i = 0; i < 7; i++) {
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_COLORS+i] = tmpWord;
+ }
+ act->BaseStats[IE_COLORCOUNT] = tmpByte; //hack
+
+ str->Seek(31, GEM_CURRENT_POS);
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SPECIES]=tmpByte; // offset: 0x311
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TEAM]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_FACTION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_EA]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_GENERAL]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RACE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_CLASS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SPECIFIC]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SEX]=tmpByte;
+ str->Seek( 5, GEM_CURRENT_POS );
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_ALIGNMENT]=tmpByte;
+ str->Seek( 4, GEM_CURRENT_POS );
+ ieVariable scriptname;
+ str->Read( scriptname, 32);
+ scriptname[32]=0;
+ act->SetScriptName(scriptname);
+ strnspccpy(act->KillVar, KillVar, 32);
+ memset(act->IncKillVar, 0, 32);
+
+ str->ReadDword( &KnownSpellsOffset );
+ str->ReadDword( &KnownSpellsCount );
+ str->ReadDword( &SpellMemorizationOffset );
+ str->ReadDword( &SpellMemorizationCount );
+ str->ReadDword( &MemorizedSpellsOffset );
+ str->ReadDword( &MemorizedSpellsCount );
+
+ str->ReadDword( &ItemSlotsOffset );
+ str->ReadDword( &ItemsOffset );
+ str->ReadDword( &ItemsCount );
+ str->ReadDword( &EffectsOffset );
+ str->ReadDword( &EffectsCount ); //also variables
+
+ ReadDialog(act);
+}
+
+void CREImporter::ReadInventory(Actor *act, unsigned int Inventory_Size)
+{
+ ieWord *indices = (ieWord *) calloc(Inventory_Size, sizeof(ieWord));
+ //CREItem** items;
+ unsigned int i,j,k;
+
+ act->inventory.SetSlotCount(Inventory_Size+1);
+ str->Seek( ItemSlotsOffset+CREOffset, GEM_STREAM_START );
+
+ //first read the indices
+ for (i = 0;i<Inventory_Size; i++) {
+ str->ReadWord(indices+i);
+ }
+ //this word contains the equipping info (which slot is selected)
+ // 0,1,2,3 - weapon slots
+ // 1000 - fist
+ // -24,-23,-22,-21 - quiver
+ //the equipping effects are delayed until the actor gets an area
+ str->ReadWordSigned( &act->Equipped );
+ //the equipped slot's selected ability is stored here
+ str->ReadWord( &act->EquippedHeader );
+
+ //read the item entries based on the previously read indices
+ //an item entry may be read multiple times if the indices are repeating
+ for (i = 0;i<Inventory_Size;) {
+ //the index was intentionally increased here, the fist slot isn't saved
+ ieWord index = indices[i++];
+ if (index != 0xffff) {
+ if (index>=ItemsCount) {
+ printMessage("CREImporter"," ",LIGHT_RED);
+ printf("Invalid item index (%d) in creature!\n", index);
+ continue;
+ }
+ //20 is the size of CREItem on disc (8+2+3x2+4)
+ str->Seek( ItemsOffset+index*20 + CREOffset, GEM_STREAM_START );
+ //the core allocates this item data
+ CREItem *item = core->ReadItem(str);
+ int Slot = core->QuerySlot(i);
+ if (item) {
+ act->inventory.SetSlotItem(item, Slot);
+ } else {
+ printMessage("CREImporter"," ",LIGHT_RED);
+ printf("Invalid item index (%d) in creature!\n", index);
+ }
+ }
+ }
+
+ free (indices);
+
+ // Reading spellbook
+ CREKnownSpell **known_spells=(CREKnownSpell **) calloc(KnownSpellsCount, sizeof(CREKnownSpell *) );
+ CREMemorizedSpell **memorized_spells=(CREMemorizedSpell **) calloc(MemorizedSpellsCount, sizeof(CREKnownSpell *) );
+
+ str->Seek( KnownSpellsOffset+CREOffset, GEM_STREAM_START );
+ for (i = 0; i < KnownSpellsCount; i++) {
+ known_spells[i]=GetKnownSpell();
+ }
+
+ str->Seek( MemorizedSpellsOffset+CREOffset, GEM_STREAM_START );
+ for (i = 0; i < MemorizedSpellsCount; i++) {
+ memorized_spells[i]=GetMemorizedSpell();
+ }
+
+ str->Seek( SpellMemorizationOffset+CREOffset, GEM_STREAM_START );
+ for (i = 0; i < SpellMemorizationCount; i++) {
+ CRESpellMemorization* sm = GetSpellMemorization(act);
+
+ j=KnownSpellsCount;
+ while(j--) {
+ CREKnownSpell* spl = known_spells[j];
+ if (!spl) {
+ continue;
+ }
+ if ((spl->Type == sm->Type) && (spl->Level == sm->Level)) {
+ sm->known_spells.push_back( spl );
+ known_spells[j] = NULL;
+ continue;
+ }
+ }
+ for (j = 0; j < MemorizedCount; j++) {
+ k = MemorizedIndex+j;
+ assert(k < MemorizedSpellsCount);
+ if (memorized_spells[k]) {
+ sm->memorized_spells.push_back( memorized_spells[k]);
+ memorized_spells[k] = NULL;
+ continue;
+ }
+ printf("[CREImporter]: Duplicate memorized spell (%d) in creature!\n", k);
+ }
+ }
+
+ i=KnownSpellsCount;
+ while(i--) {
+ if (known_spells[i]) {
+ printMessage("CREImporter"," ", YELLOW);
+ printf("Dangling spell in creature: %s!\n", known_spells[i]->SpellResRef);
+ delete known_spells[i];
+ }
+ }
+ free(known_spells);
+
+ i=MemorizedSpellsCount;
+ while(i--) {
+ if (memorized_spells[i]) {
+ printMessage("CREImporter"," ", YELLOW);
+ printf("Dangling spell in creature: %s!\n", memorized_spells[i]->SpellResRef);
+ delete memorized_spells[i];
+ }
+ }
+ free(memorized_spells);
+}
+
+void CREImporter::ReadEffects(Actor *act)
+{
+ unsigned int i;
+
+ str->Seek( EffectsOffset+CREOffset, GEM_STREAM_START );
+
+ for (i = 0; i < EffectsCount; i++) {
+ Effect fx;
+ GetEffect( &fx );
+ // NOTE: AddEffect() allocates a new effect
+ act->fxqueue.AddEffect( &fx ); // FIXME: don't reroll dice, time, etc!!
+ }
+}
+
+void CREImporter::GetEffect(Effect *fx)
+{
+ PluginHolder<EffectMgr> eM(IE_EFF_CLASS_ID);
+
+ eM->Open( str, false );
+ if (TotSCEFF) {
+ eM->GetEffectV20( fx );
+ } else {
+ eM->GetEffectV1( fx );
+ }
+}
+
+ieDword CREImporter::GetActorGemRB(Actor *act)
+{
+ ieByte tmpByte;
+ ieWord tmpWord;
+
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_REPUTATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
+ //skipping a word( useful for something)
+ str->ReadWord( &tmpWord );
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ARMORCLASS]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TOHIT]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_NUMBEROFATTACKS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSBREATH]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSSPELL]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_DETECTILLUSIONS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SETTRAPS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LORE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LOCKPICKING]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_STEALTH]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TRAPS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_PICKPOCKET]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_FATIGUE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_INTOXICATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LUCK]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ //these could be used to save iwd2 skills
+ //TODO: gemrb format
+ act->BaseStats[IE_TRACKING]=tmpByte;
+ for (int i=0;i<100;i++) {
+ str->ReadDword( &act->StrRefs[i] );
+ }
+ return 0;
+}
+
+void CREImporter::GetActorBG(Actor *act)
+{
+ int i;
+ ieByte tmpByte;
+ ieWord tmpWord;
+
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_REPUTATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
+ str->ReadWord( &tmpWord );
+ //skipping a word
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ARMORCLASS]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TOHIT]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ tmpWord = tmpByte * 2;
+ if (tmpWord>10) tmpWord-=11;
+ act->BaseStats[IE_NUMBEROFATTACKS]=(ieByte) tmpWord;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSBREATH]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSSPELL]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_DETECTILLUSIONS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SETTRAPS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LORE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LOCKPICKING]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_STEALTH]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TRAPS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_PICKPOCKET]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_FATIGUE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_INTOXICATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LUCK]=tmpByte;
+ for (i=0;i<21;i++) {
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_PROFICIENCYBASTARDSWORD+i]=tmpByte;
+ }
+
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TRACKING]=tmpByte;
+ str->Seek( 32, GEM_CURRENT_POS );
+ for (i=0;i<100;i++) {
+ str->ReadDword( &act->StrRefs[i] );
+ }
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LEVEL]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LEVEL2]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LEVEL3]=tmpByte;
+ //this is rumoured to be IE_SEX, but we use the gender field for this
+ str->Read( &tmpByte, 1);
+ //skipping a byte
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_STR]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_STREXTRA]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_INT]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_WIS]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_DEX]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_CON]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_CHR]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_MORALE]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_MORALEBREAK]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_HATEDRACE]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_MORALERECOVERYTIME]=tmpByte;
+ str->Read( &tmpByte, 1);
+ //skipping a byte
+ str->ReadDword( &act->BaseStats[IE_KIT] );
+ act->BaseStats[IE_KIT] = ((act->BaseStats[IE_KIT] & 0xffff) << 16) +
+ ((act->BaseStats[IE_KIT] & 0xffff0000) >> 16);
+ ReadScript(act, SCR_OVERRIDE);
+ ReadScript(act, SCR_CLASS);
+ ReadScript(act, SCR_RACE);
+ ReadScript(act, SCR_GENERAL);
+ ReadScript(act, SCR_DEFAULT);
+
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_EA]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_GENERAL]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_RACE]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_CLASS]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_SPECIFIC]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_SEX]=tmpByte;
+ str->Seek( 5, GEM_CURRENT_POS );
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_ALIGNMENT]=tmpByte;
+ str->Seek( 4, GEM_CURRENT_POS );
+ ieVariable scriptname;
+ str->Read( scriptname, 32);
+ scriptname[32]=0;
+ act->SetScriptName(scriptname);
+ memset(act->KillVar, 0, 32);
+ memset(act->IncKillVar, 0, 32);
+
+ str->ReadDword( &KnownSpellsOffset );
+ str->ReadDword( &KnownSpellsCount );
+ str->ReadDword( &SpellMemorizationOffset );
+ str->ReadDword( &SpellMemorizationCount );
+ str->ReadDword( &MemorizedSpellsOffset );
+ str->ReadDword( &MemorizedSpellsCount );
+
+ str->ReadDword( &ItemSlotsOffset );
+ str->ReadDword( &ItemsOffset );
+ str->ReadDword( &ItemsCount );
+ str->ReadDword( &EffectsOffset );
+ str->ReadDword( &EffectsCount );
+
+ ReadDialog(act);
+}
+
+void CREImporter::GetIWD2Spellpage(Actor *act, ieIWD2SpellType type, int level, int count)
+{
+ ieDword spellindex;
+ ieDword totalcount;
+ ieDword memocount;
+ ieDword tmpDword;
+
+ int check = 0;
+ CRESpellMemorization* sm = act->spellbook.GetSpellMemorization(type, level);
+ assert(sm && sm->Number == 0 && sm->Number2 == 0); // unused
+ while(count--) {
+ str->ReadDword(&spellindex);
+ str->ReadDword(&totalcount);
+ str->ReadDword(&memocount);
+ str->ReadDword(&tmpDword);
+ check+=totalcount;
+ const ieResRef *tmp = ResolveSpellIndex(spellindex, level, type, act->BaseStats[IE_KIT]);
+ if(tmp) {
+ CREKnownSpell *known = new CREKnownSpell;
+ known->Level=level;
+ known->Type=type;
+ strnlwrcpy(known->SpellResRef,*tmp,8);
+ sm->known_spells.push_back(known);
+ while(memocount--) {
+ if(totalcount) {
+ totalcount--;
+ } else {
+ printMessage("CREImporter", "More spells still known than memorised.\n", LIGHT_RED);
+ break;
+ }
+ CREMemorizedSpell *memory = new CREMemorizedSpell;
+ memory->Flags=1;
+ strnlwrcpy(memory->SpellResRef,*tmp,8);
+ sm->memorized_spells.push_back(memory);
+ }
+
+ while(totalcount--) {
+ CREMemorizedSpell *memory = new CREMemorizedSpell;
+ memory->Flags=0;
+ strnlwrcpy(memory->SpellResRef,*tmp,8);
+ sm->memorized_spells.push_back(memory);
+ }
+ } else {
+ printMessage("CREImporter","Unresolved spell index: ", LIGHT_RED);
+ printf("%d level:%d, type: %d\n", spellindex, level+1, type);
+ }
+ }
+ str->ReadDword(&tmpDword);
+ sm->Number = (ieWord) tmpDword;
+ str->ReadDword(&tmpDword);
+ sm->Number2 = (ieWord) tmpDword;
+}
+
+void CREImporter::GetActorIWD2(Actor *act)
+{
+ int i;
+ ieByte tmpByte;
+ ieWord tmpWord;
+
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_REPUTATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ARMORCLASS]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TOHIT]=(ieByteSigned) tmpByte;//Unknown in CRE V2.2
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_NUMBEROFATTACKS]=tmpByte;//Unknown in CRE V2.2
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;//Fortitude Save in V2.2
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;//Reflex Save in V2.2
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;// will Save in V2.2
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MAGICDAMAGERESISTANCE]=(ieByteSigned) tmpByte;
+ str->Seek( 4, GEM_CURRENT_POS );
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_FATIGUE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_INTOXICATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LUCK]=tmpByte;
+ str->Seek( 34, GEM_CURRENT_POS ); //unknowns
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_CLASSLEVELSUM]=tmpByte; //total levels
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELBARBARIAN]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELBARD]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELCLERIC]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELDRUID]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELFIGHTER]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELMONK]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELPALADIN]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELRANGER]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELTHIEF]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELSORCEROR]=tmpByte;
+ str->Read( & tmpByte, 1 );
+ act->BaseStats[IE_LEVELMAGE]=tmpByte;
+ str->Seek( 22, GEM_CURRENT_POS ); //levels for classes
+ for (i=0;i<64;i++) {
+ str->ReadDword( &act->StrRefs[i] );
+ }
+ ReadScript( act, SCR_AREA);
+ ReadScript( act, SCR_RESERVED);
+ str->Seek( 4, GEM_CURRENT_POS );
+ str->ReadDword( &act->BaseStats[IE_FEATS1]);
+ str->ReadDword( &act->BaseStats[IE_FEATS2]);
+ str->ReadDword( &act->BaseStats[IE_FEATS3]);
+ str->Seek( 12, GEM_CURRENT_POS );
+ //proficiencies
+ for (i=0;i<26;i++) {
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_PROFICIENCYBASTARDSWORD+i]=tmpByte;
+ }
+ //skills
+ str->Seek( 38, GEM_CURRENT_POS );
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_ALCHEMY]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_ANIMALS]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_BLUFF]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_CONCENTRATION]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_DIPLOMACY]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_TRAPS]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_INTIMIDATE]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_LORE]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_STEALTH]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_LOCKPICKING]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_PICKPOCKET]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_SEARCH]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_SPELLCRAFT]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_MAGICDEVICE]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_TRACKING]=tmpByte;
+ str->Seek( 50, GEM_CURRENT_POS );
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_CR]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_HATEDRACE]=tmpByte;
+ //we got 7 more hated races
+ for (i=0;i<7;i++) {
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_HATEDRACE2+i]=tmpByte;
+ }
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SUBRACE]=tmpByte;
+ str->ReadWord( &tmpWord );
+ //skipping 2 bytes, one is SEX (could use it for sounds)
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_STR]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_INT]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_WIS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_DEX]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_CON]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_CHR]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MORALE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MORALEBREAK]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ //HatedRace is a list of races, so this is skipped here
+ //act->BaseStats[IE_HATEDRACE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MORALERECOVERYTIME]=tmpByte;
+ //No KIT word order magic for IWD2
+ str->ReadDword( &act->BaseStats[IE_KIT] );
+ ReadScript(act, SCR_OVERRIDE);
+ ReadScript(act, SCR_CLASS);
+ ReadScript(act, SCR_RACE);
+ ReadScript(act, SCR_GENERAL);
+ ReadScript(act, SCR_DEFAULT);
+ //new scripting flags, one on each byte
+ str->Read( &tmpByte, 1); //hidden
+ if (tmpByte) {
+ act->BaseStats[IE_AVATARREMOVAL]=tmpByte;
+ }
+ str->Read( &act->SetDeathVar, 1); //set death variable
+ str->Read( &act->IncKillCount, 1); //increase kill count
+ str->Read( &act->UnknownField, 1);
+ for (i = 0; i<5; i++) {
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_INTERNAL_0+i]=tmpWord;
+ }
+ ieVariable KillVar;
+ str->Read(KillVar,32);
+ KillVar[32]=0;
+ strnspccpy(act->KillVar, KillVar, 32);
+ str->Read(KillVar,32);
+ KillVar[32]=0;
+ strnspccpy(act->IncKillVar, KillVar, 32);
+ str->Seek( 2, GEM_CURRENT_POS);
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_SAVEDXPOS] = tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_SAVEDYPOS] = tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_SAVEDFACE] = tmpWord;
+ str->Seek( 146, GEM_CURRENT_POS );
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_EA]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_GENERAL]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_RACE]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_CLASS]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_SPECIFIC]=tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_SEX]=tmpByte;
+ str->Seek( 5, GEM_CURRENT_POS );
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_ALIGNMENT]=tmpByte;
+ str->Seek( 4, GEM_CURRENT_POS );
+ ieVariable scriptname;
+ str->Read( scriptname, 32);
+ scriptname[32]=0;
+ act->SetScriptName(scriptname);
+
+ KnownSpellsOffset = 0;
+ KnownSpellsCount = 0;
+ SpellMemorizationOffset = 0;
+ SpellMemorizationCount = 0;
+ MemorizedSpellsOffset = 0;
+ MemorizedSpellsCount = 0;
+ //6 bytes unknown, 600 bytes spellbook offsets
+ //skipping spellbook offsets
+ ieWord tmp1, tmp2, tmp3;
+ str->ReadWord( &tmp1);
+ str->ReadWord( &tmp2);
+ str->ReadWord( &tmp3);
+ ieDword ClassSpellOffsets[8*9];
+
+ //spellbook spells
+ for (i=0;i<7*9;i++) {
+ str->ReadDword(ClassSpellOffsets+i);
+ }
+ ieDword ClassSpellCounts[8*9];
+ for (i=0;i<7*9;i++) {
+ str->ReadDword(ClassSpellCounts+i);
+ }
+
+ //domain spells
+ for (i=7*9;i<8*9;i++) {
+ str->ReadDword(ClassSpellOffsets+i);
+ }
+ for (i=7*9;i<8*9;i++) {
+ str->ReadDword(ClassSpellCounts+i);
+ }
+
+ ieDword InnateOffset, InnateCount;
+ ieDword SongOffset, SongCount;
+ ieDword ShapeOffset, ShapeCount;
+ str->ReadDword( &InnateOffset );
+ str->ReadDword( &InnateCount );
+ str->ReadDword( &SongOffset );
+ str->ReadDword( &SongCount );
+ str->ReadDword( &ShapeOffset );
+ str->ReadDword( &ShapeCount );
+ //str->Seek( 606, GEM_CURRENT_POS);
+
+ str->ReadDword( &ItemSlotsOffset );
+ str->ReadDword( &ItemsOffset );
+ str->ReadDword( &ItemsCount );
+ str->ReadDword( &EffectsOffset );
+ str->ReadDword( &EffectsCount );
+
+ ReadDialog(act);
+
+ for(i=0;i<8;i++) {
+ for(int lev=0;lev<9;lev++) {
+ //if everything is alright, then this seek is not needed
+ str->Seek(CREOffset+ClassSpellOffsets[i*9+lev], GEM_STREAM_START);
+ GetIWD2Spellpage(act, (ieIWD2SpellType) i, lev, ClassSpellCounts[i*9+lev]);
+ }
+ }
+ str->Seek(CREOffset+InnateOffset, GEM_STREAM_START);
+ GetIWD2Spellpage(act, IE_IWD2_SPELL_INNATE, 0, InnateCount);
+
+ str->Seek(CREOffset+SongOffset, GEM_STREAM_START);
+ GetIWD2Spellpage(act, IE_IWD2_SPELL_SONG, 0, SongCount);
+
+ str->Seek(CREOffset+ShapeOffset, GEM_STREAM_START);
+ GetIWD2Spellpage(act, IE_IWD2_SPELL_SHAPE, 0, ShapeCount);
+}
+
+void CREImporter::GetActorIWD1(Actor *act) //9.0
+{
+ int i;
+ ieByte tmpByte;
+ ieWord tmpWord;
+
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_REPUTATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_HIDEINSHADOWS]=tmpByte;
+ str->ReadWord( &tmpWord );
+ //skipping a word
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ARMORCLASS]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACCRUSHINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACMISSILEMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACPIERCINGMOD]=(ieWordSigned) tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_ACSLASHINGMOD]=(ieWordSigned) tmpWord;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TOHIT]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ tmpByte = tmpByte * 2;
+ if (tmpByte>10) tmpByte-=11;
+ act->BaseStats[IE_NUMBEROFATTACKS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSDEATH]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSWANDS]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSPOLY]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSBREATH]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SAVEVSSPELL]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTELECTRICITY]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTACID]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGIC]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICFIRE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMAGICCOLD]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTSLASHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTCRUSHING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTPIERCING]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_RESISTMISSILE]=(ieByteSigned) tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_DETECTILLUSIONS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_SETTRAPS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LORE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LOCKPICKING]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_STEALTH]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TRAPS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_PICKPOCKET]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_FATIGUE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_INTOXICATION]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LUCK]=tmpByte;
+ for (i=0;i<21;i++) {
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_PROFICIENCYBASTARDSWORD+i]=tmpByte;
+ }
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_TRACKING]=tmpByte;
+ str->Seek( 32, GEM_CURRENT_POS );
+ for (i=0;i<100;i++) {
+ str->ReadDword( &act->StrRefs[i] );
+ }
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LEVEL]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LEVEL2]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_LEVEL3]=tmpByte;
+ //this is rumoured to be IE_SEX, but we use the gender field for this
+ str->Read( &tmpByte, 1 );
+ //skipping a byte
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_STR]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_STREXTRA]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_INT]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_WIS]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_DEX]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_CON]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_CHR]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MORALE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MORALEBREAK]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_HATEDRACE]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ act->BaseStats[IE_MORALERECOVERYTIME]=tmpByte;
+ str->Read( &tmpByte, 1 );
+ //skipping a byte
+ str->ReadDword( &act->BaseStats[IE_KIT] );
+ act->BaseStats[IE_KIT] = ((act->BaseStats[IE_KIT] & 0xffff) << 16) +
+ ((act->BaseStats[IE_KIT] & 0xffff0000) >> 16);
+ ReadScript(act, SCR_OVERRIDE);
+ ReadScript(act, SCR_CLASS);
+ ReadScript(act, SCR_RACE);
+ ReadScript(act, SCR_GENERAL);
+ ReadScript(act, SCR_DEFAULT);
+ //new scripting flags, one on each byte
+ str->Read( &tmpByte, 1); //hidden
+ if (tmpByte) {
+ act->BaseStats[IE_AVATARREMOVAL]=tmpByte;
+ }
+ str->Read( &act->SetDeathVar, 1); //set death variable
+ str->Read( &act->IncKillCount, 1); //increase kill count
+ str->Read( &act->UnknownField, 1);
+ for (i = 0; i<5; i++) {
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_INTERNAL_0+i]=tmpWord;
+ }
+ ieVariable KillVar;
+ str->Read(KillVar,32); //use these as needed
+ KillVar[32]=0;
+ strnspccpy(act->KillVar, KillVar, 32);
+ str->Read(KillVar,32);
+ KillVar[32]=0;
+ strnspccpy(act->IncKillVar, KillVar, 32);
+ str->Seek( 2, GEM_CURRENT_POS);
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_SAVEDXPOS] = tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_SAVEDYPOS] = tmpWord;
+ str->ReadWord( &tmpWord );
+ act->BaseStats[IE_SAVEDFACE] = tmpWord;
+ str->Seek( 18, GEM_CURRENT_POS );
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_EA] = tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_GENERAL] = tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_RACE] = tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_CLASS] = tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_SPECIFIC] = tmpByte;
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_SEX] = tmpByte;
+ str->Seek( 5, GEM_CURRENT_POS );
+ str->Read( &tmpByte, 1);
+ act->BaseStats[IE_ALIGNMENT]=tmpByte;
+ str->Seek( 4, GEM_CURRENT_POS );
+ ieVariable scriptname;
+ str->Read( scriptname, 32);
+ scriptname[32]=0;
+ act->SetScriptName(scriptname);
+
+ str->ReadDword( &KnownSpellsOffset );
+ str->ReadDword( &KnownSpellsCount );
+ str->ReadDword( &SpellMemorizationOffset );
+ str->ReadDword( &SpellMemorizationCount );
+ str->ReadDword( &MemorizedSpellsOffset );
+ str->ReadDword( &MemorizedSpellsCount );
+
+ str->ReadDword( &ItemSlotsOffset );
+ str->ReadDword( &ItemsOffset );
+ str->ReadDword( &ItemsCount );
+ str->ReadDword( &EffectsOffset );
+ str->ReadDword( &EffectsCount );
+
+ ReadDialog(act);
+}
+
+int CREImporter::GetStoredFileSize(Actor *actor)
+{
+ int headersize;
+ unsigned int Inventory_Size;
+ unsigned int i;
+
+ CREVersion = actor->version;
+ switch (CREVersion) {
+ case IE_CRE_GEMRB:
+ headersize = 0x2d4;
+ //minus fist
+ Inventory_Size=actor->inventory.GetSlotCount()-1;
+ TotSCEFF = 1;
+ break;
+ case IE_CRE_V1_1://totsc/bg2/tob (still V1.0, but large effects)
+ case IE_CRE_V1_0://bg1
+ headersize = 0x2d4;
+ Inventory_Size=38;
+ //we should know it is bg1
+ if (actor->version == IE_CRE_V1_1) {
+ TotSCEFF = 1;
+ } else {
+ TotSCEFF = 0;
+ }
+ break;
+ case IE_CRE_V1_2: //pst
+ headersize = 0x378;
+ Inventory_Size=46;
+ TotSCEFF = 0;
+ break;
+ case IE_CRE_V2_2://iwd2
+ headersize = 0x62e; //with offsets
+ Inventory_Size=50;
+ TotSCEFF = 1;
+ break;
+ case IE_CRE_V9_0://iwd
+ headersize = 0x33c;
+ Inventory_Size=38;
+ TotSCEFF = 1;
+ break;
+ default:
+ return -1;
+ }
+ KnownSpellsOffset = headersize;
+
+ if (actor->version==IE_CRE_V2_2) { //iwd2
+ //adding spellbook header sizes
+ //class spells, domains, (shapes,songs,innates)
+ headersize += 7*9*8 + 9*8 + 3*8;
+ } else {//others
+ //adding known spells
+ KnownSpellsCount = actor->spellbook.GetTotalKnownSpellsCount();
+ headersize += KnownSpellsCount * 12;
+ SpellMemorizationOffset = headersize;
+
+ //adding spell pages
+ SpellMemorizationCount = actor->spellbook.GetTotalPageCount();
+ headersize += SpellMemorizationCount * 16;
+ MemorizedSpellsOffset = headersize;
+
+ MemorizedSpellsCount = actor->spellbook.GetTotalMemorizedSpellsCount();
+ headersize += MemorizedSpellsCount * 12;
+ }
+ EffectsOffset = headersize;
+
+ //adding effects
+ EffectsCount = actor->fxqueue.GetSavedEffectsCount();
+ VariablesCount = actor->locals->GetCount();
+ if (VariablesCount) {
+ TotSCEFF=1;
+ }
+ if (TotSCEFF) {
+ headersize += (VariablesCount + EffectsCount) * 264;
+ } else {
+ //if there are variables, then TotSCEFF is set
+ headersize += EffectsCount * 48;
+ }
+ ItemsOffset = headersize;
+
+ //counting items (calculating item storage)
+ ItemsCount = 0;
+ for (i=0;i<Inventory_Size;i++) {
+ unsigned int j = core->QuerySlot(i+1);
+ CREItem *it = actor->inventory.GetSlotItem(j);
+ if (it) {
+ ItemsCount++;
+ }
+ }
+ headersize += ItemsCount * 20;
+ ItemSlotsOffset = headersize;
+
+ //adding itemslot table size and equipped slot fields
+ return headersize + (Inventory_Size)*sizeof(ieWord)+sizeof(ieWord)*2;
+}
+
+int CREImporter::PutInventory(DataStream *stream, Actor *actor, unsigned int size)
+{
+ unsigned int i;
+ ieDword tmpDword;
+ ieWord tmpWord;
+ ieWord ItemCount = 0;
+ ieWord *indices =(ieWord *) malloc(size*sizeof(ieWord) );
+
+ for (i=0;i<size;i++) {
+ indices[i]=(ieWord) -1;
+ }
+
+ for (i=0;i<size;i++) {
+ //ignore first element, getinventorysize makes space for fist
+ unsigned int j = core->QuerySlot(i+1);
+ CREItem *it = actor->inventory.GetSlotItem( j );
+ if (!it) {
+ continue;
+ }
+ stream->WriteResRef( it->ItemResRef);
+ stream->WriteWord( &it->Expired);
+ stream->WriteWord( &it->Usages[0]);
+ stream->WriteWord( &it->Usages[1]);
+ stream->WriteWord( &it->Usages[2]);
+ tmpDword = it->Flags;
+ //IWD uses this bit differently
+ if (MagicBit) {
+ if (it->Flags&IE_INV_ITEM_MAGICAL) {
+ tmpDword|=IE_INV_ITEM_UNDROPPABLE;
+ } else {
+ tmpDword&=~IE_INV_ITEM_UNDROPPABLE;
+ }
+ }
+ stream->WriteDword( &tmpDword);
+ indices[i] = ItemCount++;
+ }
+ for (i=0;i<size;i++) {
+ stream->WriteWord( indices+i);
+ }
+ tmpWord = (ieWord) actor->inventory.GetEquipped();
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) actor->inventory.GetEquippedHeader();
+ stream->WriteWord( &tmpWord);
+ free(indices);
+ return 0;
+}
+
+int CREImporter::PutHeader(DataStream *stream, Actor *actor)
+{
+ char Signature[8];
+ ieByte tmpByte;
+ ieWord tmpWord;
+ ieDword tmpDword;
+ int i;
+ char filling[51];
+
+ memset(filling,0,sizeof(filling));
+ memcpy( Signature, "CRE V0.0", 8);
+ Signature[5]+=CREVersion/10;
+ if (actor->version!=IE_CRE_V1_1) {
+ Signature[7]+=CREVersion%10;
+ }
+ stream->Write( Signature, 8);
+ stream->WriteDword( &actor->LongStrRef);
+ stream->WriteDword( &actor->ShortStrRef);
+ stream->WriteDword( &actor->BaseStats[IE_MC_FLAGS]);
+ stream->WriteDword( &actor->BaseStats[IE_XPVALUE]);
+ stream->WriteDword( &actor->BaseStats[IE_XP]);
+ stream->WriteDword( &actor->BaseStats[IE_GOLD]);
+ stream->WriteDword( &actor->BaseStats[IE_STATE_ID]);
+ tmpWord = actor->BaseStats[IE_HITPOINTS];
+ //decrease the hp back to the one without constitution bonus
+ //this is probably still not perfect
+ tmpWord = (ieWord) (tmpWord - actor->GetHpAdjustment(actor->GetXPLevel(false)));
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->BaseStats[IE_MAXHITPOINTS];
+ stream->WriteWord( &tmpWord);
+ stream->WriteDword( &actor->BaseStats[IE_ANIMATION_ID]);
+ for (i=0;i<7;i++) {
+ Signature[i] = (char) actor->BaseStats[IE_COLORS+i];
+ }
+ //old effect type
+ Signature[7] = TotSCEFF;
+ stream->Write( Signature, 8);
+ stream->WriteResRef( actor->SmallPortrait);
+ stream->WriteResRef( actor->LargePortrait);
+ tmpByte = actor->BaseStats[IE_REPUTATION];
+ stream->Write( &tmpByte, 1 );
+ tmpByte = actor->BaseStats[IE_HIDEINSHADOWS];
+ stream->Write( &tmpByte, 1 );
+ //from here it differs, slightly
+ tmpWord = actor->BaseStats[IE_ARMORCLASS];
+ stream->WriteWord( &tmpWord);
+ //iwd2 doesn't store this a second time,
+ //probably gemrb format shouldn't either?
+ if (actor->version != IE_CRE_V2_2) {
+ tmpWord = actor->BaseStats[IE_ARMORCLASS];
+ stream->WriteWord( &tmpWord);
+ }
+ tmpWord = actor->BaseStats[IE_ACCRUSHINGMOD];
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->BaseStats[IE_ACMISSILEMOD];
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->BaseStats[IE_ACPIERCINGMOD];
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->BaseStats[IE_ACSLASHINGMOD];
+ stream->WriteWord( &tmpWord);
+ tmpByte = actor->BaseStats[IE_TOHIT];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_NUMBEROFATTACKS];
+ if (actor->version == IE_CRE_V2_2) {
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SAVEFORTITUDE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SAVEREFLEX];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SAVEWILL];
+ stream->Write( &tmpByte, 1);
+ } else {
+ if (actor->version!=IE_CRE_GEMRB) {
+ if (tmpByte&1) tmpByte = tmpByte/2+6;
+ else tmpByte /=2;
+ }
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SAVEVSDEATH];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SAVEVSWANDS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SAVEVSPOLY];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SAVEVSBREATH];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SAVEVSSPELL];
+ stream->Write( &tmpByte, 1);
+ }
+ tmpByte = actor->BaseStats[IE_RESISTFIRE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTCOLD];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTELECTRICITY];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTACID];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTMAGIC];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTMAGICFIRE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTMAGICCOLD];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTSLASHING];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTCRUSHING];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTPIERCING];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RESISTMISSILE];
+ stream->Write( &tmpByte, 1);
+ if (actor->version == IE_CRE_V2_2) {
+ tmpByte = actor->BaseStats[IE_MAGICDAMAGERESISTANCE];
+ stream->Write( &tmpByte, 1);
+ stream->Write( Signature, 4);
+ } else {
+ tmpByte = actor->BaseStats[IE_DETECTILLUSIONS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SETTRAPS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LORE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LOCKPICKING];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_STEALTH];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_TRAPS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_PICKPOCKET];
+ stream->Write( &tmpByte, 1);
+ }
+ tmpByte = actor->BaseStats[IE_FATIGUE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_INTOXICATION];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LUCK];
+ stream->Write( &tmpByte, 1);
+
+ if (actor->version == IE_CRE_V2_2) {
+ //this is rather fuzzy
+ //turnundead level, + 33 bytes of zero
+ tmpByte = actor->BaseStats[IE_TURNUNDEADLEVEL];
+ stream->Write(&tmpByte, 1);
+ stream->Write( filling,33);
+ //total levels
+ tmpByte = actor->BaseStats[IE_CLASSLEVELSUM];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELBARBARIAN];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELBARD];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELCLERIC];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELDRUID];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELFIGHTER];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELMONK];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELPALADIN];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELRANGER];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELTHIEF];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELSORCEROR];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVELMAGE];
+ stream->Write( &tmpByte, 1);
+ //some stuffing
+ stream->Write( filling, 22);
+ //string references
+ for (i=0;i<64;i++) {
+ stream->WriteDword( &actor->StrRefs[i]);
+ }
+ stream->WriteResRef( actor->Scripts[SCR_AREA]->GetName() );
+ stream->WriteResRef( actor->Scripts[SCR_RESERVED]->GetName() );
+ //unknowns before feats
+ stream->Write( filling,4);
+ //feats
+ stream->WriteDword( &actor->BaseStats[IE_FEATS1]);
+ stream->WriteDword( &actor->BaseStats[IE_FEATS2]);
+ stream->WriteDword( &actor->BaseStats[IE_FEATS3]);
+ stream->Write( filling, 12);
+ //proficiencies
+ for (i=0;i<26;i++) {
+ tmpByte = actor->BaseStats[IE_PROFICIENCYBASTARDSWORD+i];
+ stream->Write( &tmpByte, 1);
+ }
+ stream->Write( filling, 38);
+ //alchemy
+ tmpByte = actor->BaseStats[IE_ALCHEMY];
+ stream->Write( &tmpByte, 1);
+ //animals
+ tmpByte = actor->BaseStats[IE_ANIMALS];
+ stream->Write( &tmpByte, 1);
+ //bluff
+ tmpByte = actor->BaseStats[IE_BLUFF];
+ stream->Write( &tmpByte, 1);
+ //concentration
+ tmpByte = actor->BaseStats[IE_CONCENTRATION];
+ stream->Write( &tmpByte, 1);
+ //diplomacy
+ tmpByte = actor->BaseStats[IE_DIPLOMACY];
+ stream->Write( &tmpByte, 1);
+ //disarm trap
+ tmpByte = actor->BaseStats[IE_TRAPS];
+ stream->Write( &tmpByte, 1);
+ //hide
+ tmpByte = actor->BaseStats[IE_HIDEINSHADOWS];
+ stream->Write( &tmpByte, 1);
+ //intimidate
+ tmpByte = actor->BaseStats[IE_INTIMIDATE];
+ stream->Write( &tmpByte, 1);
+ //lore
+ tmpByte = actor->BaseStats[IE_LORE];
+ stream->Write( &tmpByte, 1);
+ //move silently
+ tmpByte = actor->BaseStats[IE_STEALTH];
+ stream->Write( &tmpByte, 1);
+ //open lock
+ tmpByte = actor->BaseStats[IE_LOCKPICKING];
+ stream->Write( &tmpByte, 1);
+ //pickpocket
+ tmpByte = actor->BaseStats[IE_PICKPOCKET];
+ stream->Write( &tmpByte, 1);
+ //search
+ tmpByte = actor->BaseStats[IE_SEARCH];
+ stream->Write( &tmpByte, 1);
+ //spellcraft
+ tmpByte = actor->BaseStats[IE_SPELLCRAFT];
+ stream->Write( &tmpByte, 1);
+ //use magic device
+ tmpByte = actor->BaseStats[IE_MAGICDEVICE];
+ stream->Write( &tmpByte, 1);
+ //tracking
+ tmpByte = actor->BaseStats[IE_TRACKING];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling, 50);
+ tmpByte = actor->BaseStats[IE_CR];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_HATEDRACE];
+ stream->Write( &tmpByte, 1);
+ for (i=0;i<7;i++) {
+ tmpByte = actor->BaseStats[IE_HATEDRACE2+i];
+ stream->Write( &tmpByte, 1);
+ }
+ tmpByte = actor->BaseStats[IE_SUBRACE];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling, 1); //unknown
+ tmpByte = actor->BaseStats[IE_SEX]; //
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_STR];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_INT];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_WIS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_DEX];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_CON];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_CHR];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_MORALE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_MORALEBREAK];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_MORALERECOVERYTIME];
+ stream->Write( &tmpByte, 1);
+ // unknown byte
+ stream->Write( &filling,1);
+ // no kit word order magic for iwd2
+ stream->WriteDword( &actor->BaseStats[IE_KIT] );
+ stream->WriteResRef( actor->Scripts[SCR_OVERRIDE]->GetName() );
+ stream->WriteResRef( actor->Scripts[SCR_CLASS]->GetName() );
+ stream->WriteResRef( actor->Scripts[SCR_RACE]->GetName() );
+ stream->WriteResRef( actor->Scripts[SCR_GENERAL]->GetName() );
+ stream->WriteResRef( actor->Scripts[SCR_DEFAULT]->GetName() );
+ } else {
+ for (i=0;i<21;i++) {
+ tmpByte = actor->BaseStats[IE_PROFICIENCYBASTARDSWORD+i];
+ stream->Write( &tmpByte, 1);
+ }
+ tmpByte = actor->BaseStats[IE_TRACKING];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling, 32);
+ for (i=0;i<100;i++) {
+ stream->WriteDword( &actor->StrRefs[i]);
+ }
+ tmpByte = actor->BaseStats[IE_LEVEL];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVEL2];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_LEVEL3];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SEX]; //
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_STR];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_STREXTRA];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_INT];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_WIS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_DEX];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_CON];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_CHR];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_MORALE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_MORALEBREAK];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_HATEDRACE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_MORALERECOVERYTIME];
+ stream->Write( &tmpByte, 1);
+ // unknown byte
+ stream->Write( &Signature,1);
+ tmpDword = ((actor->BaseStats[IE_KIT] & 0xffff) << 16) +
+ ((actor->BaseStats[IE_KIT] & 0xffff0000) >> 16);
+ stream->WriteDword( &tmpDword );
+ stream->WriteResRef( actor->Scripts[SCR_OVERRIDE]->GetName() );
+ stream->WriteResRef( actor->Scripts[SCR_CLASS]->GetName() );
+ stream->WriteResRef( actor->Scripts[SCR_RACE]->GetName() );
+ stream->WriteResRef( actor->Scripts[SCR_GENERAL]->GetName() );
+ stream->WriteResRef( actor->Scripts[SCR_DEFAULT]->GetName() );
+ }
+ //now follows the fuzzy part in separate putactor... functions
+ return 0;
+}
+
+int CREImporter::PutActorGemRB(DataStream *stream, Actor *actor, ieDword InvSize)
+{
+ ieByte tmpByte;
+ char filling[5];
+
+ memset(filling,0,sizeof(filling));
+ //similar in all engines
+ tmpByte = actor->BaseStats[IE_EA];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_GENERAL];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RACE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_CLASS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SPECIFIC];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SEX];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling, 5); //unknown bytes
+ tmpByte = actor->BaseStats[IE_ALIGNMENT];
+ stream->Write( &tmpByte, 1);
+ stream->WriteDword( &InvSize ); //saving the inventory size to this unused part
+ stream->Write( actor->GetScriptName(), 32);
+ return 0;
+}
+
+int CREImporter::PutActorBG(DataStream *stream, Actor *actor)
+{
+ ieByte tmpByte;
+ char filling[5];
+
+ memset(filling,0,sizeof(filling));
+ //similar in all engines
+ tmpByte = actor->BaseStats[IE_EA];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_GENERAL];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RACE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_CLASS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SPECIFIC];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SEX];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling, 5); //unknown bytes
+ tmpByte = actor->BaseStats[IE_ALIGNMENT];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling,4); //this is called ID in iwd2, and contains 2 words
+ stream->Write( actor->GetScriptName(), 32);
+ return 0;
+}
+
+int CREImporter::PutActorPST(DataStream *stream, Actor *actor)
+{
+ ieByte tmpByte;
+ ieWord tmpWord;
+ int i;
+ char filling[44];
+
+ memset(filling,0,sizeof(filling));
+ stream->Write(filling, 44); //11*4 totally unknown
+ stream->WriteDword( &actor->BaseStats[IE_XP_MAGE]);
+ stream->WriteDword( &actor->BaseStats[IE_XP_THIEF]);
+ for (i = 0; i<10; i++) {
+ tmpWord = actor->BaseStats[IE_INTERNAL_0];
+ stream->WriteWord( &tmpWord );
+ }
+ for (i = 0; i<4; i++) {
+ tmpByte = (ieByte) (actor->DeathCounters[i]);
+ stream->Write( &tmpByte, 1);
+ }
+ stream->Write( actor->KillVar, 32);
+ stream->Write( filling,3); //unknown
+ tmpByte=actor->BaseStats[IE_COLORCOUNT];
+ stream->Write( &tmpByte, 1);
+ //stream->WriteWord( &actor->AppearanceFlags1);
+ //stream->WriteWord( &actor->AppearanceFlags2);
+ stream->WriteDword( &actor->AppearanceFlags);
+
+ for (i=0;i<7;i++) {
+ tmpWord = actor->BaseStats[IE_COLORS+i];
+ stream->WriteWord( &tmpWord);
+ }
+ stream->Write(filling,31);
+ tmpByte = actor->BaseStats[IE_SPECIES];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_TEAM];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_FACTION];
+ stream->Write( &tmpByte, 1);
+ //similar in all engines
+ tmpByte = actor->BaseStats[IE_EA];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_GENERAL];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RACE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_CLASS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SPECIFIC];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SEX];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling, 5); //unknown bytes
+ tmpByte = actor->BaseStats[IE_ALIGNMENT];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling,4); //this is called ID in iwd2, and contains 2 words
+ stream->Write( actor->GetScriptName(), 32);
+ return 0;
+}
+
+int CREImporter::PutActorIWD1(DataStream *stream, Actor *actor)
+{
+ ieByte tmpByte;
+ ieWord tmpWord;
+ int i;
+ char filling[52];
+
+ memset(filling,0,sizeof(filling));
+ tmpByte=(ieByte) actor->BaseStats[IE_AVATARREMOVAL];
+ stream->Write( &tmpByte, 1);
+ stream->Write( &actor->SetDeathVar, 1);
+ stream->Write( &actor->IncKillCount, 1);
+ stream->Write( &actor->UnknownField, 1); //unknown
+ for (i=0;i<5;i++) {
+ tmpWord = actor->BaseStats[IE_INTERNAL_0+i];
+ stream->WriteWord( &tmpWord);
+ }
+ stream->Write( actor->KillVar, 32); //some variable names in iwd
+ stream->Write( actor->IncKillVar, 32); //some variable names in iwd
+ stream->Write( filling, 2);
+ tmpWord = actor->BaseStats[IE_SAVEDXPOS];
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->BaseStats[IE_SAVEDYPOS];
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->BaseStats[IE_SAVEDFACE];
+ stream->WriteWord( &tmpWord);
+ stream->Write( filling, 18);
+ //similar in all engines
+ tmpByte = actor->BaseStats[IE_EA];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_GENERAL];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RACE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_CLASS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SPECIFIC];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SEX];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling, 5); //unknown bytes
+ tmpByte = actor->BaseStats[IE_ALIGNMENT];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling,4); //this is called ID in iwd2, and contains 2 words
+ stream->Write( actor->GetScriptName(), 32);
+ return 0;
+}
+
+int CREImporter::PutActorIWD2(DataStream *stream, Actor *actor)
+{
+ ieByte tmpByte;
+ ieWord tmpWord;
+ int i;
+ char filling[146];
+
+ memset(filling,0,sizeof(filling));
+ tmpByte=(ieByte) actor->BaseStats[IE_AVATARREMOVAL];
+ stream->Write( &tmpByte, 1);
+ stream->Write( &actor->SetDeathVar, 1);
+ stream->Write( &actor->IncKillCount, 1);
+ stream->Write( &actor->UnknownField, 1); //unknown
+ for (i=0;i<5;i++) {
+ tmpWord = actor->BaseStats[IE_INTERNAL_0+i];
+ stream->WriteWord( &tmpWord);
+ }
+ stream->Write( actor->KillVar, 32); //some variable names in iwd
+ stream->Write( actor->IncKillVar, 32); //some variable names in iwd
+ stream->Write( filling, 2);
+ tmpWord = actor->BaseStats[IE_SAVEDXPOS];
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->BaseStats[IE_SAVEDYPOS];
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->BaseStats[IE_SAVEDFACE];
+ stream->WriteWord( &tmpWord);
+ stream->Write( filling, 146);
+ //similar in all engines
+ tmpByte = actor->BaseStats[IE_EA];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_GENERAL];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_RACE];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_CLASS];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SPECIFIC];
+ stream->Write( &tmpByte, 1);
+ tmpByte = actor->BaseStats[IE_SEX];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling, 5); //unknown bytes
+ tmpByte = actor->BaseStats[IE_ALIGNMENT];
+ stream->Write( &tmpByte, 1);
+ stream->Write( filling,4); //this is called ID in iwd2, and contains 2 words
+ stream->Write( actor->GetScriptName(), 32);
+ //3 unknown words
+ stream->WriteWord( &tmpWord);
+ stream->WriteWord( &tmpWord);
+ stream->WriteWord( &tmpWord);
+ return 0;
+}
+
+int CREImporter::PutKnownSpells( DataStream *stream, Actor *actor)
+{
+ int type=actor->spellbook.GetTypes();
+ for (int i=0;i<type;i++) {
+ unsigned int level = actor->spellbook.GetSpellLevelCount(i);
+ for (unsigned int j=0;j<level;j++) {
+ unsigned int count = actor->spellbook.GetKnownSpellsCount(i, j);
+ for (unsigned int k=0;k<count;k++) {
+ CREKnownSpell *ck = actor->spellbook.GetKnownSpell(i, j, k);
+ stream->WriteResRef(ck->SpellResRef);
+ stream->WriteWord( &ck->Level);
+ stream->WriteWord( &ck->Type);
+ }
+ }
+ }
+ return 0;
+}
+
+int CREImporter::PutSpellPages( DataStream *stream, Actor *actor)
+{
+ ieWord tmpWord;
+ ieDword tmpDword;
+ ieDword SpellIndex = 0;
+
+ int type=actor->spellbook.GetTypes();
+ for (int i=0;i<type;i++) {
+ unsigned int level = actor->spellbook.GetSpellLevelCount(i);
+ for (unsigned int j=0;j<level;j++) {
+ tmpWord = j; //+1
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->spellbook.GetMemorizableSpellsCount(i,j,false);
+ stream->WriteWord( &tmpWord);
+ tmpWord = actor->spellbook.GetMemorizableSpellsCount(i,j,true);
+ stream->WriteWord( &tmpWord);
+ tmpWord = i;
+ stream->WriteWord( &tmpWord);
+ stream->WriteDword( &SpellIndex);
+ tmpDword = actor->spellbook.GetMemorizedSpellsCount(i,j);
+ stream->WriteDword( &tmpDword);
+ SpellIndex += tmpDword;
+ }
+ }
+ return 0;
+}
+
+int CREImporter::PutMemorizedSpells(DataStream *stream, Actor *actor)
+{
+ int type=actor->spellbook.GetTypes();
+ for (int i=0;i<type;i++) {
+ unsigned int level = actor->spellbook.GetSpellLevelCount(i);
+ for (unsigned int j=0;j<level;j++) {
+ unsigned int count = actor->spellbook.GetMemorizedSpellsCount(i,j);
+ for (unsigned int k=0;k<count;k++) {
+ CREMemorizedSpell *cm = actor->spellbook.GetMemorizedSpell(i,j,k);
+
+ stream->WriteResRef( cm->SpellResRef);
+ stream->WriteDword( &cm->Flags);
+ }
+ }
+ }
+ return 0;
+}
+
+int CREImporter::PutEffects( DataStream *stream, Actor *actor)
+{
+ PluginHolder<EffectMgr> eM(IE_EFF_CLASS_ID);
+ assert(eM != NULL);
+
+ std::list< Effect* >::const_iterator f=actor->fxqueue.GetFirstEffect();
+ for(unsigned int i=0;i<EffectsCount;i++) {
+ const Effect *fx = actor->fxqueue.GetNextSavedEffect(f);
+
+ assert(fx!=NULL);
+
+ if (TotSCEFF) {
+ eM->PutEffectV2(stream, fx);
+ } else {
+ ieWord tmpWord;
+ ieByte tmpByte;
+ char filling[60];
+
+ memset(filling,0,sizeof(filling) );
+
+ tmpWord = (ieWord) fx->Opcode;
+ stream->WriteWord( &tmpWord);
+ tmpByte = (ieByte) fx->Target;
+ stream->Write(&tmpByte, 1);
+ tmpByte = (ieByte) fx->Power;
+ stream->Write(&tmpByte, 1);
+ stream->WriteDword( &fx->Parameter1);
+ stream->WriteDword( &fx->Parameter2);
+ tmpByte = (ieByte) fx->TimingMode;
+ stream->Write(&tmpByte, 1);
+ tmpByte = (ieByte) fx->Resistance;
+ stream->Write(&tmpByte, 1);
+ stream->WriteDword( &fx->Duration);
+ tmpByte = (ieByte) fx->Probability1;
+ stream->Write(&tmpByte, 1);
+ tmpByte = (ieByte) fx->Probability2;
+ stream->Write(&tmpByte, 1);
+ stream->Write(fx->Resource, 8);
+ stream->WriteDword( &fx->DiceThrown );
+ stream->WriteDword( &fx->DiceSides );
+ stream->WriteDword( &fx->SavingThrowType );
+ stream->WriteDword( &fx->SavingThrowBonus );
+ //isvariable
+ stream->Write( filling,4 );
+ }
+ }
+ return 0;
+}
+
+//add as effect!
+int CREImporter::PutVariables( DataStream *stream, Actor *actor)
+{
+ char filling[104];
+ Variables::iterator pos=NULL;
+ const char *name;
+ ieDword tmpDword, value;
+
+ for (unsigned int i=0;i<VariablesCount;i++) {
+ memset(filling,0,sizeof(filling) );
+ pos = actor->locals->GetNextAssoc( pos, name, value);
+ stream->Write(filling,8);
+ tmpDword = FAKE_VARIABLE_OPCODE;
+ stream->WriteDword( &tmpDword);
+ stream->Write(filling,8); //type, power
+ stream->WriteDword( &value); //param #1
+ stream->Write(filling,4); //param #2
+ //HoW has an assertion to ensure timing is nonzero (even for variables)
+ value = 1;
+ stream->WriteDword( &value); //timing
+ //duration, chance, resource, dices, saves
+ stream->Write( filling, 32);
+ tmpDword = FAKE_VARIABLE_MARKER;
+ stream->WriteDword( &tmpDword); //variable marker
+ stream->Write( filling, 92); //23 * 4
+ strnspccpy(filling, name, 32);
+ stream->Write( filling, 104); //32 + 72
+ }
+ return 0;
+}
+
+/* this function expects GetStoredFileSize to be called before */
+int CREImporter::PutActor(DataStream *stream, Actor *actor, bool chr)
+{
+ ieDword tmpDword=0;
+ int ret;
+
+ if (!stream || !actor) {
+ return -1;
+ }
+
+ IsCharacter = chr;
+ if (chr) {
+ WriteChrHeader( stream, actor );
+ }
+ assert(TotSCEFF==0 || TotSCEFF==1);
+
+ CREOffset = stream->GetPos(); // for asserts
+
+ ret = PutHeader( stream, actor);
+ if (ret) {
+ return ret;
+ }
+ //here comes the fuzzy part
+ ieDword Inventory_Size;
+
+ switch (CREVersion) {
+ case IE_CRE_GEMRB:
+ //don't add fist
+ Inventory_Size=(ieDword) actor->inventory.GetSlotCount()-1;
+ ret = PutActorGemRB(stream, actor, Inventory_Size);
+ break;
+ case IE_CRE_V1_2:
+ Inventory_Size=46;
+ ret = PutActorPST(stream, actor);
+ break;
+ case IE_CRE_V1_1:
+ case IE_CRE_V1_0: //bg1/bg2
+ Inventory_Size=38;
+ ret = PutActorBG(stream, actor);
+ break;
+ case IE_CRE_V2_2:
+ Inventory_Size=50;
+ ret = PutActorIWD2(stream, actor);
+ break;
+ case IE_CRE_V9_0:
+ Inventory_Size=38;
+ ret = PutActorIWD1(stream, actor);
+ break;
+ default:
+ return -1;
+ }
+ if (ret) {
+ return ret;
+ }
+
+ //writing offsets
+ if (actor->version==IE_CRE_V2_2) {
+ int i;
+
+ //class spells
+ for (i=0;i<7*9;i++) {
+ stream->WriteDword(&KnownSpellsOffset);
+ KnownSpellsOffset+=8;
+ }
+ for (i=0;i<7*9;i++) {
+ stream->WriteDword(&tmpDword);
+ }
+ //domain spells
+ for (i=0;i<9;i++) {
+ stream->WriteDword(&KnownSpellsOffset);
+ KnownSpellsOffset+=8;
+ }
+ for (i=0;i<9;i++) {
+ stream->WriteDword(&tmpDword);
+ }
+ //innates, shapes, songs
+ for (i=0;i<3;i++) {
+ stream->WriteDword(&KnownSpellsOffset);
+ KnownSpellsOffset+=8;
+ stream->WriteDword(&tmpDword);
+ }
+ } else {
+ stream->WriteDword( &KnownSpellsOffset);
+ stream->WriteDword( &KnownSpellsCount);
+ stream->WriteDword( &SpellMemorizationOffset );
+ stream->WriteDword( &SpellMemorizationCount );
+ stream->WriteDword( &MemorizedSpellsOffset );
+ stream->WriteDword( &MemorizedSpellsCount );
+ }
+ stream->WriteDword( &ItemSlotsOffset );
+ stream->WriteDword( &ItemsOffset );
+ stream->WriteDword( &ItemsCount );
+ stream->WriteDword( &EffectsOffset );
+ tmpDword = EffectsCount+VariablesCount;
+ stream->WriteDword( &tmpDword );
+ tmpDword = 0;
+ stream->WriteResRef( actor->GetDialog(false) );
+ //spells, spellbook etc
+
+ if (actor->version==IE_CRE_V2_2) {
+ int i;
+
+ //putting out book headers
+ for (i=0;i<7*9;i++) {
+ stream->WriteDword(&tmpDword);
+ stream->WriteDword(&tmpDword);
+ }
+ //domain headers
+ for (i=0;i<9;i++) {
+ stream->WriteDword(&tmpDword);
+ stream->WriteDword(&tmpDword);
+ }
+ //innates, shapes, songs
+ for (i=0;i<3;i++) {
+ stream->WriteDword(&tmpDword);
+ stream->WriteDword(&tmpDword);
+ }
+ } else {
+ assert(stream->GetPos() == CREOffset+KnownSpellsOffset);
+ ret = PutKnownSpells( stream, actor);
+ if (ret) {
+ return ret;
+ }
+ assert(stream->GetPos() == CREOffset+SpellMemorizationOffset);
+ ret = PutSpellPages( stream, actor);
+ if (ret) {
+ return ret;
+ }
+ assert(stream->GetPos() == CREOffset+MemorizedSpellsOffset);
+ ret = PutMemorizedSpells( stream, actor);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ assert(stream->GetPos() == CREOffset+EffectsOffset);
+ ret = PutEffects(stream, actor);
+ if (ret) {
+ return ret;
+ }
+ //effects and variables
+ ret = PutVariables(stream, actor);
+ if (ret) {
+ return ret;
+ }
+
+ //items and inventory slots
+ assert(stream->GetPos() == CREOffset+ItemsOffset);
+ ret = PutInventory( stream, actor, Inventory_Size);
+ if (ret) {
+ return ret;
+ }
+ return 0;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xE507B60, "CRE File Importer")
+PLUGIN_CLASS(IE_CRE_CLASS_ID, CREImporter)
+PLUGIN_INITIALIZER(Initializer)
+PLUGIN_CLEANUP(ReleaseMemoryCRE)
+END_PLUGIN()
diff --git a/gemrb/plugins/CREImporter/CREImporter.h b/gemrb/plugins/CREImporter/CREImporter.h
new file mode 100644
index 0000000..a0417e1
--- /dev/null
+++ b/gemrb/plugins/CREImporter/CREImporter.h
@@ -0,0 +1,112 @@
+/* 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 CREIMPORTER_H
+#define CREIMPORTER_H
+
+#include "ActorMgr.h"
+
+#define IE_CRE_GEMRB 0
+#define IE_CRE_V1_0 10 //bg1
+#define IE_CRE_V1_1 11 //bg2 (still V1.0)
+#define IE_CRE_V1_2 12
+#define IE_CRE_V2_2 22
+#define IE_CRE_V9_0 90
+
+class CREImporter : public ActorMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ unsigned char CREVersion;
+ ieDword KnownSpellsOffset;
+ ieDword KnownSpellsCount;
+ ieDword SpellMemorizationOffset;
+ ieDword SpellMemorizationCount;
+ ieDword MemorizedSpellsOffset;
+ ieDword MemorizedSpellsCount;
+ ieDword MemorizedIndex;
+ ieDword MemorizedCount;
+ ieDword ItemSlotsOffset;
+ ieDword ItemsOffset;
+ ieDword ItemsCount;
+ ieDword EffectsOffset;
+ ieDword EffectsCount;
+ ieByte TotSCEFF;
+ ieByte IsCharacter;
+ ieDword CREOffset;
+ ieDword VariablesCount;
+ ieDword OverlayOffset;
+ ieDword OverlayMemorySize;
+ //used in CHR header
+ int QWPCount; //weapons
+ int QSPCount; //spells
+ int QITCount; //items
+public:
+ CREImporter(void);
+ ~CREImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ Actor* GetActor(unsigned char is_in_party);
+
+ //returns saved size, updates internal offsets before save
+ int GetStoredFileSize(Actor *ac);
+ //saves file
+ int PutActor(DataStream *stream, Actor *actor, bool chr=false);
+private:
+ /** sets up some variables based on creature version for serializing the object */
+ void SetupSlotCounts();
+ /** writes out the chr header */
+ void WriteChrHeader(DataStream *stream, Actor *actor);
+ /** reads the chr header data (into PCStatStructs) */
+ void ReadChrHeader(Actor *actor);
+ /** skips the chr header */
+ bool SeekCreHeader(char *Signature);
+ void GetActorPST(Actor *actor);
+ ieDword GetActorGemRB(Actor *act);
+ void GetActorBG(Actor *actor);
+ void GetActorIWD1(Actor *actor);
+ void GetActorIWD2(Actor *actor);
+ void GetIWD2Spellpage(Actor *act, ieIWD2SpellType type, int level, int count);
+ void ReadInventory(Actor*, unsigned int);
+ void ReadEffects(Actor* actor);
+ void GetEffect(Effect *fx);
+ void ReadScript(Actor *actor, int ScriptLevel);
+ void ReadDialog(Actor *actor);
+ CREKnownSpell* GetKnownSpell();
+ CRESpellMemorization* GetSpellMemorization(Actor *act);
+ CREMemorizedSpell* GetMemorizedSpell();
+ CREItem* GetItem();
+ void SetupColor(ieDword&);
+ int GetHpAdjustment(Actor *actor);
+
+ int PutActorGemRB(DataStream *stream, Actor *actor, ieDword InvSize);
+ int PutActorPST(DataStream *stream, Actor *actor);
+ int PutActorBG(DataStream *stream, Actor *actor);
+ int PutActorIWD1(DataStream *stream, Actor *actor);
+ int PutActorIWD2(DataStream *stream, Actor *actor);
+ int PutKnownSpells(DataStream *stream, Actor *actor);
+ int PutSpellPages(DataStream *stream, Actor *actor);
+ int PutMemorizedSpells(DataStream *stream, Actor *actor);
+ int PutEffects(DataStream *stream, Actor *actor);
+ int PutVariables(DataStream *stream, Actor *actor);
+ int PutInventory(DataStream *stream, Actor *actor, unsigned int size);
+ int PutHeader(DataStream *stream, Actor *actor);
+};
+
+#endif
diff --git a/gemrb/plugins/CREImporter/Makefile.am b/gemrb/plugins/CREImporter/Makefile.am
new file mode 100644
index 0000000..5d1b795
--- /dev/null
+++ b/gemrb/plugins/CREImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = CREImporter.la
+CREImporter_la_LDFLAGS = -module -avoid-version -shared
+CREImporter_la_SOURCES = CREImporter.cpp CREImporter.h
diff --git a/gemrb/plugins/DLGImporter/CMakeLists.txt b/gemrb/plugins/DLGImporter/CMakeLists.txt
new file mode 100644
index 0000000..b038852
--- /dev/null
+++ b/gemrb/plugins/DLGImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (DLGImporter DLGImporter.cpp)
diff --git a/gemrb/plugins/DLGImporter/DLGImporter.cpp b/gemrb/plugins/DLGImporter/DLGImporter.cpp
new file mode 100644
index 0000000..576831b
--- /dev/null
+++ b/gemrb/plugins/DLGImporter/DLGImporter.cpp
@@ -0,0 +1,392 @@
+/* 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 "DLGImporter.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+#include "GameScript/GameScript.h"
+#include "System/FileStream.h"
+
+DLGImporter::DLGImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+ Version = 0;
+}
+
+DLGImporter::~DLGImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+}
+
+bool DLGImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strnicmp( Signature, "DLG V1.0", 8 ) != 0) {
+ printMessage( "DLGImporter", "Not a valid DLG File...", WHITE );
+ printStatus( "ERROR", LIGHT_RED );
+ Version = 0;
+ return false;
+ }
+ str->ReadDword( &StatesCount );
+ str->ReadDword( &StatesOffset );
+ // bg2
+ if (StatesOffset == 0x34 ) {
+ Version = 104;
+ }
+ else {
+ Version = 100;
+ }
+ str->ReadDword( &TransitionsCount );
+ str->ReadDword( &TransitionsOffset );
+ str->ReadDword( &StateTriggersOffset );
+ str->ReadDword( &StateTriggersCount );
+ str->ReadDword( &TransitionTriggersOffset );
+ str->ReadDword( &TransitionTriggersCount );
+ str->ReadDword( &ActionsOffset );
+ str->ReadDword( &ActionsCount );
+ if (Version == 104) {
+ str->ReadDword( &Flags );
+ }
+ else {
+ Flags = 0;
+ }
+ return true;
+}
+
+Dialog* DLGImporter::GetDialog() const
+{
+ if(!Version) {
+ return NULL;
+ }
+ Dialog* d = new Dialog();
+ d->Flags = Flags;
+ d->TopLevelCount = StatesCount;
+ d->Order = (unsigned int *) calloc (StatesCount, sizeof(unsigned int *) );
+ d->initialStates = (DialogState **) calloc (StatesCount, sizeof(DialogState *) );
+ for (unsigned int i = 0; i < StatesCount; i++) {
+ DialogState* ds = GetDialogState( d, i );
+ d->initialStates[i] = ds;
+ }
+ return d;
+}
+
+DialogState* DLGImporter::GetDialogState(Dialog *d, unsigned int index) const
+{
+ DialogState* ds = new DialogState();
+ //16 = sizeof(State)
+ str->Seek( StatesOffset + ( index * 16 ), GEM_STREAM_START );
+ ieDword FirstTransitionIndex;
+ ieDword TriggerIndex;
+ str->ReadDword( &ds->StrRef );
+ str->ReadDword( &FirstTransitionIndex );
+ str->ReadDword( &ds->transitionsCount );
+ str->ReadDword( &TriggerIndex );
+ ds->condition = GetStateTrigger( TriggerIndex );
+ ds->transitions = GetTransitions( FirstTransitionIndex, ds->transitionsCount );
+ if (TriggerIndex<StatesCount)
+ d->Order[TriggerIndex] = index;
+ return ds;
+}
+
+DialogTransition** DLGImporter::GetTransitions(unsigned int firstIndex, unsigned int count) const
+{
+ DialogTransition** trans = ( DialogTransition** )
+ malloc( count*sizeof( DialogTransition* ) );
+ for (unsigned int i = 0; i < count; i++) {
+ trans[i] = GetTransition( firstIndex + i );
+ }
+ return trans;
+}
+
+DialogTransition* DLGImporter::GetTransition(unsigned int index) const
+{
+ if (index >= TransitionsCount) {
+ return NULL;
+ }
+ //32 = sizeof(Transition)
+ str->Seek( TransitionsOffset + ( index * 32 ), GEM_STREAM_START );
+ DialogTransition* dt = new DialogTransition();
+ str->ReadDword( &dt->Flags );
+ str->ReadDword( &dt->textStrRef );
+ if (!(dt->Flags & IE_DLG_TR_TEXT)) {
+ dt->textStrRef = 0xffffffff;
+ }
+ str->ReadDword( &dt->journalStrRef );
+ if (!(dt->Flags & IE_DLG_TR_JOURNAL)) {
+ dt->journalStrRef = 0xffffffff;
+ }
+ ieDword TriggerIndex;
+ ieDword ActionIndex;
+ str->ReadDword( &TriggerIndex );
+ str->ReadDword( &ActionIndex );
+ str->ReadResRef( dt->Dialog );
+ str->ReadDword( &dt->stateIndex );
+ if (dt->Flags &IE_DLG_TR_TRIGGER) {
+ dt->condition = GetTransitionTrigger( TriggerIndex );
+ }
+ else {
+ dt->condition = NULL;
+ }
+ if (dt->Flags & IE_DLG_TR_ACTION) {
+ dt->actions = GetAction( ActionIndex );
+ }
+ return dt;
+}
+
+static char** GetStrings(char* string, unsigned int& count);
+
+Condition* GetCondition(char* string)
+{
+ unsigned int count;
+ char **lines = GetStrings( string, count );
+ Condition *condition = new Condition();
+ for (size_t i = 0; i < count; ++i) {
+ Trigger *trigger = GenerateTrigger(lines[i]);
+ if (!trigger) {
+ printMessage( "DLGImporter", "Can't compile trigger: " ,YELLOW);
+ printf("%s\n", lines[i]);
+ } else {
+ condition->triggers.push_back(trigger);
+ }
+ free(lines[i]);
+ }
+ free(lines);
+ return condition;
+}
+
+Condition* DLGImporter::GetStateTrigger(unsigned int index) const
+{
+ if (index >= StateTriggersCount) {
+ return NULL;
+ }
+ //8 = sizeof(VarOffset)
+ str->Seek( StateTriggersOffset + ( index * 8 ), GEM_STREAM_START );
+ ieDword Offset, Length;
+ str->ReadDword( &Offset );
+ str->ReadDword( &Length );
+ //a zero length trigger counts as no trigger
+ //a // comment counts as true(), so we simply ignore zero
+ //length trigger text like it isn't there
+ if (!Length) {
+ return NULL;
+ }
+ str->Seek( Offset, GEM_STREAM_START );
+ char* string = ( char* ) malloc( Length + 1 );
+ str->Read( string, Length );
+ string[Length] = 0;
+ Condition *condition = GetCondition(string);
+ free(string);
+ return condition;
+}
+
+Condition* DLGImporter::GetTransitionTrigger(unsigned int index) const
+{
+ if (index >= TransitionTriggersCount) {
+ return NULL;
+ }
+ str->Seek( TransitionTriggersOffset + ( index * 8 ), GEM_STREAM_START );
+ ieDword Offset, Length;
+ str->ReadDword( &Offset );
+ str->ReadDword( &Length );
+ str->Seek( Offset, GEM_STREAM_START );
+ char* string = ( char* ) malloc( Length + 1 );
+ str->Read( string, Length );
+ string[Length] = 0;
+ Condition *condition = GetCondition(string);
+ free( string );
+ return condition;
+}
+
+std::vector<Action*> DLGImporter::GetAction(unsigned int index) const
+{
+ if (index >= ActionsCount) {
+ return std::vector<Action*>();
+ }
+ str->Seek( ActionsOffset + ( index * 8 ), GEM_STREAM_START );
+ ieDword Offset, Length;
+ str->ReadDword( &Offset );
+ str->ReadDword( &Length );
+ str->Seek( Offset, GEM_STREAM_START );
+ char* string = ( char* ) malloc( Length + 1 );
+ str->Read( string, Length );
+ string[Length] = 0;
+ unsigned int count;
+ char ** lines = GetStrings( string, count );
+ std::vector<Action*> actions;
+ for (size_t i = 0; i < count; ++i) {
+ Action *action = GenerateAction(lines[i]);
+ if (!action) {
+ printMessage( "DLGImporter", "Can't compile action: " ,YELLOW);
+ printf("%s\n", lines[i]);
+ } else {
+ action->IncRef();
+ actions.push_back(action);
+ }
+ free(lines[i]);
+ }
+ return actions;
+}
+
+int GetActionLength(const char* string)
+{
+ int i;
+ int level = 0;
+ bool quotes = true;
+ const char* poi = string;
+
+ for (i = 0; *poi; i++) {
+ switch (*poi++) {
+ case '"':
+ quotes = !quotes;
+ break;
+ case '(':
+ if (quotes) {
+ level++;
+ }
+ break;
+ case ')':
+ if (quotes && level) {
+ level--;
+ if (level == 0) {
+ return i + 1;
+ }
+ }
+ break;
+ case '\r':
+ case '\n':
+ // force reset on newline if quotes are open
+ if (!quotes) return i;
+ break;
+ default:
+ break;
+ }
+ }
+ return i;
+}
+
+#define MyIsSpace(c) (((c) == ' ') || ((c) == '\n') || ((c) == '\r'))
+
+/* this function will break up faulty script strings that lack the CRLF
+ between commands, common in PST dialog */
+/* misc test cases (just examples, there are more):
+ pst's FORGE.DLG (trigger split across two lines),
+ bg2's SAHIMP02.DLG (missing quotemark in string),
+ bg2's QUAYLE.DLG (missing closing bracket) */
+char** GetStrings(char* string, unsigned int& count)
+{
+ int col = 0;
+ int level = 0;
+ bool quotes = true;
+ bool ignore = false;
+ char* poi = string;
+
+ count = 0;
+ while (*poi) {
+ switch (*poi++) {
+ case '/':
+ if(col==0) {
+ if(*poi=='/') {
+ poi++;
+ ignore=true;
+ }
+ }
+ break;
+ case '"':
+ quotes = !quotes;
+ break;
+ case '(':
+ if (quotes) {
+ level++;
+ }
+ break;
+ case ')':
+ if (quotes && level) {
+ level--;
+ if (level == 0) {
+ if(!ignore) {
+ count++;
+ }
+ ignore=false;
+ }
+ }
+ break;
+ case '\r':
+ case '\n':
+ // force reset on newline if quotes are open, or we had a comment
+ if (!quotes || ignore) {
+ level = 0;
+ quotes = true;
+ ignore = false;
+ count++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if(!count) {
+ return NULL;
+ }
+ char** strings = ( char** ) calloc( count, sizeof( char* ) );
+ if (strings == NULL) {
+ count = 0;
+ return strings;
+ }
+ poi = string;
+ for (int i = 0; i < (int)count; i++) {
+ while (MyIsSpace( *poi ))
+ poi++;
+ int len = GetActionLength( poi );
+ if((*poi=='/') && (*(poi+1)=='/') ) {
+ poi+=len;
+ i--;
+ continue;
+ }
+ strings[i] = ( char * ) malloc( len + 1 );
+ int j;
+ for (j = 0; len; poi++,len--) {
+ if (isspace( *poi ))
+ continue;
+ strings[i][j++] = *poi;
+ }
+ strings[i][j] = 0;
+ }
+ return strings;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x1970D894, "DLG File Importer")
+PLUGIN_CLASS(IE_DLG_CLASS_ID, DLGImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/DLGImporter/DLGImporter.h b/gemrb/plugins/DLGImporter/DLGImporter.h
new file mode 100644
index 0000000..10cade5
--- /dev/null
+++ b/gemrb/plugins/DLGImporter/DLGImporter.h
@@ -0,0 +1,85 @@
+/* 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 DLGIMPORTER_H
+#define DLGIMPORTER_H
+
+#include "DialogMgr.h"
+
+#include "globals.h"
+
+/*
+struct State {
+ ieStrRef StrRef;
+ ieDword FirstTransitionIndex;
+ ieDword TransitionsCount;
+ ieDword TriggerIndex;
+};
+*/
+/*
+struct Transition {
+ ieDword Flags;
+ ieStrRef AnswerStrRef;
+ ieStrRef JournalStrRef;
+ ieDword TriggerIndex;
+ ieDword ActionIndex;
+ ieResRef DLGResRef;
+ ieDword NextStateIndex;
+};
+*/
+/*
+struct VarOffset {
+ ieDword Offset;
+ ieDword Length;
+};
+*/
+class DLGImporter : public DialogMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ ieDword StatesCount;
+ ieDword StatesOffset;
+ ieDword TransitionsCount;
+ ieDword TransitionsOffset;
+ ieDword StateTriggersCount;
+ ieDword StateTriggersOffset;
+ ieDword TransitionTriggersCount;
+ ieDword TransitionTriggersOffset;
+ ieDword ActionsCount;
+ ieDword ActionsOffset;
+ ieDword Flags;
+ ieDword Version;
+
+public:
+ DLGImporter(void);
+ ~DLGImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ Dialog* GetDialog() const;
+private:
+ DialogState* GetDialogState(Dialog *d, unsigned int index) const;
+ DialogTransition* GetTransition(unsigned int index) const;
+ Condition* GetStateTrigger(unsigned int index) const;
+ Condition* GetTransitionTrigger(unsigned int index) const;
+ std::vector<Action*> GetAction(unsigned int index) const;
+ DialogTransition** GetTransitions(unsigned int firstIndex,
+ unsigned int count) const;
+};
+
+#endif
diff --git a/gemrb/plugins/DLGImporter/Makefile.am b/gemrb/plugins/DLGImporter/Makefile.am
new file mode 100644
index 0000000..bcc9bca
--- /dev/null
+++ b/gemrb/plugins/DLGImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = DLGImporter.la
+DLGImporter_la_LDFLAGS = -module -avoid-version -shared
+DLGImporter_la_SOURCES = DLGImporter.cpp DLGImporter.h
diff --git a/gemrb/plugins/DirectoryImporter/CMakeLists.txt b/gemrb/plugins/DirectoryImporter/CMakeLists.txt
new file mode 100644
index 0000000..bc673bc
--- /dev/null
+++ b/gemrb/plugins/DirectoryImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (DirectoryImporter DirectoryImporter.cpp)
diff --git a/gemrb/plugins/DirectoryImporter/DirectoryImporter.cpp b/gemrb/plugins/DirectoryImporter/DirectoryImporter.cpp
new file mode 100644
index 0000000..9a4ba39
--- /dev/null
+++ b/gemrb/plugins/DirectoryImporter/DirectoryImporter.cpp
@@ -0,0 +1,99 @@
+/* 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 "DirectoryImporter.h"
+
+#include "globals.h"
+#include "win32def.h"
+
+#include "Interface.h"
+#include "ResourceDesc.h"
+#include "System/FileStream.h"
+
+DirectoryImporter::DirectoryImporter(void)
+{
+ description = NULL;
+}
+
+DirectoryImporter::~DirectoryImporter(void)
+{
+ free(description);
+}
+
+bool DirectoryImporter::Open(const char *dir, const char *desc)
+{
+ free(description);
+ description = strdup(desc);
+ strcpy(path, dir);
+ return true;
+}
+
+static bool FindIn(const char *Path, const char *ResRef, const char *Type)
+{
+ char p[_MAX_PATH], f[_MAX_PATH] = {0};
+ strcpy(f, ResRef);
+ strcat(f, Type);
+ strlwr(f);
+
+ return PathJoin(p, Path, f, NULL);
+}
+
+static FileStream *SearchIn(const char * Path,const char * ResRef, const char *Type)
+{
+ char p[_MAX_PATH], f[_MAX_PATH] = {0};
+ strcpy(f, ResRef);
+ strcat(f, Type);
+ strlwr(f);
+
+ if (!PathJoin(p, Path, f, NULL))
+ return NULL;
+
+ FileStream * fs = new FileStream();
+ if(!fs) return NULL;
+ if(!fs->Open(p, true)) {
+ delete fs;
+ return NULL;
+ }
+ return fs;
+}
+
+bool DirectoryImporter::HasResource(const char* resname, SClass_ID type)
+{
+ return FindIn( path, resname, core->TypeExt(type) );
+}
+
+bool DirectoryImporter::HasResource(const char* resname, const ResourceDesc &type)
+{
+ return FindIn( path, resname, type.GetExt() );
+}
+
+DataStream* DirectoryImporter::GetResource(const char* resname, SClass_ID type)
+{
+ return SearchIn( path, resname, core->TypeExt(type) );
+}
+
+DataStream* DirectoryImporter::GetResource(const char* resname, const ResourceDesc &type)
+{
+ return SearchIn( path, resname, type.GetExt() );
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xAB4534, "Directory Importer")
+PLUGIN_CLASS(PLUGIN_RESOURCE_DIRECTORY, DirectoryImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/DirectoryImporter/DirectoryImporter.h b/gemrb/plugins/DirectoryImporter/DirectoryImporter.h
new file mode 100644
index 0000000..46a0c84
--- /dev/null
+++ b/gemrb/plugins/DirectoryImporter/DirectoryImporter.h
@@ -0,0 +1,46 @@
+/* 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 KEYIMP_H
+#define KEYIMP_H
+
+#include "ResourceSource.h"
+
+#include <cstring>
+#include <map>
+#include <vector>
+
+class Resource;
+class ResourceDesc;
+
+class DirectoryImporter : public ResourceSource {
+private:
+ char path[_MAX_PATH];
+public:
+ DirectoryImporter(void);
+ ~DirectoryImporter(void);
+ bool Open(const char *dir, const char *desc);
+ /** predicts the availability of a resource */
+ bool HasResource(const char* resname, SClass_ID type);
+ bool HasResource(const char* resname, const ResourceDesc &type);
+ /** returns resource */
+ DataStream* GetResource(const char* resname, SClass_ID type);
+ DataStream* GetResource(const char* resname, const ResourceDesc &type);
+};
+
+#endif
diff --git a/gemrb/plugins/DirectoryImporter/Makefile.am b/gemrb/plugins/DirectoryImporter/Makefile.am
new file mode 100644
index 0000000..41a5211
--- /dev/null
+++ b/gemrb/plugins/DirectoryImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = DirectoryImporter.la
+DirectoryImporter_la_LDFLAGS = -module -avoid-version -shared
+DirectoryImporter_la_SOURCES = DirectoryImporter.cpp DirectoryImporter.h
diff --git a/gemrb/plugins/EFFImporter/CMakeLists.txt b/gemrb/plugins/EFFImporter/CMakeLists.txt
new file mode 100644
index 0000000..ea37f7a
--- /dev/null
+++ b/gemrb/plugins/EFFImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (EFFImporter EFFImporter.cpp)
diff --git a/gemrb/plugins/EFFImporter/EFFImporter.cpp b/gemrb/plugins/EFFImporter/EFFImporter.cpp
new file mode 100644
index 0000000..5da8be8
--- /dev/null
+++ b/gemrb/plugins/EFFImporter/EFFImporter.cpp
@@ -0,0 +1,243 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 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 "EFFImporter.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+
+EFFImporter::EFFImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+EFFImporter::~EFFImporter(void)
+{
+ if (autoFree) {
+ delete str;
+ }
+}
+
+bool EFFImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (this->autoFree) {
+ delete str;
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "EFF V2.0", 8 ) == 0) {
+ version = 20;
+ } else {
+ version = 1;
+ }
+ str->Seek( -8, GEM_CURRENT_POS );
+ return true;
+}
+
+//level resistance is checked when DiceSides or DiceThrown
+//are greater than 0 (sometimes they used -1 for our amusement)
+//if level>than maximum affected or level<than minimum affected, then the
+//effect is resisted
+// copy the info into the EFFV2 fields (separate), so it is clearer
+inline static void fixAffectedLevels(Effect *fx) {
+ if (fx->DiceSides > 0 || fx->DiceThrown > 0) {
+ fx->MinAffectedLevel = fx->DiceThrown;
+ fx->MaxAffectedLevel = fx->DiceSides;
+ }
+}
+
+Effect* EFFImporter::GetEffect(Effect *fx)
+{
+ if (version == 1) {
+ return GetEffectV1( fx );
+ }
+ else {
+ // Skip over Signature1
+ str->Seek( 8, GEM_CURRENT_POS );
+ return GetEffectV20( fx );
+ }
+}
+
+Effect* EFFImporter::GetEffectV1(Effect *fx)
+{
+ ieByte tmpByte;
+ ieWord tmpWord;
+
+ memset( fx, 0, sizeof( Effect ) );
+
+ str->ReadWord( &tmpWord );
+ fx->Opcode = tmpWord;
+ str->Read( &tmpByte, 1 );
+ fx->Target = tmpByte;
+ str->Read( &tmpByte, 1 );
+ fx->Power = tmpByte;
+ str->ReadDword( &fx->Parameter1 );
+ str->ReadDword( &fx->Parameter2 );
+ str->Read( &tmpByte, 1 );
+ fx->TimingMode = tmpByte;
+ str->Read( &tmpByte, 1 );
+ fx->Resistance = tmpByte;
+ str->ReadDword( &fx->Duration );
+ str->Read( &tmpByte, 1 );
+ fx->Probability1 = tmpByte;
+ str->Read( &tmpByte, 1 );
+ fx->Probability2 = tmpByte;
+ str->ReadResRef( fx->Resource );
+ str->ReadDword( &fx->DiceThrown );
+ str->ReadDword( &fx->DiceSides );
+ str->ReadDword( &fx->SavingThrowType );
+ str->ReadDword( &fx->SavingThrowBonus );
+ str->ReadWord( &fx->IsVariable );
+ str->ReadWord( &fx->IsSaveForHalfDamage );
+ fixAffectedLevels( fx );
+
+ fx->PosX=0xffffffff;
+ fx->PosY=0xffffffff;
+ return fx;
+}
+
+Effect* EFFImporter::GetEffectV20(Effect *fx)
+{
+ ieDword tmp;
+ memset( fx, 0, sizeof( Effect ) );
+
+ str->Seek(8, GEM_CURRENT_POS);
+ str->ReadDword( &fx->Opcode );
+ str->ReadDword( &fx->Target );
+ str->ReadDword( &fx->Power );
+ str->ReadDword( &fx->Parameter1 );
+ str->ReadDword( &fx->Parameter2 );
+ str->ReadWord( &fx->TimingMode );
+ str->ReadWord( &fx->unknown2 );
+ str->ReadDword( &fx->Duration );
+ str->ReadWord( &fx->Probability1 );
+ str->ReadWord( &fx->Probability2 );
+ str->ReadResRef( fx->Resource );
+ str->ReadDword( &fx->DiceThrown );
+ str->ReadDword( &fx->DiceSides );
+ str->ReadDword( &fx->SavingThrowType );
+ str->ReadDword( &fx->SavingThrowBonus );
+ str->ReadWord( &fx->IsVariable ); //if this field was set to 1, this is a variable
+ str->ReadWord( &fx->IsSaveForHalfDamage ); //if this field was set to 1, save for half damage
+ str->ReadDword( &fx->PrimaryType );
+ str->Seek( 4, GEM_CURRENT_POS );
+ str->ReadDword( &fx->MinAffectedLevel );
+ str->ReadDword( &fx->MaxAffectedLevel );
+ str->ReadDword( &fx->Resistance );
+ str->ReadDword( &fx->Parameter3 );
+ str->ReadDword( &fx->Parameter4 );
+ str->Seek( 8, GEM_CURRENT_POS );
+ str->ReadResRef( fx->Resource2 );
+ str->ReadResRef( fx->Resource3 );
+ str->ReadDword( &fx->PosX);
+ str->ReadDword( &fx->PosY);
+ //FIXME: these two points are actually different
+ str->ReadDword( &fx->PosX);
+ str->ReadDword( &fx->PosY);
+ str->ReadDword( &fx->SourceType );
+ str->ReadResRef( fx->Source );
+ str->ReadDword( &fx->SourceFlags );
+ str->ReadDword( &fx->Projectile );
+ str->ReadDword( &tmp );
+ fx->InventorySlot=(ieDwordSigned) (tmp);
+ //Variable simply overwrites the resource fields (Keep them grouped)
+ //They have to be continuous
+ if (fx->IsVariable) {
+ str->Read( fx->Resource, 32 );
+ strnlwrcpy( fx->Resource, fx->Resource, 32 );
+ } else {
+ str->Seek( 32, GEM_CURRENT_POS);
+ }
+ str->ReadDword( &fx->CasterLevel );
+ str->Seek( 4, GEM_CURRENT_POS );
+ str->ReadDword( &fx->SecondaryType );
+ str->Seek( 60, GEM_CURRENT_POS );
+
+ return fx;
+}
+
+void EFFImporter::PutEffectV2(DataStream *stream, const Effect *fx) {
+ ieDword tmpDword1,tmpDword2;
+ char filling[60];
+
+ memset(filling,0,sizeof(filling) );
+
+ stream->Write( filling,8 ); //signature
+ stream->WriteDword( &fx->Opcode);
+ stream->WriteDword( &fx->Target);
+ stream->WriteDword( &fx->Power);
+ stream->WriteDword( &fx->Parameter1);
+ stream->WriteDword( &fx->Parameter2);
+ stream->WriteWord( &fx->TimingMode);
+ stream->WriteWord( &fx->unknown2);
+ stream->WriteDword( &fx->Duration);
+ stream->WriteWord( &fx->Probability1);
+ stream->WriteWord( &fx->Probability2);
+ stream->WriteResRef(fx->Resource);
+ stream->WriteDword( &fx->DiceThrown );
+ stream->WriteDword( &fx->DiceSides );
+ stream->WriteDword( &fx->SavingThrowType );
+ stream->WriteDword( &fx->SavingThrowBonus );
+ //isvariable
+ stream->Write( filling,4 );
+ stream->WriteDword( &fx->PrimaryType );
+ stream->Write( filling,12 );
+ stream->WriteDword( &fx->Resistance );
+ stream->WriteDword( &fx->Parameter3 );
+ stream->WriteDword( &fx->Parameter4 );
+ stream->Write( filling,8 );
+ if (fx->IsVariable) {
+ stream->Write(fx->Resource+8, 8);
+ //resource1-4 are used as a continuous memory
+ stream->Write(((ieByte *) fx->Resource)+16, 8);
+ } else {
+ stream->WriteResRef(fx->Resource2);
+ stream->WriteResRef(fx->Resource3);
+ }
+ tmpDword1 = (ieDword) fx->PosX;
+ tmpDword2 = (ieDword) fx->PosY;
+ stream->WriteDword( &tmpDword1 );
+ stream->WriteDword( &tmpDword2 );
+ //FIXME: these two points are actually different
+ stream->WriteDword( &tmpDword1 );
+ stream->WriteDword( &tmpDword2 );
+ stream->WriteDword( &fx->SourceType );
+ stream->WriteResRef( fx->Source );
+ stream->WriteDword( &fx->SourceFlags );
+ stream->WriteDword( &fx->Projectile );
+ tmpDword1 = (ieDword) fx->InventorySlot;
+ stream->WriteDword( &tmpDword1 );
+ stream->Write( filling,40 ); //12+32+8
+ stream->WriteDword( &fx->SecondaryType );
+ stream->Write( filling,60 );
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x14E81128, "EFF File Importer")
+PLUGIN_CLASS(IE_EFF_CLASS_ID, EFFImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/EFFImporter/EFFImporter.h b/gemrb/plugins/EFFImporter/EFFImporter.h
new file mode 100644
index 0000000..b978257
--- /dev/null
+++ b/gemrb/plugins/EFFImporter/EFFImporter.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 EFFIMPORTER_H
+#define EFFIMPORTER_H
+
+#include "EffectMgr.h"
+
+#include "ie_types.h"
+
+#include "Effect.h"
+
+class EFFImporter : public EffectMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ int version;
+
+public:
+ EFFImporter(void);
+ ~EFFImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ Effect* GetEffect(Effect *fx);
+ Effect* GetEffectV1(Effect *fx);
+ Effect* GetEffectV20(Effect *fx);
+ void PutEffectV2(DataStream *stream, const Effect *fx); // used in the area and cre importer
+};
+
+
+#endif
diff --git a/gemrb/plugins/EFFImporter/Makefile.am b/gemrb/plugins/EFFImporter/Makefile.am
new file mode 100644
index 0000000..92d9863
--- /dev/null
+++ b/gemrb/plugins/EFFImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = EFFImporter.la
+EFFImporter_la_LDFLAGS = -module -avoid-version -shared
+EFFImporter_la_SOURCES = EFFImporter.cpp EFFImporter.h
diff --git a/gemrb/plugins/FXOpcodes/CMakeLists.txt b/gemrb/plugins/FXOpcodes/CMakeLists.txt
new file mode 100644
index 0000000..ece46e7
--- /dev/null
+++ b/gemrb/plugins/FXOpcodes/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (FXOpcodes FXOpcodes.cpp)
diff --git a/gemrb/plugins/FXOpcodes/FXOpcodes.cpp b/gemrb/plugins/FXOpcodes/FXOpcodes.cpp
new file mode 100644
index 0000000..ee921da
--- /dev/null
+++ b/gemrb/plugins/FXOpcodes/FXOpcodes.cpp
@@ -0,0 +1,6683 @@
+/* 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 "ie_feats.h" //cannot avoid declaring these
+#include "opcode_params.h"
+#include "overlays.h"
+#include "strrefs.h"
+#include "win32def.h"
+
+#include "Audio.h"
+#include "DisplayMessage.h"
+#include "EffectQueue.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "PolymorphCache.h" // fx_polymorph
+#include "Projectile.h" //needs for clearair
+#include "Spell.h" //needed for fx_cast_spell feedback
+#include "TileMap.h" //needs for knock!
+#include "damages.h"
+#include "GameScript/GSUtils.h" //needs for MoveBetweenAreasCore
+#include "GameScript/Matching.h" //needs for GetAllObjects
+#include "GUI/GameControl.h"
+#include "Scriptable/Actor.h"
+#include "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.h"
+#include "Scriptable/PCStatStruct.h" //fx_polymorph (action definitions)
+
+//FIXME: find a way to handle portrait icons better
+#define PI_RIGID 2
+#define PI_CONFUSED 3
+#define PI_BERSERK 4
+#define PI_POISONED 6
+#define PI_HELD 13
+#define PI_SLEEP 14
+#define PI_BLESS 17
+#define PI_PANIC 36
+#define PI_HASTED 38
+#define PI_FATIGUE 39
+#define PI_SLOWED 41
+#define PI_HOPELESS 44
+#define PI_LEVELDRAIN 53
+#define PI_FEEBLEMIND 54
+#define PI_STUN 55
+#define PI_AID 57
+#define PI_HOLY 59
+#define PI_BOUNCE 65
+#define PI_BOUNCE2 67
+
+#define PI_BLOODRAGE 76 //iwd2
+#define PI_MAZE 78
+#define PI_PRISON 79
+#define PI_STONESKIN 80
+#define PI_DEAFNESS 83 //iwd2
+#define PI_SEQUENCER 92
+#define PI_BLUR 109
+#define PI_IMPROVEDHASTE 110
+#define PI_SPELLTRAP 117
+#define PI_CSHIELD 162
+#define PI_CSHIELD2 163
+
+static ieResRef *casting_glows = NULL;
+static int cgcount = -1;
+static ieResRef *spell_hits = NULL;
+static bool enhanced_effects = false;
+static int shcount = -1;
+static int *spell_abilities = NULL;
+static ieDword splabcount = 0;
+static int *polymorph_stats = NULL;
+static int polystatcount = 0;
+static bool pstflags = false;
+
+//the original engine stores the colors in sprklclr.2da in a different order
+
+static ScriptedAnimation default_spell_hit;
+
+int fx_ac_vs_damage_type_modifier (Scriptable* Owner, Actor* target, Effect* fx);//00
+int fx_attacks_per_round_modifier (Scriptable* Owner, Actor* target, Effect* fx);//01
+int fx_cure_sleep_state (Scriptable* Owner, Actor* target, Effect* fx);//02
+int fx_set_berserk_state (Scriptable* Owner, Actor* target, Effect* fx);//03
+int fx_cure_berserk_state (Scriptable* Owner, Actor* target, Effect* fx);//04
+int fx_set_charmed_state (Scriptable* Owner, Actor* target, Effect* fx);//05
+int fx_charisma_modifier (Scriptable* Owner, Actor* target, Effect* fx);//06
+int fx_set_color_gradient (Scriptable* Owner, Actor* target, Effect* fx);//07
+int fx_set_color_rgb (Scriptable* Owner, Actor* target, Effect* fx);//08
+int fx_set_color_rgb_global (Scriptable* Owner, Actor* target, Effect* fx);//08
+int fx_set_color_pulse_rgb (Scriptable* Owner, Actor* target, Effect* fx);//09
+int fx_set_color_pulse_rgb_global (Scriptable* Owner, Actor* target, Effect* fx);//09
+int fx_constitution_modifier (Scriptable* Owner, Actor* target, Effect* fx);//0a
+int fx_cure_poisoned_state (Scriptable* Owner, Actor* target, Effect* fx);//0b
+int fx_damage (Scriptable* Owner, Actor* target, Effect* fx);//0c
+int fx_death (Scriptable* Owner, Actor* target, Effect* fx);//0d
+int fx_cure_frozen_state (Scriptable* Owner, Actor* target, Effect* fx);//0e
+int fx_dexterity_modifier (Scriptable* Owner, Actor* target, Effect* fx);//0f
+int fx_set_hasted_state (Scriptable* Owner, Actor* target, Effect* fx);//10
+int fx_current_hp_modifier (Scriptable* Owner, Actor* target, Effect* fx);//11
+int fx_maximum_hp_modifier (Scriptable* Owner, Actor* target, Effect* fx);//12
+int fx_intelligence_modifier (Scriptable* Owner, Actor* target, Effect* fx);//13
+int fx_set_invisible_state (Scriptable* Owner, Actor* target, Effect* fx);//14
+int fx_lore_modifier (Scriptable* Owner, Actor* target, Effect* fx);//15
+int fx_luck_modifier (Scriptable* Owner, Actor* target, Effect* fx);//16
+int fx_morale_modifier (Scriptable* Owner, Actor* target, Effect* fx);//17
+int fx_set_panic_state (Scriptable* Owner, Actor* target, Effect* fx);//18
+int fx_set_poisoned_state (Scriptable* Owner, Actor* target, Effect* fx);//19
+int fx_remove_curse (Scriptable* Owner, Actor* target, Effect* fx);//1a
+int fx_acid_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//1b
+int fx_cold_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//1c
+int fx_electricity_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//1d
+int fx_fire_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//1e
+int fx_magic_damage_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//1f
+int fx_cure_dead_state (Scriptable* Owner, Actor* target, Effect* fx);//20
+int fx_save_vs_death_modifier (Scriptable* Owner, Actor* target, Effect* fx);//21
+int fx_save_vs_wands_modifier (Scriptable* Owner, Actor* target, Effect* fx);//22
+int fx_save_vs_poly_modifier (Scriptable* Owner, Actor* target, Effect* fx);//23
+int fx_save_vs_breath_modifier (Scriptable* Owner, Actor* target, Effect* fx);//24
+int fx_save_vs_spell_modifier (Scriptable* Owner, Actor* target, Effect* fx);//25
+int fx_set_silenced_state (Scriptable* Owner, Actor* target, Effect* fx);//26
+int fx_set_unconscious_state (Scriptable* Owner, Actor* target, Effect* fx);//27
+int fx_set_slowed_state (Scriptable* Owner, Actor* target, Effect* fx);//28
+int fx_sparkle(Scriptable* Owner, Actor* target, Effect* fx);//29
+int fx_bonus_wizard_spells (Scriptable* Owner, Actor* target, Effect* fx);//2a
+int fx_cure_petrified_state (Scriptable* Owner, Actor* target, Effect* fx);//2b
+int fx_strength_modifier (Scriptable* Owner, Actor* target, Effect* fx);//2c
+int fx_set_stun_state (Scriptable* Owner, Actor* target, Effect* fx);//2d
+int fx_cure_stun_state (Scriptable* Owner, Actor* target, Effect* fx);//2e
+int fx_cure_invisible_state (Scriptable* Owner, Actor* target, Effect* fx);//2f
+int fx_cure_silenced_state (Scriptable* Owner, Actor* target, Effect* fx);//30
+int fx_wisdom_modifier (Scriptable* Owner, Actor* target, Effect* fx);//31
+int fx_brief_rgb (Scriptable* Owner, Actor* target, Effect* fx);//32
+int fx_darken_rgb (Scriptable* Owner, Actor* target, Effect* fx);//33
+int fx_glow_rgb (Scriptable* Owner, Actor* target, Effect* fx);//34
+int fx_animation_id_modifier (Scriptable* Owner, Actor* target, Effect* fx);//35
+int fx_to_hit_modifier (Scriptable* Owner, Actor* target, Effect* fx);//36
+int fx_kill_creature_type (Scriptable* Owner, Actor* target, Effect* fx);//37
+int fx_alignment_invert (Scriptable* Owner, Actor* target, Effect* fx);//38
+int fx_alignment_change (Scriptable* Owner, Actor* target, Effect* fx);//39
+int fx_dispel_effects (Scriptable* Owner, Actor* target, Effect* fx);//3a
+int fx_stealth_modifier (Scriptable* Owner, Actor* target, Effect* fx);//3b
+int fx_miscast_magic_modifier (Scriptable* Owner, Actor* target, Effect* fx);//3c
+int fx_alchemy_modifier (Scriptable* Owner, Actor* target, Effect* fx);//3d
+int fx_bonus_priest_spells (Scriptable* Owner, Actor* target, Effect* fx);//3e
+int fx_set_infravision_state (Scriptable* Owner, Actor* target, Effect* fx);//3f
+int fx_cure_infravision_state (Scriptable* Owner, Actor* target, Effect* fx);//40
+int fx_set_blur_state (Scriptable* Owner, Actor* target, Effect* fx);//41
+int fx_transparency_modifier (Scriptable* Owner, Actor* target, Effect* fx);//42
+int fx_summon_creature (Scriptable* Owner, Actor* target, Effect* fx);//43
+int fx_unsummon_creature (Scriptable* Owner, Actor* target, Effect* fx);//44
+int fx_set_nondetection_state (Scriptable* Owner, Actor* target, Effect* fx);//45
+int fx_cure_nondetection_state (Scriptable* Owner, Actor* target, Effect* fx);//46
+int fx_sex_modifier (Scriptable* Owner, Actor* target, Effect* fx);//47
+int fx_ids_modifier (Scriptable* Owner, Actor* target, Effect* fx);//48
+int fx_damage_bonus_modifier (Scriptable* Owner, Actor* target, Effect* fx);//49
+int fx_set_blind_state (Scriptable* Owner, Actor* target, Effect* fx);//4a
+int fx_cure_blind_state (Scriptable* Owner, Actor* target, Effect* fx);//4b
+int fx_set_feebleminded_state (Scriptable* Owner, Actor* target, Effect* fx);//4c
+int fx_cure_feebleminded_state (Scriptable* Owner, Actor* target, Effect* fx);//4d
+int fx_set_diseased_state (Scriptable* Owner, Actor* target, Effect*fx);//4e
+int fx_cure_diseased_state (Scriptable* Owner, Actor* target, Effect* fx);//4f
+int fx_set_deaf_state (Scriptable* Owner, Actor* target, Effect* fx); //50
+int fx_set_deaf_state_iwd2 (Scriptable* Owner, Actor* target, Effect* fx); //50
+int fx_cure_deaf_state (Scriptable* Owner, Actor* target, Effect* fx);//51
+int fx_set_ai_script (Scriptable* Owner, Actor* target, Effect*fx);//52
+int fx_protection_from_projectile (Scriptable* Owner, Actor* target, Effect*fx);//53
+int fx_magical_fire_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//54
+int fx_magical_cold_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//55
+int fx_slashing_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//56
+int fx_crushing_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//57
+int fx_piercing_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//58
+int fx_missiles_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//59
+int fx_open_locks_modifier (Scriptable* Owner, Actor* target, Effect* fx);//5a
+int fx_find_traps_modifier (Scriptable* Owner, Actor* target, Effect* fx);//5b
+int fx_pick_pockets_modifier (Scriptable* Owner, Actor* target, Effect* fx);//5c
+int fx_fatigue_modifier (Scriptable* Owner, Actor* target, Effect* fx);//5d
+int fx_intoxication_modifier (Scriptable* Owner, Actor* target, Effect* fx);//5e
+int fx_tracking_modifier (Scriptable* Owner, Actor* target, Effect* fx);//5f
+int fx_level_modifier (Scriptable* Owner, Actor* target, Effect* fx);//60
+int fx_strength_bonus_modifier (Scriptable* Owner, Actor* target, Effect* fx);//61
+int fx_set_regenerating_state (Scriptable* Owner, Actor* target, Effect* fx);//62
+int fx_spell_duration_modifier (Scriptable* Owner, Actor* target, Effect* fx);///63
+int fx_generic_effect (Scriptable* Owner, Actor* target, Effect* fx);//64 protection from creature is a generic effect
+int fx_protection_opcode(Scriptable* Owner, Actor* target, Effect* fx); //65
+int fx_protection_spelllevel (Scriptable* Owner, Actor* target, Effect* fx); //66
+int fx_change_name (Scriptable* Owner, Actor* target, Effect* fx);//67
+int fx_experience_modifier (Scriptable* Owner, Actor* target, Effect* fx);//68
+int fx_gold_modifier (Scriptable* Owner, Actor* target, Effect* fx);//69
+int fx_morale_break_modifier (Scriptable* Owner, Actor* target, Effect* fx);//6a
+int fx_portrait_change (Scriptable* Owner, Actor* target, Effect* fx);//6b
+int fx_reputation_modifier (Scriptable* Owner, Actor* target, Effect* fx);//6c
+int fx_hold_creature_no_icon (Scriptable* Owner, Actor* target, Effect* fx);//6d
+//int fx_retreat_from (Scriptable* Owner, Actor* target, Effect* fx);//6e reused
+int fx_create_magic_item (Scriptable* Owner, Actor* target, Effect* fx);//6f
+int fx_remove_item (Scriptable* Owner, Actor* target, Effect* fx);//70
+int fx_equip_item (Scriptable* Owner, Actor* target, Effect* fx);//71
+int fx_dither (Scriptable* Owner, Actor* target, Effect* fx);//72
+int fx_detect_alignment (Scriptable* Owner, Actor* target, Effect* fx);//73
+//int fx_cure_improved_invisible_state (Scriptable* Owner, Actor* target, Effect* fx);//74 (2f)
+int fx_reveal_area (Scriptable* Owner, Actor* target, Effect* fx);//75
+int fx_reveal_creatures (Scriptable* Owner, Actor* target, Effect* fx);//76
+int fx_mirror_image (Scriptable* Owner, Actor* target, Effect* fx);//77
+int fx_immune_to_weapon (Scriptable* Owner, Actor* target, Effect* fx);//78
+int fx_visual_animation_effect (Scriptable* Owner, Actor* target, Effect* fx);//79 unknown
+int fx_create_inventory_item (Scriptable* Owner, Actor* target, Effect* fx);//7a
+int fx_remove_inventory_item (Scriptable* Owner, Actor* target, Effect* fx);//7b
+int fx_dimension_door (Scriptable* Owner, Actor* target, Effect* fx);//7c
+int fx_knock (Scriptable* Owner, Actor* target, Effect* fx);//7d
+int fx_movement_modifier (Scriptable* Owner, Actor* target, Effect* fx);//7e
+int fx_monster_summoning (Scriptable* Owner, Actor* target, Effect* fx);//7f
+int fx_set_confused_state (Scriptable* Owner, Actor* target, Effect* fx);//80
+int fx_set_aid_state (Scriptable* Owner, Actor* target, Effect* fx);//81
+int fx_set_bless_state (Scriptable* Owner, Actor* target, Effect* fx);//82
+int fx_set_chant_state (Scriptable* Owner, Actor* target, Effect* fx);//83
+int fx_set_holy_state (Scriptable* Owner, Actor* target, Effect* fx);//84
+int fx_luck_non_cumulative (Scriptable* Owner, Actor* target, Effect* fx);//85
+int fx_luck_cumulative (Scriptable* Owner, Actor* target, Effect* fx);//85
+int fx_set_petrified_state (Scriptable* Owner, Actor* target, Effect* fx);//86
+int fx_polymorph (Scriptable* Owner, Actor* target, Effect* fx);//87
+int fx_force_visible (Scriptable* Owner, Actor* target, Effect* fx);//88
+int fx_set_chantbad_state (Scriptable* Owner, Actor* target, Effect* fx);//89
+int fx_animation_stance (Scriptable* Owner, Actor* target, Effect* fx);//8a
+int fx_display_string (Scriptable* Owner, Actor* target, Effect* fx);//8b
+int fx_casting_glow (Scriptable* Owner, Actor* target, Effect* fx);//8c
+int fx_visual_spell_hit (Scriptable* Owner, Actor* target, Effect* fx);//8d
+int fx_display_portrait_icon (Scriptable* Owner, Actor* target, Effect* fx);//8e
+int fx_create_item_in_slot (Scriptable* Owner, Actor* target, Effect* fx);//8f
+int fx_disable_button (Scriptable* Owner, Actor* target, Effect* fx);//90
+int fx_disable_spellcasting (Scriptable* Owner, Actor* target, Effect* fx);//91
+int fx_cast_spell (Scriptable* Owner, Actor* target, Effect *fx);//92
+int fx_learn_spell (Scriptable* Owner, Actor* target, Effect *fx);//93
+int fx_cast_spell_point (Scriptable* Owner, Actor* target, Effect *fx);//94
+int fx_identify (Scriptable* Owner, Actor* target, Effect *fx);//95
+int fx_find_traps (Scriptable* Owner, Actor* target, Effect *fx);//96
+int fx_replace_creature (Scriptable* Owner, Actor* target, Effect *fx);//97
+int fx_play_movie (Scriptable* Owner, Actor* target, Effect* fx);//98
+int fx_set_sanctuary_state (Scriptable* Owner, Actor* target, Effect* fx);//99
+int fx_set_entangle_state (Scriptable* Owner, Actor* target, Effect* fx);//9a
+int fx_set_minorglobe_state (Scriptable* Owner, Actor* target, Effect* fx);//9b
+int fx_set_shieldglobe_state (Scriptable* Owner, Actor* target, Effect* fx);//9c
+int fx_set_web_state (Scriptable* Owner, Actor* target, Effect* fx);//9d
+int fx_set_grease_state (Scriptable* Owner, Actor* target, Effect* fx);//9e
+int fx_mirror_image_modifier (Scriptable* Owner, Actor* target, Effect* fx);//9f
+int fx_cure_sanctuary_state (Scriptable* Owner, Actor* target, Effect* fx);//a0
+int fx_cure_panic_state (Scriptable* Owner, Actor* target, Effect* fx);//a1
+int fx_cure_hold_state (Scriptable* Owner, Actor* target, Effect* fx);//a2 //cures 175
+int fx_cure_slow_state (Scriptable* Owner, Actor* target, Effect* fx);//a3
+int fx_cure_intoxication (Scriptable* Owner, Actor* target, Effect* fx);//a4
+int fx_pause_target (Scriptable* Owner, Actor* target, Effect* fx);//a5
+int fx_magic_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//a6
+int fx_missile_to_hit_modifier (Scriptable* Owner, Actor* target, Effect* fx);//a7
+int fx_remove_creature (Scriptable* Owner, Actor* target, Effect* fx);//a8
+int fx_disable_portrait_icon (Scriptable* Owner, Actor* target, Effect* fx);//a9
+int fx_damage_animation (Scriptable* Owner, Actor* target, Effect* fx);//aa
+int fx_add_innate (Scriptable* Owner, Actor* target, Effect* fx);//ab
+int fx_remove_spell (Scriptable* Owner, Actor* target, Effect* fx);//ac
+int fx_poison_resistance_modifier (Scriptable* Owner, Actor* target, Effect* fx);//ad
+int fx_playsound (Scriptable* Owner, Actor* target, Effect* fx);//ae
+int fx_hold_creature (Scriptable* Owner, Actor* target, Effect* fx);//af
+// this function is exactly the same as 0x7e fx_movement_modifier (in bg2 at least)//b0
+int fx_apply_effect (Scriptable* Owner, Actor* target, Effect* fx);//b1
+//b2 //hitbonus against creature (generic_effect)
+//b3 //damagebonus against creature (generic effect)
+//b4 //restrict item (generic effect)
+//b5 //restrict itemtype (generic effect)
+int fx_apply_effect_item (Scriptable* Owner, Actor* target, Effect* fx);//b6
+int fx_apply_effect_item_type (Scriptable* Owner, Actor* target, Effect* fx);//b7
+int fx_dontjump_modifier (Scriptable* Owner, Actor* target, Effect* fx);//b8
+// this function is exactly the same as 0xaf hold_creature (in bg2 at least) //b9
+int fx_move_to_area (Scriptable* Owner, Actor* target, Effect* fx);//ba
+int fx_local_variable (Scriptable* Owner, Actor* target, Effect* fx);//bb
+int fx_auracleansing_modifier (Scriptable* Owner, Actor* target, Effect* fx);//bc
+int fx_castingspeed_modifier (Scriptable* Owner, Actor* target, Effect* fx);//bd
+int fx_attackspeed_modifier (Scriptable* Owner, Actor* target, Effect* fx);//be
+int fx_castinglevel_modifier (Scriptable* Owner, Actor* target, Effect* fx);//bf
+int fx_find_familiar (Scriptable* Owner, Actor* target, Effect* fx);//c0
+int fx_see_invisible_modifier (Scriptable* Owner, Actor* target, Effect* fx);//c1
+int fx_ignore_dialogpause_modifier (Scriptable* Owner, Actor* target, Effect* fx);//c2
+int fx_familiar_constitution_loss (Scriptable* Owner, Actor* target, Effect* fx);//c3
+int fx_familiar_marker (Scriptable* Owner, Actor* target, Effect* fx);//c4
+int fx_bounce_projectile (Scriptable* Owner, Actor* target, Effect* fx);//c5
+int fx_bounce_opcode (Scriptable* Owner, Actor* target, Effect* fx);//c6
+int fx_bounce_spelllevel (Scriptable* Owner, Actor* target, Effect* fx);//c7
+int fx_bounce_spelllevel_dec (Scriptable* Owner, Actor* target, Effect* fx);//c8
+int fx_protection_spelllevel_dec (Scriptable* Owner, Actor* target, Effect* fx);//c9
+int fx_bounce_school (Scriptable* Owner, Actor* target, Effect* fx);//ca
+int fx_bounce_secondary_type (Scriptable* Owner, Actor* target, Effect* fx);//cb
+int fx_protection_school (Scriptable* Owner, Actor* target, Effect* fx); //cc
+int fx_protection_secondary_type (Scriptable* Owner, Actor* target, Effect* fx); //cd
+int fx_resist_spell (Scriptable* Owner, Actor* target, Effect* fx);//ce
+int fx_resist_spell_dec (Scriptable* Owner, Actor* target, Effect* fx);//??
+int fx_bounce_spell (Scriptable* Owner, Actor* target, Effect* fx);//cf
+int fx_bounce_spell_dec (Scriptable* Owner, Actor* target, Effect* fx);//??
+int fx_minimum_hp_modifier (Scriptable* Owner, Actor* target, Effect* fx);//d0
+int fx_power_word_kill (Scriptable* Owner, Actor* target, Effect* fx);//d1
+int fx_power_word_stun (Scriptable* Owner, Actor* target, Effect* fx);//d2
+int fx_imprisonment (Scriptable* Owner, Actor* target, Effect* fx);//d3
+int fx_freedom (Scriptable* Owner, Actor* target, Effect* fx);//d4
+int fx_maze (Scriptable* Owner, Actor* target, Effect* fx);//d5
+int fx_select_spell (Scriptable* Owner, Actor* target, Effect* fx);//d6
+int fx_play_visual_effect (Scriptable* Owner, Actor* target, Effect* fx); //d7
+int fx_leveldrain_modifier (Scriptable* Owner, Actor* target, Effect* fx);//d8
+int fx_power_word_sleep (Scriptable* Owner, Actor* target, Effect* fx);//d9
+int fx_stoneskin_modifier (Scriptable* Owner, Actor* target, Effect* fx);//da
+//db ac vs creature type (general effect)
+int fx_dispel_school (Scriptable* Owner, Actor* target, Effect* fx);//dc
+int fx_dispel_secondary_type (Scriptable* Owner, Actor* target, Effect* fx);//dd
+int fx_teleport_field (Scriptable* Owner, Actor* target, Effect* fx);//de
+int fx_protection_school_dec (Scriptable* Owner, Actor* target, Effect* fx);//df
+int fx_cure_leveldrain (Scriptable* Owner, Actor* target, Effect* fx);//e0
+int fx_reveal_magic (Scriptable* Owner, Actor* target, Effect* fx);//e1
+int fx_protection_secondary_type_dec (Scriptable* Owner, Actor* target, Effect* fx);//e2
+int fx_bounce_school_dec (Scriptable* Owner, Actor* target, Effect* fx);//e3
+int fx_bounce_secondary_type_dec (Scriptable* Owner, Actor* target, Effect* fx);//e4
+int fx_dispel_school_one (Scriptable* Owner, Actor* target, Effect* fx);//e5
+int fx_dispel_secondary_type_one (Scriptable* Owner, Actor* target, Effect* fx);//e6
+int fx_timestop (Scriptable* Owner, Actor* target, Effect* fx);//e7
+int fx_cast_spell_on_condition (Scriptable* Owner, Actor* target, Effect* fx);//e8
+int fx_proficiency (Scriptable* Owner, Actor* target, Effect* fx);//e9
+int fx_create_contingency (Scriptable* Owner, Actor* target, Effect* fx);//ea
+int fx_wing_buffet (Scriptable* Owner, Actor* target, Effect* fx);//eb
+int fx_puppet_master (Scriptable* Owner, Actor* target, Effect* fx);//ec
+int fx_puppet_marker (Scriptable* Owner, Actor* target, Effect* fx);//ed
+int fx_disintegrate (Scriptable* Owner, Actor* target, Effect* fx);//ee
+int fx_farsee (Scriptable* Owner, Actor* target, Effect* fx);//ef
+int fx_remove_portrait_icon (Scriptable* Owner, Actor* target, Effect* fx);//f0
+//f1 control creature (see charm)
+int fx_cure_confused_state (Scriptable* Owner, Actor* target, Effect* fx);//f2
+int fx_drain_items (Scriptable* Owner, Actor* target, Effect* fx);//f3
+int fx_drain_spells (Scriptable* Owner, Actor* target, Effect* fx);//f4
+int fx_checkforberserk_modifier (Scriptable* Owner, Actor* target, Effect* fx);//f5
+int fx_berserkstage1_modifier (Scriptable* Owner, Actor* target, Effect* fx);//f6
+int fx_berserkstage2_modifier (Scriptable* Owner, Actor* target, Effect* fx);//f7
+//int fx_melee_effect (Scriptable* Owner, Actor* target, Effect* fx);//f8
+//int fx_ranged_effect (Scriptable* Owner, Actor* target, Effect* fx);//f9
+int fx_damageluck_modifier (Scriptable* Owner, Actor* target, Effect* fx);//fa
+int fx_change_bardsong (Scriptable* Owner, Actor* target, Effect* fx);//fb
+int fx_set_area_effect (Scriptable* Owner, Actor* target, Effect* fx);//fc (set trap)
+int fx_set_map_note (Scriptable* Owner, Actor* target, Effect* fx);//fd
+int fx_remove_map_note (Scriptable* Owner, Actor* target, Effect* fx);//fe
+int fx_create_item_days (Scriptable* Owner, Actor* target, Effect* fx);//ff
+int fx_store_spell_sequencer (Scriptable* Owner, Actor* target, Effect* fx);//0x100
+int fx_create_spell_sequencer (Scriptable* Owner, Actor* target, Effect* fx);//101
+int fx_activate_spell_sequencer (Scriptable* Owner, Actor* target, Effect* fx);//102
+int fx_spelltrap (Scriptable* Owner, Actor* target, Effect* fx);//103
+int fx_crash (Scriptable* Owner, Actor* target, Effect* fx);//104, disabled
+int fx_restore_spell_level (Scriptable* Owner, Actor* target, Effect* fx);//105
+int fx_visual_range_modifier (Scriptable* Owner, Actor* target, Effect* fx);//106
+int fx_backstab_modifier (Scriptable* Owner, Actor* target, Effect* fx);//107
+int fx_drop_weapon (Scriptable* Owner, Actor* target, Effect* fx);//108
+int fx_modify_global_variable (Scriptable* Owner, Actor* target, Effect* fx);//109
+int fx_remove_immunity (Scriptable* Owner, Actor* target, Effect* fx);//10a
+int fx_protection_from_string (Scriptable* Owner, Actor* target, Effect* fx);//10b
+int fx_explore_modifier (Scriptable* Owner, Actor* target, Effect* fx);//10c
+int fx_screenshake (Scriptable* Owner, Actor* target, Effect* fx);//10d
+int fx_unpause_caster (Scriptable* Owner, Actor* target, Effect* fx);//10e
+int fx_summon_disable (Scriptable* Owner, Actor* target, Effect* fx);//10f
+int fx_apply_effect_repeat (Scriptable* Owner, Actor* target, Effect* fx);//110
+int fx_remove_projectile (Scriptable* Owner, Actor* target, Effect* fx);//111
+int fx_teleport_to_target (Scriptable* Owner, Actor* target, Effect* fx);//112
+int fx_hide_in_shadows_modifier (Scriptable* Owner, Actor* target, Effect* fx);//113
+int fx_detect_illusion_modifier (Scriptable* Owner, Actor* target, Effect* fx);//114
+int fx_set_traps_modifier (Scriptable* Owner, Actor* target, Effect* fx);//115
+int fx_to_hit_bonus_modifier (Scriptable* Owner, Actor* target, Effect* fx);//116
+int fx_renable_button (Scriptable* Owner, Actor* target, Effect* fx);//117
+int fx_force_surge_modifier (Scriptable* Owner, Actor* target, Effect* fx);//118
+int fx_wild_surge_modifier (Scriptable* Owner, Actor* target, Effect* fx);//119
+int fx_scripting_state (Scriptable* Owner, Actor* target, Effect* fx);//11a
+int fx_apply_effect_curse (Scriptable* Owner, Actor* target, Effect* fx);//11b
+int fx_melee_to_hit_modifier (Scriptable* Owner, Actor* target, Effect* fx);//11c
+int fx_melee_damage_modifier (Scriptable* Owner, Actor* target, Effect* fx);//11d
+int fx_missile_damage_modifier (Scriptable* Owner, Actor* target, Effect* fx);//11e
+int fx_no_circle_state (Scriptable* Owner, Actor* target, Effect* fx);//11f
+int fx_fist_to_hit_modifier (Scriptable* Owner, Actor* target, Effect* fx);//120
+int fx_fist_damage_modifier (Scriptable* Owner, Actor* target, Effect* fx);//121
+int fx_title_modifier (Scriptable* Owner, Actor* target, Effect* fx);//122
+int fx_disable_overlay_modifier (Scriptable* Owner, Actor* target, Effect* fx);//123
+int fx_no_backstab_modifier (Scriptable* Owner, Actor* target, Effect* fx);//124
+int fx_offscreenai_modifier (Scriptable* Owner, Actor* target, Effect* fx);//125
+int fx_existance_delay_modifier (Scriptable* Owner, Actor* target, Effect* fx);//126
+int fx_disable_chunk_modifier (Scriptable* Owner, Actor* target, Effect* fx);//127
+int fx_protection_from_animation (Scriptable* Owner, Actor* target, Effect* fx);//128
+int fx_protection_from_turn (Scriptable* Owner, Actor* target, Effect* fx);//129
+int fx_cutscene2 (Scriptable* Owner, Actor* target, Effect* fx);//12a
+int fx_chaos_shield_modifier (Scriptable* Owner, Actor* target, Effect* fx);//12b
+int fx_npc_bump (Scriptable* Owner, Actor* target, Effect* fx);//12c
+int fx_critical_hit_modifier (Scriptable* Owner, Actor* target, Effect* fx);//12d
+int fx_can_use_any_item_modifier (Scriptable* Owner, Actor* target, Effect* fx);//12e
+int fx_always_backstab_modifier (Scriptable* Owner, Actor* target, Effect* fx);//12f
+int fx_mass_raise_dead (Scriptable* Owner, Actor* target, Effect* fx);//130
+int fx_left_to_hit_modifier (Scriptable* Owner, Actor* target, Effect* fx);//131
+int fx_right_to_hit_modifier (Scriptable* Owner, Actor* target, Effect* fx);//132
+int fx_reveal_tracks (Scriptable* Owner, Actor* target, Effect* fx);//133
+int fx_protection_from_tracking (Scriptable* Owner, Actor* target, Effect* fx);//134
+int fx_modify_local_variable (Scriptable* Owner, Actor* target, Effect* fx);//135
+int fx_timeless_modifier (Scriptable* Owner, Actor* target, Effect* fx);//136
+int fx_generate_wish (Scriptable* Owner, Actor* target, Effect* fx);//137
+//138 see fx_crash
+//139 HLA generic effect
+int fx_golem_stoneskin_modifier (Scriptable* Owner, Actor* target, Effect* fx);//13a
+int fx_avatar_removal_modifier (Scriptable* Owner, Actor* target, Effect* fx);//13b
+int fx_magical_rest (Scriptable* Owner, Actor* target, Effect* fx);//13c
+//int fx_improved_haste_state (Scriptable* Owner, Actor* target, Effect* fx);//13d same as haste
+int fx_change_weather (Scriptable* Owner, Actor* target, Effect* fx);//13e ChangeWeather
+
+int fx_unknown (Scriptable* Owner, Actor* target, Effect* fx);//???
+
+// FIXME: Make this an ordered list, so we could use bsearch!
+static EffectDesc effectnames[] = {
+ { "*Crash*", fx_crash, EFFECT_NO_ACTOR, -1 },
+ { "AcidResistanceModifier", fx_acid_resistance_modifier, 0, -1 },
+ { "ACVsCreatureType", fx_generic_effect, 0, -1 }, //0xdb
+ { "ACVsDamageTypeModifier", fx_ac_vs_damage_type_modifier, 0, -1 },
+ { "ACVsDamageTypeModifier2", fx_ac_vs_damage_type_modifier, 0, -1 }, // used in IWD
+ { "AidNonCumulative", fx_set_aid_state, 0, -1 },
+ { "AIIdentifierModifier", fx_ids_modifier, 0, -1 },
+ { "AlchemyModifier", fx_alchemy_modifier, 0, -1 },
+ { "Alignment:Change", fx_alignment_change, 0, -1 },
+ { "Alignment:Invert", fx_alignment_invert, 0, -1 },
+ { "AlwaysBackstab", fx_always_backstab_modifier, 0, -1 },
+ { "AnimationIDModifier", fx_animation_id_modifier, 0, -1 },
+ { "AnimationStateChange", fx_animation_stance, 0, -1 },
+ { "ApplyEffect", fx_apply_effect, 0, -1 },
+ { "ApplyEffectCurse", fx_apply_effect_curse, 0, -1 },
+ { "ApplyEffectItem", fx_apply_effect_item, 0, -1 },
+ { "ApplyEffectItemType", fx_apply_effect_item_type, 0, -1 },
+ { "ApplyEffectRepeat", fx_apply_effect_repeat, 0, -1 },
+ { "CutScene2", fx_cutscene2, EFFECT_NO_ACTOR, -1 },
+ { "AttackSpeedModifier", fx_attackspeed_modifier, 0, -1 },
+ { "AttacksPerRoundModifier", fx_attacks_per_round_modifier, 0, -1 },
+ { "AuraCleansingModifier", fx_auracleansing_modifier, 0, -1 },
+ { "SummonDisable", fx_summon_disable, 0, -1 }, //unknown
+ { "AvatarRemovalModifier", fx_avatar_removal_modifier, 0, -1 },
+ { "BackstabModifier", fx_backstab_modifier, 0, -1 },
+ { "BerserkStage1Modifier", fx_berserkstage1_modifier, 0, -1 },
+ { "BerserkStage2Modifier", fx_berserkstage2_modifier, 0, -1 },
+ { "BlessNonCumulative", fx_set_bless_state, 0, -1 },
+ { "Bounce:School", fx_bounce_school, 0, -1 },
+ { "Bounce:SchoolDec", fx_bounce_school_dec, 0, -1 },
+ { "Bounce:SecondaryType", fx_bounce_secondary_type, 0, -1 },
+ { "Bounce:SecondaryTypeDec", fx_bounce_secondary_type_dec, 0, -1 },
+ { "Bounce:Spell", fx_bounce_spell, 0, -1 },
+ { "Bounce:SpellDec", fx_bounce_spell_dec, 0, -1 },
+ { "Bounce:SpellLevel", fx_bounce_spelllevel, 0, -1 },
+ { "Bounce:SpellLevelDec", fx_bounce_spelllevel_dec, 0, -1 },
+ { "Bounce:Opcode", fx_bounce_opcode, 0, -1 },
+ { "Bounce:Projectile", fx_bounce_projectile, 0, -1 },
+ { "CantUseItem", fx_generic_effect, EFFECT_NO_ACTOR, -1 },
+ { "CantUseItemType", fx_generic_effect, 0, -1 },
+ { "CanUseAnyItem", fx_can_use_any_item_modifier, 0, -1 },
+ { "CastFromList", fx_select_spell, 0, -1 },
+ { "CastingGlow", fx_casting_glow, 0, -1 },
+ { "CastingGlow2", fx_casting_glow, 0, -1 }, //used in iwd
+ { "CastingLevelModifier", fx_castinglevel_modifier, 0, -1 },
+ { "CastingSpeedModifier", fx_castingspeed_modifier, 0, -1 },
+ { "CastSpellOnCondition", fx_cast_spell_on_condition, 0, -1 },
+ { "ChangeBardSong", fx_change_bardsong, 0, -1 },
+ { "ChangeName", fx_change_name, 0, -1 },
+ { "ChangeWeather", fx_change_weather, EFFECT_NO_ACTOR, -1 },
+ { "ChantBadNonCumulative", fx_set_chantbad_state, 0, -1 },
+ { "ChantNonCumulative", fx_set_chant_state, 0, -1 },
+ { "ChaosShieldModifier", fx_chaos_shield_modifier, 0, -1 },
+ { "CharismaModifier", fx_charisma_modifier, 0, -1 },
+ { "CheckForBerserkModifier", fx_checkforberserk_modifier, 0, -1 },
+ { "ColdResistanceModifier", fx_cold_resistance_modifier, 0, -1 },
+ { "Color:BriefRGB", fx_brief_rgb, 0, -1 },
+ { "Color:GlowRGB", fx_glow_rgb, 0, -1 },
+ { "Color:DarkenRGB", fx_darken_rgb, 0, -1 },
+ { "Color:SetPalette", fx_set_color_gradient, 0, -1 },
+ { "Color:SetRGB", fx_set_color_rgb, 0, -1 },
+ { "Color:SetRGBGlobal", fx_set_color_rgb_global, 0, -1 }, //08
+ { "Color:PulseRGB", fx_set_color_pulse_rgb, 0, -1 }, //9
+ { "Color:PulseRGBGlobal", fx_set_color_pulse_rgb_global, 0, -1 }, //9
+ { "ConstitutionModifier", fx_constitution_modifier, 0, -1 },
+ { "ControlCreature", fx_set_charmed_state, 0, -1 }, //0xf1 same as charm
+ { "CreateContingency", fx_create_contingency, 0, -1 },
+ { "CriticalHitModifier", fx_critical_hit_modifier, 0, -1 },
+ { "CrushingResistanceModifier", fx_crushing_resistance_modifier, 0, -1 },
+ { "Cure:Berserk", fx_cure_berserk_state, 0, -1 },
+ { "Cure:Blind", fx_cure_blind_state, 0, -1 },
+ { "Cure:CasterHold", fx_unpause_caster, 0, -1 },
+ { "Cure:Confusion", fx_cure_confused_state, 0, -1 },
+ { "Cure:Deafness", fx_cure_deaf_state, 0, -1 },
+ { "Cure:Death", fx_cure_dead_state, 0, -1 },
+ { "Cure:Defrost", fx_cure_frozen_state, 0, -1 },
+ { "Cure:Disease", fx_cure_diseased_state, 0, -1 },
+ { "Cure:Feeblemind", fx_cure_feebleminded_state, 0, -1 },
+ { "Cure:Hold", fx_cure_hold_state, 0, -1 },
+ { "Cure:Imprisonment", fx_freedom, 0, -1 },
+ { "Cure:Infravision", fx_cure_infravision_state, 0, -1 },
+ { "Cure:Intoxication", fx_cure_intoxication, 0, -1 }, //0xa4 (iwd2 has this working)
+ { "Cure:Invisible", fx_cure_invisible_state, 0, -1 }, //0x2f
+ { "Cure:Invisible2", fx_cure_invisible_state, 0, -1 }, //0x74
+ //{ "Cure:ImprovedInvisible", fx_cure_improved_invisible_state, 0, -1 },
+ { "Cure:LevelDrain", fx_cure_leveldrain, 0, -1 }, //restoration
+ { "Cure:Nondetection", fx_cure_nondetection_state, 0, -1 },
+ { "Cure:Panic", fx_cure_panic_state, 0, -1 },
+ { "Cure:Petrification", fx_cure_petrified_state, 0, -1 },
+ { "Cure:Poison", fx_cure_poisoned_state, 0, -1 },
+ { "Cure:Sanctuary", fx_cure_sanctuary_state, 0, -1 },
+ { "Cure:Silence", fx_cure_silenced_state, 0, -1 },
+ { "Cure:Sleep", fx_cure_sleep_state, 0, -1 },
+ { "Cure:Stun", fx_cure_stun_state, 0, -1 },
+ { "CurrentHPModifier", fx_current_hp_modifier, EFFECT_DICED, -1 },
+ { "Damage", fx_damage, EFFECT_DICED, -1 },
+ { "DamageAnimation", fx_damage_animation, 0, -1 },
+ { "DamageBonusModifier", fx_damage_bonus_modifier, 0, -1 },
+ { "DamageLuckModifier", fx_damageluck_modifier, 0, -1 },
+ { "DamageVsCreature", fx_generic_effect, 0, -1 },
+ { "Death", fx_death, 0, -1 },
+ { "Death2", fx_death, 0, -1 }, //(iwd2 effect)
+ { "Death3", fx_death, 0, -1 }, //(iwd2 effect too, Banish)
+ { "DetectAlignment", fx_detect_alignment, 0, -1 },
+ { "DetectIllusionsModifier", fx_detect_illusion_modifier, 0, -1 },
+ { "DexterityModifier", fx_dexterity_modifier, 0, -1 },
+ { "DimensionDoor", fx_dimension_door, 0, -1 },
+ { "DisableButton", fx_disable_button, 0, -1 }, //sets disable button flag
+ { "DisableChunk", fx_disable_chunk_modifier, 0, -1 },
+ { "DisableOverlay", fx_disable_overlay_modifier, 0, -1 },
+ { "DisableCasting", fx_disable_spellcasting, 0, -1 },
+ { "Disintegrate", fx_disintegrate, 0, -1 },
+ { "DispelEffects", fx_dispel_effects, 0, -1 },
+ { "DispelSchool", fx_dispel_school, 0, -1 },
+ { "DispelSchoolOne", fx_dispel_school_one, 0, -1 },
+ { "DispelSecondaryType", fx_dispel_secondary_type, 0, -1 },
+ { "DispelSecondaryTypeOne", fx_dispel_secondary_type_one, 0, -1 },
+ { "DisplayString", fx_display_string, 0, -1 },
+ { "Dither", fx_dither, 0, -1 },
+ { "DontJumpModifier", fx_dontjump_modifier, 0, -1 },
+ { "DrainItems", fx_drain_items, 0, -1 },
+ { "DrainSpells", fx_drain_spells, 0, -1 },
+ { "DropWeapon", fx_drop_weapon, 0, -1 },
+ { "ElectricityResistanceModifier", fx_electricity_resistance_modifier, 0, -1 },
+ { "ExistanceDelayModifier", fx_existance_delay_modifier , 0, -1 }, //unknown
+ { "ExperienceModifier", fx_experience_modifier, 0, -1 },
+ { "ExploreModifier", fx_explore_modifier, 0, -1 },
+ { "FamiliarBond", fx_familiar_constitution_loss, 0, -1 },
+ { "FamiliarMarker", fx_familiar_marker, 0, -1 },
+ { "Farsee", fx_farsee, 0, -1 },
+ { "FatigueModifier", fx_fatigue_modifier, 0, -1 },
+ { "FindFamiliar", fx_find_familiar, 0, -1 },
+ { "FindTraps", fx_find_traps, 0, -1 },
+ { "FindTrapsModifier", fx_find_traps_modifier, 0, -1 },
+ { "FireResistanceModifier", fx_fire_resistance_modifier, 0, -1 },
+ { "FistDamageModifier", fx_fist_damage_modifier, 0, -1 },
+ { "FistHitModifier", fx_fist_to_hit_modifier, 0, -1 },
+ { "ForceSurgeModifier", fx_force_surge_modifier, 0, -1 },
+ { "ForceVisible", fx_force_visible, 0, -1 }, //not invisible but improved invisible
+ { "FreeAction", fx_cure_slow_state, 0, -1 },
+ { "GenerateWish", fx_generate_wish, 0, -1 },
+ { "GoldModifier", fx_gold_modifier, 0, -1 },
+ { "HideInShadowsModifier", fx_hide_in_shadows_modifier, 0, -1 },
+ { "HLA", fx_generic_effect, 0, -1 },
+ { "HolyNonCumulative", fx_set_holy_state, 0, -1 },
+ { "Icon:Disable", fx_disable_portrait_icon, 0, -1 },
+ { "Icon:Display", fx_display_portrait_icon, 0, -1 },
+ { "Icon:Remove", fx_remove_portrait_icon, 0, -1 },
+ { "Identify", fx_identify, 0, -1 },
+ { "IgnoreDialogPause", fx_ignore_dialogpause_modifier, 0, -1 },
+ { "IntelligenceModifier", fx_intelligence_modifier, 0, -1 },
+ { "IntoxicationModifier", fx_intoxication_modifier, 0, -1 },
+ { "InvisibleDetection", fx_see_invisible_modifier, 0, -1 },
+ { "Item:CreateDays", fx_create_item_days, 0, -1 },
+ { "Item:CreateInSlot", fx_create_item_in_slot, 0, -1 },
+ { "Item:CreateInventory", fx_create_inventory_item, 0, -1 },
+ { "Item:CreateMagic", fx_create_magic_item, 0, -1 },
+ { "Item:Equip", fx_equip_item, 0, -1 }, //71
+ { "Item:Remove", fx_remove_item, 0, -1 }, //70
+ { "Item:RemoveInventory", fx_remove_inventory_item, 0, -1 },
+ { "KillCreatureType", fx_kill_creature_type, 0, -1 },
+ { "LevelModifier", fx_level_modifier, 0, -1 },
+ { "LevelDrainModifier", fx_leveldrain_modifier, 0, -1 },
+ { "LoreModifier", fx_lore_modifier, 0, -1 },
+ { "LuckModifier", fx_luck_modifier, 0, -1 },
+ { "LuckCumulative", fx_luck_cumulative, 0, -1 },
+ { "LuckNonCumulative", fx_luck_non_cumulative, 0, -1 },
+ { "MagicalColdResistanceModifier", fx_magical_cold_resistance_modifier, 0, -1 },
+ { "MagicalFireResistanceModifier", fx_magical_fire_resistance_modifier, 0, -1 },
+ { "MagicalRest", fx_magical_rest, 0, -1 },
+ { "MagicDamageResistanceModifier", fx_magic_damage_resistance_modifier, 0, -1 },
+ { "MagicResistanceModifier", fx_magic_resistance_modifier, 0, -1 },
+ { "MassRaiseDead", fx_mass_raise_dead, EFFECT_NO_ACTOR, -1 },
+ { "MaximumHPModifier", fx_maximum_hp_modifier, EFFECT_DICED, -1 },
+ { "Maze", fx_maze, 0, -1 },
+ { "MeleeDamageModifier", fx_melee_damage_modifier, 0, -1 },
+ { "MeleeHitModifier", fx_melee_to_hit_modifier, 0, -1 },
+ { "MinimumHPModifier", fx_minimum_hp_modifier, 0, -1 },
+ { "MiscastMagicModifier", fx_miscast_magic_modifier, 0, -1 },
+ { "MissileDamageModifier", fx_missile_damage_modifier, 0, -1 },
+ { "MissileHitModifier", fx_missile_to_hit_modifier, 0, -1 },
+ { "MissilesResistanceModifier", fx_missiles_resistance_modifier, 0, -1 },
+ { "MirrorImage", fx_mirror_image, 0, -1 },
+ { "MirrorImageModifier", fx_mirror_image_modifier, 0, -1 },
+ { "ModifyGlobalVariable", fx_modify_global_variable, EFFECT_NO_ACTOR, -1 },
+ { "ModifyLocalVariable", fx_modify_local_variable, 0, -1 },
+ { "MonsterSummoning", fx_monster_summoning, EFFECT_NO_ACTOR, -1 },
+ { "MoraleBreakModifier", fx_morale_break_modifier, 0, -1 },
+ { "MoraleModifier", fx_morale_modifier, 0, -1 },
+ { "MovementRateModifier", fx_movement_modifier, 0, -1 }, //fast (7e)
+ { "MovementRateModifier2", fx_movement_modifier, 0, -1 },//slow (b0)
+ { "MovementRateModifier3", fx_movement_modifier, 0, -1 },//forced (IWD - 10a)
+ { "MovementRateModifier4", fx_movement_modifier, 0, -1 },//slow (IWD2 - 1b9)
+ { "MoveToArea", fx_move_to_area, 0, -1 }, //0xba
+ { "NoCircleState", fx_no_circle_state, 0, -1 },
+ { "NPCBump", fx_npc_bump, 0, -1 },
+ { "OffscreenAIModifier", fx_offscreenai_modifier, 0, -1 },
+ { "OffhandHitModifier", fx_left_to_hit_modifier, 0, -1 },
+ { "OpenLocksModifier", fx_open_locks_modifier, 0, -1 },
+ { "Overlay:Entangle", fx_set_entangle_state, 0, -1 },
+ { "Overlay:Grease", fx_set_grease_state, 0, -1 },
+ { "Overlay:MinorGlobe", fx_set_minorglobe_state, 0, -1 },
+ { "Overlay:Sanctuary", fx_set_sanctuary_state, 0, -1 },
+ { "Overlay:ShieldGlobe", fx_set_shieldglobe_state, 0, -1 },
+ { "Overlay:Web", fx_set_web_state, 0, -1 },
+ { "PauseTarget", fx_pause_target, 0, -1 }, //also known as casterhold
+ { "PickPocketsModifier", fx_pick_pockets_modifier, 0, -1 },
+ { "PiercingResistanceModifier", fx_piercing_resistance_modifier, 0, -1 },
+ { "PlayMovie", fx_play_movie, EFFECT_NO_ACTOR, -1 },
+ { "PlaySound", fx_playsound, EFFECT_NO_ACTOR, -1 },
+ { "PlayVisualEffect", fx_play_visual_effect, 0, -1 },
+ { "PoisonResistanceModifier", fx_poison_resistance_modifier, 0, -1 },
+ { "Polymorph", fx_polymorph, 0, -1 },
+ { "PortraitChange", fx_portrait_change, 0, -1 },
+ { "PowerWordKill", fx_power_word_kill, 0, -1 },
+ { "PowerWordSleep", fx_power_word_sleep, 0, -1 },
+ { "PowerWordStun", fx_power_word_stun, 0, -1 },
+ { "PriestSpellSlotsModifier", fx_bonus_priest_spells, 0, -1 },
+ { "Proficiency", fx_proficiency, 0, -1 },
+// { "Protection:Animation", fx_protection_from_animation, 0, -1 },
+ { "Protection:Animation", fx_generic_effect, 0, -1 },
+ { "Protection:Backstab", fx_no_backstab_modifier, 0, -1 },
+ { "Protection:Creature", fx_generic_effect, 0, -1 },
+ { "Protection:Opcode", fx_protection_opcode, 0, -1 },
+ { "Protection:Opcode2", fx_protection_opcode, 0, -1 },
+ { "Protection:Projectile",fx_protection_from_projectile, 0, -1 },
+ { "Protection:School",fx_generic_effect, 0, -1 },//overlay?
+ { "Protection:SchoolDec",fx_protection_school_dec, 0, -1 },//overlay?
+ { "Protection:SecondaryType",fx_protection_secondary_type, 0, -1 },//overlay?
+ { "Protection:SecondaryTypeDec",fx_protection_secondary_type_dec, 0, -1 },//overlay?
+ { "Protection:Spell",fx_resist_spell, 0, -1 },//overlay?
+ { "Protection:SpellDec",fx_resist_spell_dec, 0, -1 },//overlay?
+ { "Protection:SpellLevel",fx_protection_spelllevel, 0, -1 },//overlay?
+ { "Protection:SpellLevelDec",fx_protection_spelllevel_dec, 0, -1 },//overlay?
+ { "Protection:String", fx_generic_effect, 0, -1 },
+ { "Protection:Tracking", fx_protection_from_tracking, 0, -1 },
+ { "Protection:Turn", fx_protection_from_turn, 0, -1 },
+ { "Protection:Weapons", fx_immune_to_weapon, EFFECT_NO_ACTOR, -1 },
+ { "PuppetMarker", fx_puppet_marker, 0, -1 },
+ { "ProjectImage", fx_puppet_master, 0, -1 },
+ { "Reveal:Area", fx_reveal_area, EFFECT_NO_ACTOR, -1 },
+ { "Reveal:Creatures", fx_reveal_creatures, 0, -1 },
+ { "Reveal:Magic", fx_reveal_magic, 0, -1 },
+ { "Reveal:Tracks", fx_reveal_tracks, 0, -1 },
+ { "RemoveCurse", fx_remove_curse, 0, -1 },
+ { "RemoveImmunity", fx_remove_immunity, 0, -1 },
+ { "RemoveMapNote", fx_remove_map_note, EFFECT_NO_ACTOR, -1 },
+ { "RemoveProjectile", fx_remove_projectile, 0, -1 }, //removes effects from actor and area
+ { "RenableButton", fx_renable_button, 0, -1 }, //removes disable button flag
+ { "RemoveCreature", fx_remove_creature, EFFECT_NO_ACTOR, -1 },
+ { "ReplaceCreature", fx_replace_creature, 0, -1 },
+ { "ReputationModifier", fx_reputation_modifier, 0, -1 },
+ { "RestoreSpells", fx_restore_spell_level, 0, -1 },
+ { "RightHitModifier", fx_right_to_hit_modifier, 0, -1 },
+ { "SaveVsBreathModifier", fx_save_vs_breath_modifier, 0, -1 },
+ { "SaveVsDeathModifier", fx_save_vs_death_modifier, 0, -1 },
+ { "SaveVsPolyModifier", fx_save_vs_poly_modifier, 0, -1 },
+ { "SaveVsSpellsModifier", fx_save_vs_spell_modifier, 0, -1 },
+ { "SaveVsWandsModifier", fx_save_vs_wands_modifier, 0, -1 },
+ { "ScreenShake", fx_screenshake, EFFECT_NO_ACTOR, -1 },
+ { "ScriptingState", fx_scripting_state, 0, -1 },
+ { "Sequencer:Activate", fx_activate_spell_sequencer, 0, -1 },
+ { "Sequencer:Create", fx_create_spell_sequencer, 0, -1 },
+ { "Sequencer:Store", fx_store_spell_sequencer, 0, -1 },
+ { "SetAIScript", fx_set_ai_script, 0, -1 },
+ { "SetMapNote", fx_set_map_note, EFFECT_NO_ACTOR, -1 },
+ { "SetMeleeEffect", fx_generic_effect, 0, -1 },
+ { "SetRangedEffect", fx_generic_effect, 0, -1 },
+ { "SetTrap", fx_set_area_effect, 0, -1 },
+ { "SetTrapsModifier", fx_set_traps_modifier, 0, -1 },
+ { "SexModifier", fx_sex_modifier, 0, -1 },
+ { "SlashingResistanceModifier", fx_slashing_resistance_modifier, 0, -1 },
+ { "Sparkle", fx_sparkle, 0, -1 },
+ { "SpellDurationModifier", fx_spell_duration_modifier, 0, -1 },
+ { "Spell:Add", fx_add_innate, 0, -1 },
+ { "Spell:Cast", fx_cast_spell, 0, -1 },
+ { "Spell:CastPoint", fx_cast_spell_point, 0, -1 },
+ { "Spell:Learn", fx_learn_spell, 0, -1 },
+ { "Spell:Remove", fx_remove_spell, 0, -1 },
+ { "Spelltrap",fx_spelltrap , 0, -1 }, //overlay: spmagglo
+ { "State:Berserk", fx_set_berserk_state, 0, -1 },
+ { "State:Blind", fx_set_blind_state, 0, -1 },
+ { "State:Blur", fx_set_blur_state, 0, -1 },
+ { "State:Charmed", fx_set_charmed_state, EFFECT_NO_LEVEL_CHECK, -1 }, //0x05
+ { "State:Confused", fx_set_confused_state, 0, -1 },
+ { "State:Deafness", fx_set_deaf_state, 0, -1 },
+ { "State:DeafnessIWD2", fx_set_deaf_state_iwd2, 0, -1 }, //this is a modified version
+ { "State:Diseased", fx_set_diseased_state, 0, -1 },
+ { "State:Feeblemind", fx_set_feebleminded_state, 0, -1 },
+ { "State:Hasted", fx_set_hasted_state, 0, -1 },
+ { "State:Haste2", fx_set_hasted_state, 0, -1 },
+ { "State:Hold", fx_hold_creature, 0, -1 }, //175 (doesn't work in original iwd2)
+ { "State:Hold2", fx_hold_creature, 0, -1 },//185 (doesn't work in original iwd2)
+ { "State:Hold3", fx_hold_creature, 0, -1 },//109 iwd2
+ { "State:HoldNoIcon", fx_hold_creature_no_icon, 0, -1 }, //109
+ { "State:HoldNoIcon2", fx_hold_creature_no_icon, 0, -1 }, //0xfb (iwd/iwd2)
+ { "State:HoldNoIcon3", fx_hold_creature_no_icon, 0, -1 }, //0x1a8 (iwd2)
+ { "State:Imprisonment", fx_imprisonment, 0, -1 },
+ { "State:Infravision", fx_set_infravision_state, 0, -1 },
+ { "State:Invisible", fx_set_invisible_state, 0, -1 }, //both invis or improved invis
+ { "State:Nondetection", fx_set_nondetection_state, 0, -1 },
+ { "State:Panic", fx_set_panic_state, 0, -1 },
+ { "State:Petrification", fx_set_petrified_state, 0, -1 },
+ { "State:Poisoned", fx_set_poisoned_state, 0, -1 },
+ { "State:Regenerating", fx_set_regenerating_state, 0, -1 },
+ { "State:Silenced", fx_set_silenced_state, 0, -1 },
+ { "State:Helpless", fx_set_unconscious_state, 0, -1 },
+ { "State:Sleep", fx_set_unconscious_state, 0, -1 },
+ { "State:Slowed", fx_set_slowed_state, 0, -1 },
+ { "State:Stun", fx_set_stun_state, 0, -1 },
+ { "StealthModifier", fx_stealth_modifier, 0, -1 },
+ { "StoneSkinModifier", fx_stoneskin_modifier, 0, -1 },
+ { "StoneSkin2Modifier", fx_golem_stoneskin_modifier, 0, -1 },
+ { "StrengthModifier", fx_strength_modifier, 0, -1 },
+ { "StrengthBonusModifier", fx_strength_bonus_modifier, 0, -1 },
+ { "SummonCreature", fx_summon_creature, EFFECT_NO_ACTOR, -1 },
+ { "RandomTeleport", fx_teleport_field, 0, -1 },
+ { "TeleportToTarget", fx_teleport_to_target, 0, -1 },
+ { "TimelessState", fx_timeless_modifier, 0, -1 },
+ { "Timestop", fx_timestop, 0, -1 },
+ { "TitleModifier", fx_title_modifier, 0, -1 },
+ { "ToHitModifier", fx_to_hit_modifier, 0, -1 },
+ { "ToHitBonusModifier", fx_to_hit_bonus_modifier, 0, -1 },
+ { "ToHitVsCreature", fx_generic_effect, 0, -1 },
+ { "TrackingModifier", fx_tracking_modifier, 0, -1 },
+ { "TransparencyModifier", fx_transparency_modifier, 0, -1 },
+ { "Unknown", fx_unknown, EFFECT_NO_ACTOR, -1 },
+ { "Unlock", fx_knock, EFFECT_NO_ACTOR, -1 }, //open doors/containers
+ { "UnsummonCreature", fx_unsummon_creature, 0, -1 },
+ { "Variable:StoreLocalVariable", fx_local_variable, 0, -1 },
+ { "VisualAnimationEffect", fx_visual_animation_effect, 0, -1 }, //unknown
+ { "VisualRangeModifier", fx_visual_range_modifier, 0, -1 },
+ { "VisualSpellHit", fx_visual_spell_hit, 0, -1 },
+ { "WildSurgeModifier", fx_wild_surge_modifier, 0, -1 },
+ { "WingBuffet", fx_wing_buffet, 0, -1 },
+ { "WisdomModifier", fx_wisdom_modifier, 0, -1 },
+ { "WizardSpellSlotsModifier", fx_bonus_wizard_spells, 0, -1 },
+ { NULL, NULL, 0, 0 },
+};
+
+static void Cleanup()
+{
+ core->FreeResRefTable(casting_glows, cgcount);
+ core->FreeResRefTable(spell_hits, shcount);
+ if(spell_abilities) free(spell_abilities);
+ spell_abilities=NULL;
+ if(polymorph_stats) free(polymorph_stats);
+ polymorph_stats=NULL;
+}
+
+void RegisterCoreOpcodes()
+{
+ core->RegisterOpcodes( sizeof( effectnames ) / sizeof( EffectDesc ) - 1, effectnames );
+ enhanced_effects=!!core->HasFeature(GF_ENHANCED_EFFECTS);
+ pstflags=!!core->HasFeature(GF_PST_STATE_FLAGS);
+ default_spell_hit.SequenceFlags|=IE_VVC_BAM;
+}
+
+
+static inline void SetGradient(Actor *target, ieDword gradient)
+{
+ gradient |= (gradient <<16);
+ gradient |= (gradient <<8);
+ for(int i=0;i<7;i++) {
+ STAT_SET(IE_COLORS+i, gradient);
+ }
+}
+
+static inline void HandleBonus(Actor *target, int stat, int mod, int mode)
+{
+ if (mode==FX_DURATION_INSTANT_PERMANENT) {
+ if (target->IsReverseToHit()) {
+ BASE_SUB( stat, mod );
+ } else {
+ BASE_ADD( stat, mod );
+ }
+ return;
+ }
+ if (target->IsReverseToHit()) {
+ STAT_SUB( stat, mod );
+ } else {
+ STAT_ADD( stat, mod );
+ }
+}
+
+//whoseeswho:
+#define ENEMY_SEES_ORIGIN 1
+#define ORIGIN_SEES_ENEMY 2
+
+inline Actor *GetNearestEnemyOf(Map *map, Actor *origin, int whoseeswho)
+{
+ //determining the allegiance of the origin
+ int type = GetGroup(origin);
+
+ //neutral has no enemies
+ if (type==2) {
+ return NULL;
+ }
+
+ Targets *tgts = new Targets();
+
+ int i = map->GetActorCount(true);
+ Actor *ac;
+ while (i--) {
+ ac=map->GetActor(i,true);
+ int distance = Distance(ac, origin);
+ if (whoseeswho&ENEMY_SEES_ORIGIN) {
+ if (!CanSee(ac, origin, true, GA_NO_DEAD)) {
+ continue;
+ }
+ }
+ if (whoseeswho&ORIGIN_SEES_ENEMY) {
+ if (!CanSee(ac, origin, true, GA_NO_DEAD)) {
+ continue;
+ }
+ }
+
+ if (type) { //origin is PC
+ if (ac->GetStat(IE_EA) >= EA_EVILCUTOFF) {
+ tgts->AddTarget(ac, distance, GA_NO_DEAD);
+ }
+ }
+ else {
+ if (ac->GetStat(IE_EA) <= EA_GOODCUTOFF) {
+ tgts->AddTarget(ac, distance, GA_NO_DEAD);
+ }
+ }
+ }
+ ac = (Actor *) tgts->GetTarget(0, ST_ACTOR);
+ delete tgts;
+ return ac;
+}
+
+//resurrect code used in many places
+void Resurrect(Scriptable *Owner, Actor *target, Effect *fx, Point &p)
+{
+ Scriptable *caster = GetCasterObject();
+ if (!caster) {
+ caster = Owner;
+ }
+ Map *area = caster->GetCurrentArea();
+
+ if (area && target->GetCurrentArea()!=area) {
+ MoveBetweenAreasCore(target, area->GetScriptName(), p, fx->Parameter2, true);
+ }
+ target->Resurrect();
+}
+
+
+// handles the percentage damage spread over time by converting it to absolute damage
+inline void HandlePercentageDamage(Effect *fx, Actor *target) {
+ if (fx->Parameter2 == RPD_PERCENT && fx->FirstApply) {
+ // distribute the damage to one second intervals
+ int seconds = (fx->Duration - core->GetGame()->GameTime) / AI_UPDATE_TIME;
+ fx->Parameter1 = target->GetStat(IE_MAXHITPOINTS) * fx->Parameter1 / 100 / seconds;
+ }
+}
+// Effect opcodes
+
+// 0x00 ACVsDamageTypeModifier
+int fx_ac_vs_damage_type_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_ac_vs_damage_type_modifier (%2d): AC Modif: %d ; Type: %d ; MinLevel: %d ; MaxLevel: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2, (int) fx->DiceSides, (int) fx->DiceThrown );
+ //check level was pulled outside as a common functionality
+ //CHECK_LEVEL();
+
+ // it is a bitmask
+ int type = fx->Parameter2;
+ if (type == 0) {
+ HandleBonus(target, IE_ARMORCLASS, fx->Parameter1, fx->TimingMode);
+ return FX_PERMANENT;
+ }
+
+ //convert to signed so -1 doesn't turn to an astronomical number
+ if (type == 16) {
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ if ((signed)BASE_GET( IE_ARMORCLASS) > (signed)fx->Parameter1) {
+ BASE_SET( IE_ARMORCLASS, fx->Parameter1 );
+ }
+ } else {
+ if ((signed)STAT_GET( IE_ARMORCLASS) > (signed)fx->Parameter1) {
+ STAT_SET( IE_ARMORCLASS, fx->Parameter1 );
+ }
+ }
+ return FX_INSERT;
+ }
+
+ //the original engine did work with the combination of these bits
+ //but since it crashed, we are not bound to the same rules
+ if (type & 1) {
+ HandleBonus(target, IE_ACCRUSHINGMOD, fx->Parameter1, fx->TimingMode);
+ }
+ if (type & 2) {
+ HandleBonus(target, IE_ACMISSILEMOD, fx->Parameter1, fx->TimingMode);
+ }
+ if (type & 4) {
+ HandleBonus(target, IE_ACPIERCINGMOD, fx->Parameter1, fx->TimingMode);
+ }
+ if (type & 8) {
+ HandleBonus(target, IE_ACSLASHINGMOD, fx->Parameter1, fx->TimingMode);
+ }
+
+ return FX_PERMANENT;
+}
+
+// 0x01 AttacksPerRoundModifier
+int fx_attacks_per_round_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_attacks_per_round_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ int tmp = (signed) fx->Parameter1;
+ if (fx->Parameter2!=2) {
+ if (tmp>10) tmp=10;
+ else if (tmp<-10) tmp=-10;
+ tmp <<= 1;
+ if (tmp>10) tmp-=11;
+ else if (tmp<-10) tmp+=11;
+ }
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_MOD_VAR(IE_NUMBEROFATTACKS, tmp);
+ } else {
+ STAT_MOD_VAR(IE_NUMBEROFATTACKS, tmp);
+ }
+ return FX_PERMANENT;
+}
+
+// 0x02 Cure:Sleep (Awaken)
+// this effect clears the STATE_SLEEP (1) bit, but clearing it alone wouldn't remove the
+// unconscious effect, which is combined with STATE_HELPLESS (0x20+1)
+static EffectRef fx_set_sleep_state_ref = { "State:Helpless", -1 };
+//this reference is used by many other effects
+static EffectRef fx_display_portrait_icon_ref = { "Icon:Display", -1 };
+
+int fx_cure_sleep_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_sleep_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_SLEEP );
+ target->fxqueue.RemoveAllEffects(fx_set_sleep_state_ref);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_display_portrait_icon_ref, PI_SLEEP);
+ return FX_NOT_APPLIED;
+}
+
+// 0x03 State:Berserk
+// this effect sets the STATE_BERSERK bit, but bg2 actually ignores the bit
+int fx_set_berserk_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_berserk_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ // atleast how and bg2 allow this to only work on pcs
+ if (!core->HasFeature(GF_3ED_RULES) && !target->InParty) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (fx->FirstApply) {
+ target->inventory.EquipBestWeapon(EQUIP_MELEE);
+ }
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_STATE_SET( STATE_BERSERK );
+ } else {
+ STATE_SET( STATE_BERSERK );
+ }
+
+ switch(fx->Parameter2) {
+ case 1: //always berserk
+ target->SetSpellState(SS_BERSERK);
+ default:
+ target->AddPortraitIcon(PI_BERSERK);
+ break;
+ case 2: //blood rage
+ target->SetSpellState(SS_BERSERK);
+ //immunity to effects:
+ //5 charm
+ //0x11 heal
+ //0x18 panic
+ //0x27 sleep
+ //0x2d stun
+ //0x6d hold
+ //0x80 confusion
+ //400 hopelessness
+ //
+ target->SetSpellState(SS_BLOODRAGE);
+ target->SetSpellState(SS_NOHPINFO);
+ target->SetColorMod(0xff, RGBModifier::ADD, 15, 128, 0, 0);
+ target->AddPortraitIcon(PI_BLOODRAGE);
+ break;
+ }
+ return FX_PERMANENT;
+}
+
+// 0x04 Cure:Berserk
+// this effect clears the STATE_BERSERK (2) bit, but bg2 actually ignores the bit
+// it also removes effect 04
+static EffectRef fx_set_berserk_state_ref = { "State:Berserk", -1 };
+
+int fx_cure_berserk_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_berserk_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_BERSERK );
+ target->fxqueue.RemoveAllEffects(fx_set_berserk_state_ref);
+ return FX_NOT_APPLIED;
+}
+
+// 0x05 State:Charmed
+// 0xf1 ControlCreature (iwd2)
+int fx_set_charmed_state (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_charmed_state (%2d): General: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //blood rage berserking gives immunity to charm (in iwd2)
+ if (target->HasSpellState(SS_BLOODRAGE)) {
+ return FX_NOT_APPLIED;
+ }
+
+ //protection from evil gives immunity to charm (in iwd2)
+ if (target->HasSpellState(SS_PROTFROMEVIL)) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (fx->Parameter1 && (STAT_GET(IE_GENERAL)!=fx->Parameter1)) {
+ return FX_NOT_APPLIED;
+ }
+
+ bool playercharmed;
+ bool casterenemy;
+ if (fx->FirstApply) {
+ Scriptable *caster = GetCasterObject();
+ if (!caster) caster = Owner;
+ if (caster->Type==ST_ACTOR) {
+ casterenemy = ((Actor *) caster)->GetStat(IE_EA)>EA_GOODCUTOFF; //or evilcutoff?
+ } else {
+ casterenemy = target->GetStat(IE_EA)>EA_GOODCUTOFF;
+ }
+ fx->DiceThrown=casterenemy;
+
+ playercharmed = target->InParty;
+ fx->DiceSides = playercharmed;
+ } else {
+ casterenemy = fx->DiceThrown;
+ playercharmed = fx->DiceSides;
+ }
+
+
+ switch (fx->Parameter2) {
+ case 0: //charmed (target neutral after charm)
+ if (fx->FirstApply) {
+ displaymsg->DisplayConstantStringName(STR_CHARMED, 0xf0f0f0, target);
+ }
+ case 1000:
+ break;
+ case 1: //charmed (target hostile after charm)
+ if (fx->FirstApply) {
+ displaymsg->DisplayConstantStringName(STR_CHARMED, 0xf0f0f0, target);
+ }
+ case 1001:
+ if (!target->InParty) {
+ target->SetBaseNoPCF(IE_EA, EA_ENEMY);
+ }
+ break;
+ case 2: //dire charmed (target neutral after charm)
+ if (fx->FirstApply) {
+ displaymsg->DisplayConstantStringName(STR_DIRECHARMED, 0xf0f0f0, target);
+ }
+ case 1002:
+ break;
+ case 3: //dire charmed (target hostile after charm)
+ if (fx->FirstApply) {
+ displaymsg->DisplayConstantStringName(STR_DIRECHARMED, 0xf0f0f0, target);
+ }
+ case 1003:
+ if (!target->InParty) {
+ target->SetBaseNoPCF(IE_EA, EA_ENEMY);
+ }
+ break;
+ case 4: //controlled by cleric
+ if (fx->FirstApply) {
+ displaymsg->DisplayConstantStringName(STR_CONTROLLED, 0xf0f0f0, target);
+ }
+ case 1004:
+ if (!target->InParty) {
+ target->SetBaseNoPCF(IE_EA, EA_ENEMY);
+ }
+ break;
+ case 5: //thrall (typo comes from original engine doc)
+ if (fx->FirstApply) {
+ displaymsg->DisplayConstantStringName(STR_CHARMED, 0xf0f0f0, target);
+ }
+ case 1005:
+ STAT_SET(IE_EA, EA_ENEMY );
+ STAT_SET(IE_THRULLCHARM, 1);
+ return FX_PERMANENT;
+ }
+
+ STATE_SET( STATE_CHARMED );
+ if (playercharmed) {
+ STAT_SET_PCF( IE_EA, casterenemy?EA_CHARMEDPC:EA_CHARMED );
+ } else {
+ STAT_SET_PCF( IE_EA, casterenemy?EA_ENEMY:EA_CHARMED );
+ }
+ //don't stick if permanent
+ return FX_PERMANENT;
+}
+
+// 0x06 CharismaModifier
+int fx_charisma_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_charisma_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_MOD( IE_CHR );
+ } else {
+ STAT_MOD( IE_CHR );
+ }
+ return FX_PERMANENT;
+}
+
+// 0x07 Color:SetPalette
+// this effect might not work in pst, they don't have separate weapon slots
+int fx_set_color_gradient (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_color_gradient (%2d): Gradient: %d, Location: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ EffectQueue::HackColorEffects(target, fx);
+ target->SetColor( fx->Parameter2, fx->Parameter1 );
+ return FX_APPLIED;
+}
+
+// 08 Color:SetRGB
+int fx_set_color_rgb (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_color_rgb (%2d): RGB: %x, Location: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ EffectQueue::HackColorEffects(target, fx);
+ ieDword location = fx->Parameter2 & 0xff;
+ target->SetColorMod(location, RGBModifier::ADD, -1, fx->Parameter1 >> 8,
+ fx->Parameter1 >> 16, fx->Parameter1 >> 24);
+
+ return FX_APPLIED;
+}
+// 08 Color:SetRGBGlobal
+int fx_set_color_rgb_global (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_color_rgb_global (%2d): RGB: %x, Location: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ target->SetColorMod(0xff, RGBModifier::ADD, -1, fx->Parameter1 >> 8,
+ fx->Parameter1 >> 16, fx->Parameter1 >> 24);
+
+ return FX_APPLIED;
+}
+
+// 09 Color:PulseRGB
+int fx_set_color_pulse_rgb (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_color_pulse_rgb (%2d): RGB: %x, Location: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ EffectQueue::HackColorEffects(target, fx);
+ ieDword location = fx->Parameter2 & 0xff;
+ int speed = (fx->Parameter2 >> 16) & 0xFF;
+ target->SetColorMod(location, RGBModifier::ADD, speed,
+ fx->Parameter1 >> 8, fx->Parameter1 >> 16,
+ fx->Parameter1 >> 24);
+
+ return FX_APPLIED;
+}
+
+// 09 Color:PulseRGBGlobal (pst variant)
+int fx_set_color_pulse_rgb_global (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_color_pulse_rgb_global (%2d): RGB: %x, Location: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ int speed = (fx->Parameter2 >> 16) & 0xFF;
+ target->SetColorMod(0xff, RGBModifier::ADD, speed,
+ fx->Parameter1 >> 8, fx->Parameter1 >> 16,
+ fx->Parameter1 >> 24);
+
+ return FX_APPLIED;
+}
+
+// 0x0A ConstitutionModifier
+int fx_constitution_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_constitution_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_MOD( IE_CON );
+ } else {
+ STAT_MOD( IE_CON );
+ }
+ return FX_PERMANENT;
+}
+
+// 0x0B Cure:Poison
+static EffectRef fx_poisoned_state_ref = { "State:Poisoned", -1 };
+
+int fx_cure_poisoned_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_poisoned_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //all three steps are present in bg2 and iwd2
+ BASE_STATE_CURE( STATE_POISONED );
+ target->fxqueue.RemoveAllEffects( fx_poisoned_state_ref );
+ target->fxqueue.RemoveAllEffectsWithParam(fx_display_portrait_icon_ref, PI_POISONED);
+ return FX_NOT_APPLIED;
+}
+
+// 0x0c Damage
+// this is a very important effect
+int fx_damage (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_damage (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //save for half damage type
+ ieDword damagetype = fx->Parameter2>>16;
+ ieDword modtype = fx->Parameter2&3;
+ if (modtype==3) {
+ modtype&=~3;
+ }
+ Scriptable *caster = GetCasterObject();
+
+ target->Damage(fx->Parameter1, damagetype, caster, modtype);
+ //this effect doesn't stick
+ return FX_NOT_APPLIED;
+}
+
+// 0x0d Death
+int fx_death (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_death (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword damagetype = 0;
+ switch (fx->Parameter2) {
+ case 1:
+ BASE_STATE_SET(STATE_FLAME); //not sure, should be charred
+ damagetype = DAMAGE_FIRE;
+ break;
+ case 2:
+ damagetype = DAMAGE_CRUSHING;
+ break;
+ case 4:
+ damagetype = DAMAGE_CRUSHING;
+ break;
+ case 8:
+ damagetype = DAMAGE_CRUSHING|DAMAGE_CHUNKING;
+ break;
+ case 16:
+ BASE_STATE_SET(STATE_PETRIFIED);
+ damagetype = DAMAGE_CRUSHING;
+ break;
+ case 32:
+ BASE_STATE_SET(STATE_FROZEN);
+ damagetype = DAMAGE_COLD;
+ break;
+ case 64:
+ BASE_STATE_SET(STATE_PETRIFIED);
+ damagetype = DAMAGE_CRUSHING|DAMAGE_CHUNKING;
+ break;
+ case 128:
+ BASE_STATE_SET(STATE_FROZEN);
+ damagetype = DAMAGE_COLD|DAMAGE_CHUNKING;
+ break;
+ case 256:
+ damagetype = DAMAGE_ELECTRICITY;
+ break;
+ case 512:
+
+ default:
+ damagetype = DAMAGE_ACID;
+ }
+ //these two bits are turned off on death
+ BASE_STATE_CURE(STATE_FROZEN|STATE_PETRIFIED);
+
+ target->Damage(0, damagetype, Owner);
+ //death has damage type too
+ target->Die(Owner);
+ //this effect doesn't stick
+ return FX_NOT_APPLIED;
+}
+
+// 0xE Cure:Defrost
+int fx_cure_frozen_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_frozen_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_FROZEN );
+ return FX_NOT_APPLIED;
+}
+
+// 0x0F DexterityModifier
+
+#define CSA_DEX 0
+#define CSA_STR 1
+#define CSA_CNT 2
+int SpellAbilityDieRoll(Actor *target, int which)
+{
+ if (which>=CSA_CNT) return 6;
+
+ ieDword cls = STAT_GET(IE_CLASS);
+ if (!spell_abilities) {
+ AutoTable tab("clssplab");
+ if (!tab) {
+ spell_abilities = (int *) malloc(sizeof(int)*CSA_CNT);
+ for (int ab=0;ab<CSA_CNT;ab++) {
+ spell_abilities[ab*splabcount]=6;
+ }
+ splabcount=1;
+ return 6;
+ }
+ splabcount = tab->GetRowCount();
+ spell_abilities=(int *) malloc(sizeof(int)*splabcount*CSA_CNT);
+ for (int ab=0;ab<CSA_CNT;ab++) {
+ for (ieDword i=0;i<splabcount;i++) {
+ spell_abilities[ab*splabcount+i]=atoi(tab->QueryField(i,ab));
+ }
+ }
+ }
+ if (cls>=splabcount) cls=0;
+ return spell_abilities[which*splabcount+cls];
+}
+
+int fx_dexterity_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_dexterity_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ ////how cat's grace: value is based on class
+ if (fx->Parameter2==3) {
+ fx->Parameter1 = core->Roll(1,SpellAbilityDieRoll(target,0),0);
+ fx->Parameter2 = 0;
+ }
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_MOD( IE_DEX );
+ } else {
+ STAT_MOD( IE_DEX );
+ }
+ return FX_PERMANENT;
+}
+
+static EffectRef fx_set_slow_state_ref = { "State:Slowed", -1 };
+// 0x10 State:Hasted
+// this function removes slowed state, or sets hasted state
+int fx_set_hasted_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_hasted_state (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ target->fxqueue.RemoveAllEffects(fx_set_slow_state_ref);
+ target->fxqueue.RemoveAllEffectsWithParam( fx_display_portrait_icon_ref, PI_SLOWED );
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_STATE_CURE( STATE_SLOWED );
+ BASE_STATE_SET( STATE_HASTED );
+ } else {
+ STATE_CURE( STATE_SLOWED );
+ STATE_SET( STATE_HASTED );
+ }
+ target->NewStat(IE_MOVEMENTRATE, 200, MOD_PERCENT);
+ switch (fx->Parameter2) {
+ case 0: //normal haste
+ target->AddPortraitIcon(PI_HASTED);
+ STAT_SET(IE_IMPROVEDHASTE,0);
+ STAT_SET(IE_ATTACKNUMBERDOUBLE,0);
+ STAT_ADD(IE_NUMBEROFATTACKS, 2);
+ // -2 initiative bonus
+ STAT_ADD(IE_PHYSICALSPEED, 2);
+ break;
+ case 1://improved haste
+ target->AddPortraitIcon(PI_IMPROVEDHASTE);
+ STAT_SET(IE_IMPROVEDHASTE,1);
+ STAT_SET(IE_ATTACKNUMBERDOUBLE,0);
+ target->NewStat(IE_NUMBEROFATTACKS, 200, MOD_PERCENT);
+ // -2 initiative bonus
+ STAT_ADD(IE_PHYSICALSPEED, 2);
+ break;
+ case 2://speed haste only
+ target->AddPortraitIcon(PI_HASTED);
+ STAT_SET(IE_IMPROVEDHASTE,0);
+ STAT_SET(IE_ATTACKNUMBERDOUBLE,1);
+ break;
+ }
+
+ return FX_PERMANENT;
+}
+
+// 0x11 CurrentHPModifier
+int fx_current_hp_modifier (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_current_hp_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (fx->Parameter2&0x10000) {
+ Point p(fx->PosX, fx->PosY);
+ Resurrect(Owner, target, fx, p);
+ }
+ if (fx->Parameter2&0x20000) {
+ target->fxqueue.RemoveAllNonPermanentEffects();
+ }
+ //Cannot heal bloodrage
+ if (target->HasSpellState(SS_BLOODRAGE)) {
+ return FX_NOT_APPLIED;
+ }
+
+ //current hp percent is relative to modified max hp
+ switch(fx->Parameter2&0xffff) {
+ case MOD_ADDITIVE:
+ case MOD_ABSOLUTE:
+ target->NewBase( IE_HITPOINTS, fx->Parameter1, fx->Parameter2&0xffff);
+ break;
+ case MOD_PERCENT:
+ target->NewBase( IE_HITPOINTS, target->GetSafeStat(IE_MAXHITPOINTS)*fx->Parameter1/100, MOD_ABSOLUTE);
+ }
+ //never stay permanent
+ return FX_NOT_APPLIED;
+}
+
+// 0x12 MaximumHPModifier
+// 0 and 3 differ in that 3 doesn't modify current hitpoints
+// 1,4 and 2,5 are analogous to them, but with different modifiers
+int fx_maximum_hp_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_maximum_hp_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //state_exploding is different in PST, probably not needed anyway
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN|STATE_ACID|STATE_FLAME) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (BASE_GET(IE_HITPOINTS)<=0 ) {
+ return FX_NOT_APPLIED;
+ }
+
+ bool base = fx->TimingMode==FX_DURATION_INSTANT_PERMANENT;
+
+ switch (fx->Parameter2) {
+ case 0:
+ // random value Parameter1 is set by level_check in EffectQueue
+ if (base) {
+ BASE_ADD( IE_MAXHITPOINTS, fx->Parameter1 );
+ BASE_ADD( IE_HITPOINTS, fx->Parameter1 );
+ } else {
+ STAT_ADD( IE_MAXHITPOINTS, fx->Parameter1 );
+ if (fx->FirstApply) {
+ BASE_ADD( IE_HITPOINTS, fx->Parameter1 );
+ }
+ }
+ break;
+ case 3: // no current hp bonus
+ // random value Parameter1 is set by level_check in EffectQueue
+ if (base) {
+ BASE_ADD( IE_MAXHITPOINTS, fx->Parameter1 );
+ } else {
+ STAT_ADD( IE_MAXHITPOINTS, fx->Parameter1 );
+ }
+ break;
+ case 1: // with current hp bonus, but unimplemented in the original
+ case 4:
+ if (base) {
+ BASE_SET( IE_MAXHITPOINTS, fx->Parameter1 );
+ } else {
+ STAT_SET( IE_MAXHITPOINTS, fx->Parameter1 );
+ }
+ break;
+ case 2:
+ if (base) {
+ BASE_MUL(IE_MAXHITPOINTS, fx->Parameter1 );
+ BASE_MUL(IE_HITPOINTS, fx->Parameter1 );
+ } else {
+ target->NewStat( IE_MAXHITPOINTS, target->GetStat(IE_MAXHITPOINTS)*fx->Parameter1/100, MOD_ABSOLUTE);
+ if (fx->FirstApply) {
+ target->NewBase( IE_HITPOINTS, target->GetSafeStat(IE_HITPOINTS)*fx->Parameter1/100, MOD_ABSOLUTE);
+ }
+ }
+ break;
+ case 5: // no current hp bonus
+ if (base) {
+ BASE_MUL( IE_MAXHITPOINTS, fx->Parameter1 );
+ } else {
+ STAT_MUL( IE_MAXHITPOINTS, fx->Parameter1 );
+ }
+ break;
+ }
+ return FX_PERMANENT;
+}
+
+// 0x13 IntelligenceModifier
+int fx_intelligence_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_intelligence_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_MOD( IE_INT );
+ } else {
+ STAT_MOD( IE_INT );
+ }
+ return FX_PERMANENT;
+}
+
+// 0x14 State:Invisible
+// this is more complex, there is a half-invisibility state
+// and there is a hidden state
+int fx_set_invisible_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ switch (fx->Parameter2) {
+ case 0:
+ if (pstflags) {
+ STATE_SET( STATE_PST_INVIS );
+ } else {
+ STATE_SET( STATE_INVISIBLE );
+ }
+ STAT_ADD(IE_TOHIT, 4);
+ break;
+ case 1:
+ STATE_SET( STATE_INVIS2 );
+ HandleBonus(target, IE_ARMORCLASS, 4, fx->TimingMode);
+ break;
+ default:
+ break;
+ }
+ ieDword Trans = fx->Parameter4;
+ if (fx->Parameter3) {
+ if (Trans>=240) {
+ fx->Parameter3=0;
+ } else {
+ Trans+=16;
+ }
+ } else {
+ if (Trans<=32) {
+ fx->Parameter3=1;
+ } else {
+ Trans-=16;
+ }
+ }
+ fx->Parameter4=Trans;
+ STAT_SET( IE_TRANSLUCENT, Trans);
+ return FX_APPLIED;
+}
+
+// 0x15 LoreModifier
+int fx_lore_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_lore_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_LORE );
+ return FX_APPLIED;
+}
+
+// 0x16 LuckModifier
+int fx_luck_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_luck_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_LUCK );
+ STAT_MOD( IE_DAMAGELUCK );
+ return FX_APPLIED;
+}
+
+// 0x17 MoraleModifier
+int fx_morale_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_morale_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //FIXME: in bg2 this is hacked to set param1=10, param2=1, we might need some flag for this
+ STAT_MOD( IE_MORALE );
+ return FX_APPLIED;
+}
+
+// 0x18 State:Panic
+int fx_set_panic_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_panic_state (%2d)\n", fx->Opcode );
+
+ if (target->HasSpellState(SS_BLOODRAGE)) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_STATE_SET( STATE_PANIC );
+ } else {
+ STATE_SET( STATE_PANIC );
+ }
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_PANIC);
+ }
+ return FX_PERMANENT;
+}
+
+// 0x19 State:Poisoned
+int fx_set_poisoned_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_poisoned_state (%2d): Damage: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STATE_SET( STATE_POISONED );
+
+ ieDword damage;
+ int tmp = fx->Parameter1;
+
+ HandlePercentageDamage(fx, target);
+
+ switch(fx->Parameter2) {
+ case RPD_ROUNDS:
+ tmp *= core->Time.round_sec;
+ damage = 1;
+ break;
+ case RPD_TURNS:
+ tmp *= core->Time.turn_sec;
+ damage = 1;
+ break;
+ case RPD_SECONDS:
+ damage = 1;
+ break;
+ case RPD_PERCENT: // handled in HandlePercentageDamage
+ case RPD_POINTS:
+ tmp = 1; // hit points per second
+ damage = fx->Parameter1;
+ break;
+ default:
+ tmp = 1;
+ damage = 1;
+ break;
+ }
+
+ // all damage is at most per-second
+ tmp *= AI_UPDATE_TIME;
+ if (tmp && (core->GetGame()->GameTime%tmp)) {
+ return FX_APPLIED;
+ }
+
+ Scriptable *caster = GetCasterObject();
+
+ //percent
+ target->Damage(damage, DAMAGE_POISON, caster);
+ return FX_APPLIED;
+}
+
+// 0x1a RemoveCurse
+static EffectRef fx_apply_effect_curse_ref = { "ApplyEffectCurse", -1 };
+static EffectRef fx_pst_jumble_curse_ref = { "JumbleCurse", -1 };
+
+// gemrb extension: if the resource field is filled, it will remove curse only from the specified item
+int fx_remove_curse (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_curse (%2d): Resource: %s Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+
+ switch(fx->Parameter2)
+ {
+ case 1:
+ //this is pst specific
+ target->fxqueue.RemoveAllEffects(fx_pst_jumble_curse_ref);
+ break;
+ default:
+ Inventory *inv = &target->inventory;
+ int i = target->inventory.GetSlotCount();
+ while(i--) {
+ //does this slot need unequipping
+ if (core->QuerySlotEffects(i) ) {
+ if (fx->Resource[0] && strnicmp(inv->GetSlotItem(i)->ItemResRef, fx->Resource,8) ) {
+ continue;
+ }
+ if (!(inv->GetItemFlag(i)&IE_INV_ITEM_CURSED)) {
+ continue;
+ }
+ inv->ChangeItemFlag(i, IE_INV_ITEM_CURSED, BM_NAND);
+ if (inv->UnEquipItem(i,true)) {
+ CREItem *tmp = inv->RemoveItem(i);
+ if(inv->AddSlotItem(tmp,-3)!=ASI_SUCCESS) {
+ //if the item couldn't be placed in the inventory, then put it back to the original slot
+ inv->SetSlotItem(tmp,i);
+ //and drop it in the area. (If there is no area, then the item will stay in the inventory)
+ target->DropItem(i,0);
+ }
+ }
+ }
+ }
+ target->fxqueue.RemoveAllEffects(fx_apply_effect_curse_ref);
+ }
+
+ //this is an instant effect
+ return FX_NOT_APPLIED;
+}
+
+// 0x1b AcidResistanceModifier
+int fx_acid_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_acid_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTACID );
+ return FX_APPLIED;
+}
+
+// 0x1c ColdResistanceModifier
+int fx_cold_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cold_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTCOLD );
+ return FX_APPLIED;
+}
+
+// 0x1d ElectricityResistanceModifier
+int fx_electricity_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_electricity_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTELECTRICITY );
+ return FX_APPLIED;
+}
+
+// 0x1e FireResistanceModifier
+int fx_fire_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_fire_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTFIRE );
+ return FX_APPLIED;
+}
+
+// 0x1f MagicDamageResistanceModifier
+int fx_magic_damage_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_magic_damage_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_MAGICDAMAGERESISTANCE );
+ return FX_APPLIED;
+}
+
+// 0x20 Cure:Death
+int fx_cure_dead_state (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_dead_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //call this only if the target is dead, otherwise some variables can get wrong
+ if (STATE_GET(STATE_DEAD) ) {
+ Point p(fx->PosX, fx->PosY);
+ Resurrect(Owner, target, fx, p);
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x21 SaveVsDeathModifier
+int fx_save_vs_death_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_save_vs_death_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ HandleBonus( target, IE_SAVEVSDEATH, fx->Parameter1, fx->TimingMode );
+ return FX_APPLIED;
+}
+
+// 0x22 SaveVsWandsModifier
+int fx_save_vs_wands_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_save_vs_wands_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ HandleBonus( target, IE_SAVEVSWANDS, fx->Parameter1, fx->TimingMode );
+ return FX_APPLIED;
+}
+
+// 0x23 SaveVsPolyModifier
+int fx_save_vs_poly_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_save_vs_poly_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ HandleBonus( target, IE_SAVEVSPOLY, fx->Parameter1, fx->TimingMode );
+ return FX_APPLIED;
+}
+
+// 0x24 SaveVsBreathModifier
+int fx_save_vs_breath_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_save_vs_breath_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ HandleBonus( target, IE_SAVEVSBREATH, fx->Parameter1, fx->TimingMode );
+ return FX_APPLIED;
+}
+
+// 0x25 SaveVsSpellsModifier
+int fx_save_vs_spell_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_save_vs_spell_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ HandleBonus( target, IE_SAVEVSSPELL, fx->Parameter1, fx->TimingMode );
+ return FX_APPLIED;
+}
+
+// 0x26 State:Silenced
+int fx_set_silenced_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_silenced_state (%2d)\n", fx->Opcode );
+ STATE_SET( STATE_SILENCED );
+ return FX_APPLIED;
+}
+
+static EffectRef fx_animation_stance_ref = { "AnimationStateChange", -1 };
+
+// 0x27 State:Helpless
+// this effect sets both bits, but 'awaken' only removes the sleep bit
+// FIXME: this is probably a persistent effect
+int fx_set_unconscious_state (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_unconscious_state (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+
+ if (target->HasSpellState(SS_BLOODRAGE)) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (fx->FirstApply) {
+ Effect *newfx;
+
+ newfx = EffectQueue::CreateEffectCopy(fx, fx_animation_stance_ref, 0, IE_ANI_SLEEP);
+ core->ApplyEffect(newfx, target, Owner);
+
+ delete newfx;
+ }
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_STATE_SET( STATE_HELPLESS | STATE_SLEEP ); //don't awaken on damage
+ } else {
+ STATE_SET( STATE_HELPLESS | STATE_SLEEP ); //don't awaken on damage
+ if (fx->Parameter2) {
+ target->SetSpellState(SS_NOAWAKE);
+ }
+ target->AddPortraitIcon(PI_SLEEP);
+ }
+ return FX_PERMANENT;
+}
+
+// 0x28 State:Slowed
+// this function removes hasted state, or sets slowed state
+static EffectRef fx_set_haste_state_ref = { "State:Hasted", -1 };
+
+int fx_set_slowed_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_slowed_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //iwd2 free action or aegis disables this effect
+ if (target->HasSpellState(SS_FREEACTION)) return FX_NOT_APPLIED;
+ if (target->HasSpellState(SS_AEGIS)) return FX_NOT_APPLIED;
+
+ if (STATE_GET(STATE_HASTED) ) {
+ BASE_STATE_CURE( STATE_HASTED );
+ target->fxqueue.RemoveAllEffects( fx_set_haste_state_ref );
+ target->fxqueue.RemoveAllEffectsWithParam( fx_display_portrait_icon_ref, PI_HASTED );
+ } else {
+ STATE_SET( STATE_SLOWED );
+ target->AddPortraitIcon(PI_SLOWED);
+ // halve apr and speed
+ STAT_MUL(IE_NUMBEROFATTACKS, 50);
+ STAT_MUL(IE_MOVEMENTRATE, 50);
+ }
+ return FX_PERMANENT;
+}
+
+// 0x29 Sparkle
+int fx_sparkle (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_sparkle (%2d): Sparkle colour: %d ; Sparkle type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (!target) {
+ return FX_NOT_APPLIED;
+ }
+
+ Map *map = target->GetCurrentArea();
+ if (!map) {
+ return FX_APPLIED;
+ }
+ Point p(fx->PosX, fx->PosY);
+
+ map->Sparkle( fx->Duration, fx->Parameter1, fx->Parameter2, p, fx->Parameter3);
+ return FX_NOT_APPLIED;
+}
+
+// 0x2A WizardSpellSlotsModifier
+int fx_bonus_wizard_spells (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bonus_wizard_spells (%2d): Spell Add: %d ; Spell Level: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ int i=1;
+ //if param2 is 0, then double spells up to param1
+ if(!fx->Parameter2) {
+ for (unsigned int j=0;j<fx->Parameter1 && j<MAX_SPELL_LEVEL;j++) {
+ target->spellbook.SetMemorizableSpellsCount(0, IE_SPELL_TYPE_WIZARD, j, true);
+ }
+ return FX_APPLIED;
+ }
+ //HoW specific
+ //if param2 is 0x200, then double spells at param1
+ if (fx->Parameter2==0x200) {
+ unsigned int j = fx->Parameter1-1;
+ if (j<MAX_SPELL_LEVEL) {
+ target->spellbook.SetMemorizableSpellsCount(0, IE_SPELL_TYPE_WIZARD, j, true);
+ }
+ }
+
+ for(unsigned int j=0;j<MAX_SPELL_LEVEL;j++) {
+ if (fx->Parameter2&i) {
+ target->spellbook.SetMemorizableSpellsCount(fx->Parameter1, IE_SPELL_TYPE_WIZARD, j, true);
+ }
+ i<<=1;
+ }
+ return FX_APPLIED;
+}
+
+// 0x2B Cure:Petrification
+int fx_cure_petrified_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_petrified_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_PETRIFIED );
+ return FX_NOT_APPLIED;
+}
+
+// 0x2C StrengthModifier
+int fx_strength_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_strength_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ ////how strength: value is based on class
+ ////pst power of one also depends on this!
+ if (fx->Parameter2==3) {
+ //TODO: strextra for stats>=18
+ fx->Parameter1 = core->Roll(1,SpellAbilityDieRoll(target,1),0);
+ fx->Parameter2 = 0;
+ }
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_MOD( IE_STR );
+ } else {
+ STAT_MOD( IE_STR );
+ }
+ return FX_PERMANENT;
+}
+
+// 0x2D State:Stun
+int power_word_stun_iwd2(Actor *target, Effect *fx)
+{
+ int hp = BASE_GET(IE_HITPOINTS);
+ if (hp>150) return FX_NOT_APPLIED;
+ int stuntime;
+ if (hp>100) stuntime = core->Roll(1,4,0);
+ else if (hp>50) stuntime = core->Roll(2,4,0);
+ else stuntime = core->Roll(4,4,0);
+ fx->Parameter2 = 0;
+ fx->TimingMode = FX_DURATION_ABSOLUTE;
+ fx->Duration = stuntime*6*core->Time.round_size + core->GetGame()->GameTime;
+ STATE_SET( STATE_STUNNED );
+ target->AddPortraitIcon(PI_STUN);
+ return FX_APPLIED;
+}
+
+int fx_set_stun_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_stun_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //actually the original engine just skips this effect if the target is dead
+ if ( STATE_GET(STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //this is an IWD extension
+ if (target->HasSpellState(SS_BLOODRAGE)) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (fx->Parameter2==2) {
+ //don't reroll the duration next time we get here
+ if (fx->FirstApply) {
+ return power_word_stun_iwd2(target, fx);
+ }
+ }
+ STATE_SET( STATE_STUNNED );
+ target->AddPortraitIcon(PI_STUN);
+ if (fx->Parameter2==1) {
+ target->SetSpellState(SS_AWAKE);
+ }
+ return FX_APPLIED;
+}
+
+// 0x2E Cure:Stun
+static EffectRef fx_set_stun_state_ref = { "State:Stun", -1 };
+static EffectRef fx_hold_creature_no_icon_ref = { "State:HoldNoIcon", -1 };
+
+int fx_cure_stun_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_stun_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_STUNNED );
+ target->fxqueue.RemoveAllEffects(fx_set_stun_state_ref);
+ target->fxqueue.RemoveAllEffects(fx_hold_creature_no_icon_ref);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_display_portrait_icon_ref, PI_HELD);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_display_portrait_icon_ref, PI_HOPELESS);
+ return FX_NOT_APPLIED;
+}
+
+// 0x2F Cure:Invisible
+// 0x74 Cure:Invisible2
+static EffectRef fx_set_invisible_state_ref = { "State:Invisible", -1 };
+
+int fx_cure_invisible_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_invisible_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (!STATE_GET(STATE_NONDET)) {
+ if (pstflags) {
+ BASE_STATE_CURE( STATE_PST_INVIS );
+ } else {
+ BASE_STATE_CURE( STATE_INVISIBLE | STATE_INVIS2 );
+ }
+ target->fxqueue.RemoveAllEffects(fx_set_invisible_state_ref);
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x30 Cure:Silence
+static EffectRef fx_set_silenced_state_ref = { "State:Silenced", -1 };
+
+int fx_cure_silenced_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_silenced_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_SILENCED );
+ target->fxqueue.RemoveAllEffects(fx_set_silenced_state_ref);
+ return FX_NOT_APPLIED;
+}
+
+// 0x31 WisdomModifier
+int fx_wisdom_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_wisdom_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_MOD( IE_WIS );
+ } else {
+ STAT_MOD( IE_WIS );
+ }
+ return FX_PERMANENT;
+}
+
+// 0x32 Color:BriefRGB
+int fx_brief_rgb (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_brief_rgb (%2d): RGB: %d, Location and speed: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ int speed = (fx->Parameter2 >> 16) & 0xff;
+ target->SetColorMod(0xff, RGBModifier::ADD, speed,
+ fx->Parameter1 >> 8, fx->Parameter1 >> 16,
+ fx->Parameter1 >> 24, 0);
+
+ return FX_NOT_APPLIED;
+}
+
+// 0x33 Color:DarkenRGB
+int fx_darken_rgb (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_darken_rgb (%2d): RGB: %d, Location and speed: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ EffectQueue::HackColorEffects(target, fx);
+ ieDword location = fx->Parameter2 & 0xff;
+ target->SetColorMod(location, RGBModifier::TINT, -1, fx->Parameter1 >> 8,
+ fx->Parameter1 >> 16, fx->Parameter1 >> 24);
+ return FX_APPLIED;
+}
+
+// 0x34 Color:GlowRGB
+int fx_glow_rgb (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_glow_rgb (%2d): RGB: %d, Location and speed: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ EffectQueue::HackColorEffects(target, fx);
+ ieDword location = fx->Parameter2 & 0xff;
+ target->SetColorMod(location, RGBModifier::BRIGHTEN, -1,
+ fx->Parameter1 >> 8, fx->Parameter1 >> 16,
+ fx->Parameter1 >> 24);
+
+ return FX_APPLIED;
+}
+
+// 0x35 AnimationIDModifier
+static EffectRef fx_animation_id_modifier_ref = { "AnimationIDModifier", -1 };
+
+int fx_animation_id_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_animation_id_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ switch (fx->Parameter2) {
+ case 0: //non permanent animation change
+ default:
+ STAT_SET( IE_ANIMATION_ID, fx->Parameter1 );
+ return FX_APPLIED;
+ case 1: //remove any non permanent change
+ target->fxqueue.RemoveAllEffects(fx_animation_id_modifier_ref);
+ //return FX_NOT_APPLIED;
+ //intentionally passing through (perma change removes previous changes)
+ case 2: //permanent animation id change
+ target->SetBaseNoPCF(IE_ANIMATION_ID, fx->Parameter1);
+ return FX_NOT_APPLIED;
+ }
+}
+
+// 0x36 ToHitModifier
+int fx_to_hit_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_to_hit_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ HandleBonus( target, IE_TOHIT, fx->Parameter1, fx->TimingMode );
+ return FX_APPLIED;
+}
+
+// 0x37 KillCreatureType
+static EffectRef fx_death_ref = { "Death", -1 };
+
+int fx_kill_creature_type (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_kill_creature_type (%2d): Value: %d, IDS: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (EffectQueue::match_ids( target, fx->Parameter2, fx->Parameter1) ) {
+ //convert it to a death opcode or apply the new effect?
+ fx->Opcode = EffectQueue::ResolveEffect(fx_death_ref);
+ fx->TimingMode = FX_DURATION_INSTANT_PERMANENT;
+ fx->Parameter1 = 0;
+ fx->Parameter2 = 4;
+ return FX_APPLIED;
+ }
+ //doesn't stick
+ return FX_NOT_APPLIED;
+}
+
+// 0x38 Alignment:Invert
+//switch good to evil and evil to good
+//also switch chaotic to lawful and vice versa
+//gemrb extension: param2 actually controls which parts should be reversed
+// 0 - switch both (as original)
+// 1 - switch good and evil
+// 2 - switch lawful and chaotic
+
+static int al_switch_both[16]={0,0x33,0x32,0x31,0,0x23,0x22,0x21,0,0x13,0x12,0x11};
+static int al_switch_law[16]={0,0x31,0x32,0x33,0,0x21,0x22,0x23,0,0x11,0x12,0x13};
+static int al_switch_good[16]={0,0x13,0x12,0x11,0,0x23,0x22,0x21,0,0x33,0x32,0x31};
+int fx_alignment_invert (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_alignment_invert (%2d)\n", fx->Opcode );
+ register ieDword newalign = target->GetStat( IE_ALIGNMENT );
+ //compress the values. GNE is the first 2 bits originally
+ //LNC is the 4/5. bits.
+ newalign = (newalign & AL_GE_MASK) | ((newalign & AL_LC_MASK)>>2);
+ switch (fx->Parameter2) {
+ default:
+ newalign = al_switch_both[newalign];
+ break;
+ case 1: //switch good/evil
+ newalign = al_switch_good[newalign];
+ break;
+ case 2:
+ newalign = al_switch_law[newalign];
+ break;
+ }
+ STAT_SET( IE_ALIGNMENT, newalign );
+ return FX_APPLIED;
+}
+
+// 0x39 Alignment:Change
+int fx_alignment_change (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_alignment_change (%2d): Value: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_SET( IE_ALIGNMENT, fx->Parameter2 );
+ return FX_APPLIED;
+}
+
+// 0x3a DispelEffects
+int fx_dispel_effects (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_dispel_effects (%2d): Value: %d, IDS: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieResRef Removed;
+ ieDword level;
+
+ switch (fx->Parameter2) {
+ case 0:
+ default:
+ level = 0xffffffff;
+ break;
+ case 1:
+ //same level: 50% success, each diff modifies it by 5%
+ level = core->Roll(1,20,fx->Power-10);
+ if (level>=0x80000000) level = 0;
+ break;
+ case 2:
+ //same level: 50% success, each diff modifies it by 5%
+ level = core->Roll(1,20,fx->Parameter1-10);
+ if (level>=0x80000000) level = 0;
+ break;
+ }
+ //if signed would it be negative?
+ target->fxqueue.RemoveLevelEffects(Removed, level, RL_DISPELLABLE, 0);
+ return FX_NOT_APPLIED;
+}
+
+// 0x3B StealthModifier
+int fx_stealth_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_stealth_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_STEALTH );
+ return FX_APPLIED;
+}
+
+// 0x3C MiscastMagicModifier
+int fx_miscast_magic_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_miscast_magic_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ switch (fx->Parameter2) {
+ case 3:
+ STAT_SET( IE_DEADMAGIC, 1);
+ case 0:
+ STAT_SET( IE_SPELLFAILUREMAGE, fx->Parameter1);
+ break;
+ case 4:
+ STAT_SET( IE_DEADMAGIC, 1);
+ case 1:
+ STAT_SET( IE_SPELLFAILUREPRIEST, fx->Parameter1);
+ break;
+ case 5:
+ STAT_SET( IE_DEADMAGIC, 1);
+ case 2:
+ STAT_SET( IE_SPELLFAILUREINNATE, fx->Parameter1);
+ break;
+ default:
+ return FX_NOT_APPLIED;
+ }
+ return FX_APPLIED;
+}
+
+// 0x3D AlchemyModifier
+// this crashes in bg2 due to assertion failure (disabled intentionally)
+// and in iwd it doesn't really follow the stat_mod convention (quite lame)
+int fx_alchemy_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_alchemy_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ switch(fx->Parameter2) {
+ case 0:
+ STAT_ADD( IE_ALCHEMY, fx->Parameter1 );
+ break;
+ case 1:
+ STAT_SET( IE_ALCHEMY, fx->Parameter1 );
+ break;
+ case 2:
+ STAT_SET( IE_ALCHEMY, 100 );
+ break;
+ }
+ return FX_APPLIED;
+}
+
+// 0x3E PriestSpellSlotsModifier
+int fx_bonus_priest_spells (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bonus_priest_spells (%2d): Spell Add: %d ; Spell Level: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ int i=1;
+ //if param2 is 0, then double spells up to param1
+ if(!fx->Parameter2) {
+ for (unsigned int j=0;j<fx->Parameter1 && j<MAX_SPELL_LEVEL;j++) {
+ target->spellbook.SetMemorizableSpellsCount(0, IE_SPELL_TYPE_PRIEST, j, true);
+ }
+ return FX_APPLIED;
+ }
+
+ //HoW specific
+ //if param2 is 0x200, then double spells at param1
+ if (fx->Parameter2==0x200) {
+ unsigned int j = fx->Parameter1-1;
+ target->spellbook.SetMemorizableSpellsCount(fx->Parameter1, IE_SPELL_TYPE_PRIEST, j, true);
+ return FX_APPLIED;
+ }
+
+ for(unsigned int j=0;j<MAX_SPELL_LEVEL;j++) {
+ if (fx->Parameter2&i) {
+ target->spellbook.SetMemorizableSpellsCount(fx->Parameter1, IE_SPELL_TYPE_PRIEST, j, true);
+ }
+ i<<=1;
+ }
+ return FX_APPLIED;
+}
+
+// 0x3F State:Infravision
+int fx_set_infravision_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_infravision_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STATE_SET( STATE_INFRA );
+ return FX_APPLIED;
+}
+
+// 0x40 Cure:Infravision
+static EffectRef fx_set_infravision_state_ref = { "State:Infravision", -1 };
+
+int fx_cure_infravision_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_infravision_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_INFRA );
+ target->fxqueue.RemoveAllEffects(fx_set_infravision_state_ref);
+ return FX_NOT_APPLIED;
+}
+
+// 0x41 State:Blur
+int fx_set_blur_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_blur_state (%2d)\n", fx->Opcode );
+ //death stops this effect
+ if (STATE_GET( STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_STATE_SET( STATE_BLUR );
+ } else {
+ STATE_SET( STATE_BLUR );
+ }
+ //iwd2 specific
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_BLUR);
+ }
+ return FX_PERMANENT;
+}
+
+// 0x42 TransparencyModifier
+int fx_transparency_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_transparency_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //maybe this needs some timing
+ switch (fx->Parameter2) {
+ case 1: //fade in
+ if (fx->Parameter1<255) {
+ if (core->GetGame()->GameTime%2) {
+ fx->Parameter1++;
+ }
+ }
+ break;
+ case 2://fade out
+ if (fx->Parameter1) {
+ if (core->GetGame()->GameTime%2) {
+ fx->Parameter1--;
+ }
+ }
+ break;
+ }
+ STAT_MOD( IE_TRANSLUCENT );
+ return FX_APPLIED;
+}
+
+// 0x43 SummonCreature
+
+static int eamods[]={EAM_ALLY,EAM_ALLY,EAM_DEFAULT,EAM_ALLY,EAM_DEFAULT,EAM_ENEMY,EAM_ALLY};
+
+int fx_summon_creature (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_summon_creature (%2d): ResRef:%s Anim:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Resource2, fx->Parameter2 );
+
+ //summon creature (resource), play vvc (resource2)
+ //creature's lastsummoner is Owner
+ //creature's target is target
+ //position of appearance is target's pos
+ int eamod = -1;
+ if (fx->Parameter2<6){
+ eamod = eamods[fx->Parameter2];
+ }
+
+ //the monster should appear near the effect position
+ Point p(fx->PosX, fx->PosY);
+
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ core->SummonCreature(fx->Resource, fx->Resource2, Owner, target, p, eamod, 0, newfx);
+ delete newfx;
+ return FX_NOT_APPLIED;
+}
+
+// 0x44 UnsummonCreature
+int fx_unsummon_creature (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_unsummon_creature (%2d)\n", fx->Opcode );
+
+ //to be compatible with the original engine, unsummon doesn't work with PC's
+ //but it works on anything else
+ if (!target->InParty) {
+ //play the vanish animation
+ ScriptedAnimation* sca = gamedata->GetScriptedAnimation(fx->Resource, false);
+ if (sca) {
+ sca->XPos+=target->Pos.x;
+ sca->YPos+=target->Pos.y;
+ target->GetCurrentArea()->AddVVCell(sca);
+ }
+ //remove the creature
+ target->DestroySelf();
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x45 State:Nondetection
+int fx_set_nondetection_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_nondetection_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STATE_SET( STATE_NONDET );
+ return FX_APPLIED;
+}
+
+// 0x46 Cure:Nondetection
+static EffectRef fx_set_nondetection_state_ref = { "State:Nondetection", -1 };
+
+int fx_cure_nondetection_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_nondetection_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_NONDET );
+ target->fxqueue.RemoveAllEffects(fx_set_nondetection_state_ref);
+ return FX_NOT_APPLIED;
+}
+
+// 0x47 SexModifier
+int fx_sex_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_sex_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword value;
+ if (fx->Parameter2) {
+ value = fx->Parameter1;
+ } else {
+ if (STAT_GET(IE_SEX_CHANGED)) {
+ return FX_NOT_APPLIED;
+ }
+ STAT_SET( IE_SEX_CHANGED, 1);
+ value = STAT_GET(IE_SEX);
+ if (value==SEX_MALE) {
+ value = SEX_FEMALE;
+ } else {
+ value = SEX_MALE;
+ }
+ }
+ STAT_SET( IE_SEX, value );
+ return FX_APPLIED;
+}
+
+// 0x48 AIIdentifierModifier
+int fx_ids_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_ids_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ switch (fx->Parameter2) {
+ case 0:
+ STAT_SET(IE_EA, fx->Parameter1);
+ break;
+ case 1:
+ STAT_SET(IE_GENERAL, fx->Parameter1);
+ break;
+ case 2:
+ STAT_SET(IE_RACE, fx->Parameter1);
+ break;
+ case 3:
+ STAT_SET(IE_CLASS, fx->Parameter1);
+ break;
+ case 4:
+ STAT_SET(IE_SPECIFIC, fx->Parameter1);
+ break;
+ case 5:
+ STAT_SET(IE_SEX, fx->Parameter1);
+ break;
+ case 6:
+ STAT_SET(IE_ALIGNMENT, fx->Parameter1);
+ break;
+ default:
+ return FX_NOT_APPLIED;
+ }
+ //not sure, need a check if this action could be permanent
+ return FX_APPLIED;
+}
+
+// 0x49 DamageBonusModifier
+int fx_damage_bonus_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_damage_bonus_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_DAMAGEBONUS );
+ return FX_APPLIED;
+}
+
+// 0x4a State:Blind
+int fx_set_blind_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_blind_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //pst power word blind projectile support
+ if (fx->Parameter2==1) {
+ fx->Parameter2 = 0;
+ int stat = target->GetSafeStat(IE_HITPOINTS);
+ if (stat<25) {
+ stat = core->Roll(1,240,150);
+ } else if (stat<50) {
+ stat = core->Roll(1,120,70);
+ } else if (stat<100) {
+ stat = core->Roll(1,30,15);
+ } else stat = 0;
+ fx->Duration = core->GetGame()->GameTime+stat;
+
+ }
+
+ //don't do this effect twice (bug exists in BG2, but fixed in IWD2)
+ if (!STATE_GET(STATE_BLIND)) {
+ STATE_SET( STATE_BLIND );
+ //the feat normally exists only in IWD2, but won't hurt
+ if (!target->GetFeat(FEAT_BLIND_FIGHT)) {
+ STAT_SUB (IE_TOHIT, 10); // all other tohit stats are treated as bonuses
+ }
+ }
+ return FX_APPLIED;
+}
+
+// 0x4b Cure:Blind
+static EffectRef fx_set_blind_state_ref = { "State:Blind", -1 };
+
+int fx_cure_blind_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_blind_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_BLIND );
+ target->fxqueue.RemoveAllEffects(fx_set_blind_state_ref);
+ return FX_NOT_APPLIED;
+}
+
+// 0x4c State:Feeblemind
+int fx_set_feebleminded_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_feebleminded_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STATE_SET( STATE_FEEBLE );
+ STAT_SET( IE_INT, 3);
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_FEEBLEMIND);
+ }
+ return FX_APPLIED;
+}
+
+// 0x4d Cure:Feeblemind
+static EffectRef fx_set_feebleminded_state_ref = { "State:Feeblemind", -1 };
+
+int fx_cure_feebleminded_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_feebleminded_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_FEEBLE );
+ target->fxqueue.RemoveAllEffects(fx_set_feebleminded_state_ref);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_display_portrait_icon_ref, PI_FEEBLEMIND);
+ return FX_NOT_APPLIED;
+}
+
+// 0x4e State:Diseased
+int fx_set_diseased_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_diseased_state (%2d): Damage: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //setting damage to 0 because not all types do damage
+ ieDword damage = 0;
+
+ HandlePercentageDamage(fx, target);
+
+ switch(fx->Parameter2) {
+ case RPD_SECONDS:
+ damage = 1;
+ if (fx->Parameter1 && (core->GetGame()->GameTime%(fx->Parameter1*AI_UPDATE_TIME))) {
+ return FX_APPLIED;
+ }
+ break;
+ case RPD_PERCENT: // handled in HandlePercentageDamage
+ case RPD_POINTS:
+ damage = fx->Parameter1;
+ // per second
+ if (core->GetGame()->GameTime%AI_UPDATE_TIME) {
+ return FX_APPLIED;
+ }
+ break;
+ case RPD_STR: //strength
+ STAT_ADD(IE_STR, fx->Parameter1);
+ break;
+ case RPD_DEX: //dex
+ STAT_ADD(IE_DEX, fx->Parameter1);
+ break;
+ case RPD_CON: //con
+ STAT_ADD(IE_CON, fx->Parameter1);
+ break;
+ case RPD_INT: //int
+ STAT_ADD(IE_INT, fx->Parameter1);
+ break;
+ case RPD_WIS: //wis
+ STAT_ADD(IE_WIS, fx->Parameter1);
+ break;
+ case RPD_CHA: //cha
+ STAT_ADD(IE_CHR, fx->Parameter1);
+ break;
+ case RPD_SLOW: //slow
+ break;
+ case RPD_MOLD: //mold touch (how)
+ EXTSTATE_SET(EXTSTATE_MOLD);
+ damage = 1;
+ break;
+ case RPD_MOLD2:
+ break;
+ default:
+ damage = 1;
+ break;
+ }
+ //percent
+ Scriptable *caster = GetCasterObject();
+
+ if (damage) {
+ target->Damage(damage, DAMAGE_POISON, caster);
+ }
+ return FX_APPLIED;
+}
+
+
+// 0x4f Cure:Disease
+static EffectRef fx_diseased_state_ref = { "State:Diseased", -1 };
+
+int fx_cure_diseased_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_diseased_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //STATE_CURE( STATE_DISEASED ); //the bit flagged as disease is actually the active state. so this is even more unlikely to be used as advertised
+ target->fxqueue.RemoveAllEffects( fx_diseased_state_ref ); //this is what actually happens in bg2
+ return FX_NOT_APPLIED;
+}
+
+// 0x50 State:Deafness
+// gemrb extension: modifiable amount
+// none of the engines care about stacking
+int fx_set_deaf_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_deaf_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //gemrb fix
+ if (target->SetSpellState(SS_DEAF)) return FX_APPLIED;
+
+ if (!fx->Parameter1) {
+ fx->Parameter1 = 50;
+ }
+ STAT_ADD(IE_SPELLFAILUREMAGE, fx->Parameter1);
+ if (!fx->Parameter2) {
+ fx->Parameter1 = 50;
+ }
+ STAT_ADD(IE_SPELLFAILUREPRIEST, fx->Parameter2);
+ EXTSTATE_SET(EXTSTATE_DEAF); //iwd1/how needs this
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_DEAFNESS);
+ }
+ return FX_APPLIED;
+}
+
+int fx_set_deaf_state_iwd2 (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_deaf_state_iwd2 (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //gemrb fix
+ if (target->SetSpellState(SS_DEAF)) return FX_APPLIED;
+
+ if (!fx->Parameter1) {
+ //this is a bad hack
+ fx->Parameter1 = 20;
+ }
+ STAT_ADD(IE_SPELLFAILUREMAGE, fx->Parameter1);
+ if (!fx->Parameter2) {
+ fx->Parameter1 = 20;
+ }
+ STAT_ADD(IE_SPELLFAILUREPRIEST, fx->Parameter2);
+ EXTSTATE_SET(EXTSTATE_DEAF); //iwd1/how needs this
+ target->AddPortraitIcon(PI_DEAFNESS); //iwd2 specific
+ return FX_APPLIED;
+}
+
+// 0x51 Cure:Deafness
+static EffectRef fx_deaf_state_ref = { "State:Deafness", -1 };
+static EffectRef fx_deaf_state_iwd2_ref = { "State:DeafnessIWD2", -1 };
+
+//removes the deafness effect
+int fx_cure_deaf_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_deaf_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ target->fxqueue.RemoveAllEffects(fx_deaf_state_ref);
+ target->fxqueue.RemoveAllEffects(fx_deaf_state_iwd2_ref);
+ return FX_NOT_APPLIED;
+}
+
+// 0x52 SetAIScript
+int fx_set_ai_script (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_ai_state (%2d): Resource: %s, Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ target->SetScript (fx->Resource, fx->Parameter2);
+ return FX_NOT_APPLIED;
+}
+
+// 0x53 Protection:Projectile
+int fx_protection_from_projectile (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_from_projectile (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_IMMUNITY, IMM_PROJECTILE);
+ return FX_APPLIED;
+}
+
+// 0x54 MagicalFireResistanceModifier
+int fx_magical_fire_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_magical_fire_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTMAGICFIRE );
+ return FX_APPLIED;
+}
+
+// 0x55 MagicalColdResistanceModifier
+int fx_magical_cold_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_magical_cold_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTMAGICCOLD );
+ return FX_APPLIED;
+}
+
+// 0x56 SlashingResistanceModifier
+int fx_slashing_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_slashing_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTSLASHING );
+ return FX_APPLIED;
+}
+
+// 0x57 CrushingResistanceModifier
+int fx_crushing_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_crushing_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTCRUSHING );
+ return FX_APPLIED;
+}
+
+// 0x58 PiercingResistanceModifier
+int fx_piercing_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_piercing_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTPIERCING );
+ return FX_APPLIED;
+}
+
+// 0x59 MissilesResistanceModifier
+int fx_missiles_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_missiles_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTMISSILE );
+ return FX_APPLIED;
+}
+
+// 0x5A OpenLocksModifier
+int fx_open_locks_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_open_locks_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_LOCKPICKING );
+ return FX_APPLIED;
+}
+
+// 0x5B FindTrapsModifier
+int fx_find_traps_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_find_traps_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_TRAPS );
+ return FX_APPLIED;
+}
+
+// 0x5C PickPocketsModifier
+int fx_pick_pockets_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_pick_pockets_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_PICKPOCKET );
+ return FX_APPLIED;
+}
+
+// 0x5D FatigueModifier
+int fx_fatigue_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_fatigue_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_FATIGUE );
+ // TODO: fatigue has a negative effect on luck -> add fatigmod.2da support
+ return FX_APPLIED;
+}
+
+// 0x5E IntoxicationModifier
+int fx_intoxication_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_intoxication_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_INTOXICATION );
+ return FX_APPLIED;
+}
+
+// 0x5F TrackingModifier
+int fx_tracking_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_tracking_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_TRACKING );
+ return FX_APPLIED;
+}
+
+// 0x60 LevelModifier
+int fx_level_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_level_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_LEVEL );
+ return FX_APPLIED;
+}
+
+// 0x61 StrengthBonusModifier
+int fx_strength_bonus_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_strength_bonus_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_STREXTRA );
+ return FX_APPLIED;
+}
+
+// 0x62 State:Regenerating
+int fx_set_regenerating_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_regenerating_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ int damage;
+ int tmp = fx->Parameter1;
+ ieDword gameTime = core->GetGame()->GameTime;
+ ieDword nextHeal;
+
+ if (!fx->Parameter3) {
+ //hack to ensure our first call gets through
+ nextHeal = gameTime-1;
+ } else {
+ nextHeal = fx->Parameter3;
+ }
+
+ //we can have multiple calls at the same gameTime, so we
+ //just go to gameTime+1 to ensure one call
+ if (nextHeal>=gameTime) return FX_APPLIED;
+
+ HandlePercentageDamage(fx, target);
+
+ switch(fx->Parameter2) {
+ case RPD_TURNS: //restore param3 hp every param1 turns
+ tmp *= core->Time.rounds_per_turn;
+ //fall
+ case RPD_ROUNDS: //restore param3 hp every param1 rounds
+ tmp *= core->Time.round_sec;
+ //fall
+ case RPD_SECONDS: //restore param3 hp every param1 seconds
+ fx->Parameter3 = nextHeal + tmp*AI_UPDATE_TIME;
+ damage = 1;
+ break;
+ case RPD_PERCENT: // handled in HandlePercentageDamage
+ case RPD_POINTS: //restore param1 hp every second? that's crazy!
+ damage = fx->Parameter1;
+ fx->Parameter3 = nextHeal + AI_UPDATE_TIME;
+ break;
+ default:
+ fx->Parameter3 = nextHeal + AI_UPDATE_TIME;
+ damage = 1;
+ break;
+ }
+
+ //This should take care of the change of the modified stat
+ //So there is no need to do anything else here other than increasing
+ //the base current hp
+ target->NewBase(IE_HITPOINTS, damage, MOD_ADDITIVE);
+ return FX_APPLIED;
+}
+// 0x63 SpellDurationModifier
+int fx_spell_duration_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_spell_duration_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ switch (fx->Parameter2) {
+ case 0:
+ STAT_SET( IE_SPELLDURATIONMODMAGE, fx->Parameter1);
+ break;
+ case 1:
+ STAT_SET( IE_SPELLDURATIONMODPRIEST, fx->Parameter1);
+ break;
+ default:
+ return FX_NOT_APPLIED;
+ }
+ return FX_APPLIED;
+}
+// 0x64 Protection:Creature
+int fx_generic_effect (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_generic_effect (%2d): Param1: %d, Param2: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ return FX_APPLIED;
+}
+
+// 0x65 Protection:Opcode
+int fx_protection_opcode (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_opcode (%2d): Opcode: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR(IE_IMMUNITY, IMM_OPCODE);
+ return FX_APPLIED;
+}
+
+// 0x66 Protection:SpellLevel
+int fx_protection_spelllevel (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_spelllevel (%2d) Level: %d\n", fx->Opcode, fx->Parameter1);
+
+ int value = fx->Parameter1;
+ if (value<9) {
+ STAT_BIT_OR(IE_MINORGLOBE, 1<<value);
+ STAT_BIT_OR(IE_IMMUNITY, IMM_LEVEL);
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x67 ChangeName
+int fx_change_name (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_change_name_modifier (%2d): StrRef: %d\n", fx->Opcode, fx->Parameter1 );
+ //this also changes the base stat
+ target->SetName(fx->Parameter1, 0);
+ return FX_NOT_APPLIED;
+}
+
+// 0x68 ExperienceModifier
+int fx_experience_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_experience_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //FIXME: this has mode too
+ //target->AddExperience (fx->Parameter1);
+ STAT_MOD( IE_XP );
+ return FX_NOT_APPLIED;
+}
+
+// 0x69 GoldModifier
+//in BG2 this effect subtracts gold when type is MOD_ADDITIVE
+//no one uses it, though. To keep the function, the default branch will do the subtraction
+int fx_gold_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_gold_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (!target->InParty) {
+ STAT_MOD( IE_GOLD );
+ return FX_NOT_APPLIED;
+ }
+ ieDword gold;
+ Game *game = core->GetGame();
+ //for party members, the gold is stored in the game object
+ switch( fx->Parameter2) {
+ case MOD_ADDITIVE:
+ gold = fx->Parameter1;
+ break;
+ case MOD_ABSOLUTE:
+ gold = fx->Parameter1-game->PartyGold;
+ break;
+ case MOD_PERCENT:
+ gold = game->PartyGold*fx->Parameter1/100-game->PartyGold;
+ break;
+ default:
+ gold = (ieDword) -fx->Parameter1;
+ break;
+ }
+ game->AddGold (gold);
+ return FX_NOT_APPLIED;
+}
+
+// 0x6a MoraleBreakModifier
+int fx_morale_break_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_morale_break_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD(IE_MORALEBREAK);
+ return FX_PERMANENT; //permanent morale break doesn't stick
+}
+
+// 0x6b PortraitChange
+int fx_portrait_change (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_portrait_change (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ target->SetPortrait( fx->Resource, fx->Parameter2);
+ return FX_NOT_APPLIED;
+}
+
+// 0x6c ReputationModifier
+int fx_reputation_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_reputation_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD(IE_REPUTATION);
+ return FX_NOT_APPLIED; //needs testing
+}
+
+// 0x6d --> see later
+
+// 0x6e works only in PST, reused for turning undead
+
+// 0x6f Item:CreateMagic
+
+static EffectRef fx_remove_item_ref = { "Item:Remove", -1 };
+
+int fx_create_magic_item (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ //charge count is incorrect
+ target->inventory.SetSlotItemRes(fx->Resource, target->inventory.GetMagicSlot(),fx->Parameter1,fx->Parameter3,fx->Parameter4);
+ //equip the weapon
+ target->inventory.SetEquippedSlot(target->inventory.GetMagicSlot()-target->inventory.GetWeaponSlot(), 0);
+ if ((fx->TimingMode&0xff) == FX_DURATION_INSTANT_LIMITED) {
+ //if this effect has expiration, then it will remain as a remove_item
+ //on the effect queue, inheriting all the parameters
+ fx->Opcode=EffectQueue::ResolveEffect(fx_remove_item_ref);
+ fx->TimingMode=FX_DURATION_DELAY_PERMANENT;
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x70 Item:Remove
+int fx_remove_item (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ //will destroy the first item
+ if (target->inventory.DestroyItem(fx->Resource,0,1)) {
+ target->ReinitQuickSlots();
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x71 Item:Equip
+int fx_equip_item (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ int eff = core->QuerySlotEffects( fx->Parameter2 );
+ switch(eff) {
+ case SLOT_EFFECT_NONE:
+ case SLOT_EFFECT_MELEE:
+ target->inventory.SetEquippedSlot( fx->Parameter2, fx->Parameter1 );
+ break;
+ default:
+ target->inventory.EquipItem( fx->Parameter2 );
+ break;
+ }
+ target->ReinitQuickSlots();
+ return FX_NOT_APPLIED;
+}
+
+// 0x72 Dither
+int fx_dither (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_dither (%2d): Value: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //this effect doesn't work in any engine versions
+ return FX_NOT_APPLIED;
+}
+
+// 0x73 DetectAlignment
+//gemrb extension: chaotic/lawful detection
+int fx_detect_alignment (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ ieDword msk;
+ ieDword stat;
+ static int ge[] = {AL_EVIL, AL_GE_NEUTRAL, AL_GOOD, AL_CHAOTIC, AL_LC_NEUTRAL, AL_LAWFUL};
+
+ msk = ge[fx->Parameter2];
+ if (fx->Parameter2<3) {
+ //0,1,2 -> 3,2,1
+ stat = target->GetStat(IE_ALIGNMENT)&AL_GE_MASK;
+ }
+ else {
+ //3,4,5 -> 0x30, 0x20, 0x10
+ stat = target->GetStat(IE_ALIGNMENT)&AL_LC_MASK;
+ }
+ if (stat != msk) return FX_NOT_APPLIED;
+
+ ieDword color = fx->Parameter1;
+ switch (msk) {
+ case AL_EVIL:
+ if (!color) color = 0xff0000;
+ displaymsg->DisplayConstantStringName(STR_EVIL, color, target);
+ //glow red
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0xff, 0, 0, 0);
+ break;
+ case AL_GOOD:
+ if (!color) color = 0xff00;
+ displaymsg->DisplayConstantStringName(STR_GOOD, color, target);
+ //glow green
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0, 0xff, 0, 0);
+ break;
+ case AL_GE_NEUTRAL:
+ if (!color) color = 0xff;
+ displaymsg->DisplayConstantStringName(STR_GE_NEUTRAL, color, target);
+ //glow blue
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0, 0, 0xff, 0);
+ break;
+ case AL_CHAOTIC:
+ if (!color) color = 0xff00ff;
+ displaymsg->DisplayConstantStringName(STR_CHAOTIC, color, target);
+ //glow purple
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0xff, 0, 0xff, 0);
+ break;
+ case AL_LAWFUL:
+ if (!color) color = 0xffffff;
+ displaymsg->DisplayConstantStringName(STR_LAWFUL, color, target);
+ //glow white
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0xff, 0xff, 0xff, 0);
+ break;
+ case AL_LC_NEUTRAL:
+ if (!color) color = 0xff;
+ displaymsg->DisplayConstantStringName(STR_LC_NEUTRAL, color, target);
+ //glow blue
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0, 0, 0xff, 0);
+ break;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x74 Cure:Invisible2 (see 0x2f)
+
+// 0x75 Reveal:Area
+// 0 reveal whole area
+// 1 reveal area in pattern
+int fx_reveal_area (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_reveal_area (%2d): Value: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ Map *map = NULL;
+
+ if (target) {
+ map = target->GetCurrentArea();
+ }
+ else {
+ map = core->GetGame()->GetCurrentArea();
+ }
+ if (!map) {
+ return FX_APPLIED;
+ }
+
+ if (fx->Parameter2) {
+ map->Explore(fx->Parameter1);
+ } else {
+ map->Explore(-1);
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x76 Reveal:Creatures
+int fx_reveal_creatures (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_reveal_creatures (%2d): Value: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //reveals creatures (not working in original IE)
+ return FX_NOT_APPLIED;
+}
+
+// 0x77 MirrorImage
+static EffectRef fx_mirror_image_modifier_ref = { "MirrorImageModifier", -1 };
+
+int fx_mirror_image (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_mirror_image (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword images;
+
+ if (fx->Parameter2) {
+ images = 1; //reflection
+ }
+ else {
+ // the original uses only IE_LEVEL, but that can be awefully bad in
+ // the case of dual- and multiclasses
+ unsigned int level = target->GetCasterLevel(IE_SPL_WIZARD);
+ // 2-8 mirror images
+ images = level/3 + 2;
+ if (images > 8) images = 8;
+ }
+
+ Effect *fx2 = target->fxqueue.HasEffect(fx_mirror_image_modifier_ref);
+ if (fx2) {
+ //update old effect with our numbers if our numbers are more
+ if (fx2->Parameter1<images) {
+ fx2->Parameter1=images;
+ }
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ fx2->TimingMode = FX_DURATION_INSTANT_PERMANENT;
+ }
+ return FX_NOT_APPLIED;
+ }
+ fx->Opcode = EffectQueue::ResolveEffect(fx_mirror_image_modifier_ref);
+ fx->Parameter1=images;
+ //parameter2 could be 0 or 1 (mirror image or reflection)
+ //execute the translated effect
+ return fx_mirror_image_modifier(Owner, target, fx);
+}
+
+// 0x78 Protection:Weapons
+int fx_immune_to_weapon (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_immune_to_weapon (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (!fx->FirstApply) return FX_APPLIED;
+
+ int level;
+ ieDword mask, value;
+
+ level = -1;
+ mask = 0;
+ value = 0;
+ switch(fx->Parameter2) {
+ case 0: //enchantment level
+ level = fx->Parameter1;
+ break;
+ case 1: //all magical weapons
+ value = IE_INV_ITEM_MAGICAL;
+ //fallthrough
+ case 2: //all nonmagical weapons
+ mask = IE_INV_ITEM_MAGICAL;
+ break;
+ case 3: //all silver weapons
+ value = IE_INV_ITEM_SILVER;
+ //fallthrough
+ case 4: //all non silver weapons
+ mask = IE_INV_ITEM_SILVER;
+ break;
+ case 5:
+ value = IE_INV_ITEM_SILVER;
+ mask = IE_INV_ITEM_SILVER;
+ level = 0;
+ break;
+ case 6: //all twohanded
+ value = IE_INV_ITEM_TWOHANDED;
+ //fallthrough
+ case 7: //all not twohanded
+ mask = IE_INV_ITEM_TWOHANDED;
+ break;
+ case 8: //all twohanded
+ value = IE_INV_ITEM_CURSED;
+ //fallthrough
+ case 9: //all not twohanded
+ mask = IE_INV_ITEM_CURSED;
+ break;
+ case 10: //all twohanded
+ value = IE_INV_ITEM_COLDIRON;
+ //fallthrough
+ case 11: //all not twohanded
+ mask = IE_INV_ITEM_COLDIRON;
+ break;
+ case 12:
+ mask = fx->Parameter1;
+ case 13:
+ value = fx->Parameter1;
+ break;
+ default:;
+ }
+
+ fx->Parameter1 = (ieDword) level; //putting the corrected value back
+ fx->Parameter3 = mask;
+ fx->Parameter4 = value;
+ return FX_APPLIED;
+}
+
+// 0x79 VisualAnimationEffect (unknown)
+int fx_visual_animation_effect (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+//this is an unknown effect
+ if (0) printf( "fx_visual_animation_effect (%2d)\n", fx->Opcode );
+ return FX_NOT_APPLIED;
+}
+
+// 0x7a Item:CreateInventory
+static EffectRef fx_remove_inventory_item_ref = { "Item:RemoveInventory", -1 };
+
+int fx_create_inventory_item (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_create_inventory_item (%2d)\n", fx->Opcode );
+ target->inventory.AddSlotItemRes( fx->Resource, SLOT_ONLYINVENTORY, fx->Parameter1, fx->Parameter3, fx->Parameter4 );
+ if ((fx->TimingMode&0xff) == FX_DURATION_INSTANT_LIMITED) {
+ //if this effect has expiration, then it will remain as a remove_item
+ //on the effect queue, inheriting all the parameters
+ fx->Opcode=EffectQueue::ResolveEffect(fx_remove_inventory_item_ref);
+ fx->TimingMode=FX_DURATION_DELAY_PERMANENT;
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x7b Item:RemoveInventory
+int fx_remove_inventory_item (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_inventory_item (%2d)\n", fx->Opcode );
+ //FIXME: now equipped items are only wielded weapons
+ //why would it not let equipped items to be destructed?
+ target->inventory.DestroyItem(fx->Resource,IE_INV_ITEM_EQUIPPED,1);
+ return FX_NOT_APPLIED;
+}
+
+// 0x7c DimensionDoor
+// iwd2 has several options
+int fx_dimension_door (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_dimension_door (%2d) Type:%d\n", fx->Opcode, fx->Parameter2 );
+ Point p;
+
+ switch(fx->Parameter2)
+ {
+ case 0: //target to point
+ p.x=fx->PosX;
+ p.y=fx->PosY;
+ break;
+ case 1: //owner to target
+ if (Owner->Type!=ST_ACTOR) {
+ return FX_NOT_APPLIED;
+ }
+ p=target->Pos;
+ target = (Actor *) Owner;
+ break;
+ case 2: //target to saved location
+ p.x=STAT_GET(IE_SAVEDXPOS);
+ p.x=STAT_GET(IE_SAVEDYPOS);
+ target->SetOrientation(STAT_GET(IE_SAVEDFACE), false);
+ break;
+ case 3: //owner swapped with target
+ if (Owner->Type!=ST_ACTOR) {
+ return FX_NOT_APPLIED;
+ }
+ p=target->Pos;
+ target->SetPosition(Owner->Pos, true, 0);
+ target = (Actor *) Owner;
+ break;
+ }
+ target->SetPosition(p, true, 0 );
+ return FX_NOT_APPLIED;
+}
+
+// 0x7d Unlock
+int fx_knock (Scriptable* Owner, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_knock (%2d) [%d.%d]\n", fx->Opcode, fx->PosX, fx->PosY );
+ Map *map = Owner->GetCurrentArea();
+ if (!map) {
+ return FX_NOT_APPLIED;
+ }
+ Point p(fx->PosX, fx->PosY);
+
+printf("KNOCK Pos: %d.%d\n", fx->PosX, fx->PosY);
+ Door *door = map->TMap->GetDoorByPosition(p);
+ if (door) {
+printf("Got a door\n");
+ if (door->LockDifficulty<100) {
+ door->SetDoorLocked(false, true);
+ }
+ return FX_NOT_APPLIED;
+ }
+ Container *container = map->TMap->GetContainerByPosition(p);
+ if (container) {
+printf("Got a container\n");
+ if(container->LockDifficulty<100) {
+ container->SetContainerLocked(false);
+ }
+ return FX_NOT_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x7e MovementRateModifier
+// 0xb0 MovementRateModifier2
+int fx_movement_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_movement_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //iwd2 freeaction disables only 0xb0, who cares
+ if (target->HasSpellState(SS_FREEACTION)) return FX_NOT_APPLIED;
+ //iwd2 aegis doesn't protect against grease/acid fog slowness, but that is
+ //definitely a bug
+ if (target->HasSpellState(SS_AEGIS)) return FX_NOT_APPLIED;
+
+ STAT_MOD(IE_MOVEMENTRATE);
+ return FX_APPLIED;
+}
+
+#define FX_MS 10
+static const ieResRef monster_summoning_2da[FX_MS]={"MONSUM01","MONSUM02","MONSUM03",
+ "ANISUM01","ANISUM02", "MONSUM01", "MONSUM02","MONSUM03","ANISUM01","ANISUM02"};
+
+// 0x7f MonsterSummoning
+int fx_monster_summoning (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_monster_summoning (%2d): Number: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //check the summoning limit?
+ if (!Owner) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (!Owner->GetCurrentArea()) {
+ return FX_APPLIED;
+ }
+
+ //get monster resref from 2da determined by fx->Resource or fx->Parameter2
+ //the only addition to the original engine is that fx->Resource can be
+ //used to specify a 2da (if parameter2 is >= 10)
+ ieResRef monster;
+ ieResRef hit;
+ ieResRef areahit;
+ ieResRef table;
+ int level = fx->Parameter1;
+
+ if (fx->Parameter2>=FX_MS) {
+ if (fx->Resource[0]) {
+ strnuprcpy(table, fx->Resource, 8);
+ } else {
+ strnuprcpy(table, "ANISUM03", 8);
+ }
+ } else {
+ strnuprcpy(table, monster_summoning_2da[fx->Parameter2], 8);
+ }
+ core->GetResRefFrom2DA(monster_summoning_2da[fx->Parameter2], monster, hit, areahit);
+
+ if (!hit[0]) {
+ strnuprcpy(hit, fx->Resource2, 8);
+ }
+ if (!areahit[0]) {
+ strnuprcpy(areahit, fx->Resource3, 8);
+ }
+
+ //the monster should appear near the effect position
+ Point p(fx->PosX, fx->PosY);
+
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ //The hostile flag should cover these cases, all else is arbitrary
+ //0,1,2,3,4 - friendly to target
+ //5,6,7,8,9 - hostile to target
+ //10 - friendly to target
+
+ int eamod;
+ if (fx->Parameter2>=5 && fx->Parameter2<=9) {
+ eamod = EAM_ENEMY;
+ }
+ else {
+ eamod = EAM_ALLY;
+ }
+
+ //caster may be important here (Source), even if currently the EA modifiers
+ //don't use it
+ Scriptable *caster = GetCasterObject();
+ core->SummonCreature(monster, hit, caster, target, p, eamod, level, newfx);
+ delete newfx;
+ return FX_NOT_APPLIED;
+}
+
+// 0x80 State:Confused
+int fx_set_confused_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_confused_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (target->HasSpellState(SS_BLOODRAGE)) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (fx->TimingMode==FX_DURATION_DELAY_PERMANENT) {
+ BASE_STATE_SET( STATE_CONFUSED );
+ } else {
+ STATE_SET( STATE_CONFUSED );
+ }
+ //NOTE: iwd2 is also unable to display the portrait icon
+ //for permanent confusion
+ //the portrait icon cannot be made common because rigid thinking uses a different icon
+ //in bg2/how
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_CONFUSED);
+ }
+ return FX_PERMANENT;
+}
+
+// 0x81 AidNonCumulative
+int fx_set_aid_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_aid_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (!fx->Parameter2) {
+ fx->Parameter2=core->Roll(fx->Parameter1,8,0);
+ }
+ if (STATE_GET (STATE_AID) ) //aid is non cumulative
+ return FX_NOT_APPLIED;
+ STATE_SET( STATE_AID );
+ target->SetSpellState(SS_AID);
+ STAT_ADD( IE_MAXHITPOINTS, fx->Parameter2);
+ //This better happens after increasing maxhitpoints
+ if (fx->FirstApply) {
+ BASE_ADD( IE_HITPOINTS, fx->Parameter1);
+ }
+ STAT_ADD( IE_SAVEVSDEATH, fx->Parameter1);
+ STAT_ADD( IE_SAVEVSWANDS, fx->Parameter1);
+ STAT_ADD( IE_SAVEVSPOLY, fx->Parameter1);
+ STAT_ADD( IE_SAVEVSBREATH, fx->Parameter1);
+ STAT_ADD( IE_SAVEVSSPELL, fx->Parameter1);
+ //bless effect too?
+ STAT_ADD( IE_TOHIT, fx->Parameter1);
+ STAT_ADD( IE_MORALEBREAK, fx->Parameter1);
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_AID);
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 50, 50, 50);
+ }
+ return FX_APPLIED;
+}
+
+// 0x82 BlessNonCumulative
+
+static EffectRef fx_bane_ref = { "Bane", -1 };
+
+int fx_set_bless_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_bless_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (STATE_GET (STATE_BLESS) ) //bless is non cumulative
+ return FX_NOT_APPLIED;
+
+ //do this once
+ target->fxqueue.RemoveAllEffects(fx_bane_ref);
+
+ STATE_SET( STATE_BLESS );
+ target->SetSpellState(SS_BLESS);
+ STAT_ADD( IE_TOHIT, fx->Parameter1);
+ STAT_ADD( IE_MORALEBREAK, fx->Parameter1);
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_BLESS);
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0xc0, 0x80, 0);
+ }
+ return FX_APPLIED;
+}
+// 0x83 ChantNonCumulative
+int fx_set_chant_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_chant_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (STATE_GET (STATE_CHANT) ) //chant is non cumulative
+ return FX_NOT_APPLIED;
+ STATE_SET( STATE_CHANT );
+ target->SetSpellState(SS_GOODCHANT);
+ STAT_ADD( IE_LUCK, fx->Parameter1 );
+ return FX_APPLIED;
+}
+
+// 0x84 HolyNonCumulative
+int fx_set_holy_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_holy_state (%2d): Modifier: %d\n", fx->Opcode, fx->Parameter1 );
+
+ if (STATE_GET (STATE_HOLY) ) //holy power is non cumulative
+ return FX_NOT_APPLIED;
+ STATE_SET( STATE_HOLY );
+ //setting the spell state to be compatible with iwd2
+ target->SetSpellState(SS_HOLYMIGHT);
+ STAT_ADD( IE_STR, fx->Parameter1);
+ STAT_ADD( IE_CON, fx->Parameter1);
+ STAT_ADD( IE_DEX, fx->Parameter1);
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_HOLY);
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0x80, 0x80, 0x80);
+ }
+ return FX_APPLIED;
+}
+
+// 0x85 LuckNonCumulative
+int fx_luck_non_cumulative (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_luck_non_cumulative (%2d): Modifier: %d\n", fx->Opcode, fx->Parameter1);
+
+ if (STATE_GET (STATE_LUCK) ) //this luck is non cumulative
+ return FX_NOT_APPLIED;
+ STATE_SET( STATE_LUCK );
+ target->SetSpellState(SS_LUCK);
+ STAT_ADD( IE_LUCK, fx->Parameter1 );
+ STAT_ADD( IE_DAMAGELUCK, fx->Parameter1 );
+ return FX_APPLIED;
+}
+
+// 0x85 LuckCumulative (iwd2)
+int fx_luck_cumulative (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_luck_cumulative (%2d): Modifier: %d\n", fx->Opcode, fx->Parameter1);
+
+ target->SetSpellState(SS_LUCK);
+ STAT_ADD( IE_LUCK, fx->Parameter1 );
+ STAT_ADD( IE_DAMAGELUCK, fx->Parameter1 );
+ return FX_APPLIED;
+}
+
+// 0x86 State:Petrification
+int fx_set_petrified_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_petrified_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ BASE_STATE_SET( STATE_PETRIFIED );
+ return FX_NOT_APPLIED; //permanent effect
+}
+
+// 0x87 Polymorph
+static EffectRef fx_polymorph_ref = { "Polymorph", -1 };
+
+void CopyPolymorphStats(Actor *source, Actor *target)
+{
+ int i;
+
+ if(!polymorph_stats) {
+ AutoTable tab("polystat");
+ if (!tab) {
+ polymorph_stats = (int *) malloc(0);
+ polystatcount=0;
+ return;
+ }
+ polystatcount = tab->GetRowCount();
+ polymorph_stats=(int *) malloc(sizeof(int)*polystatcount);
+ for (i=0;i<polystatcount;i++) {
+ polymorph_stats[i]=core->TranslateStat(tab->QueryField(i,0));
+ }
+ }
+
+ assert(target->polymorphCache);
+
+ if (!target->polymorphCache->stats) {
+ target->polymorphCache->stats = new ieDword[polystatcount];
+ }
+
+ for(i=0;i<polystatcount;i++) {
+ target->polymorphCache->stats[i] = source->Modified[polymorph_stats[i]];
+ }
+}
+
+int fx_polymorph (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_polymorph_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (!gamedata->Exists(fx->Resource,IE_CRE_CLASS_ID)) {
+ //kill all polymorph effects
+ target->fxqueue.RemoveAllEffectsWithParam(fx_polymorph_ref, fx->Parameter2);
+ //destroy the magic item slot
+ target->inventory.RemoveItem(target->inventory.GetMagicSlot() );
+ return FX_NOT_APPLIED;
+ }
+
+ // to avoid repeatedly loading the file or keeping all the data around
+ // wasting memory, we keep a PolymorphCache object around, with only
+ // the data we need from the polymorphed creature
+ bool cached = true;
+ if (!target->polymorphCache) {
+ cached = false;
+ target->polymorphCache = new PolymorphCache();
+ }
+ if (!cached || strnicmp(fx->Resource,target->polymorphCache->Resource,sizeof(fx->Resource))) {
+ Actor *newCreature = gamedata->GetCreature(fx->Resource,0);
+
+ //I don't know how could this happen, existance of the resource was already checked
+ if (!newCreature) {
+ return FX_NOT_APPLIED;
+ }
+
+ memcpy(target->polymorphCache->Resource, fx->Resource, sizeof(fx->Resource));
+ CopyPolymorphStats(newCreature, target);
+
+ delete newCreature;
+ }
+
+ //copy all polymorphed stats
+ if(!fx->Parameter2) {
+ STAT_SET( IE_POLYMORPHED, 1 );
+ //disable mage and cleric spells (see IE_CASTING doc above)
+ STAT_BIT_OR(IE_CASTING, 6);
+ STAT_BIT_OR(IE_DISABLEDBUTTON, (1<<ACT_CAST)|(1<<ACT_QSPELL1)|(1<<ACT_QSPELL2)|(1<<ACT_QSPELL3) );
+ }
+
+ for(int i=0;i<polystatcount;i++) {
+ //copy only the animation ID
+ if (fx->Parameter2 && polymorph_stats[i] != IE_ANIMATION_ID) continue;
+
+ target->SetStat(polymorph_stats[i], target->polymorphCache->stats[i], 1);
+ }
+
+ return FX_APPLIED;
+}
+
+// 0x88 ForceVisible
+int fx_force_visible (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_force_visible (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (pstflags) {
+ BASE_STATE_CURE(STATE_PST_INVIS);
+ } else {
+ BASE_STATE_CURE(STATE_INVISIBLE);
+ }
+ target->fxqueue.RemoveAllEffectsWithParam(fx_set_invisible_state_ref,0);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_set_invisible_state_ref,2);
+ return FX_NOT_APPLIED;
+}
+
+// 0x89 ChantBadNonCumulative
+int fx_set_chantbad_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_chantbad_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (STATE_GET (STATE_CHANTBAD) ) //chant is non cumulative
+ return FX_NOT_APPLIED;
+ STATE_SET( STATE_CHANTBAD );
+ target->SetSpellState(SS_BADCHANT);
+ STAT_SUB( IE_LUCK, fx->Parameter1 );
+ return FX_APPLIED;
+}
+
+// 0x8A AnimationStateChange
+int fx_animation_stance (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_animation_stance (%2d): Stance: %d\n", fx->Opcode, fx->Parameter2 );
+
+ //this effect works only on living actors
+ if ( !STATE_GET(STATE_DEAD) ) {
+ target->SetStance(fx->Parameter2);
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x8B DisplayString
+// gemrb extension: rgb colour for displaystring
+// gemrb extension: resource may be an strref list (src or 2da)
+static EffectRef fx_protection_from_display_string_ref = { "Protection:String", -1 };
+
+int fx_display_string (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_display_string (%2d): StrRef: %d\n", fx->Opcode, fx->Parameter1 );
+ if(fx->Resource[0]) {
+ //TODO: create a single list reader that handles src and 2da too
+ SrcVector *rndstr=LoadSrc(fx->Resource);
+ if (rndstr) {
+ fx->Parameter1 = rndstr->at(rand()%rndstr->size());
+ FreeSrc(rndstr, fx->Resource);
+ DisplayStringCore(target, fx->Parameter1, DS_HEAD);
+ *(ieDword *) &target->overColor=fx->Parameter2;
+ return FX_NOT_APPLIED;
+ }
+
+ //random text for other games
+ ieDword *rndstr2 = core->GetListFrom2DA(fx->Resource);
+ int cnt = rndstr2[0];
+ if (cnt) {
+ fx->Parameter1 = rndstr2[core->Roll(1,cnt,0)];
+ }
+ }
+
+ if (!target->fxqueue.HasEffectWithParamPair(fx_protection_from_display_string_ref, fx->Parameter1, 0) ) {
+ displaymsg->DisplayStringName(fx->Parameter1, fx->Parameter2?fx->Parameter2:0xffffff, target, IE_STR_SOUND|IE_STR_SPEECH);
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x8c CastingGlow
+static const int ypos_by_direction[16]={10,10,10,0,-10,-10,-10,-10,-10,-10,-10,-10,0,10,10,10};
+static const int xpos_by_direction[16]={0,-10,-12,-14,-16,-14,-12,-10,0,10,12,14,16,14,12,10};
+
+int fx_casting_glow (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_casting_glow (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ if (cgcount<0) {
+ cgcount = core->ReadResRefTable("cgtable",casting_glows);
+ }
+ //remove effect if map is not loaded
+ Map *map = target->GetCurrentArea();
+ if (!map) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (fx->Parameter2<(ieDword) cgcount) {
+ ScriptedAnimation *sca = gamedata->GetScriptedAnimation(casting_glows[fx->Parameter2], false);
+ //remove effect if animation doesn't exist
+ if (!sca) {
+ return FX_NOT_APPLIED;
+ }
+ //12 is just an approximate value to set the height of the casting glow
+ //based on the avatar's size
+ int heightmod = target->GetAnims()->GetCircleSize()*12;
+ sca->XPos+=fx->PosX+xpos_by_direction[target->GetOrientation()];
+ sca->YPos+=fx->PosY+ypos_by_direction[target->GetOrientation()];
+ sca->ZPos+=heightmod;
+ sca->SetBlend();
+ sca->PlayOnce();
+ if (fx->Duration) {
+ sca->SetDefaultDuration(fx->Duration-core->GetGame()->GameTime);
+ } else {
+ sca->SetDefaultDuration(10000);
+ }
+ map->AddVVCell(sca);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0x8d VisualSpellHit
+int fx_visual_spell_hit (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_visual_spell_hit (%2d): Target: %d Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (shcount<0) {
+ shcount = core->ReadResRefTable("shtable",spell_hits);
+ }
+ //remove effect if map is not loaded
+ Map *map = target->GetCurrentArea();
+ if (!map) {
+ return FX_NOT_APPLIED;
+ }
+ if (fx->Parameter2<(ieDword) shcount) {
+ ScriptedAnimation *sca = gamedata->GetScriptedAnimation(spell_hits[fx->Parameter2], false);
+ //remove effect if animation doesn't exist
+ if (!sca) {
+ return FX_NOT_APPLIED;
+ }
+ if (fx->Parameter1) {
+ sca->XPos+=target->Pos.x;
+ sca->YPos+=target->Pos.y;
+ } else {
+ sca->XPos+=fx->PosX;
+ sca->YPos+=fx->PosY;
+ }
+ if (fx->Parameter2<32) {
+ int tmp = fx->Parameter2>>2;
+ if (tmp) {
+ sca->SetFullPalette(tmp);
+ }
+ }
+ sca->SetBlend();
+ sca->PlayOnce();
+ map->AddVVCell(sca);
+ } else {
+ printf("fx_visual_spell_hit: Unhandled Type: %d\n", fx->Parameter2);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0x8e Icon:Display
+int fx_display_portrait_icon (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_display_string (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ target->AddPortraitIcon(fx->Parameter2);
+ return FX_APPLIED;
+}
+
+//0x8f Item:CreateInSlot
+int fx_create_item_in_slot (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_create_item_in_slot (%2d): Button: %d\n", fx->Opcode, fx->Parameter2 );
+ //create item and set it in target's slot
+ target->inventory.SetSlotItemRes( fx->Resource, core->QuerySlot(fx->Parameter2), fx->Parameter1, fx->Parameter3, fx->Parameter4 );
+ if ((fx->TimingMode&0xff) == FX_DURATION_INSTANT_LIMITED) {
+ //convert it to a destroy item
+ fx->Opcode=EffectQueue::ResolveEffect(fx_remove_item_ref);
+ fx->TimingMode=FX_DURATION_DELAY_PERMANENT;
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x90 DisableButton
+// different in iwd2 and the rest (maybe also in how: 0-7?)
+int fx_disable_button (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_disable_button (%2d): Button: %d\n", fx->Opcode, fx->Parameter2 );
+
+ // iwd2 has a flexible action bar, so there are more possible parameter values
+ // only values 0-5 match the bg2 constants (which map to ACT_*)
+ // FIXME: support disabling all iwd2 buttons
+ if (target->spellbook.IsIWDSpellBook()) {
+ if (fx->Parameter2 < 6) STAT_BIT_OR( IE_DISABLEDBUTTON, 1<<fx->Parameter2 );
+ } else {
+ STAT_BIT_OR( IE_DISABLEDBUTTON, 1<<fx->Parameter2 );
+ }
+
+ if (fx->FirstApply && target->GetStat(IE_EA) < EA_CONTROLLABLE) {
+ core->SetEventFlag(EF_ACTION);
+ }
+ return FX_APPLIED;
+}
+
+//0x91 DisableSpellCasting
+//bg2: (-1 item), 0 - mage, 1 - cleric, 2 - innate, 3 - class
+//iwd2: (-1 item), 0 - all, 1 - mage+cleric, 2 - mage, 3 - cleric , 4 - innate,( 5 - class)
+
+/*internal representation of disabled spells in IE_CASTING (bitfield):
+1 - items (SPIT)
+2 - cleric (SPPR)
+4 - mage (SPWI)
+8 - innate (SPIN)
+16 - class (SPCL)
+*/
+
+static ieDword dsc_bits_iwd2[7]={1, 14, 6, 2, 4, 8, 16};
+static ieDword dsc_bits_bg2[7]={1, 4, 2, 8, 16, 14, 6};
+int fx_disable_spellcasting (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_disable_spellcasting (%2d): Button: %d\n", fx->Opcode, fx->Parameter2 );
+
+ bool display_warning = false;
+ ieDword tmp = fx->Parameter2+1;
+
+ //IWD2 Style spellbook
+ if (target->spellbook.IsIWDSpellBook()) {
+ switch(fx->Parameter2) {
+ case 0: // all
+ case 1: // mage and cleric
+ case 2: // mage
+ if (target->spellbook.GetKnownSpellsCount(IE_IWD2_SPELL_BARD, 0)) display_warning = true;
+ if (target->spellbook.GetKnownSpellsCount(IE_IWD2_SPELL_SORCEROR, 0)) display_warning = true;
+ if (target->spellbook.GetKnownSpellsCount(IE_IWD2_SPELL_WIZARD, 0)) display_warning = true;
+ break;
+ }
+ if (tmp<7) {
+ STAT_BIT_OR(IE_CASTING, dsc_bits_iwd2[tmp] );
+ }
+ } else { // bg2
+ if (fx->Parameter2 == 0) {
+ if (target->spellbook.GetKnownSpellsCount(IE_SPELL_TYPE_WIZARD, 0)) display_warning = true;
+ }
+ //-1-> 1 (item)
+ //0 -> 4 (mage)
+ //1 -> 2 (cleric)
+ //2 -> 8 (innate)
+ //3 -> 16 (class)
+ if (tmp<31) {
+ STAT_BIT_OR(IE_CASTING, dsc_bits_bg2[tmp] );
+ }
+ }
+ if (fx->FirstApply && display_warning && target->GetStat(IE_EA) < EA_CONTROLLABLE) {
+ displaymsg->DisplayConstantStringName(STR_DISABLEDMAGE, 0xff0000, target);
+ core->SetEventFlag(EF_ACTION);
+ }
+ return FX_APPLIED;
+}
+
+//0x92 Spell:Cast
+int fx_cast_spell (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cast_spell (%2d): Resource:%s Mode: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ if (fx->Parameter2) {
+ //apply spell on target
+ core->ApplySpell(fx->Resource, target, Owner, fx->Parameter1);
+
+ // give feedback: Caster - spellname : target
+ char tmp[100];
+ Spell *spl = gamedata->GetSpell(fx->Resource);
+ if (spl) {
+ snprintf(tmp, sizeof(tmp), "%s : %s", core->GetString(spl->SpellName), target->GetName(-1));
+ displaymsg->DisplayStringName(tmp, 0xffffff, Owner);
+ }
+ } else {
+ // save the current spell ref, so the rest of its effects can be applied afterwards
+ ieResRef OldSpellResRef;
+ memcpy(OldSpellResRef, Owner->SpellResRef, sizeof(OldSpellResRef));
+ Owner->SetSpellResRef(fx->Resource);
+ //cast spell on target
+ Owner->CastSpell(fx->Resource, target, false);
+ //actually finish casting (if this is not good enough, use an action???)
+ Owner->CastSpellEnd(fx->Parameter1);
+ Owner->SetSpellResRef(OldSpellResRef);
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x93 Spell:Learn
+int fx_learn_spell (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_learn_spell (%2d): Resource:%s Mode: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ //parameter1 is unused, gemrb lets you to make it not give XP
+ //probably we should also let this via a game flag if we want
+ //full compatibility with bg1
+ //parameter2 is used in bg1 and pst to specify the spell type; bg2 and iwd2 figure it out from the resource
+ target->LearnSpell(fx->Resource, fx->Parameter1);
+ return FX_NOT_APPLIED;
+}
+// 0x94 Spell:CastSpellPoint
+int fx_cast_spell_point (Scriptable* Owner, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_cast_spell_point (%2d): Resource:%s Mode: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ // save the current spell ref, so the rest of its effects can be applied afterwards
+ ieResRef OldSpellResRef;
+ memcpy(OldSpellResRef, Owner->SpellResRef, sizeof(OldSpellResRef));
+ Owner->SetSpellResRef(fx->Resource);
+ Point p(fx->PosX, fx->PosY);
+ Owner->CastSpellPoint(fx->Resource, p, false);
+ //actually finish casting (if this is not good enough, use an action???)
+ Owner->CastSpellPointEnd(fx->Parameter1);
+ Owner->SetSpellResRef(OldSpellResRef);
+ return FX_NOT_APPLIED;
+}
+
+// 0x95 Identify
+int fx_identify (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_identify (%2d): Resource:%s Mode: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ if (target->InParty) {
+ BASE_SET (IE_IDENTIFYMODE, 1);
+ core->SetEventFlag(EF_IDENTIFY);
+ }
+ return FX_NOT_APPLIED;
+}
+// 0x96 FindTraps
+// (actually, in bg2 the effect targets area objects and the range is implemented
+// by the inareans projectile) - inanimate, area, no sprite
+// TODO: effects should target inanimates using different code
+// 0 - detect traps automatically
+// 1 - detect traps by skill
+// 2 - detect secret doors automatically
+// 3 - detect secret doors by luck
+int fx_find_traps (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_find_traps (%2d)\n", fx->Opcode );
+ //reveal trapped containers, doors, triggers that are in the visible range
+ ieDword range = target->GetStat(IE_VISUALRANGE)*10;
+ ieDword skill;
+ bool detecttraps = true;
+
+ switch(fx->Parameter2) {
+ case 1:
+ skill = target->GetStat(IE_TRAPS);
+ break; //find traps
+ case 3:
+ //detect secret doors
+ skill = target->LuckyRoll(1, 100, 0, 0);
+ detecttraps = false;
+ break;
+ case 2:
+ detecttraps = false;
+ default:
+ //automatic find traps
+ skill = 256;
+ break;
+ }
+
+ TileMap *TMap = target->GetCurrentArea()->TMap;
+
+ int Count = 0;
+ while (true) {
+ Door* door = TMap->GetDoor( Count++ );
+ if (!door)
+ break;
+ if (Distance(door->Pos, target->Pos)<range) {
+ if (detecttraps) {
+ //when was door trap noticed
+ door->DetectTrap(skill);
+ }
+ door->TryDetectSecret(skill);
+ }
+ }
+
+ if (!detecttraps) {
+ return FX_NOT_APPLIED;
+ }
+
+ Count = 0;
+ while (true) {
+ Container* container = TMap->GetContainer( Count++ );
+ if (!container)
+ break;
+ if (Distance(container->Pos, target->Pos)<range) {
+ //when was door trap noticed
+ container->DetectTrap(skill);
+ }
+ }
+
+
+ Count = 0;
+ while (true) {
+ InfoPoint* trap = TMap->GetInfoPoint( Count++ );
+ if (!trap)
+ break;
+ if (Distance(trap->Pos, target->Pos)<range) {
+ //when was door trap noticed
+ trap->DetectTrap(skill);
+ }
+ }
+
+ return FX_NOT_APPLIED;
+}
+
+// 0x97 ReplaceCreature
+int fx_replace_creature (Scriptable* Owner, Actor* target, Effect *fx)
+{
+ if (0) printf( "fx_replace_creature (%2d): Resource: %s\n", fx->Opcode, fx->Resource );
+
+ //this safeguard exists in the original engine too
+ if (!gamedata->Exists(fx->Resource,IE_CRE_CLASS_ID)) {
+ return FX_NOT_APPLIED;
+ }
+
+ //the monster should appear near the effect position? (unsure)
+ Point p(fx->PosX, fx->PosY);
+
+ //remove old creature
+ switch(fx->Parameter2) {
+ case 0: //remove silently
+ target->DestroySelf();
+ break;
+ case 1: //chunky death
+ target->NewBase(IE_HITPOINTS,(ieDword) -100, MOD_ABSOLUTE);
+ target->Die(Owner);
+ break;
+ case 2: //normal death
+ target->Die(Owner);
+ break;
+ default:;
+ }
+ //create replacement; should we be passing the target instead of NULL?
+ //noooo, don't unsummon replacement creatures! - fuzzie
+ //Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ core->SummonCreature(fx->Resource, fx->Resource2, Owner, NULL,p, EAM_DEFAULT,-1, NULL, 0);
+ //delete newfx;
+ return FX_NOT_APPLIED;
+}
+
+// 0x98 PlayMovie
+int fx_play_movie (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_play_movie (%2d): Resource: %s\n", fx->Opcode, fx->Resource );
+ core->PlayMovie (fx->Resource);
+ return FX_NOT_APPLIED;
+}
+// 0x99 Overlay:Sanctuary
+#define ICE_GRADIENT 71
+
+static const ieDword fullwhite[7]={ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT,ICE_GRADIENT};
+
+int fx_set_sanctuary_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ //iwd and bg are a bit different, but we solve the whole stuff in a single opcode
+ if (0) printf( "fx_set_sanctuary_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (target->HasSpellState(SS_SANCTUARY)) return FX_NOT_APPLIED;
+ if (!fx->Parameter2) {
+ fx->Parameter2=1;
+ }
+ //this effect needs the pcf run immediately
+ STAT_SET_PCF( IE_SANCTUARY, fx->Parameter2);
+ //a rare event, but this effect gives more in bg2 than in iwd2
+ //so we use this flag
+ if (!enhanced_effects)
+ {
+ target->SetLockedPalette(fullwhite);
+ }
+ return FX_APPLIED;
+}
+
+// 0x9a Overlay:Entangle
+int fx_set_entangle_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_entangle_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //iwd2 effects that disable entangle
+ if (target->HasSpellState(SS_FREEACTION)) return FX_NOT_APPLIED;
+ if (target->HasSpellState(SS_AEGIS)) return FX_NOT_APPLIED;
+
+ if (!fx->Parameter2) {
+ fx->Parameter2=1;
+ }
+ STAT_SET_PCF( IE_ENTANGLE, fx->Parameter2);
+ return FX_APPLIED;
+}
+
+// 0x9b Overlay:MinorGlobe
+int fx_set_minorglobe_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_minorglobe_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //the resisted levels are stored in minor globe (bit 2-)
+ //the globe effect is stored in the first bit
+ STAT_BIT_OR_PCF( IE_MINORGLOBE, 1);
+ return FX_APPLIED;
+}
+
+// 0x9c Overlay:ShieldGlobe
+int fx_set_shieldglobe_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_shieldglobe_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //the shield vanishes on dead
+ if (STATE_GET(STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+ STAT_SET_PCF( IE_SHIELDGLOBE, 1);
+ return FX_APPLIED;
+}
+
+// 0x9d Overlay:Web
+int fx_set_web_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_web_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //iwd2 effects that disable web
+ if (target->HasSpellState(SS_FREEACTION)) return FX_NOT_APPLIED;
+ if (target->HasSpellState(SS_AEGIS)) return FX_NOT_APPLIED;
+
+ target->SetSpellState(SS_WEB);
+ //attack penalty in IWD2
+ STAT_SET_PCF( IE_WEB, 1);
+ STAT_SET(IE_MOVEMENTRATE, 0); //
+ return FX_APPLIED;
+}
+
+// 0x9e Overlay:Grease
+int fx_set_grease_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_grease_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //iwd2 effects that disable grease
+ if (target->HasSpellState(SS_FREEACTION)) return FX_NOT_APPLIED;
+ if (target->HasSpellState(SS_AEGIS)) return FX_NOT_APPLIED;
+
+ target->SetSpellState(SS_GREASE);
+ STAT_SET_PCF( IE_GREASE, 1);
+ //the movement rate is set by separate opcodes in all engines
+ return FX_APPLIED;
+}
+
+// 0x9f MirrorImageModifier
+int fx_mirror_image_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_mirror_image_modifier (%2d): Mod: %d\n", fx->Opcode, fx->Parameter1 );
+ if (STATE_GET(STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+ if (!fx->Parameter1) {
+ return FX_NOT_APPLIED;
+ }
+ if (pstflags) {
+ STATE_SET( STATE_PST_MIRROR );
+ }
+ else {
+ STATE_SET( STATE_MIRROR );
+ }
+ if (fx->Parameter2) {
+ target->SetSpellState(SS_REFLECTION);
+ } else {
+ target->SetSpellState(SS_MIRRORIMAGE);
+ }
+ //actually, there is no such stat in the original IE
+ STAT_SET( IE_MIRRORIMAGES, fx->Parameter1);
+ return FX_APPLIED;
+}
+
+// 0xa0 Cure:Sanctuary
+static EffectRef fx_sanctuary_state_ref = { "Overlay:Sanctuary", -1 };
+
+int fx_cure_sanctuary_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_sanctuary_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET( IE_SANCTUARY, 0);
+ target->fxqueue.RemoveAllEffects(fx_sanctuary_state_ref);
+ return FX_NOT_APPLIED;
+}
+
+// 0xa1 Cure:Panic
+static EffectRef fx_set_panic_state_ref = { "State:Panic", -1 };
+
+int fx_cure_panic_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_panic_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_PANIC );
+ target->fxqueue.RemoveAllEffects(fx_set_panic_state_ref);
+ return FX_NOT_APPLIED;
+}
+
+// 0xa2 Cure:Hold
+static EffectRef fx_hold_creature_ref = { "State:Hold", -1 };
+
+int fx_cure_hold_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_hold_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //note that this effect doesn't remove 185 (another hold effect)
+ target->fxqueue.RemoveAllEffects( fx_hold_creature_ref );
+ target->fxqueue.RemoveAllEffects(fx_hold_creature_no_icon_ref);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_display_portrait_icon_ref, PI_HELD);
+ return FX_NOT_APPLIED;
+}
+
+// 0xa3 FreeAction
+static EffectRef fx_movement_modifier_ref = { "MovementRateModifier2", -1 };
+
+int fx_cure_slow_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_slow_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ target->fxqueue.RemoveAllEffects( fx_movement_modifier_ref );
+// STATE_CURE( STATE_SLOWED );
+ return FX_NOT_APPLIED;
+}
+
+// 0xa4 Cure:Intoxication
+static EffectRef fx_intoxication_ref = { "IntoxicationModifier", -1 };
+
+int fx_cure_intoxication (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_intoxication (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ target->fxqueue.RemoveAllEffects( fx_intoxication_ref );
+ BASE_SET(IE_INTOXICATION,0);
+ return FX_NOT_APPLIED;
+}
+
+// 0xa5 PauseTarget
+int fx_pause_target (Scriptable* /*Owner*/, Actor * target, Effect* fx)
+{
+ if (0) printf( "fx_pause_target (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_CASTERHOLD );
+ return FX_PERMANENT;
+}
+
+// 0xa6 MagicResistanceModifier
+int fx_magic_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_magic_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTMAGIC );
+ return FX_APPLIED;
+}
+
+// 0xa7 MissileHitModifier
+int fx_missile_to_hit_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_missile_to_hit_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_MISSILEHITBONUS );
+ return FX_APPLIED;
+}
+
+// 0xa8 RemoveCreature
+// removes targeted creature
+// removes creature specified by resource key (gemrb extension)
+int fx_remove_creature (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_creature (%2d)\n", fx->Opcode);
+
+ Map *map = NULL;
+
+ if (target) {
+ map = target->GetCurrentArea();
+ }
+ else {
+ map = core->GetGame()->GetCurrentArea();
+ }
+ Actor *actor = target;
+
+ if (fx->Resource[0]) {
+ actor = map->GetActorByResource(fx->Resource);
+ }
+
+ if (actor) {
+ //leaveparty will be handled automagically
+ //plot critical items are not handled, shall we?
+ actor->DestroySelf();
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0xa9 Icon:Disable
+int fx_disable_portrait_icon (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_disable_portrait_icon (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ target->DisablePortraitIcon(fx->Parameter2);
+ return FX_APPLIED;
+}
+
+// 0xaa DamageAnimation
+int fx_damage_animation (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_damage_animation (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //Parameter1 is a gemrb extension
+ target->PlayDamageAnimation(fx->Parameter2, !fx->Parameter1);
+ return FX_NOT_APPLIED;
+}
+
+// 0xab Spell:Add
+int fx_add_innate (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_add_innate (%2d): Resource: %s Mode: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ target->LearnSpell(fx->Resource, fx->Parameter2^LS_MEMO);
+ //this is an instant, so it shouldn't stick
+ return FX_NOT_APPLIED;
+}
+
+// 0xac Spell:Remove
+//gemrb extension: deplete spell by resref
+int fx_remove_spell (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_spell (%2d): Resource: %s Type:%d\n", fx->Opcode, fx->Resource, fx->Parameter2);
+ switch (fx->Parameter2) {
+ default:
+ target->spellbook.RemoveSpell(fx->Resource);
+ break;
+ case 1: //forget all spells of Resource
+ do {} while(target->spellbook.HaveSpell( fx->Resource, HS_DEPLETE ));
+ break;
+ case 2: //forget x spells of resource
+ while( fx->Parameter1--) {
+ target->spellbook.HaveSpell( fx->Resource, HS_DEPLETE );
+ }
+ break;
+ }
+ //this is an instant, so it shouldn't stick
+ return FX_NOT_APPLIED;
+}
+
+// 0xad PoisonResistanceModifier
+int fx_poison_resistance_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_poison_resistance_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_RESISTPOISON );
+ return FX_APPLIED;
+}
+
+//0xae PlaySound
+int fx_playsound (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_playsound (%s)", fx->Resource );
+ //this is probably inaccurate
+ if (target) {
+ core->GetAudioDrv()->Play(fx->Resource, target->Pos.x, target->Pos.y);
+ } else {
+ core->GetAudioDrv()->Play(fx->Resource);
+ }
+ //this is an instant, it shouldn't stick
+ return FX_NOT_APPLIED;
+}
+
+//0x6d State:Hold3
+//0xfb State:Hold4
+int fx_hold_creature_no_icon (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_hold_creature_no_icon (%2d): Value: %d, IDS: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //actually the original engine just skips this effect if the target is dead
+ if ( STATE_GET(STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (!EffectQueue::match_ids( target, fx->Parameter2, fx->Parameter1) ) {
+ //if the ids don't match, the effect doesn't stick
+ return FX_NOT_APPLIED;
+ }
+ target->SetSpellState(SS_HELD);
+ STAT_SET( IE_HELD, 1);
+ return FX_APPLIED;
+}
+
+//0xaf State:Hold
+//0xb9 State:Hold2
+//(0x6d/0x1a8 for iwd2)
+int fx_hold_creature (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_hold_creature (%2d): Value: %d, IDS: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //actually the original engine just skips this effect if the target is dead
+ if ( STATE_GET(STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //iwd2 free action or blood rage disables this effect
+ if (target->HasSpellState(SS_FREEACTION)) return FX_NOT_APPLIED;
+ if (target->HasSpellState(SS_BLOODRAGE)) return FX_NOT_APPLIED;
+ if (target->HasSpellState(SS_AEGIS)) return FX_NOT_APPLIED;
+
+ if (!EffectQueue::match_ids( target, fx->Parameter2, fx->Parameter1) ) {
+ //if the ids don't match, the effect doesn't stick
+ return FX_NOT_APPLIED;
+ }
+ target->SetSpellState(SS_HELD);
+ STAT_SET( IE_HELD, 1);
+ target->AddPortraitIcon(PI_HELD);
+ return FX_APPLIED;
+}
+//0xb0 see: fx_movement_modifier
+
+//0xb1 ApplyEffect
+int fx_apply_effect (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_apply_effect (%2d) %s", fx->Opcode, fx->Resource );
+
+ //this effect executes a file effect in place of this effect
+ //the file effect inherits the target and the timingmode, but gets
+ //a new chance to roll percents
+ int ret = FX_NOT_APPLIED;
+ if (!target) {
+ return ret;
+ }
+ if (EffectQueue::match_ids( target, fx->Parameter2, fx->Parameter1) ) {
+ Point p(fx->PosX, fx->PosY);
+
+ //apply effect, if the effect is a goner, then kill
+ //this effect too
+ Effect *newfx = core->GetEffect(fx->Resource, fx->Power, p);
+ if (newfx) {
+ Effect *myfx = new Effect;
+ memcpy(myfx, newfx, sizeof(Effect));
+ myfx->random_value = core->Roll(1,100,-1);
+ myfx->Target = FX_TARGET_PRESET;
+ myfx->TimingMode = fx->TimingMode;
+ myfx->Duration = fx->Duration;
+ myfx->CasterID = fx->CasterID;
+ ret = target->fxqueue.ApplyEffect(target, myfx, fx->FirstApply, !fx->Parameter3);
+ fx->Parameter3 = 1;
+ delete myfx;
+ }
+ //newfx is a borrowed reference don't delete it
+ }
+ return ret;
+}
+
+//0xb2 hitbonus generic effect ToHitVsCreature
+//0xb3 damagebonus generic effect DamageVsCreature
+// b4 can't use item (resource) generic effect CantUseItem
+// b5 can't use itemtype (resource) generic effect CantUseItemType
+
+// b6 generic effect ApplyEffectItem
+int fx_apply_effect_item (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf("fx_apply_effect_item (%2d) (%.8s)\n", fx->Opcode, fx->Resource);
+ if (target->inventory.HasItem(fx->Resource, 0) ) {
+ core->ApplySpell(fx->Resource2, target, Owner, fx->Parameter1);
+ return FX_NOT_APPLIED;
+ }
+ return FX_APPLIED;
+}
+
+// b7 generic effect ApplyEffectItemType
+int fx_apply_effect_item_type (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf("fx_apply_effect_item (%2d), Type: %d\n", fx->Opcode, fx->Parameter2);
+ if (target->inventory.HasItemType(fx->Parameter2) ) {
+ core->ApplySpell(fx->Resource, target, Owner, fx->Parameter1);
+ return FX_NOT_APPLIED;
+ }
+ return FX_APPLIED;
+}
+
+// b8 DontJumpModifier
+int fx_dontjump_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_dontjump_modifier (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_SET( IE_DONOTJUMP, fx->Parameter2 );
+ return FX_APPLIED;
+}
+
+//0xb9 see above: fx_hold_creature
+
+//0xba MoveToArea
+int fx_move_to_area (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_move_to_area (%2d) %s", fx->Opcode, fx->Resource );
+ //delay effect until the target has finished the previous move to an area
+ //hopefully this fixes an evil bug
+ Map *map = target->GetCurrentArea();
+ if (!map || !map->HasActor(target)) {
+ //stay around for the next evaluation
+ return FX_APPLIED;
+ }
+ Point p(fx->PosX,fx->PosY);
+ MoveBetweenAreasCore(target, fx->Resource, p, fx->Parameter2, true);
+ //this effect doesn't stick
+ return FX_NOT_APPLIED;
+}
+
+// 0xbb Variable:StoreLocalVariable
+int fx_local_variable (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ //this is a hack, the variable name spreads across the resources
+ if (0) printf( "fx_local_variable (%2d) %s=%d", fx->Opcode, fx->Resource, fx->Parameter1 );
+ target->locals->SetAt(fx->Resource, fx->Parameter1);
+ //local variable effects are not applied, they will be resaved though
+ return FX_NOT_APPLIED;
+}
+
+// 0xbc AuraCleansingModifier
+int fx_auracleansing_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_auracleansing_modifier (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_SET( IE_AURACLEANSING, fx->Parameter2 );
+ return FX_APPLIED;
+}
+
+// 0xbd CastingSpeedModifier
+int fx_castingspeed_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_castingspeed_modifier (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_MOD( IE_MENTALSPEED );
+ return FX_APPLIED;
+}
+
+// 0xbe PhysicalSpeedModifier
+int fx_attackspeed_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_attackspeed_modifier (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_MOD( IE_PHYSICALSPEED );
+ return FX_APPLIED;
+}
+
+// 0xbf CastingLevelModifier
+// gemrb extension: if the resource key is set, apply param1 as a percentual modifier
+int fx_castinglevel_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_castinglevel_modifier (%2d) Value:%d Type:%d", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ switch (fx->Parameter2) {
+ case 0:
+ if (fx->Resource[0]) {
+ STAT_MUL( IE_CASTINGLEVELBONUSMAGE, fx->Parameter1 );
+ } else {
+ STAT_SET( IE_CASTINGLEVELBONUSMAGE, fx->Parameter1 );
+ }
+ break;
+ case 1:
+ if (fx->Resource[0]) {
+ STAT_MUL( IE_CASTINGLEVELBONUSCLERIC, fx->Parameter1 );
+ } else {
+ STAT_SET( IE_CASTINGLEVELBONUSCLERIC, fx->Parameter1 );
+ }
+ break;
+ default:
+ return FX_NOT_APPLIED;
+ }
+ return FX_APPLIED;
+}
+
+// 0xc0 FindFamiliar
+// param2 = 1 alignment is in param1
+// param2 = 2 resource used
+#define FAMILIAR_NORMAL 0
+#define FAMILIAR_ALIGNMENT 1
+#define FAMILIAR_RESOURCE 2
+
+static EffectRef fx_familiar_constitution_loss_ref = { "FamiliarBond", -1 };
+static EffectRef fx_familiar_marker_ref = { "FamiliarMarker", -1 };
+static EffectRef fx_maximum_hp_modifier_ref = { "MaximumHPModifier", -1 };
+
+int fx_find_familiar (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_find_familiar (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+
+ if (!target) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (!target->GetCurrentArea()) {
+ //this will delay casting until we get an area
+ return FX_APPLIED;
+ }
+
+ Game *game = core->GetGame();
+ //FIXME: the familiar block field is not saved in the game and not set when the
+ //familiar is itemized, so a game reload will clear it (see how this is done in original)
+ if (game->familiarBlock) {
+ displaymsg->DisplayConstantStringName(STR_FAMBLOCK, 0xff0000, target);
+ return FX_NOT_APPLIED;
+ }
+
+ //The protagonist is ALWAYS in the first slot
+ if (game->GetPC(0, false)!=target) {
+ displaymsg->DisplayConstantStringName(STR_FAMPROTAGONIST, 0xff0000, target);
+ return FX_NOT_APPLIED;
+ }
+
+
+ if (fx->Parameter2!=FAMILIAR_RESOURCE) {
+ ieDword alignment;
+
+ if (fx->Parameter2==FAMILIAR_ALIGNMENT) {
+ alignment = fx->Parameter1;
+ } else {
+ alignment = target->GetStat(IE_ALIGNMENT);
+ alignment = ((alignment&AL_LC_MASK)>>4)*3+(alignment&AL_GE_MASK)-4;
+ }
+ if (alignment>8) {
+ return FX_NOT_APPLIED;
+ }
+ memcpy(fx->Resource, core->GetGame()->Familiars[alignment],sizeof(ieResRef) );
+ fx->Parameter2=FAMILIAR_RESOURCE;
+ }
+
+ //summon familiar with fx->Resource
+ Actor *fam = gamedata->GetCreature(fx->Resource);
+ if (!fam) {
+ return FX_NOT_APPLIED;
+ }
+ fam->SetBase(IE_EA, EA_FAMILIAR);
+ fam->LastSummoner = Owner->GetGlobalID();
+
+ Map *map = target->GetCurrentArea();
+ map->AddActor(fam);
+ Point p(fx->PosX, fx->PosY);
+ fam->SetPosition(p, true, 0);
+ fam->RefreshEffects(NULL);
+
+ if (fx->Resource2[0]) {
+ ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(fx->Resource2, false);
+ if (vvc) {
+ //This is the final position of the summoned creature
+ //not the original target point
+ vvc->XPos=fam->Pos.x;
+ vvc->YPos=fam->Pos.y;
+ //force vvc to play only once
+ vvc->PlayOnce();
+ map->AddVVCell( vvc );
+ }
+ }
+
+ //Make the familiar an NPC (MoveGlobal needs this)
+ core->GetGame()->AddNPC(fam);
+
+ //Add some essential effects
+ Effect *newfx = EffectQueue::CreateEffect(fx_familiar_constitution_loss_ref, fam->GetBase(IE_HITPOINTS)/2, 0, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, fam, fam);
+ delete newfx;
+
+ newfx = EffectQueue::CreateEffect(fx_familiar_marker_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, fam, fam);
+ delete newfx;
+
+ //maximum hp bonus of half the familiar's hp
+ newfx = EffectQueue::CreateEffect(fx_maximum_hp_modifier_ref, fam->GetBase(IE_HITPOINTS)/2, MOD_ADDITIVE, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, (Actor *) Owner, Owner);
+ delete newfx;
+
+ return FX_NOT_APPLIED;
+}
+
+// 0xc1 InvisibleDetection
+int fx_see_invisible_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_see_invisible_modifier (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_SET( IE_SEEINVISIBLE, fx->Parameter2 );
+ return FX_APPLIED;
+}
+
+// 0xc2 IgnoreDialogPause
+int fx_ignore_dialogpause_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_ignore_dialogpause_modifier (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_SET( IE_IGNOREDIALOGPAUSE, fx->Parameter2 );
+ return FX_APPLIED;
+}
+
+//0xc3 FamiliarBond
+//when this effect's target dies it should incur damage on protagonist
+static EffectRef fx_damage_opcode_ref = { "Damage", -1 };
+static EffectRef fx_constitution_modifier_ref = { "ConstitutionModifier", -1 };
+
+int fx_familiar_constitution_loss (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_familiar_constitution_loss (%2d): Loss: %d\n", fx->Opcode,(signed) fx->Parameter1 );
+ if (! (STAT_GET(IE_STATE_ID)&STATE_NOSAVE)) {
+ return FX_APPLIED;
+ }
+ Effect *newfx;
+ //familiar died
+ Actor *master = core->GetGame()->FindPC(1);
+ if (!master) return FX_NOT_APPLIED;
+
+ //lose 1 point of constitution
+ newfx = EffectQueue::CreateEffect(fx_constitution_modifier_ref, (ieDword) -1, MOD_ADDITIVE, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, master, master);
+ delete newfx;
+
+ //remove the maximum hp bonus
+ newfx = EffectQueue::CreateEffect(fx_maximum_hp_modifier_ref, (ieDword) -fx->Parameter1, 3, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, master, master);
+ delete newfx;
+
+ //damage for half of the familiar's hitpoints
+ newfx = EffectQueue::CreateEffect(fx_damage_opcode_ref, fx->Parameter1, DAMAGE_CRUSHING, FX_DURATION_INSTANT_PERMANENT);
+ core->ApplyEffect(newfx, master, master);
+ delete newfx;
+
+ return FX_NOT_APPLIED;
+}
+
+//0xc4 FamiliarMarker
+int fx_familiar_marker (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_familiar_marker (%2d)\n", fx->Opcode );
+ if (! (STAT_GET(IE_STATE_ID)&STATE_NOSAVE)) {
+ core->GetGame()->familiarBlock=true;
+ return FX_APPLIED;
+ }
+ core->GetGame()->familiarBlock=false;
+ return FX_NOT_APPLIED;
+}
+
+// 0xc5 Bounce:Projectile
+int fx_bounce_projectile (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_projectile (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_PROJECTILE );
+ return FX_APPLIED;
+}
+
+// 0xc6 Bounce:Opcode
+int fx_bounce_opcode (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_opcode (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_OPCODE );
+ target->AddPortraitIcon(PI_BOUNCE2);
+ return FX_APPLIED;
+}
+
+// 0xc7 Bounce:SpellLevel
+int fx_bounce_spelllevel (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_spellevel (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_LEVEL );
+ target->AddPortraitIcon(PI_BOUNCE2);
+ return FX_APPLIED;
+}
+
+// 0xc8 Bounce:SpellLevelDec
+int fx_bounce_spelllevel_dec (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_spellevel_dec (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_LEVEL_DEC );
+ target->AddPortraitIcon(PI_BOUNCE);
+ return FX_APPLIED;
+}
+
+//0xc9 Protection:SpellLevelDec
+int fx_protection_spelllevel_dec (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_spelllevel_dec (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_IMMUNITY, IMM_LEVEL_DEC );
+ target->AddPortraitIcon(PI_BOUNCE2);
+ return FX_APPLIED;
+}
+
+//0xca Bounce:School
+int fx_bounce_school (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_school (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_SCHOOL );
+ target->AddPortraitIcon(PI_BOUNCE2);
+ return FX_APPLIED;
+}
+
+// 0xcb Bounce:SecondaryType
+int fx_bounce_secondary_type (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_secondary_type (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_SECTYPE );
+ target->AddPortraitIcon(PI_BOUNCE2);
+ return FX_APPLIED;
+}
+
+// 0xcc //resist school
+int fx_protection_school (Scriptable* /*Owner*/, Actor* target, Effect *fx)
+{
+ if (0) printf( "fx_protection_school (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_IMMUNITY, IMM_SCHOOL);
+ return FX_APPLIED;
+}
+
+// 0xcd //resist sectype
+int fx_protection_secondary_type (Scriptable* /*Owner*/, Actor* target, Effect *fx)
+{
+ if (0) printf( "fx_protection_secondary_type (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_IMMUNITY, IMM_SECTYPE);
+ return FX_APPLIED;
+}
+
+//0xce Protection:Spell
+int fx_resist_spell (Scriptable* /*Owner*/, Actor* target, Effect *fx)
+{
+ if (0) printf( "fx_resist_spell (%2d): Resource: %s\n", fx->Opcode, fx->Resource );
+ if (strnicmp(fx->Resource,fx->Source,sizeof(fx->Resource)) ) {
+ STAT_BIT_OR( IE_IMMUNITY, IMM_RESOURCE);
+ return FX_APPLIED;
+ }
+ //this has effect only on first apply, it will stop applying the spell
+ return FX_ABORT;
+}
+
+// ??? Protection:SpellDec
+int fx_resist_spell_dec (Scriptable* /*Owner*/, Actor* target, Effect *fx)
+{
+ if (0) printf( "fx_resist_spell_dec (%2d): Resource: %s\n", fx->Opcode, fx->Resource );
+ if (strnicmp(fx->Resource,fx->Source,sizeof(fx->Resource)) ) {
+ STAT_BIT_OR( IE_IMMUNITY, IMM_RESOURCE_DEC);
+ return FX_APPLIED;
+ }
+ //this has effect only on first apply, it will stop applying the spell
+ return FX_ABORT;
+}
+
+// 0xcf Bounce:Spell
+int fx_bounce_spell (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_spell (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_RESOURCE );
+ return FX_APPLIED;
+}
+
+// ??? Bounce:SpellDec
+int fx_bounce_spell_dec (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_spell (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_RESOURCE_DEC );
+ return FX_APPLIED;
+}
+
+// 0xd0 MinimumHPModifier
+// the original engine didn't allow modifying of this stat
+// it allowed only setting it, and only by one instance
+int fx_minimum_hp_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_minimum_hp_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_MINHITPOINTS );
+ return FX_APPLIED;
+}
+
+//0xd1 PowerWordKill
+int fx_power_word_kill (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_power_word_kill (%2d): HP: %d Stat: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword limit = 60;
+
+ if (fx->Parameter1) {
+ limit = fx->Parameter1;
+ }
+ //normally this would work only with hitpoints
+ //but why not add some extra features
+ ieDword stat = target->GetStat (fx->Parameter2&0xffff);
+
+ if (stat < limit) {
+ target->Die( Owner );
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xd2 PowerWordStun
+int fx_power_word_stun (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_power_word_stun (%2d): HP: %d Stat: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword limit = 90;
+
+ if (fx->Parameter1) {
+ limit = fx->Parameter1;
+ }
+ //normally this would work only with hitpoints
+ //but why not add some extra features
+ ieDword stat = target->GetStat (fx->Parameter2&0xffff);
+ ieDword x = fx->Parameter2>>16; //dice sides
+
+ if (stat > limit) {
+ return FX_NOT_APPLIED;
+ }
+ //recalculate delay
+ stat = (stat * 3 + limit - 1) / limit;
+ //delay will be calculated as 1dx/2dx/3dx
+ //depending on the current hitpoints (or the stat in param2)
+ stat = core->Roll(stat,x?x:4,0) * core->Time.round_size;
+ fx->Duration = core->GetGame()->GameTime+stat;
+ fx->TimingMode = FX_DURATION_ABSOLUTE;
+ fx->Opcode = EffectQueue::ResolveEffect(fx_set_stun_state_ref);
+ return fx_set_stun_state(Owner,target,fx);
+}
+
+//0xd3 State:Imprisonment (avatar removal plus portrait icon)
+int fx_imprisonment (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_imprisonment (%2d)\n", fx->Opcode );
+ target->SetMCFlag(MC_HIDDEN, BM_OR);
+ target->AddPortraitIcon(PI_PRISON);
+ return FX_APPLIED;
+}
+
+//0xd4 Cure:Imprisonment
+static EffectRef fx_imprisonment_ref = { "Imprisonment", -1 };
+static EffectRef fx_maze_ref = { "Maze", -1 };
+
+int fx_freedom (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_freedom (%2d)\n", fx->Opcode );
+ target->fxqueue.RemoveAllEffects( fx_imprisonment_ref );
+ target->fxqueue.RemoveAllEffects( fx_maze_ref );
+ return FX_NOT_APPLIED;
+}
+
+//0xd5 Maze
+int fx_maze (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_maze (%2d)\n", fx->Opcode );
+ if (!fx->Parameter2 && fx->FirstApply) {
+ //get the maze dice number (column 3)
+ int stat = target->GetSafeStat(IE_INT);
+ int size = core->GetIntelligenceBonus(3, stat);
+ int dice = core->GetIntelligenceBonus(4, stat);
+ fx->Duration = core->GetGame()->GameTime+target->LuckyRoll(dice, size, 0, 0)*100;
+ }
+ target->SetMCFlag(MC_HIDDEN, BM_OR);
+ target->AddPortraitIcon(PI_MAZE);
+ return FX_APPLIED;
+}
+
+//0xd6 CastFromList
+//GemRB extension: if fx->Parameter1 is set, it is the bitfield of spell types (could be priest spells)
+int fx_select_spell (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_select_spell (%2d) %d\n", fx->Opcode, fx->Parameter2 );
+ Spellbook *sb = &target->spellbook;
+ if(fx->Parameter2) {
+ //all known spells, no need to memorize
+ if (!fx->Parameter1) {
+ fx->Parameter1=1<<IE_SPELL_TYPE_WIZARD;
+ }
+ sb->SetCustomSpellInfo(NULL, fx->Source, fx->Parameter1);
+ } else {
+ //all spells listed in 2da
+ ieResRef *data = NULL;
+
+ int count = core->ReadResRefTable(fx->Resource, data);
+ sb->SetCustomSpellInfo(data, fx->Source, count);
+ core->FreeResRefTable(data, count);
+ }
+ core->GetDictionary()->SetAt("Type",-1);
+ //this is required, because not all of these opcodes are firing right at casting
+ core->GetDictionary()->SetAt("ActionLevel", 2);
+ core->SetEventFlag(EF_ACTION);
+ return FX_NOT_APPLIED;
+}
+
+// 0xd7 PlayVisualEffect
+static EffectRef fx_protection_from_animation_ref = { "Protection:Animation", -1 };
+int fx_play_visual_effect (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_play_visual_effect (%2d): Resource: %s Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+
+ //this is in the original engine (dead actors lose this effect)
+ if (STATE_GET( STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //delay action until area is loaded to avoid crash
+ Map *map = target->GetCurrentArea();
+ if (!map) return FX_APPLIED;
+
+ //if it is sticky, don't add it if it is already played
+ if (fx->Parameter2) {
+ ScriptedAnimation *vvc = target->GetVVCCell(fx->Resource);
+ if (vvc) {
+ vvc->active = true;
+ return FX_APPLIED;
+ }
+ if (! fx->FirstApply) return FX_NOT_APPLIED;
+ }
+
+ if (target->fxqueue.HasEffectWithResource(fx_protection_from_animation_ref,fx->Resource)) {
+ //effect supressed by opcode 0x128
+
+ return FX_APPLIED;
+ }
+
+
+ ScriptedAnimation* sca = gamedata->GetScriptedAnimation(fx->Resource, false);
+
+ //don't crash on nonexistent resources
+ if (!sca) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (fx->TimingMode!=FX_DURATION_INSTANT_PERMANENT) {
+ sca->SetDefaultDuration(fx->Duration-core->GetGame()->GameTime);
+ }
+ if (fx->Parameter2 == 1) {
+ //play over target (sticky)
+ sca->effect_owned = true;
+ target->AddVVCell( sca );
+ return FX_APPLIED;
+ }
+
+ //not sticky
+ if (fx->Parameter2 == 2 || !target) {
+ sca->XPos = fx->PosX;
+ sca->YPos = fx->PosY;
+ } else {
+ sca->XPos = target->Pos.x;
+ sca->YPos = target->Pos.y;
+ }
+ sca->PlayOnce();
+ map->AddVVCell( sca );
+ return FX_NOT_APPLIED;
+}
+
+//d8 LevelDrainModifier
+
+static EffectRef fx_leveldrain_ref = { "LevelDrainModifier", -1 };
+
+// FIXME: BG2 level drain uses parameter3 to decrease the MaxHp, and parameter4 to decrease level. (unset)
+int fx_leveldrain_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_leveldrain_modifier (%2d): Mod: %d\n", fx->Opcode, fx->Parameter1 );
+
+ //never subtract more than the maximum hitpoints
+ ieDword x = STAT_GET(IE_MAXHITPOINTS)-1;
+ if (fx->Parameter1*4<x) {
+ x=fx->Parameter1*4;
+ }
+ STAT_ADD(IE_LEVELDRAIN, fx->Parameter1);
+ STAT_SUB(IE_MAXHITPOINTS, x);
+ STAT_SUB(IE_SAVEVSDEATH, fx->Parameter1);
+ STAT_SUB(IE_SAVEVSWANDS, fx->Parameter1);
+ STAT_SUB(IE_SAVEVSPOLY, fx->Parameter1);
+ STAT_SUB(IE_SAVEVSBREATH, fx->Parameter1);
+ STAT_SUB(IE_SAVEVSSPELL, fx->Parameter1);
+ target->AddPortraitIcon(PI_LEVELDRAIN);
+ //decrease current hitpoints on first apply
+ if (fx->FirstApply) {
+ //current hitpoints don't have base/modified, only current
+ BASE_SUB(IE_HITPOINTS, x);
+ }
+ // TODO: lore, thieving
+ return FX_APPLIED;
+}
+
+//d9 PowerWordSleep
+static EffectRef fx_sleep_ref = { "State:Sleep", -1 };
+
+int fx_power_word_sleep (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_power_word_sleep (%2d): HP: %d Stat: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword limit = 20;
+
+ if (fx->Parameter1) {
+ limit = fx->Parameter1;
+ }
+
+ ieDword stat = target->GetStat (fx->Parameter2&0xffff);
+ ieDword x = fx->Parameter2>>16; //rounds
+ if (!x) x = 5;
+
+ if (stat>limit) {
+ return FX_NOT_APPLIED;
+ }
+ //translate this effect to a normal sleep effect
+ //recalculate delay
+ fx->Duration = core->GetGame()->GameTime+x*core->Time.round_size;
+ fx->TimingMode = FX_DURATION_ABSOLUTE;
+ fx->Opcode = EffectQueue::ResolveEffect(fx_sleep_ref);
+ fx->Parameter2=0;
+ return fx_set_unconscious_state(Owner,target,fx);
+}
+
+// 0xda StoneSkinModifier
+int fx_stoneskin_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_stoneskin_modifier (%2d): Mod: %d\n", fx->Opcode, fx->Parameter1 );
+ if (!fx->Parameter1) {
+ return FX_NOT_APPLIED;
+ }
+
+ //dead actors lose this effect
+ if (STATE_GET( STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //this is the bg2 style stoneskin, not normally using spell states
+ //but this way we can support hybrid games
+ if (fx->Parameter2) {
+ target->SetSpellState(SS_IRONSKIN);
+ //gradient for iron skins?
+ } else {
+ target->SetSpellState(SS_STONESKIN);
+ SetGradient(target, 14);
+ }
+ STAT_SET(IE_STONESKINS, fx->Parameter1);
+ target->AddPortraitIcon(PI_STONESKIN);
+ return FX_APPLIED;
+}
+
+//0xdb ac vs creature type (general effect)
+//0xdc DispelSchool
+int fx_dispel_school (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ ieResRef Removed;
+
+ if (0) printf( "fx_dispel_school (%2d): Level: %d Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ target->fxqueue.RemoveLevelEffects(Removed, fx->Parameter1, RL_MATCHSCHOOL, fx->Parameter2);
+ return FX_NOT_APPLIED;
+}
+//0xdd DispelSecondaryType
+int fx_dispel_secondary_type (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ ieResRef Removed;
+
+ if (0) printf( "fx_dispel_secondary_type (%2d): Level: %d Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ target->fxqueue.RemoveLevelEffects(Removed, fx->Parameter1, RL_MATCHSECTYPE, fx->Parameter2);
+ return FX_NOT_APPLIED;
+}
+
+//0xde RandomTeleport
+int fx_teleport_field (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_teleport_field (%2d): Distance: %d\n", fx->Opcode, fx->Parameter1 );
+
+ Map *map = target->GetCurrentArea();
+ if (!map) {
+ return FX_NOT_APPLIED;
+ }
+ //the origin is the effect's target point
+ Point p(fx->PosX+core->Roll(1,fx->Parameter1*2,-(signed) (fx->Parameter1)),
+ fx->PosY+core->Roll(1,fx->Parameter1*2,-(signed) (fx->Parameter1)) );
+
+ target->SetPosition( p, true, 0);
+ return FX_NOT_APPLIED;
+}
+
+//0xdf //Protection:SchoolDec
+int fx_protection_school_dec (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_school_dec (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ if (fx->Parameter1) {
+ STAT_BIT_OR( IE_IMMUNITY, IMM_SCHOOL_DEC );
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xe0 Cure:LevelDrain
+
+int fx_cure_leveldrain (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_leveldrain (%2d)\n", fx->Opcode );
+ //all level drain removed at once???
+ //if not, then find old effect, remove a number
+ target->fxqueue.RemoveAllEffects( fx_leveldrain_ref );
+ return FX_NOT_APPLIED;
+}
+
+//0xe1 Reveal:Magic
+//gemrb special: speed and color are custom
+int fx_reveal_magic (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_reveal_magic (%2d)\n", fx->Opcode );
+ if (target->fxqueue.HasAnyDispellableEffect()) {
+ if (!fx->Parameter1) {
+ fx->Parameter1=0xff00; //blue
+ }
+
+ int speed = (fx->Parameter2 >> 16) & 0xFF;
+ if (!speed) speed=30;
+ target->SetColorMod(0xff, RGBModifier::ADD, speed,
+ fx->Parameter1 >> 8, fx->Parameter1 >> 16,
+ fx->Parameter1 >> 24, 0);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xe2 Protection:SecondaryTypeDec
+int fx_protection_secondary_type_dec (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_secondary_type_dec (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ if (fx->Parameter1) {
+ STAT_BIT_OR( IE_IMMUNITY, IMM_SECTYPE_DEC );
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xe3 Bounce:SchoolDecrement
+int fx_bounce_school_dec (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_school_dec (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_SCHOOL_DEC );
+ target->AddPortraitIcon(PI_BOUNCE2);
+ return FX_APPLIED;
+}
+
+//0xe4 Bounce:SecondaryTypeDecrement
+int fx_bounce_secondary_type_dec (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bounce_secondary_type_dec (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_BIT_OR( IE_BOUNCE, BNC_SECTYPE_DEC );
+ target->AddPortraitIcon(PI_BOUNCE2);
+ return FX_APPLIED;
+}
+
+//0xe5 DispelSchoolOne
+int fx_dispel_school_one (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ ieResRef Removed;
+
+ if (0) printf( "fx_dispel_school_one (%2d): Level: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ target->fxqueue.RemoveLevelEffects(Removed, fx->Parameter1, RL_MATCHSCHOOL|RL_REMOVEFIRST, fx->Parameter2);
+ return FX_NOT_APPLIED;
+}
+
+//0xe6 DispelSecondaryTypeOne
+int fx_dispel_secondary_type_one (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ ieResRef Removed;
+
+ if (0) printf( "fx_dispel_secondary_type_one (%2d): Level: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ target->fxqueue.RemoveLevelEffects(Removed, fx->Parameter1, RL_MATCHSECTYPE|RL_REMOVEFIRST, fx->Parameter2);
+ return FX_NOT_APPLIED;
+}
+
+//0xe7 Timestop
+int fx_timestop (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_timestop (%2d)\n", fx->Opcode);
+ core->GetGame()->TimeStop(target, fx->Duration);
+ return FX_NOT_APPLIED;
+}
+
+//0xe8 CastSpellOnCondition
+int fx_cast_spell_on_condition (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cast_spell_on_condition (%2d): Target: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (fx->FirstApply && fx->Parameter3) {
+ target->spellbook.HaveSpell( fx->Resource, HS_DEPLETE );
+ target->spellbook.HaveSpell( fx->Resource2, HS_DEPLETE );
+ target->spellbook.HaveSpell( fx->Resource3, HS_DEPLETE );
+ target->spellbook.HaveSpell( fx->Resource4, HS_DEPLETE );
+ }
+
+ //get subject of check
+ Actor *actor = NULL;
+ Map *map = target->GetCurrentArea();
+ switch(fx->Parameter1) {
+ //self
+ case 0: actor = target; break;
+ //last attacker
+ case 1: actor = map->GetActorByGlobalID(target->LastHitter); break;
+ //nearest enemy
+ //fix this!
+ //case 2: actor = map->GetActorByGlobalID(target->LastSeen); break;
+ case 2: actor = GetNearestEnemyOf(map, target, 0); break;
+ //nearest creature
+ case 3: actor = map->GetActorByGlobalID(target->LastSeen); break;
+ }
+ if (!actor) {
+ return FX_APPLIED;
+ }
+ int condition;
+ //check condition
+ switch(fx->Parameter2) {
+ case COND_GOTHIT: //on hit
+ condition = target->LastDamage;
+ break;
+ case COND_NEAR: //
+ condition = PersonalDistance(actor, target)<30;
+ break;
+ case COND_HP_HALF:
+ condition = actor->GetBase(IE_HITPOINTS)<actor->GetStat(IE_MAXHITPOINTS)/2;
+ break;
+ case COND_HP_QUART:
+ condition = actor->GetBase(IE_HITPOINTS)<actor->GetStat(IE_MAXHITPOINTS)/4;
+ break;
+ case COND_HP_LOW:
+ condition = actor->GetBase(IE_HITPOINTS)<actor->GetStat(IE_MAXHITPOINTS)/10;
+ break;
+ case COND_HELPLESS:
+ condition = actor->GetStat(IE_STATE_ID) & STATE_CANTMOVE;
+ break;
+ case COND_POISONED:
+ condition = actor->GetStat(IE_STATE_ID) & STATE_POISONED;
+ break;
+ case COND_ATTACKED: // once per round
+ condition = actor->LastHitter;
+ break;
+ case COND_NEAR4: // closer than 4'
+ condition = PersonalDistance(actor, target)<4;
+ break;
+ case COND_NEAR10: // closer than 10'
+ condition = PersonalDistance(actor, target)<10;
+ break;
+ case COND_EVERYROUND:
+ condition = 1;
+ break;
+ case COND_TOOKDAMAGE:
+ condition = actor->LastDamage;
+ break;
+ default:
+ condition = 0;
+ }
+
+ if (condition) {
+ core->ApplySpell(fx->Resource, actor, Owner, fx->Power);
+ core->ApplySpell(fx->Resource2, actor, Owner, fx->Power);
+ core->ApplySpell(fx->Resource3, actor, Owner, fx->Power);
+ core->ApplySpell(fx->Resource4, actor, Owner, fx->Power);
+ if (fx->Parameter3) {
+ return FX_NOT_APPLIED;
+ }
+ }
+ return FX_APPLIED;
+}
+
+// 0xe9 Proficiency
+int fx_proficiency (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_proficiency (%2d): Value: %d, Stat: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (fx->Parameter2>=MAX_STATS) return FX_NOT_APPLIED;
+
+ //this opcode works only if the previous value was smaller
+ if (STAT_GET(fx->Parameter2)<fx->Parameter1) {
+ STAT_SET (fx->Parameter2, fx->Parameter1);
+ }
+ return FX_APPLIED;
+}
+
+// 0xea CreateContingency
+static EffectRef fx_contingency_ref = { "CastSpellOnCondition", -1 };
+
+int fx_create_contingency (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_create_contingency (%2d): Level: %d, Count: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (target->fxqueue.HasEffectWithSource(fx_contingency_ref, fx->Source)) {
+ displaymsg->DisplayConstantStringName(STR_CONTDUP, 0xf0f0f0, target);
+ return FX_NOT_APPLIED;
+ }
+
+ if (target->InParty) {
+ Variables *dict = core->GetDictionary();
+
+ dict->SetAt( "P0", target->InParty );
+ dict->SetAt( "P1", fx->Parameter1 );
+ dict->SetAt( "P2", fx->Parameter2 );
+ core->SetEventFlag(EF_SEQUENCER);
+ }
+ return FX_NOT_APPLIED;
+}
+
+#define WB_AWAY 0
+#define WB_TOWARDS 1
+#define WB_FIXDIR 2
+#define WB_OWNDIR 3
+#define WB_AWAYOWNDIR 4
+
+// 0xeb WingBuffet
+int fx_wing_buffet (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_wing_buffet (%2d): Value: %d, Stat: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //create movement in actor
+
+ ieDword dir;
+ switch(fx->Parameter2) {
+ case WB_AWAY:
+ default:
+ dir = GetOrient(Owner->Pos, target->Pos);
+ break;
+ case WB_TOWARDS:
+ dir = GetOrient(target->Pos, Owner->Pos);
+ break;
+ case WB_FIXDIR:
+ dir = fx->Parameter3;
+ break;
+ case WB_OWNDIR:
+ dir = target->GetOrientation();
+ break;
+ case WB_AWAYOWNDIR:
+ dir = target->GetOrientation()^8;
+ break;
+ }
+ //could be GL_REBOUND too :)
+ //add effect to alter target's stance
+ target->MoveLine( fx->Parameter1, GL_NORMAL, dir );
+ return FX_NOT_APPLIED;
+}
+
+// 0xec ProjectImage
+
+static EffectRef fx_puppetmarker_ref = { "PuppetMarker", -1 };
+
+int fx_puppet_master (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ const char * resref = NULL;
+
+ if (0) printf( "fx_puppet_master (%2d): Value: %d, Stat: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET (IE_PUPPETMASTERTYPE, fx->Parameter1);
+
+ //copyself doesn't copy scripts, so the script clearing code is not needed
+ Actor *copy = target->CopySelf(fx->Parameter2 == 1);
+
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ if (newfx) {
+ core->ApplyEffect(newfx, copy, copy);
+ delete newfx;
+ }
+
+ ieResRef script;
+
+ //intentionally 7, to leave room for the last letter
+ strnlwrcpy(script,target->GetScript(SCR_CLASS),7);
+ //no need of buffer defense as long as you don't mess with the 7 above
+ strcat(script,"m");
+ //if the caster is inparty, the script is turned off by the AI disable flag
+ copy->SetScript(script, SCR_CLASS, target->InParty!=0);
+
+ switch(fx->Parameter2)
+ {
+ case 1:
+ resref = "mislead";
+ //set the gender to illusionary, so ids matching will work
+ copy->SetBase(IE_SEX, SEX_ILLUSION);
+ copy->SetBase(IE_MAXHITPOINTS, copy->GetBase(IE_MAXHITPOINTS)/2);
+ break;
+ case 2:
+ resref = "projimg";
+ copy->SetBase(IE_SEX, SEX_ILLUSION);
+ break;
+ case 3:
+ resref = "simulacr";
+ // healable level drain
+ // FIXME: second generation simulacri are supposedly at a different level:
+ // level = original caster - caster / 2; eg. lvl 32 -> 16 -> 24 -> 20 -> 22 -> 21
+ newfx = EffectQueue::CreateEffect(fx_leveldrain_ref, copy->GetXPLevel(1)/2, 0, FX_DURATION_INSTANT_PERMANENT);
+ if (newfx) {
+ core->ApplyEffect(newfx, copy, copy);
+ delete newfx;
+ }
+ break;
+ default:
+ resref = fx->Resource;
+ break;
+ }
+ if (resref[0]) {
+ core->ApplySpell(resref,copy,copy,0);
+ }
+
+ //FIXME: parameter1 is unsure, but something similar to what the original engine has there
+ newfx = EffectQueue::CreateEffectCopy(fx, fx_puppetmarker_ref, target->InParty-1, fx->Parameter2);
+ if (newfx) {
+ core->ApplyEffect(newfx, copy, copy);
+ delete newfx;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0xed PuppetMarker
+int fx_puppet_marker (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_puppet_marker (%2d): Value: %d, Stat: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //actually the Type is in parameter2 and the ID is in parameter1
+ //but for some reason the defines are in the opposite order
+ STAT_SET (IE_PUPPETTYPE, fx->Parameter1); //cb4 - the ID of the controller
+ STAT_SET (IE_PUPPETID, fx->Parameter2); //cb8 - the control type
+ return FX_APPLIED;
+}
+
+// 0xee Disintegrate
+int fx_disintegrate (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_disintegrate (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (EffectQueue::match_ids( target, fx->Parameter2, fx->Parameter1) ) {
+ //convert it to a death opcode or apply the new effect?
+ fx->Opcode = EffectQueue::ResolveEffect(fx_death_ref);
+ fx->TimingMode = FX_DURATION_INSTANT_PERMANENT;
+ fx->Parameter1 = 0;
+ fx->Parameter2 = 0x200;
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0xef Farsee
+// 1 view not explored sections too
+// 2 param1=range (otherwise visualrange)
+// 4 point already set (otherwise use gui)
+// 8 use line of sight
+#define FS_UNEXPLORED 1
+#define FS_VISUALRANGE 2
+#define FS_HASPOINT 4
+#define FS_LOS 8
+
+int fx_farsee (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_farsee (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ Map *map = target->GetCurrentArea();
+ if (!map) {
+ return FX_APPLIED;
+ }
+
+ if (!(fx->Parameter2&FS_VISUALRANGE)) {
+ fx->Parameter1=STAT_GET(IE_VISUALRANGE);
+ fx->Parameter2|=FS_VISUALRANGE;
+ }
+
+ if (target->InParty) {
+ //don't start graphical interface if actor isn't in party
+ if (!(fx->Parameter2&FS_HASPOINT)) {
+ //start graphical interface
+ //it will do all the rest of the opcode
+ //using RevealMap guiscript action
+ core->EventFlag|=EF_SHOWMAP;
+ return FX_NOT_APPLIED;
+ }
+ }
+
+ Point p(fx->PosX, fx->PosY);
+
+ //don't explore unexplored points
+ if (!(fx->Parameter2&FS_UNEXPLORED)) {
+ if (!map->IsVisible(p, 1)) {
+ return FX_NOT_APPLIED;
+ }
+ }
+ map->ExploreMapChunk(p, fx->Parameter1, fx->Parameter2&FS_LOS);
+ return FX_NOT_APPLIED;
+}
+
+// 0xf0 Icon:Remove
+int fx_remove_portrait_icon (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_portrait_icon (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ target->fxqueue.RemoveAllEffectsWithParam( fx_display_portrait_icon_ref, fx->Parameter2 );
+ return FX_NOT_APPLIED;
+}
+// 0xf1 control creature (same as charm)
+
+// 0xF2 Cure:Confusion
+static EffectRef fx_confused_state_ref = { "State:Confused", -1 };
+
+int fx_cure_confused_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cure_confused_state (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ BASE_STATE_CURE( STATE_CONFUSED );
+ target->fxqueue.RemoveAllEffects(fx_confused_state_ref);
+ //FIXME:oddly enough, HoW removes the confused icon
+ //no one removes the rigid thinking icon
+ //there are also several mods floating around, which change these things inconsistently
+ //probably the best is to remove them all by default
+ //New mods can still disable the icon removal by setting param2
+ if (!fx->Parameter2) {
+ target->fxqueue.RemoveAllEffectsWithParam( fx_display_portrait_icon_ref,PI_CONFUSED );
+ target->fxqueue.RemoveAllEffectsWithParam( fx_display_portrait_icon_ref,PI_RIGID );
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0xf3 DrainItems (this is disabled in ToB)
+int fx_drain_items (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_drain_items (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword i=fx->Parameter1;
+ while (i--) {
+ //deplete magic item = 0
+ //deplete weapon = 1
+ target->inventory.DepleteItem(fx->Parameter2);
+ }
+ return FX_NOT_APPLIED;
+}
+// 0xf4 DrainSpells
+int fx_drain_spells (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_drain_spells (%2d): Count: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword i=fx->Parameter1;
+ if (fx->Parameter2) {
+ while(i--) {
+ if (!target->spellbook.DepleteSpell(IE_SPELL_TYPE_PRIEST)) {
+ break;
+ }
+ }
+ return FX_NOT_APPLIED;
+ }
+ while(i--) {
+ if (!target->spellbook.DepleteSpell(IE_SPELL_TYPE_WIZARD)) {
+ break;
+ }
+ }
+ return FX_NOT_APPLIED;
+}
+// 0xf5 CheckForBerserk
+int fx_checkforberserk_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_checkforberserk_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET( IE_CHECKFORBERSERK, fx->Parameter2 );
+ return FX_APPLIED;
+}
+// 0xf6 BerserkStage1Modifier
+int fx_berserkstage1_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_berserkstage1_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET( IE_BERSERKSTAGE1, fx->Parameter2 );
+ return FX_APPLIED;
+}
+// 0xf7 BerserkStage2Modifier
+int fx_berserkstage2_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_berserkstage2_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET( IE_BERSERKSTAGE2, fx->Parameter2 );
+ STATE_SET (STATE_BERSERK);
+ return FX_APPLIED;
+}
+
+// 0xf8 set melee effect
+// adds effect to melee attacks (for monks, asssasins, fighter hlas, ...)
+// it is cumulative
+
+// 0xf9 set missile effect
+// adds effect to ranged attacks (archers, ...)
+// it is cumulative
+
+// 0xfa DamageLuckModifier
+int fx_damageluck_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_damageluck_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_DAMAGELUCK );
+ return FX_APPLIED;
+}
+
+// 0xfb BardSong
+
+int fx_change_bardsong (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_change_bardsong (%2d): %s\n", fx->Opcode, fx->Resource);
+ memcpy(target->BardSong, fx->Resource, 8);
+ return FX_APPLIED;
+}
+
+// 0xfc SetTrap
+int fx_set_area_effect (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_trap (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword skill, roll;
+ Map *map;
+
+ map = target->GetCurrentArea();
+ if (!map) return FX_NOT_APPLIED;
+
+ proIterator iter;
+
+ //check if trap count is over an amount (only saved traps count)
+ //actually, only projectiles in trigger phase should count here
+ if (map->GetTrapCount(iter)>6) {
+ displaymsg->DisplayConstantStringName(STR_NOMORETRAP, 0xf0f0f0, target);
+ return FX_NOT_APPLIED;
+ }
+
+ //check if we are under attack
+ if (GetNearestEnemyOf(map, target, ORIGIN_SEES_ENEMY|ENEMY_SEES_ORIGIN)) {
+ displaymsg->DisplayConstantStringName(STR_MAYNOTSETTRAP, 0xf0f0f0, target);
+ return FX_NOT_APPLIED;
+ }
+
+ if (Owner->Type==ST_ACTOR) {
+ skill = ((Actor *)Owner)->GetStat(IE_SETTRAPS);
+ roll = target->LuckyRoll(1,100,0,LR_NEGATIVE);
+ } else {
+ roll=0;
+ skill=0;
+ }
+
+ if (roll>skill) {
+ //failure
+ displaymsg->DisplayConstantStringName(STR_SNAREFAILED, 0xf0f0f0, target);
+ if (target->LuckyRoll(1,100,0)<25) {
+ ieResRef spl;
+
+ strnuprcpy(spl, fx->Resource, 8);
+ if (strlen(spl)<8) {
+ strcat(spl,"F");
+ } else {
+ spl[7]='F';
+ }
+ core->ApplySpell(spl, target, Owner, fx->Power);
+ }
+ return FX_NOT_APPLIED;
+ }
+ //success
+ displaymsg->DisplayConstantStringName(STR_SNARESUCCEED, 0xf0f0f0, target);
+ // save the current spell ref, so the rest of its effects can be applied afterwards
+ ieResRef OldSpellResRef;
+ memcpy(OldSpellResRef, Owner->SpellResRef, sizeof(OldSpellResRef));
+ Owner->SetSpellResRef(fx->Resource);
+ Owner->CastSpellPoint(fx->Resource, target->Pos, false);
+ Owner->CastSpellPointEnd(0);
+ Owner->SetSpellResRef(OldSpellResRef);
+ return FX_NOT_APPLIED;
+}
+
+// 0xfd SetMapNote
+int fx_set_map_note (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_map_note (%2d): StrRef: %d Color: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ Scriptable *marker = target?target:Owner;
+ Map *map = marker->GetCurrentArea();
+ if (!map) return FX_APPLIED; //delay effect
+ Point p(fx->PosX, fx->PosY);
+ char *text = core->GetString(fx->Parameter1, 0);
+ map->AddMapNote(p, fx->Parameter2, text, fx->Parameter1);
+ return FX_NOT_APPLIED;
+}
+
+// 0xfe RemoveMapNote
+int fx_remove_map_note (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_map_note (%2d)\n", fx->Opcode);
+ Scriptable *marker = target?target:Owner;
+ Map *map = marker->GetCurrentArea();
+ if (!map) return FX_APPLIED; //delay effect
+ Point p(fx->PosX, fx->PosY);
+ map->RemoveMapNote(p);
+ return FX_NOT_APPLIED;
+}
+
+// 0xff Item:CreateDays
+int fx_create_item_days (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_create_item_days (%2d)\n", fx->Opcode );
+ target->inventory.AddSlotItemRes( fx->Resource, SLOT_ONLYINVENTORY, fx->Parameter1, fx->Parameter3, fx->Parameter4 );
+ if ((fx->TimingMode&0xff) == FX_DURATION_INSTANT_LIMITED) {
+ //if this effect has expiration, then it will remain as a remove_item
+ //on the effect queue, inheriting all the parameters
+ //duration needs a hack (recalculate it for days)
+ //no idea if this multiplier is ok
+ fx->Duration+=(fx->Duration-core->GetGame()->GameTime)*2400;
+ fx->Opcode=EffectQueue::ResolveEffect(fx_remove_inventory_item_ref);
+ fx->TimingMode=FX_DURATION_DELAY_PERMANENT;
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x100 Sequencer:Store
+int fx_store_spell_sequencer(Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_store_spell_sequencer (%2d)\n", fx->Opcode );
+ //just display the spell sequencer portrait icon
+ target->AddPortraitIcon(PI_SEQUENCER);
+ if (fx->FirstApply && fx->Parameter3) {
+ target->spellbook.HaveSpell( fx->Resource, HS_DEPLETE );
+ target->spellbook.HaveSpell( fx->Resource2, HS_DEPLETE );
+ target->spellbook.HaveSpell( fx->Resource3, HS_DEPLETE );
+ target->spellbook.HaveSpell( fx->Resource4, HS_DEPLETE );
+ }
+ return FX_APPLIED;
+}
+
+// 0x101 Sequencer:Create
+static EffectRef fx_spell_sequencer_active_ref = { "Sequencer:Store", -1 };
+
+int fx_create_spell_sequencer(Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_create_spell_sequencer (%2d): Level: %d, Count: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (target->fxqueue.HasEffectWithSource(fx_spell_sequencer_active_ref, fx->Source)) {
+ displaymsg->DisplayConstantStringName(STR_SEQDUP, 0xf0f0f0, target);
+ return FX_NOT_APPLIED;
+ }
+ //just a call to activate the spell sequencer creation gui
+ if (target->InParty) {
+ Variables *dict = core->GetDictionary();
+
+ dict->SetAt( "P0", target->InParty );
+ dict->SetAt( "P1", fx->Parameter1 ); //maximum level
+ dict->SetAt( "P2", fx->Parameter2 | (2<<16) ); //count and target type
+ core->SetEventFlag(EF_SEQUENCER);
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x102 Sequencer:Activate
+
+int fx_activate_spell_sequencer(Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_activate_spell_sequencer (%2d): Resource: %s\n", fx->Opcode, fx->Resource );
+ if (Owner->Type!=ST_ACTOR) {
+ return FX_NOT_APPLIED;
+ }
+
+ Effect *sequencer = ((Actor *) Owner)->fxqueue.HasEffect(fx_spell_sequencer_active_ref);
+ if (sequencer) {
+ //cast 1-4 spells stored in the spell sequencer
+ core->ApplySpell(sequencer->Resource, target, Owner, fx->Power);
+ core->ApplySpell(sequencer->Resource2, target, Owner, fx->Power);
+ core->ApplySpell(sequencer->Resource3, target, Owner, fx->Power);
+ core->ApplySpell(sequencer->Resource4, target, Owner, fx->Power);
+ //remove the spell sequencer store effect
+ sequencer->TimingMode=FX_DURATION_JUST_EXPIRED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x103 SpellTrap (Protection:SpellLevelDec + recall spells)
+int fx_spelltrap(Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_spelltrap (%2d): Count: %d, Level: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (fx->Parameter3) {
+ target->RestoreSpellLevel(fx->Parameter3, 0);
+ fx->Parameter3=0;
+ }
+ if (fx->Parameter1<=0) {
+ //gone down to zero
+ return FX_NOT_APPLIED;
+ }
+ target->SetOverlay(OV_SPELLTRAP);
+ target->AddPortraitIcon(PI_SPELLTRAP);
+ return FX_APPLIED;
+}
+
+//0x104 Crash104
+//0x138 Crash138
+int fx_crash (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_crash (%2d): Param1: %d, Param2: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ return FX_NOT_APPLIED;
+}
+
+// 0x105 RestoreSpells
+int fx_restore_spell_level(Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_restore_spell_level (%2d): Level: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ target->RestoreSpellLevel(fx->Parameter1, fx->Parameter2);
+ return FX_NOT_APPLIED;
+}
+// 0x106 VisualRangeModifier
+int fx_visual_range_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_visual_range_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_VISUALRANGE );
+ return FX_APPLIED;
+}
+
+// 0x107 BackstabModifier
+int fx_backstab_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_visual_range_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //this is how it is done in the original engine, i don't know why they would do this
+ //ctrl-r would probably remove it otherwise
+ //Why they didn't fix it in the spell/item is beyond me
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT)
+ fx->TimingMode=FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES;
+ STAT_MOD( IE_BACKSTABDAMAGEMULTIPLIER );
+ return FX_APPLIED;
+}
+
+// 0x108 DropWeapon
+int fx_drop_weapon (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (fx->Resource[0]) {
+ target->DropItem(fx->Resource, 0);
+ return FX_NOT_APPLIED;
+ }
+ switch (fx->Parameter2) {
+ case 0:
+ target->DropItem(-1, 0);
+ break;
+ case 1:
+ target->DropItem(target->inventory.GetEquippedSlot(), 0);
+ break;
+ default:
+ target->DropItem(fx->Parameter1, 0);
+ break;
+ }
+ return FX_NOT_APPLIED;
+}
+// 0x109 ModifyGlobalVariable
+int fx_modify_global_variable (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ Game *game = core->GetGame();
+ //convert it to internal variable format
+ if (!fx->IsVariable) {
+ char *poi=fx->Resource+8;
+ memmove(poi, fx->Resource2,8);
+ poi+=8;
+ memmove(poi, fx->Resource3,8);
+ poi+=8;
+ memmove(poi, fx->Resource4,8);
+ fx->IsVariable=1;
+ }
+
+ //hack for IWD
+ if (!fx->Resource[0]) {
+ strnuprcpy(fx->Resource,"RETURN_TO_LONELYWOOD",32);
+ }
+
+ if (0) printf( "fx_modify_global_variable (%2d): Variable: %s Value: %d Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter1, fx->Parameter2 );
+ if (fx->Parameter2) {
+ ieDword var = 0;
+ //use resource memory area as variable name
+ game->locals->Lookup(fx->Resource, var);
+ game->locals->SetAt(fx->Resource, var+fx->Parameter1);
+ } else {
+ game->locals->SetAt(fx->Resource, fx->Parameter1);
+ }
+ return FX_NOT_APPLIED;
+}
+// 0x10a RemoveImmunity
+static EffectRef immunity_effect_ref = { "Protection:Spell", -1 };
+
+int fx_remove_immunity(Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_immunity (%2d): %s\n", fx->Opcode, fx->Resource );
+ target->fxqueue.RemoveAllEffectsWithResource(immunity_effect_ref, fx->Resource);
+ return FX_NOT_APPLIED;
+}
+
+// 0x10b protection from display string is a generic effect
+// 0x10c ExploreModifier
+int fx_explore_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_explore_modifier (%2d)\n", fx->Opcode );
+ if (fx->Parameter2) {
+ //gemrb modifier
+ STAT_SET (IE_EXPLORE, fx->Parameter1);
+ } else {
+ STAT_SET (IE_EXPLORE, 1);
+ }
+ return FX_APPLIED;
+}
+// 0x10d ScreenShake
+int fx_screenshake (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_screenshake (%2d): Strength: %d\n", fx->Opcode, fx->Parameter1 );
+ core->timer->SetScreenShake( fx->Parameter1, fx->Parameter1, fx->Parameter1);
+ return FX_APPLIED;
+}
+
+// 0x10e Cure:CasterHold
+static EffectRef fx_pause_caster_modifier_ref = { "PauseTarget", -1 };
+
+int fx_unpause_caster (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_unpause_caster (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ target->fxqueue.RemoveAllEffects(fx_pause_caster_modifier_ref);
+ return FX_NOT_APPLIED;
+}
+
+// 0x10f SummonDisable (bg2)
+int fx_summon_disable (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_summon_disable (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET(IE_SUMMONDISABLE, 1);
+ STAT_SET(IE_CASTERHOLD, 1);
+ if (fx->Parameter2==1) {
+ STAT_SET(IE_AVATARREMOVAL, 1);
+ }
+ return FX_APPLIED;
+}
+
+/* note/TODO from Taimon:
+What happens at a lower level is that the engine recreates the entire stats on some changes to the creature. The application of an effect to the creature is one such change.
+Since the repeating effects are stored in a list inside those stats, they are being recreated every ai update, if there has been an effect application.
+The repeating effect itself internally uses a counter to store how often it has been called. And when this counter equals the period it fires of the effect. When the list is being recreated all those counters are lost.
+*/
+static EffectRef fx_apply_effect_repeat_ref = { "ApplyEffectRepeat", -1 };
+// 0x110 ApplyEffectRepeat
+int fx_apply_effect_repeat (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ ieDword i; //moved here because msvc6 cannot handle it otherwise
+
+ if (0) printf( "fx_apply_effect_repeat (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ Point p(fx->PosX, fx->PosY);
+ Effect *newfx = core->GetEffect(fx->Resource, fx->Power, p);
+ //core->GetEffect is a borrowed reference, don't delete it
+ if (!newfx) {
+ return FX_NOT_APPLIED;
+ }
+
+ // don't apply the effect if a similar one is already applied with a shorter duration
+ Effect *oldfx = target->fxqueue.HasEffect(fx_apply_effect_repeat_ref);
+ if (oldfx && oldfx->Duration < fx->Duration) {
+ return FX_NOT_APPLIED;
+ }
+
+ switch (fx->Parameter2) {
+ case 0: //once per second
+ case 1: //crash???
+ if (!(core->GetGame()->GameTime%AI_UPDATE_TIME)) {
+ core->ApplyEffect(newfx, target, Owner);
+ }
+ break;
+ case 2://param1 times every second
+ if (!(core->GetGame()->GameTime%AI_UPDATE_TIME)) {
+ for (i=0;i<fx->Parameter1;i++) {
+ core->ApplyEffect(newfx, target, Owner);
+ }
+ }
+ break;
+ case 3: //once every Param1 second
+ if (fx->Parameter1 && (core->GetGame()->GameTime%fx->Parameter1)) {
+ core->ApplyEffect(newfx, target, Owner);
+ }
+ break;
+ case 4: //param3 times every Param1 second
+ if (fx->Parameter1 && (core->GetGame()->GameTime%fx->Parameter1)) {
+ for (i=0;i<fx->Parameter3;i++) {
+ core->ApplyEffect(newfx, target, Owner);
+ }
+ }
+ break;
+ }
+ return FX_APPLIED;
+}
+
+// 0x111 RemoveProjectile
+int fx_remove_projectile (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ //the list is now cached by Interface, no need of freeing it
+ ieDword *projectilelist;
+
+ //instant effect
+ if (0) printf( "fx_remove_projectile (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (!target) return FX_NOT_APPLIED;
+ Map *area = target->GetCurrentArea();
+ if (!area) return FX_NOT_APPLIED;
+
+ switch (fx->Parameter2) {
+ case 0: //standard bg2
+ projectilelist = core->GetListFrom2DA("clearair");
+ break;
+ case 1: //you can give a 2da for projectile list (gemrb)
+ projectilelist = core->GetListFrom2DA(fx->Resource);
+ break;
+ case 2: //or you can give one single projectile in param1 (gemrb)
+ projectilelist = (ieDword *) malloc(2*sizeof(ieDword));
+ projectilelist[0]=1;
+ projectilelist[1]=fx->Parameter1;
+ break;
+ default:
+ return FX_NOT_APPLIED;
+ }
+ //The first element is the counter, so don't decrease the counter here
+ Point p(fx->PosX, fx->PosY);
+
+ int i = projectilelist[0];
+
+ while(i) {
+ ieDword projectile = projectilelist[i];
+ proIterator piter;
+
+ size_t cnt = area->GetProjectileCount(piter);
+ while( cnt--) {
+ Projectile *pro = *piter;
+ if ((pro->GetType()==projectile) && pro->PointInRadius(p) ) {
+ pro->Cleanup();
+ }
+ }
+ if (target) {
+ target->fxqueue.RemoveAllEffectsWithProjectile(projectile);
+ }
+ i--;
+ }
+ //this one was constructed by us
+ if (fx->Parameter2==2) free(projectilelist);
+ return FX_NOT_APPLIED;
+}
+
+// 0x112 TeleportToTarget
+int fx_teleport_to_target (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_teleport_to_target (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (STATE_GET(STATE_DEAD)) {
+ return FX_NOT_APPLIED;
+ }
+
+ Map *map = target->GetCurrentArea();
+ if (map) {
+ Object oC;
+ oC.objectFields[0]=EA_ENEMY;
+ Targets *tgts = GetAllObjects(map, target, &oC, GA_NO_DEAD);
+ int rnd = core->Roll(1,tgts->Count(),-1);
+ Actor *victim = (Actor *) tgts->GetTarget(rnd, ST_ACTOR);
+ delete tgts;
+ if (victim && PersonalDistance(victim, target)>20) {
+ target->SetPosition( victim->Pos, true, 0 );
+ target->SetColorMod(0xff, RGBModifier::ADD, 0x50, 0xff, 0xff, 0xff, 0);
+ }
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x113 HideInShadowsModifier
+int fx_hide_in_shadows_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_hide_in_shadows_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_HIDEINSHADOWS );
+ return FX_APPLIED;
+}
+
+// 0x114 DetectIllusionsModifier
+int fx_detect_illusion_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_detect_illusion_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_DETECTILLUSIONS );
+ return FX_APPLIED;
+}
+
+// 0x115 SetTrapsModifier
+int fx_set_traps_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_traps_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_SETTRAPS );
+ return FX_APPLIED;
+}
+// 0x116 ToHitBonusModifier
+int fx_to_hit_bonus_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_to_hit_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ HandleBonus( target, IE_HITBONUS, fx->Parameter1, fx->TimingMode );
+ return FX_APPLIED;
+}
+
+// 0x117 RenableButton
+static EffectRef fx_disable_button_ref = { "DisableButton", -1 };
+
+int fx_renable_button (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ //removes the disable button effect
+ if (0) printf( "fx_renable_button (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ target->fxqueue.RemoveAllEffectsWithParam( fx_disable_button_ref, fx->Parameter2 );
+ return FX_NOT_APPLIED;
+}
+
+// 0x118 ForceSurgeModifier
+int fx_force_surge_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_force_surge_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD_VAR( IE_FORCESURGE, MOD_ABSOLUTE );
+ return FX_APPLIED;
+}
+
+// 0x119 WildSurgeModifier
+int fx_wild_surge_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_wild_surge_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_SURGEMOD );
+ return FX_APPLIED;
+}
+
+// 0x11a ScriptingState
+int fx_scripting_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_scripting_state (%2d): Value: %d, Stat: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //original engine didn't check boundaries, causing crashes
+ //we allow only positive indices (some extra stats are still addressable)
+ if (fx->Parameter2>100) {
+ return FX_NOT_APPLIED;
+ }
+ //original engine used only single byte value, we allow full dword
+ STAT_SET( IE_SCRIPTINGSTATE1+fx->Parameter2, fx->Parameter1 );
+ return FX_APPLIED;
+}
+
+// 0x11b ApplyEffectCurse
+int fx_apply_effect_curse (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_apply_effect_curse (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //this effect executes a file effect in place of this effect
+ //the file effect inherits the target and the timingmode, but gets
+ //a new chance to roll percents
+ int ret = FX_NOT_APPLIED;
+ if (!target) {
+ return ret;
+ }
+
+ if (EffectQueue::match_ids( target, fx->Parameter2, fx->Parameter1) ) {
+ Point p(fx->PosX, fx->PosY);
+
+ //apply effect, if the effect is a goner, then kill
+ //this effect too
+ Effect *newfx = core->GetEffect(fx->Resource, fx->Power, p);
+ if (newfx) {
+ Effect *myfx = new Effect;
+ memcpy(myfx, newfx, sizeof(Effect));
+ myfx->random_value = fx->random_value;
+ myfx->TimingMode=fx->TimingMode;
+ myfx->Duration=fx->Duration;
+ myfx->Target = FX_TARGET_PRESET;
+ myfx->CasterID = fx->CasterID;
+ ret = target->fxqueue.ApplyEffect(target, myfx, fx->FirstApply, 0);
+ delete myfx;
+ }
+ //newfx is a borrowed reference don't delete it
+ }
+ return ret;
+}
+
+// 0x11c MeleeHitModifier
+int fx_melee_to_hit_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_melee_to_hit_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_MELEETOHIT );
+ return FX_APPLIED;
+}
+
+// 0x11d MeleeDamageModifier
+int fx_melee_damage_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_melee_damage_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_MELEEDAMAGE );
+ return FX_APPLIED;
+}
+
+// 0x11e MissileDamageModifier
+int fx_missile_damage_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_missile_damage_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_MISSILEDAMAGE );
+ return FX_APPLIED;
+}
+
+// 0x11f NoCircleState
+int fx_no_circle_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_missile_damage_modifier (%2d)\n", fx->Opcode);
+ STAT_SET( IE_NOCIRCLE, 1 );
+ return FX_APPLIED;
+}
+
+// 0x120 FistHitModifier
+int fx_fist_to_hit_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_fist_to_hit_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_FISTHIT );
+ return FX_APPLIED;
+}
+
+// 0x121 FistDamageModifier
+int fx_fist_damage_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_fist_damage_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_FISTDAMAGE );
+ return FX_APPLIED;
+}
+//0x122 TitleModifier
+int fx_title_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_fist_damage_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (fx->Parameter2) {
+ STAT_SET( IE_TITLE2, fx->Parameter1 );
+ } else {
+ STAT_SET( IE_TITLE1, fx->Parameter1 );
+ }
+ return FX_APPLIED;
+}
+//0x123 DisableOverlay
+//FIXME: which overlay is disabled?
+//if one of the overlays marked by sanctuary, then
+//make the bit correspond to it
+int fx_disable_overlay_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_disable_overlay_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET( IE_DISABLEOVERLAY, fx->Parameter1 );
+ return FX_APPLIED;
+}
+//0x124 Protection:Backstab (bg2)
+//0x11f Protection:Backstab (how, iwd2)
+//3 different games, 3 different methods of flagging this
+int fx_no_backstab_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_no_backstab_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //bg2
+ STAT_SET( IE_DISABLEBACKSTAB, fx->Parameter1 );
+ //how
+ EXTSTATE_SET(EXTSTATE_NO_BACKSTAB);
+ //iwd2
+ target->SetSpellState(SS_NOBACKSTAB);
+ return FX_APPLIED;
+}
+//0x125 OffscreenAIModifier
+int fx_offscreenai_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_offscreenai_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET( IE_ENABLEOFFSCREENAI, fx->Parameter1 );
+ target->Activate();
+ return FX_APPLIED;
+}
+//0x126 ExistanceDelayModifier
+int fx_existance_delay_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_existance_delay_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET( IE_EXISTANCEDELAY, fx->Parameter1 );
+ return FX_APPLIED;
+}
+//0x127 DisableChunk
+int fx_disable_chunk_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_disable_chunk_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET( IE_DISABLECHUNKING, fx->Parameter1 );
+ return FX_APPLIED;
+}
+#if 0
+//This is done differently in the original engine, and THIS may not even work
+//0x128 Protection:Animation
+int fx_protection_from_animation (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_from_animation (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //remove vvc from actor if active
+ target->RemoveVVCell(fx->Resource, false);
+ return FX_APPLIED;
+}
+#endif
+
+//0x129 Protection:Turn
+int fx_protection_from_turn (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_non_interruptible_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_SET( IE_NOTURNABLE, fx->Parameter1 );
+ return FX_APPLIED;
+}
+//0x12a CutScene2
+//runs a predetermined script in cutscene mode
+int fx_cutscene2 (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ Game *game;
+ ieResRef resref;
+
+ if (0) printf( "fx_cutscene2 (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ if (core->InCutSceneMode()) return FX_NOT_APPLIED;
+ game = core->GetGame();
+ if (!game) return FX_NOT_APPLIED;
+
+ game->ClearPlaneLocations();
+ for (int i = 0; i < game->GetPartySize(false); i++) {
+ Actor* act = game->GetPC( i, false );
+ GAMLocationEntry *gle = game->GetPlaneLocationEntry(i);
+ if (act && gle) {
+ gle->Pos = act->Pos;
+ memcpy(gle->AreaResRef, act->Area, 9);
+ }
+ }
+
+ core->SetCutSceneMode(true);
+
+ //GemRB enhancement: allow a custom resource
+ if (fx->Parameter2) {
+ strnlwrcpy(resref,fx->Resource, 8);
+ } else {
+ strnlwrcpy(resref,"cut250a",8);
+ }
+
+ GameScript* gs = new GameScript( resref, game );
+ gs->EvaluateAllBlocks();
+ delete( gs );
+ return FX_NOT_APPLIED;
+}
+//0x12b ChaosShieldModifier
+int fx_chaos_shield_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_chaos_shield_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_ADD( IE_CHAOSSHIELD, fx->Parameter1 );
+ if (fx->Parameter2) {
+ target->AddPortraitIcon(PI_CSHIELD); //162
+ } else {
+ target->AddPortraitIcon(PI_CSHIELD2); //163
+ }
+ return FX_APPLIED;
+}
+//0x12c NPCBump
+int fx_npc_bump (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_npc_bump (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ //unknown effect, but known stat position
+ STAT_MOD( IE_NPCBUMP );
+ return FX_APPLIED;
+}
+//0x12d CriticalHitModifier
+int fx_critical_hit_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_critical_hit_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_CRITICALHITBONUS );
+ return FX_APPLIED;
+}
+// 0x12e CanUseAnyItem
+int fx_can_use_any_item_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_can_use_any_item_modifier (%2d): Value: %d\n", fx->Opcode, fx->Parameter2 );
+
+ STAT_SET( IE_CANUSEANYITEM, fx->Parameter2 );
+ return FX_APPLIED;
+}
+
+// 0x12f AlwaysBackstab
+int fx_always_backstab_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_always_backstab_modifier (%2d): Value: %d\n", fx->Opcode, fx->Parameter2 );
+
+ STAT_SET( IE_ALWAYSBACKSTAB, fx->Parameter2 );
+ return FX_APPLIED;
+}
+
+// 0x130 MassRaiseDead
+int fx_mass_raise_dead (Scriptable* Owner, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_mass_raise_dead (%2d)\n", fx->Opcode );
+
+ Game *game=core->GetGame();
+
+ int i=game->GetPartySize(false);
+ Point p(fx->PosX,fx->PosY);
+ while (i--) {
+ Actor *actor=game->GetPC(i,false);
+ Resurrect(Owner, actor, fx, p);
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x131 OffhandHitModifier
+int fx_left_to_hit_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_left_to_hit_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_HITBONUSLEFT );
+ return FX_APPLIED;
+}
+
+// 0x132 RightHitModifier
+int fx_right_to_hit_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_right_to_hit_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_HITBONUSRIGHT );
+ return FX_APPLIED;
+}
+
+// 0x133 Reveal:Tracks
+int fx_reveal_tracks (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_reveal_tracks (%2d): Distance: %d\n", fx->Opcode, fx->Parameter1 );
+ Map *map = target->GetCurrentArea();
+ if (!map) return FX_APPLIED;
+ if (!fx->Parameter2) {
+ fx->Parameter2=1;
+ //write tracks.2da entry
+ if (map->DisplayTrackString(target)) {
+ return FX_NOT_APPLIED;
+ }
+ }
+ GameControl *gc = core->GetGameControl();
+ if (gc) {
+ //highlight all living creatures (not in party, but within range)
+ gc->SetTracker(target, fx->Parameter1);
+ }
+ return FX_APPLIED;
+}
+
+// 0x134 Protection:Tracking
+int fx_protection_from_tracking (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_from_tracking (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ STAT_MOD( IE_NOTRACKING ); //highlight creature???
+ return FX_APPLIED;
+}
+// 0x135 ModifyLocalVariable
+int fx_modify_local_variable (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ //convert it to internal variable format
+ if (!fx->IsVariable) {
+ char *poi = fx->Resource+8;
+ memmove(poi, fx->Resource2, 8);
+ poi+=8;
+ memmove(poi, fx->Resource3, 8);
+ poi+=8;
+ memmove(poi, fx->Resource4, 8);
+ fx->IsVariable=1;
+ }
+ if (0) printf( "fx_modify_local_variable (%2d): %s, Mod: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ if (fx->Parameter2) {
+ ieDword var = 0;
+ //use resource memory area as variable name
+ target->locals->Lookup(fx->Resource, var);
+ target->locals->SetAt(fx->Resource, var+fx->Parameter1);
+ } else {
+ target->locals->SetAt(fx->Resource, fx->Parameter1);
+ }
+ return FX_NOT_APPLIED;
+}
+
+// 0x136 TimelessState
+int fx_timeless_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_timeless_modifier (%2d): Mod: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_SET(IE_DISABLETIMESTOP, fx->Parameter2);
+ return FX_APPLIED;
+}
+
+//0x137 GenerateWish
+//GemRB extension: you can use different tables and not only wisdom stat
+int fx_generate_wish (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ ieResRef spl;
+
+ if (0) printf( "fx_generate_wish (%2d): Mod: %d\n", fx->Opcode, fx->Parameter2 );
+ if (!fx->Parameter2) {
+ fx->Parameter2=IE_WIS;
+ }
+ int stat = target->GetSafeStat(fx->Parameter2);
+ if (!fx->Resource[0]) {
+ memcpy(fx->Resource,"wishcode",8);
+ }
+ AutoTable tm(fx->Resource);
+ if (!tm) {
+ return FX_NOT_APPLIED;
+ }
+
+ int max = tm->GetRowCount();
+ int tmp = core->Roll(1,max,0);
+ int i = tmp;
+ bool pass = true;
+ while(--i!=tmp && pass) {
+ if (i<0) {
+ pass=false;
+ i=max-1;
+ }
+ int min = atoi(tm->QueryField(i, 1));
+ int max = atoi(tm->QueryField(i, 2));
+ if (stat>=min && stat<=max) break;
+ }
+ strnuprcpy(spl, tm->QueryField(i,0), 8);
+ core->ApplySpell(spl, target, Owner, fx->Power);
+ return FX_NOT_APPLIED;
+}
+
+//0x138 //see fx_crash, this effect is not fully enabled in original bg2/tob
+int fx_immunity_sequester (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_immunity_sequester (%2d): Mod: %d\n", fx->Opcode, fx->Parameter2 );
+ //this effect is supposed to provide immunity against sequester (maze/etc?)
+ STAT_SET(IE_NOSEQUESTER, fx->Parameter2);
+ return FX_APPLIED;
+}
+
+//0x139 //HLA generic effect
+//0x13a StoneSkin2Modifier
+int fx_golem_stoneskin_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_golem_stoneskin_modifier (%2d): Mod: %d\n", fx->Opcode, fx->Parameter1 );
+ if (!fx->Parameter1) {
+ return FX_NOT_APPLIED;
+ }
+ //dead actors lose this effect
+ if (STATE_GET( STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ STAT_SET(IE_STONESKINSGOLEM, fx->Parameter1);
+ SetGradient(target, 14);
+ return FX_APPLIED;
+}
+
+// 0x13b AvatarRemovalModifier (also 0x104 iwd)
+int fx_avatar_removal_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_avatar_removal_modifier (%2d): Mod: %d\n", fx->Opcode, fx->Parameter2 );
+ STAT_SET(IE_AVATARREMOVAL, fx->Parameter2);
+ return FX_APPLIED;
+}
+
+// 0x13c MagicalRest (also 0x124 iwd)
+int fx_magical_rest (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_magical_rest (%2d)\n", fx->Opcode );
+ //instant, full rest
+ target->Rest(0);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_display_portrait_icon_ref, PI_FATIGUE);
+ return FX_NOT_APPLIED;
+}
+
+// 0x13d ImprovedHaste (See 0x10 Haste)
+
+// 0x13e ChangeWeather
+// sets the weather to param1, set it to:
+// 0 normal weather
+// 1 rain
+// 2 snow
+// 3 fog
+int fx_change_weather (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ printf( "fx_change_weather (%2d): P1: %d\n", fx->Opcode, fx->Parameter1 );
+
+ core->GetGame()->StartRainOrSnow(false, fx->Parameter1 & WB_MASK);
+
+ return FX_NOT_APPLIED;
+}
+
+// unknown
+int fx_unknown (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ printf( "fx_unknown (%2d): P1: %d P2: %d ResRef: %s\n", fx->Opcode, fx->Parameter1, fx->Parameter2, fx->Resource );
+ return FX_NOT_APPLIED;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x1AAA040A, "Effect opcodes for core games")
+PLUGIN_INITIALIZER(RegisterCoreOpcodes)
+PLUGIN_CLEANUP(Cleanup)
+END_PLUGIN()
diff --git a/gemrb/plugins/FXOpcodes/Makefile.am b/gemrb/plugins/FXOpcodes/Makefile.am
new file mode 100644
index 0000000..3ba05a7
--- /dev/null
+++ b/gemrb/plugins/FXOpcodes/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = FXOpcodes.la
+FXOpcodes_la_LDFLAGS = -module -avoid-version -shared
+FXOpcodes_la_SOURCES = FXOpcodes.cpp
diff --git a/gemrb/plugins/GAMImporter/CMakeLists.txt b/gemrb/plugins/GAMImporter/CMakeLists.txt
new file mode 100644
index 0000000..d84052b
--- /dev/null
+++ b/gemrb/plugins/GAMImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (GAMImporter GAMImporter.cpp)
diff --git a/gemrb/plugins/GAMImporter/GAMImporter.cpp b/gemrb/plugins/GAMImporter/GAMImporter.cpp
new file mode 100644
index 0000000..b04942b
--- /dev/null
+++ b/gemrb/plugins/GAMImporter/GAMImporter.cpp
@@ -0,0 +1,1294 @@
+/* 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 "GAMImporter.h"
+
+#include "globals.h"
+#include "win32def.h"
+
+#include "DataFileMgr.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "MapMgr.h"
+#include "System/MemoryStream.h"
+
+#include <cassert>
+
+#define FAMILIAR_FILL_SIZE 324
+// if your compiler chokes on this, use -1 or 0xff whichever works for you
+#define UNINITIALIZED_CHAR '\xff'
+
+GAMImporter::GAMImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+GAMImporter::~GAMImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+}
+
+bool GAMImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str) {
+ return false;
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "GAMEV0.0", 8 ) == 0) {
+ version = GAM_VER_GEMRB;
+ PCSize = 0x160;
+ } else if (strncmp( Signature, "GAMEV2.0", 8 ) == 0) {
+ //soa (soa part of tob)
+ version = GAM_VER_BG2;
+ PCSize = 0x160;
+ } else if (strncmp( Signature, "GAMEV2.1", 8 ) == 0) {
+ //tob
+ version = GAM_VER_TOB;
+ PCSize = 0x160;
+ } else if (strncmp( Signature, "GAMEV1.0", 8 ) == 0) {
+ //bg1?
+ version = GAM_VER_BG;
+ PCSize = 0x160;
+ } else if (strncmp( Signature, "GAMEV2.2", 8 ) == 0) {
+ //iwd2
+ version = GAM_VER_IWD2;
+ PCSize = 0x340;
+ } else if (strncmp( Signature, "GAMEV1.1", 8 ) == 0) {
+ //iwd, torment, totsc
+ if (core->HasFeature(GF_HAS_KAPUTZ) ) { //pst
+ PCSize = 0x168;
+ version = GAM_VER_PST;
+ //sound folder name takes up this space,
+ //so it is handy to make this check
+ } else if ( core->HasFeature(GF_SOUNDFOLDERS) ) {
+ PCSize = 0x180;
+ version = GAM_VER_IWD;
+ } else {
+ PCSize = 0x160;
+ version=GAM_VER_BG;
+ }
+ } else {
+ printMessage( "GAMImporter","This file is not a valid GAM File\n", LIGHT_RED );
+ return false;
+ }
+
+ return true;
+}
+
+Game* GAMImporter::LoadGame(Game *newGame, int ver_override)
+{
+ unsigned int i;
+
+ // saving in original version requires the original version
+ // otherwise it is set to 0 at construction time
+ if (core->SaveAsOriginal) {
+ // HACK: default icewind2.gam is 2.0! handled by script
+ if(ver_override) {
+ newGame->version = ver_override;
+ }
+ else {
+ newGame->version = version;
+ }
+ }
+
+ ieDword GameTime;
+ str->ReadDword( &GameTime );
+ newGame->GameTime = GameTime*AI_UPDATE_TIME;
+
+ str->ReadWord( &newGame->WhichFormation );
+ for (i = 0; i < 5; i++) {
+ str->ReadWord( &newGame->Formations[i] );
+ }
+ //hack for PST
+ if (version==GAM_VER_PST) {
+ newGame->Formations[0] = newGame->WhichFormation;
+ newGame->WhichFormation = 0;
+ }
+ str->ReadDword( &newGame->PartyGold );
+ //npc count in party???
+ str->ReadWord( &newGame->NpcInParty );
+ str->ReadWord( &newGame->WeatherBits );
+ str->ReadDword( &PCOffset );
+ str->ReadDword( &PCCount );
+ //these fields are not really used by any engine, and never saved
+ //str->ReadDword( &UnknownOffset );
+ //str->ReadDword( &UnknownCount );
+ str->Seek( 8, GEM_CURRENT_POS);
+ str->ReadDword( &NPCOffset );
+ str->ReadDword( &NPCCount );
+ str->ReadDword( &GlobalOffset );
+ str->ReadDword( &GlobalCount );
+ str->ReadResRef( newGame->CurrentArea );
+ str->ReadDword( &newGame->Unknown48 );//this is still unknown
+ str->ReadDword( &JournalCount );
+ str->ReadDword( &JournalOffset );
+ switch (version) {
+ default:
+ MazeOffset = 0;
+ str->ReadDword( &newGame->Reputation );
+ str->ReadResRef( newGame->CurrentArea ); // FIXME: see above
+ memcpy(newGame->AnotherArea, newGame->CurrentArea, sizeof(ieResRef) );
+ str->ReadDword( &newGame->ControlStatus );
+ str->ReadDword( &newGame->Expansion );
+ str->ReadDword( &FamiliarsOffset );
+ str->ReadDword( &SavedLocOffset );
+ str->ReadDword( &SavedLocCount );
+ str->ReadDword( &newGame->RealTime);
+ str->ReadDword( &PPLocOffset );
+ str->ReadDword( &PPLocCount );
+ str->Seek( 52, GEM_CURRENT_POS);
+ break;
+
+ case GAM_VER_PST:
+ str->ReadDword( &MazeOffset );
+ str->ReadDword( &newGame->Reputation );
+ str->ReadResRef( newGame->AnotherArea );
+ str->ReadDword( &KillVarsOffset );
+ str->ReadDword( &KillVarsCount );
+ str->ReadDword( &FamiliarsOffset ); //bestiary
+ str->ReadResRef( newGame->AnotherArea ); //yet another area
+ SavedLocOffset = 0;
+ SavedLocCount = 0;
+ PPLocOffset = 0;
+ PPLocCount = 0;
+ str->Seek( 64, GEM_CURRENT_POS);
+ break;
+ }
+
+ if (!newGame->CurrentArea[0]) {
+ // 0 - normal, 1 - tutorial, 2 - extension
+ AutoTable tm("STARTARE");
+ ieDword playmode = 0;
+ //only bg2 has 9 rows (iwd's have 6 rows - normal+extension)
+ if (tm && tm->GetRowCount()==9) {
+ core->GetDictionary()->Lookup( "PlayMode", playmode );
+ playmode *= 3;
+ }
+
+ const char* resref = tm->QueryField( playmode );
+ strnlwrcpy( newGame->CurrentArea, resref, 8 );
+ }
+
+ //Loading PCs
+ PluginHolder<ActorMgr> aM(IE_CRE_CLASS_ID);
+ for (i = 0; i < PCCount; i++) {
+ str->Seek( PCOffset + ( i * PCSize ), GEM_STREAM_START );
+ Actor *actor = GetActor( aM, true );
+ newGame->JoinParty( actor, actor->Selected?JP_SELECT:0 );
+ }
+
+ //Loading NPCs
+ for (i = 0; i < NPCCount; i++) {
+ str->Seek( NPCOffset + ( i * PCSize ), GEM_STREAM_START );
+ Actor *actor = GetActor( aM, false );
+ newGame->AddNPC( actor );
+ }
+
+ //apparently BG1/IWD2 relies on this, if chapter is unset, it is
+ //set to -1, hopefully it won't break anything
+ //PST has no chapter variable by default, and would crash on one
+ newGame->locals->SetAt("CHAPTER", (ieDword) -1, core->HasFeature(GF_NO_NEW_VARIABLES));
+
+ // load initial values from var.var
+ newGame->locals->LoadInitialValues("GLOBAL");
+
+ //Loading Global Variables
+ ieVariable Name;
+ Name[32] = 0;
+ str->Seek( GlobalOffset, GEM_STREAM_START );
+ for (i = 0; i < GlobalCount; i++) {
+ ieDword Value;
+ str->Read( Name, 32 );
+ str->Seek( 8, GEM_CURRENT_POS );
+ str->ReadDword( &Value );
+ str->Seek( 40, GEM_CURRENT_POS );
+ newGame->locals->SetAt( Name, Value );
+ }
+ if(core->HasFeature(GF_HAS_KAPUTZ) ) {
+ newGame->kaputz = new Variables();
+ newGame->kaputz->SetType( GEM_VARIABLES_INT );
+ newGame->kaputz->ParseKey( 1 );
+ // load initial values from var.var
+ newGame->kaputz->LoadInitialValues("KAPUTZ");
+ str->Seek( KillVarsOffset, GEM_STREAM_START );
+ for (i = 0; i < KillVarsCount; i++) {
+ ieDword Value;
+ str->Read( Name, 32 );
+ str->Seek( 8, GEM_CURRENT_POS );
+ str->ReadDword( &Value );
+ str->Seek( 40, GEM_CURRENT_POS );
+ newGame->kaputz->SetAt( Name, Value );
+ }
+ }
+
+ //Loading Journal entries
+ str->Seek( JournalOffset, GEM_STREAM_START );
+ for (i = 0; i < JournalCount; i++) {
+ GAMJournalEntry* je = GetJournalEntry();
+ newGame->AddJournalEntry( je );
+ }
+
+ if (version == GAM_VER_PST) {
+ //loading maze
+ if (MazeOffset) {
+ //Don't allocate memory in plugins (MSVC chokes on this)
+ newGame->AllocateMazeData();
+ str->Seek(MazeOffset, GEM_STREAM_START );
+ for (i = 0; i<MAZE_ENTRY_COUNT;i++) {
+ GetMazeEntry(newGame->mazedata+i*MAZE_ENTRY_SIZE);
+ }
+ GetMazeHeader(newGame->mazedata+MAZE_ENTRY_COUNT*MAZE_ENTRY_SIZE);
+ }
+ str->Seek( FamiliarsOffset, GEM_STREAM_START );
+ } else {
+ if (FamiliarsOffset) {
+ str->Seek( FamiliarsOffset, GEM_STREAM_START );
+ for (i=0; i<9;i++) {
+ str->ReadResRef( newGame->GetFamiliar(i) );
+ }
+ } else {
+ //clear these fields up
+ for (i=0;i<9;i++) {
+ memset(newGame->GetFamiliar(i), 0, sizeof(ieResRef));
+ }
+ }
+ }
+ // Loading known creatures array (beasts)
+ if(core->GetBeastsINI() != NULL) {
+ int beasts_count = BESTIARY_SIZE;
+ newGame->beasts = (ieByte*)calloc(sizeof(ieByte),beasts_count);
+ if(FamiliarsOffset) {
+ str->Read( newGame->beasts, beasts_count );
+ }
+ }
+
+ //TODO: these need to be corrected!
+ if (SavedLocCount && SavedLocOffset) {
+ ieWord PosX, PosY;
+
+ str->Seek( SavedLocOffset, GEM_STREAM_START );
+ for (i=0; i<SavedLocCount; i++) {
+ GAMLocationEntry *gle = newGame->GetSavedLocationEntry(i);
+ str->ReadResRef( gle->AreaResRef );
+ str->ReadWord( &PosX );
+ str->ReadWord( &PosY );
+ gle->Pos.x=PosX;
+ gle->Pos.y=PosY;
+ }
+ }
+
+ if (PPLocCount && PPLocOffset) {
+ ieWord PosX, PosY;
+
+ str->Seek( PPLocOffset, GEM_STREAM_START );
+ for (i=0; i<PPLocCount; i++) {
+ GAMLocationEntry *gle = newGame->GetPlaneLocationEntry(i);
+ str->ReadResRef( gle->AreaResRef );
+ str->ReadWord( &PosX );
+ str->ReadWord( &PosY );
+ gle->Pos.x=PosX;
+ gle->Pos.y=PosY;
+ }
+ }
+ return newGame;
+}
+
+void SanityCheck(ieWord a,ieWord &b,const char *message)
+{
+ if (a==0xffff) {
+ b=0xffff;
+ return;
+ }
+ if (b==0xffff) {
+ printMessage("GAMImporter"," ",LIGHT_RED);
+ printf("Invalid Slot Enabler caught: %s!\n", message);
+ b=0;
+ }
+}
+
+Actor* GAMImporter::GetActor(Holder<ActorMgr> aM, bool is_in_party )
+{
+ unsigned int i;
+ PCStruct pcInfo;
+ ieDword tmpDword;
+ ieWord tmpWord;
+
+ memset( &pcInfo,0,sizeof(pcInfo) );
+ str->ReadWord( &pcInfo.Selected );
+ str->ReadWord( &pcInfo.PartyOrder );
+ str->ReadDword( &pcInfo.OffsetToCRE );
+ str->ReadDword( &pcInfo.CRESize );
+ str->ReadResRef( pcInfo.CREResRef );
+ str->ReadDword( &pcInfo.Orientation );
+ str->ReadResRef( pcInfo.Area );
+ str->ReadWord( &pcInfo.XPos );
+ str->ReadWord( &pcInfo.YPos );
+ str->ReadWord( &pcInfo.ViewXPos );
+ str->ReadWord( &pcInfo.ViewYPos );
+ str->ReadWord( &pcInfo.ModalState ); //see Modal.ids
+ str->ReadWord( &pcInfo.Happiness );
+ for (i=0;i<24;i++) {
+ str->ReadDword( &pcInfo.Interact[i] ); //interact counters
+ }
+
+ bool extended = version==GAM_VER_GEMRB || version==GAM_VER_IWD2;
+ if (extended) {
+ ieResRef tmp;
+
+ for (i = 0; i < 4; i++) {
+ str->ReadWord( &pcInfo.QuickWeaponSlot[i] );
+ str->ReadWord( &pcInfo.QuickWeaponSlot[i+4] );
+ }
+ for (i = 0; i < 4; i++) {
+ str->ReadWord( &tmpWord );
+ SanityCheck( pcInfo.QuickWeaponSlot[i], tmpWord, "weapon");
+ pcInfo.QuickWeaponHeader[i]=tmpWord;
+ str->ReadWord( &tmpWord );
+ SanityCheck( pcInfo.QuickWeaponSlot[i+4], tmpWord, "weapon");
+ pcInfo.QuickWeaponHeader[i+4]=tmpWord;
+ }
+ for (i = 0; i < MAX_QSLOTS; i++) {
+ str->Read( &pcInfo.QuickSpellResRef[i], 8 );
+ }
+ str->Read( &pcInfo.QuickSpellClass, MAX_QSLOTS ); //9 bytes
+
+ str->Seek( 1, GEM_CURRENT_POS); //skipping a padding byte
+ for (i = 0; i < 3; i++) {
+ str->ReadWord( &pcInfo.QuickItemSlot[i] );
+ }
+ for (i = 0; i < 3; i++) {
+ str->ReadWord( &tmpWord );
+ SanityCheck( pcInfo.QuickItemSlot[i], tmpWord, "item");
+ pcInfo.QuickItemHeader[i]=tmpWord;
+ }
+ pcInfo.QuickItemHeader[3]=0xffff;
+ pcInfo.QuickItemHeader[4]=0xffff;
+ if (version == GAM_VER_IWD2) {
+ //quick innates
+ //we spare some memory and time by storing them in the same place
+ //this may be slightly buggy because IWD2 doesn't clear the
+ //fields, but QuickSpellClass is set correctly, problem is
+ //that GemRB doesn't clear QuickSpellClass
+ for (i = 0; i < MAX_QSLOTS; i++) {
+ str->Read( tmp, 8 );
+ if ((tmp[0]!=0) && (pcInfo.QuickSpellResRef[0]==0)) {
+ memcpy( pcInfo.QuickSpellResRef[i], tmp, 8);
+ //innates
+ pcInfo.QuickSpellClass[i]=0xff;
+ }
+ }
+ //recently discovered fields (bard songs)
+ //str->Seek( 72, GEM_CURRENT_POS);
+ for(i = 0; i<MAX_QSLOTS;i++) {
+ str->Read( tmp, 8 );
+ if ((tmp[0]!=0) && (pcInfo.QuickSpellResRef[0]==0)) {
+ memcpy( pcInfo.QuickSpellResRef[i], tmp, 8);
+ //bardsongs
+ pcInfo.QuickSpellClass[i]=0xfe;
+ }
+ }
+ }
+ //QuickSlots are customisable in iwd2 and GemRB
+ //thus we adopt the iwd2 style actor info
+ //the first 3 slots are hardcoded anyway
+ pcInfo.QSlots[0] = ACT_TALK;
+ pcInfo.QSlots[1] = ACT_WEAPON1;
+ pcInfo.QSlots[2] = ACT_WEAPON2;
+ for (i=0;i<MAX_QSLOTS;i++) {
+ str->ReadDword( &tmpDword );
+ pcInfo.QSlots[i+3] = (ieByte) tmpDword;
+ }
+ } else {
+ for (i = 0; i < 4; i++) {
+ str->ReadWord( &pcInfo.QuickWeaponSlot[i] );
+ }
+ for (i = 0; i < 4; i++) {
+ str->ReadWord( &tmpWord );
+ SanityCheck( pcInfo.QuickWeaponSlot[i], tmpWord, "weapon");
+ pcInfo.QuickWeaponHeader[i]=tmpWord;
+ }
+ for (i = 0; i < 3; i++) {
+ str->Read( &pcInfo.QuickSpellResRef[i], 8 );
+ }
+ if (version==GAM_VER_PST) { //Torment
+ for (i = 0; i < 5; i++) {
+ str->ReadWord( &pcInfo.QuickItemSlot[i] );
+ }
+ for (i = 0; i < 5; i++) {
+ str->ReadWord( &tmpWord );
+ SanityCheck( pcInfo.QuickItemSlot[i], tmpWord, "item");
+ pcInfo.QuickItemHeader[i]=tmpWord;
+ }
+ //str->Seek( 10, GEM_CURRENT_POS ); //enabler fields
+ } else {
+ for (i = 0; i < 3; i++) {
+ str->ReadWord( &pcInfo.QuickItemSlot[i] );
+ }
+ for (i = 0; i < 3; i++) {
+ str->ReadWord( &tmpWord );
+ SanityCheck( pcInfo.QuickItemSlot[i], tmpWord, "item");
+ pcInfo.QuickItemHeader[i]=tmpWord;
+ }
+ //str->Seek( 6, GEM_CURRENT_POS ); //enabler fields
+ }
+ pcInfo.QSlots[0] = 0xff; //(invalid, will be regenerated)
+ }
+ str->Read( &pcInfo.Name, 32 );
+ str->ReadDword( &pcInfo.TalkCount );
+
+ ieDword pos = str->GetPos();
+
+ Actor* actor = NULL;
+ tmpWord = is_in_party ? (pcInfo.PartyOrder + 1) : 0;
+
+ if (pcInfo.OffsetToCRE) {
+ str->Seek( pcInfo.OffsetToCRE, GEM_STREAM_START );
+ void* Buffer = malloc( pcInfo.CRESize );
+ str->Read( Buffer, pcInfo.CRESize );
+ //somehow autofree MemoryStream doesn't work on msvc 7.0
+ //separate heap for dll's?
+ MemoryStream* ms = new MemoryStream( Buffer, pcInfo.CRESize, false );
+ if (ms) {
+ aM->Open( ms, true );
+ actor = aM->GetActor(tmpWord);
+ }
+ free (Buffer);
+
+ //torment has them as 0 or -1
+ if (pcInfo.Name[0]!=0 && pcInfo.Name[0]!=UNINITIALIZED_CHAR) {
+ actor->SetName(pcInfo.Name,0); //setting both names
+ }
+ actor->TalkCount = pcInfo.TalkCount;
+ } else {
+ DataStream* ds = gamedata->GetResource(
+ pcInfo.CREResRef, IE_CRE_CLASS_ID );
+ //another plugin cannot free memory stream from this plugin
+ //so auto free is a no-no
+ if (ds) {
+ aM->Open( ds, true );
+ actor = aM->GetActor(pcInfo.PartyOrder);
+ }
+ }
+ if (!actor) {
+ return actor;
+ }
+
+ //
+ str->Seek(pos, GEM_STREAM_START);
+ //
+ actor->CreateStats();
+ PCStatsStruct *ps = actor->PCStats;
+ GetPCStats(ps, extended);
+ memcpy(ps->QSlots, pcInfo.QSlots, sizeof(pcInfo.QSlots) );
+ memcpy(ps->QuickSpells, pcInfo.QuickSpellResRef, MAX_QSLOTS*sizeof(ieResRef) );
+ memcpy(ps->QuickSpellClass, pcInfo.QuickSpellClass, MAX_QSLOTS );
+ memcpy(ps->QuickWeaponSlots, pcInfo.QuickWeaponSlot, MAX_QUICKWEAPONSLOT*sizeof(ieWord) );
+ memcpy(ps->QuickWeaponHeaders, pcInfo.QuickWeaponHeader, MAX_QUICKWEAPONSLOT*sizeof(ieWord) );
+ memcpy(ps->QuickItemSlots, pcInfo.QuickItemSlot, MAX_QUICKITEMSLOT*sizeof(ieWord) );
+ memcpy(ps->QuickItemHeaders, pcInfo.QuickItemHeader, MAX_QUICKITEMSLOT*sizeof(ieWord) );
+ actor->Destination.x = actor->Pos.x = pcInfo.XPos;
+ actor->Destination.y = actor->Pos.y = pcInfo.YPos;
+ strcpy( actor->Area, pcInfo.Area );
+ actor->SetOrientation( pcInfo.Orientation,0 );
+ actor->TalkCount = pcInfo.TalkCount;
+ actor->ModalState = pcInfo.ModalState;
+ actor->SetModalSpell(pcInfo.ModalState, 0);
+ ps->Happiness = pcInfo.Happiness;
+ memcpy(ps->Interact, pcInfo.Interact, MAX_INTERACT *sizeof(ieDword) );
+
+ actor->SetPersistent( tmpWord );
+
+ actor->Selected = pcInfo.Selected;
+ return actor;
+}
+
+void GAMImporter::GetPCStats (PCStatsStruct *ps, bool extended)
+{
+ int i;
+
+ str->ReadDword( &ps->BestKilledName );
+ str->ReadDword( &ps->BestKilledXP );
+ str->ReadDword( &ps->AwayTime );
+ str->ReadDword( &ps->JoinDate );
+ str->ReadDword( &ps->unknown10 );
+ str->ReadDword( &ps->KillsChapterXP );
+ str->ReadDword( &ps->KillsChapterCount );
+ str->ReadDword( &ps->KillsTotalXP );
+ str->ReadDword( &ps->KillsTotalCount );
+ for (i = 0; i <= 3; i++) {
+ str->ReadResRef( ps->FavouriteSpells[i] );
+ }
+ for (i = 0; i <= 3; i++)
+ str->ReadWord( &ps->FavouriteSpellsCount[i] );
+
+ for (i = 0; i <= 3; i++) {
+ str->ReadResRef( ps->FavouriteWeapons[i] );
+ }
+ for (i = 0; i <= 3; i++)
+ str->ReadWord( &ps->FavouriteWeaponsCount[i] );
+
+ str->ReadResRef( ps->SoundSet );
+
+ if (core->HasFeature(GF_SOUNDFOLDERS) ) {
+ str->Read( ps->SoundFolder, 32);
+ }
+
+ //iwd2 has some PC only stats that the player can set (this can be done via a guiscript interface)
+ if (extended) {
+ //3 - expertise
+ //4 - power attack
+ //5 - arterial strike
+ //6 - hamstring
+ //7 - rapid shot
+ for (i=0;i<16;i++) {
+ str->ReadDword( &ps->ExtraSettings[i] );
+ }
+ }
+}
+
+GAMJournalEntry* GAMImporter::GetJournalEntry()
+{
+ GAMJournalEntry* j = new GAMJournalEntry();
+
+ str->ReadDword( &j->Text );
+ str->ReadDword( &j->GameTime );
+ //this could be wrong, most likely these are 2 words, or a dword
+ str->Read( &j->Chapter, 1 );
+ str->Read( &j->unknown09, 1 );
+ str->Read( &j->Section, 1 );
+ str->Read( &j->Group, 1 ); // this is a GemRB extension
+
+ return j;
+}
+
+int GAMImporter::GetStoredFileSize(Game *game)
+{
+ int headersize;
+ unsigned int i;
+
+ //moved this here, so one can disable killvars in a pst style game
+ //or enable them in gemrb
+ if(core->HasFeature(GF_HAS_KAPUTZ) ) {
+ KillVarsCount = game->kaputz->GetCount();
+ } else {
+ KillVarsCount = 0;
+ }
+ switch(game->version)
+ {
+ case GAM_VER_GEMRB:
+ headersize = 0xb4;
+ PCSize = 0x160;
+ break;
+ case GAM_VER_IWD:
+ headersize = 0xb4;
+ PCSize = 0x180;
+ break;
+ case GAM_VER_BG:
+ case GAM_VER_BG2:
+ case GAM_VER_TOB:
+ headersize = 0xb4;
+ PCSize = 0x160;
+ break;
+ case GAM_VER_IWD2:
+ headersize = 0xb4;
+ PCSize = 0x340;
+ break;
+ case GAM_VER_PST:
+ headersize = 0xb8;
+ PCSize = 0x168;
+ break;
+ default:
+ return -1;
+ }
+ PCOffset = headersize;
+
+ PluginHolder<ActorMgr> am(IE_CRE_CLASS_ID);
+ PCCount = game->GetPartySize(false);
+ headersize += PCCount * PCSize;
+ for (i = 0;i<PCCount; i++) {
+ Actor *ac = game->GetPC(i, false);
+ headersize += am->GetStoredFileSize(ac);
+ }
+ NPCOffset = headersize;
+
+ NPCCount = game->GetNPCCount();
+ headersize += NPCCount * PCSize;
+ for (i = 0;i<NPCCount; i++) {
+ Actor *ac = game->GetNPC(i);
+ headersize += am->GetStoredFileSize(ac);
+ }
+
+ if (game->mazedata) {
+ MazeOffset = headersize;
+ //due to alignment the internal size is not the same as the external size
+ headersize += MAZE_DATA_SIZE_HARDCODED;
+ } else {
+ MazeOffset = 0;
+ }
+
+ GlobalOffset = headersize;
+
+ GlobalCount = game->locals->GetCount();
+ headersize += GlobalCount * 84;
+ JournalOffset = headersize;
+
+ JournalCount = game->GetJournalCount();
+ headersize += JournalCount * 12;
+
+ KillVarsOffset = headersize;
+ if (KillVarsCount) {
+ headersize += KillVarsCount * 84;
+ }
+
+ if (game->version==GAM_VER_BG) {
+ FamiliarsOffset = 0;
+ } else {
+ FamiliarsOffset = headersize;
+ if (core->GetBeastsINI()) {
+ headersize += BESTIARY_SIZE;
+ }
+ if (game->version!=GAM_VER_PST) {
+ headersize += 9 * 8 + 82 * 4;
+ }
+ }
+
+ SavedLocOffset = headersize;
+ SavedLocCount = game->GetSavedLocationCount();
+ //there is an unknown dword at the end of iwd2 savegames
+ if (game->version==GAM_VER_IWD2) {
+ headersize += 4;
+ }
+ headersize += SavedLocCount*12;
+
+ PPLocOffset = headersize;
+ PPLocCount = game->GetPlaneLocationCount();
+
+ return headersize + PPLocCount * 12;
+}
+
+int GAMImporter::PutJournals(DataStream *stream, Game *game)
+{
+ for (unsigned int i=0;i<JournalCount;i++) {
+ GAMJournalEntry *j = game->GetJournalEntry(i);
+
+ stream->WriteDword( &j->Text );
+ stream->WriteDword( &j->GameTime );
+ //this could be wrong, most likely these are 2 words, or a dword
+ stream->Write( &j->Chapter, 1 );
+ stream->Write( &j->unknown09, 1 );
+ stream->Write( &j->Section, 1 );
+ stream->Write( &j->Group, 1 ); // this is a GemRB extension
+ }
+
+ return 0;
+}
+
+//only in ToB (and iwd2)
+int GAMImporter::PutSavedLocations(DataStream *stream, Game *game)
+{
+ ieWord tmpWord;
+ ieDword filling = 0;
+
+ //iwd2 has a single 0 dword here (at the end of the file)
+ //it could be a hacked out saved location list (inherited from SoA)
+ //if the field is missing, original engine cannot load this saved game
+ if (game->version==GAM_VER_IWD2) {
+ stream->WriteDword(&filling);
+ return 0;
+ }
+
+ for (unsigned int i=0;i<SavedLocCount;i++) {
+ GAMLocationEntry *j = game->GetSavedLocationEntry(i);
+
+ stream->WriteResRef(j->AreaResRef);
+ tmpWord = j->Pos.x;
+ stream->WriteWord(&tmpWord);
+ tmpWord = j->Pos.y;
+ stream->WriteWord(&tmpWord);
+ }
+ return 0;
+}
+
+int GAMImporter::PutPlaneLocations(DataStream *stream, Game *game)
+{
+ ieWord tmpWord;
+
+ for (unsigned int i=0;i<PPLocCount;i++) {
+ GAMLocationEntry *j = game->GetPlaneLocationEntry(i);
+
+ stream->WriteResRef(j->AreaResRef);
+ tmpWord = j->Pos.x;
+ stream->WriteWord(&tmpWord);
+ tmpWord = j->Pos.y;
+ stream->WriteWord(&tmpWord);
+ }
+ return 0;
+}
+
+//only in PST
+int GAMImporter::PutKillVars(DataStream *stream, Game *game)
+{
+ char filling[40];
+ ieVariable tmpname;
+ Variables::iterator pos=NULL;
+ const char *name;
+ ieDword value;
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<KillVarsCount;i++) {
+ //global variables are locals for game, that's why the local/global confusion
+ pos=game->kaputz->GetNextAssoc( pos, name, value);
+ strnspccpy(tmpname,name,32);
+ stream->Write( tmpname, 32);
+ stream->Write( filling, 8);
+ stream->WriteDword( &value);
+ //40 bytes of empty crap
+ stream->Write( filling, 40);
+ }
+ return 0;
+}
+
+int GAMImporter::PutVariables(DataStream *stream, Game *game)
+{
+ char filling[40];
+ ieVariable tmpname;
+ Variables::iterator pos=NULL;
+ const char *name;
+ ieDword value;
+
+ memset(filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<GlobalCount;i++) {
+ //global variables are locals for game, that's why the local/global confusion
+ pos=game->locals->GetNextAssoc( pos, name, value);
+ strnspccpy(tmpname, name, 32);
+ stream->Write( tmpname, 32);
+ stream->Write( filling, 8);
+ stream->WriteDword( &value);
+ //40 bytes of empty crap
+ stream->Write( filling, 40);
+ }
+ return 0;
+}
+
+int GAMImporter::PutHeader(DataStream *stream, Game *game)
+{
+ int i;
+ char Signature[10];
+ ieDword tmpDword;
+
+ memcpy( Signature, "GAMEV0.0", 8);
+ Signature[5]+=game->version/10;
+ if (game->version==GAM_VER_PST || game->version==GAM_VER_BG) { //pst/bg1 saved version
+ Signature[7]+=1;
+ }
+ else {
+ Signature[7]+=game->version%10;
+ }
+ stream->Write( Signature, 8);
+ //using Signature for padding
+ memset(Signature, 0, sizeof(Signature));
+ tmpDword = game->GameTime/AI_UPDATE_TIME;
+ stream->WriteDword( &tmpDword );
+ //pst has a single preset of formations
+ if (game->version==GAM_VER_PST) {
+ stream->WriteWord( &game->Formations[0]);
+ stream->Write( Signature, 10);
+ } else {
+ stream->WriteWord( &game->WhichFormation );
+ for(i=0;i<5;i++) {
+ stream->WriteWord( &game->Formations[i]);
+ }
+ }
+ stream->WriteDword( &game->PartyGold );
+ //hack because we don't need this
+ game->NpcInParty=PCCount-1;
+ stream->WriteWord( &game->NpcInParty );
+ stream->WriteWord( &game->WeatherBits );
+ stream->WriteDword( &PCOffset );
+ stream->WriteDword( &PCCount );
+ //these fields are zeroed in any original savegame
+ tmpDword = 0;
+ stream->WriteDword( &tmpDword );
+ stream->WriteDword( &tmpDword );
+ stream->WriteDword( &NPCOffset );
+ stream->WriteDword( &NPCCount );
+ stream->WriteDword( &GlobalOffset );
+ stream->WriteDword( &GlobalCount );
+ stream->WriteResRef( game->CurrentArea );
+ stream->WriteDword( &game->Unknown48 );
+ stream->WriteDword( &JournalCount );
+ stream->WriteDword( &JournalOffset );
+
+ switch(game->version) {
+ case GAM_VER_GEMRB:
+ case GAM_VER_BG:
+ case GAM_VER_IWD:
+ case GAM_VER_BG2:
+ case GAM_VER_TOB:
+ case GAM_VER_IWD2:
+ stream->WriteDword( &game->Reputation );
+ stream->WriteResRef( game->CurrentArea );
+ stream->WriteDword( &game->ControlStatus );
+ stream->WriteDword( &game->Expansion);
+ stream->WriteDword( &FamiliarsOffset);
+ stream->WriteDword( &SavedLocOffset);
+ stream->WriteDword( &SavedLocCount);
+ break;
+ case GAM_VER_PST:
+ stream->WriteDword( &MazeOffset );
+ stream->WriteDword( &game->Reputation );
+ stream->WriteResRef( game->CurrentArea );
+ stream->WriteDword( &KillVarsOffset );
+ stream->WriteDword( &KillVarsCount );
+ stream->WriteDword( &FamiliarsOffset );
+ stream->WriteResRef( game->CurrentArea ); //again
+ break;
+ }
+ stream->WriteDword( &game->RealTime ); //this isn't correct, this field is the realtime
+ stream->WriteDword( &PPLocOffset);
+ stream->WriteDword( &PPLocCount);
+ char filling[52];
+ memset( filling, 0, sizeof(filling) );
+ stream->Write( &filling, 52); //unknown
+
+ //save failed, but it is not our fault, returning now before the asserts kill us
+ if (stream->GetPos()==0) {
+ return -1;
+ }
+ return 0;
+}
+
+int GAMImporter::PutActor(DataStream *stream, Actor *ac, ieDword CRESize, ieDword CREOffset, ieDword version)
+{
+ int i;
+ ieDword tmpDword;
+ ieWord tmpWord;
+ char filling[130];
+
+ memset(filling,0,sizeof(filling) );
+ if (ac->Selected) {
+ tmpWord=1;
+ } else {
+ tmpWord=0;
+ }
+
+ stream->WriteWord( &tmpWord);
+ tmpWord = ac->InParty-1;
+ stream->WriteWord( &tmpWord);
+
+ stream->WriteDword( &CREOffset);
+ stream->WriteDword( &CRESize);
+ //creature resref is always unused in saved games
+ //BG1 doesn't even like the * in there, zero fill
+ //seems to be accepted by all
+ stream->Write( filling, 8);
+ tmpDword = ac->GetOrientation();
+ stream->WriteDword( &tmpDword);
+ stream->WriteResRef(ac->Area);
+ tmpWord = ac->Pos.x;
+ stream->WriteWord( &tmpWord);
+ tmpWord = ac->Pos.y;
+ stream->WriteWord( &tmpWord);
+ //no viewport, we cheat
+ tmpWord = ac->Pos.x-core->Width/2;
+ stream->WriteWord( &tmpWord);
+ tmpWord = ac->Pos.y-core->Height/2;
+ stream->WriteWord( &tmpWord);
+ tmpWord = (ieWord) ac->ModalState;
+ stream->WriteWord( &tmpWord);
+ tmpWord = ac->PCStats->Happiness;
+ stream->WriteWord( &tmpWord);
+ //interact counters
+ for (i=0;i<24;i++) {
+ stream->WriteDword( ac->PCStats->Interact+i);
+ }
+
+ //quickweapons
+ if (version==GAM_VER_IWD2 || version==GAM_VER_GEMRB) {
+ for (i=0;i<4;i++) {
+ stream->WriteWord( ac->PCStats->QuickWeaponSlots+i);
+ stream->WriteWord( ac->PCStats->QuickWeaponSlots+4+i);
+ }
+ for (i=0;i<4;i++) {
+ stream->WriteWord( ac->PCStats->QuickWeaponHeaders+i);
+ stream->WriteWord( ac->PCStats->QuickWeaponHeaders+4+i);
+ }
+ } else {
+ for (i=0;i<4;i++) {
+ stream->WriteWord( ac->PCStats->QuickWeaponSlots+i);
+ }
+ for (i=0;i<4;i++) {
+ stream->WriteWord( ac->PCStats->QuickWeaponHeaders+i);
+ }
+ }
+
+ //quickspells
+ if (version==GAM_VER_IWD2 || version==GAM_VER_GEMRB) {
+ for (i=0;i<MAX_QSLOTS;i++) {
+ if ( (ieByte) ac->PCStats->QuickSpellClass[i]>=0xfe) {
+ stream->Write(filling,8);
+ } else {
+ stream->Write(ac->PCStats->QuickSpells[i],8);
+ }
+ }
+ //quick spell classes, clear the field for iwd2 if it is
+ //a bard song/innate slot (0xfe or 0xff)
+ memcpy(filling, ac->PCStats->QuickSpellClass, MAX_QSLOTS);
+ if (version==GAM_VER_IWD2) {
+ for(i=0;i<MAX_QSLOTS;i++) {
+ if((ieByte) filling[i]>=0xfe) {
+ filling[i]=0;
+ }
+ }
+ }
+ stream->Write(filling,10);
+ memset(filling,0,sizeof(filling) );
+ } else {
+ for (i=0;i<3;i++) {
+ stream->Write(ac->PCStats->QuickSpells[i],8);
+ }
+ }
+
+ //quick items
+ switch (version) {
+ case GAM_VER_PST: case GAM_VER_GEMRB:
+ for (i=0;i<MAX_QUICKITEMSLOT;i++) {
+ stream->WriteWord(ac->PCStats->QuickItemSlots+i);
+ }
+ for (i=0;i<MAX_QUICKITEMSLOT;i++) {
+ stream->WriteWord(ac->PCStats->QuickItemHeaders+i);
+ }
+ break;
+ default:
+ for (i=0;i<3;i++) {
+ stream->WriteWord(ac->PCStats->QuickItemSlots+i);
+ }
+ for (i=0;i<3;i++) {
+ stream->WriteWord(ac->PCStats->QuickItemHeaders+i);
+ }
+ break;
+ }
+
+ //innates, bard songs and quick slots are saved only in iwd2
+ if (version==GAM_VER_IWD2 || version==GAM_VER_GEMRB) {
+ for (i=0;i<MAX_QSLOTS;i++) {
+ if ( (ieByte) ac->PCStats->QuickSpellClass[i]==0xff) {
+ stream->Write(ac->PCStats->QuickSpells[i],8);
+ } else {
+ stream->Write(filling,8);
+ }
+ }
+ for (i=0;i<MAX_QSLOTS;i++) {
+ if ((ieByte) ac->PCStats->QuickSpellClass[i]==0xfe) {
+ stream->Write(ac->PCStats->QuickSpells[i],8);
+ } else {
+ stream->Write(filling,8);
+ }
+ }
+ for (i=0;i<MAX_QSLOTS;i++) {
+ tmpDword = ac->PCStats->QSlots[i+3];
+ stream->WriteDword( &tmpDword);
+ }
+ }
+
+ if (ac->LongStrRef==0xffffffff) {
+ strncpy(filling, ac->LongName, 32);
+ } else {
+ char *tmpstr = core->GetString(ac->LongStrRef, IE_STR_STRREFOFF);
+ strncpy(filling, tmpstr, 32);
+ core->FreeString( tmpstr );
+ }
+ stream->Write( filling, 32);
+ memset(filling,0,32);
+ stream->WriteDword( &ac->TalkCount);
+ stream->WriteDword( &ac->PCStats->BestKilledName);
+ stream->WriteDword( &ac->PCStats->BestKilledXP);
+ stream->WriteDword( &ac->PCStats->AwayTime);
+ stream->WriteDword( &ac->PCStats->JoinDate);
+ stream->WriteDword( &ac->PCStats->unknown10);
+ stream->WriteDword( &ac->PCStats->KillsChapterXP);
+ stream->WriteDword( &ac->PCStats->KillsChapterCount);
+ stream->WriteDword( &ac->PCStats->KillsTotalXP);
+ stream->WriteDword( &ac->PCStats->KillsTotalCount);
+ for (i=0;i<4;i++) {
+ stream->WriteResRef( ac->PCStats->FavouriteSpells[i]);
+ }
+ for (i=0;i<4;i++) {
+ stream->WriteWord( &ac->PCStats->FavouriteSpellsCount[i]);
+ }
+ for (i=0;i<4;i++) {
+ stream->WriteResRef( ac->PCStats->FavouriteWeapons[i]);
+ }
+ for (i=0;i<4;i++) {
+ stream->WriteWord( &ac->PCStats->FavouriteWeaponsCount[i]);
+ }
+ stream->Write( ac->PCStats->SoundSet, 8); //soundset
+ if (core->HasFeature(GF_SOUNDFOLDERS) ) {
+ stream->Write(ac->PCStats->SoundFolder, 32);
+ }
+ if (version==GAM_VER_IWD2 || version==GAM_VER_GEMRB) {
+ //I don't know how many fields are actually used in IWD2 saved game
+ //but we got at least 8 (and only 5 of those are actually used)
+ for(i=0;i<16;i++) {
+ stream->WriteDword( &ac->PCStats->ExtraSettings[i]);
+ }
+ stream->Write(filling, 130);
+ }
+
+ return 0;
+}
+
+int GAMImporter::PutPCs(DataStream *stream, Game *game)
+{
+ unsigned int i;
+ PluginHolder<ActorMgr> am(IE_CRE_CLASS_ID);
+ ieDword CREOffset = PCOffset + PCCount * PCSize;
+
+ for(i=0;i<PCCount;i++) {
+ assert(stream->GetPos() == PCOffset + i * PCSize);
+ Actor *ac = game->GetPC(i, false);
+ ieDword CRESize = am->GetStoredFileSize(ac);
+ PutActor(stream, ac, CRESize, CREOffset, game->version);
+ CREOffset += CRESize;
+ }
+
+ CREOffset = PCOffset + PCCount * PCSize; // just for the asserts..
+ assert(stream->GetPos() == CREOffset);
+
+ for(i=0;i<PCCount;i++) {
+ assert(stream->GetPos() == CREOffset);
+ Actor *ac = game->GetPC(i, false);
+ //reconstructing offsets again
+ CREOffset += am->GetStoredFileSize(ac);
+ am->PutActor( stream, ac);
+ }
+ assert(stream->GetPos() == CREOffset);
+ return 0;
+}
+
+int GAMImporter::PutNPCs(DataStream *stream, Game *game)
+{
+ unsigned int i;
+ PluginHolder<ActorMgr> am(IE_CRE_CLASS_ID);
+ ieDword CREOffset = NPCOffset + NPCCount * PCSize;
+
+ for(i=0;i<NPCCount;i++) {
+ assert(stream->GetPos() == NPCOffset + i * PCSize);
+ Actor *ac = game->GetNPC(i);
+ ieDword CRESize = am->GetStoredFileSize(ac);
+ PutActor(stream, ac, CRESize, CREOffset, game->version);
+ CREOffset += CRESize;
+ }
+ CREOffset = NPCOffset + NPCCount * PCSize; // just for the asserts..
+ assert(stream->GetPos() == CREOffset);
+
+ for(i=0;i<NPCCount;i++) {
+ assert(stream->GetPos() == CREOffset);
+ Actor *ac = game->GetNPC(i);
+ //reconstructing offsets again
+ CREOffset += am->GetStoredFileSize(ac);
+ am->PutActor( stream, ac);
+ }
+ assert(stream->GetPos() == CREOffset);
+ return 0;
+}
+
+void GAMImporter::GetMazeHeader(void *memory)
+{
+ maze_header *m = (maze_header *) memory;
+ str->ReadDword( &m->maze_sizex );
+ str->ReadDword( &m->maze_sizey );
+ str->ReadDword( &m->pos1x );
+ str->ReadDword( &m->pos1y );
+ str->ReadDword( &m->pos2x );
+ str->ReadDword( &m->pos2y );
+ str->ReadDword( &m->pos3x );
+ str->ReadDword( &m->pos3y );
+ str->ReadDword( &m->pos4x );
+ str->ReadDword( &m->pos4y );
+ str->ReadDword( &m->trapcount );
+ str->ReadDword( &m->initialized );
+ str->ReadDword( &m->unknown2c );
+ str->ReadDword( &m->unknown30 );
+}
+
+void GAMImporter::GetMazeEntry(void *memory)
+{
+ maze_entry *h = (maze_entry *) memory;
+
+ str->ReadDword( &h->override );
+ str->ReadDword( &h->valid );
+ str->ReadDword( &h->accessible );
+ str->ReadDword( &h->trapped );
+ str->ReadDword( &h->traptype );
+ str->ReadWord( &h->walls );
+ str->ReadDword( &h->visited );
+}
+
+void GAMImporter::PutMazeHeader(DataStream *stream, void *memory)
+{
+ maze_header *m = (maze_header *) memory;
+ stream->WriteDword( &m->maze_sizex );
+ stream->WriteDword( &m->maze_sizey );
+ stream->WriteDword( &m->pos1x );
+ stream->WriteDword( &m->pos1y );
+ stream->WriteDword( &m->pos2x );
+ stream->WriteDword( &m->pos2y );
+ stream->WriteDword( &m->pos3x );
+ stream->WriteDword( &m->pos3y );
+ stream->WriteDword( &m->pos4x );
+ stream->WriteDword( &m->pos4y );
+ stream->WriteDword( &m->trapcount );
+ stream->WriteDword( &m->initialized );
+ stream->WriteDword( &m->unknown2c );
+ stream->WriteDword( &m->unknown30 );
+}
+
+void GAMImporter::PutMazeEntry(DataStream *stream, void *memory)
+{
+ maze_entry *h = (maze_entry *) memory;
+ stream->WriteDword( &h->override );
+ stream->WriteDword( &h->valid );
+ stream->WriteDword( &h->accessible );
+ stream->WriteDword( &h->trapped );
+ stream->WriteDword( &h->traptype );
+ stream->WriteWord( &h->walls );
+ stream->WriteDword( &h->visited );
+}
+
+int GAMImporter::PutMaze(DataStream *stream, Game *game)
+{
+ for(int i=0;i<MAZE_ENTRY_COUNT;i++) {
+ PutMazeEntry(stream, game->mazedata+i*MAZE_ENTRY_SIZE);
+ }
+ PutMazeHeader(stream, game->mazedata+MAZE_ENTRY_COUNT*MAZE_ENTRY_SIZE);
+ return 0;
+}
+
+int GAMImporter::PutFamiliars(DataStream *stream, Game *game)
+{
+ int len = 0;
+ if (core->GetBeastsINI()) {
+ len = BESTIARY_SIZE;
+ if (game->version==GAM_VER_PST) {
+ //only GemRB version can have all features, return when it is PST
+ //gemrb version will have the beasts after the familiars
+ stream->Write( game->beasts, len );
+ return 0;
+ }
+ }
+
+ char filling[FAMILIAR_FILL_SIZE];
+
+ memset( filling,0,sizeof(filling) );
+ for (unsigned int i=0;i<9;i++) {
+ stream->WriteResRef( game->GetFamiliar(i) );
+ }
+ stream->WriteDword( &SavedLocOffset);
+ if (len) {
+ stream->Write( game->beasts, len );
+ }
+ stream->Write( filling, FAMILIAR_FILL_SIZE - len);
+ return 0;
+}
+
+int GAMImporter::PutGame(DataStream *stream, Game *game)
+{
+ int ret;
+
+ if (!stream || !game) {
+ return -1;
+ }
+
+ ret = PutHeader( stream, game);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutPCs( stream, game);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutNPCs( stream, game);
+ if (ret) {
+ return ret;
+ }
+
+ if (game->mazedata) {
+ ret = PutMaze( stream, game);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ ret = PutVariables( stream, game);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutJournals( stream, game);
+ if (ret) {
+ return ret;
+ }
+
+ if (core->HasFeature(GF_HAS_KAPUTZ) ) {
+ ret = PutKillVars( stream, game);
+ if (ret) {
+ return ret;
+ }
+
+ }
+ if (FamiliarsOffset) {
+ ret = PutFamiliars( stream, game);
+ if (ret) {
+ return ret;
+ }
+ }
+ if (SavedLocOffset) {
+ ret = PutSavedLocations( stream, game);
+ if (ret) {
+ return ret;
+ }
+ }
+ if (PPLocOffset) {
+ ret = PutPlaneLocations( stream, game);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xD7F7040, "GAM File Importer")
+PLUGIN_CLASS(IE_GAM_CLASS_ID, GAMImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/GAMImporter/GAMImporter.h b/gemrb/plugins/GAMImporter/GAMImporter.h
new file mode 100644
index 0000000..fba05f0
--- /dev/null
+++ b/gemrb/plugins/GAMImporter/GAMImporter.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.
+ *
+ *
+ */
+
+#ifndef GAMIMPORTER_H
+#define GAMIMPORTER_H
+
+#include "SaveGameMgr.h"
+
+#include "ActorMgr.h"
+
+#define GAM_VER_GEMRB 0
+#define GAM_VER_BG 10
+#define GAM_VER_IWD 11
+#define GAM_VER_PST 12
+#define GAM_VER_BG2 20
+#define GAM_VER_TOB 21
+#define GAM_VER_IWD2 22
+
+class GAMImporter : public SaveGameMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ int version;
+ unsigned int PCSize;
+ ieDword PCOffset, PCCount;
+ ieDword MazeOffset;
+ ieDword NPCOffset, NPCCount;
+ ieDword GlobalOffset, GlobalCount;
+ ieDword JournalOffset, JournalCount;
+ ieDword KillVarsOffset, KillVarsCount;
+ ieDword FamiliarsOffset;
+ ieDword SavedLocOffset, SavedLocCount;
+ ieDword PPLocOffset, PPLocCount;
+public:
+ GAMImporter(void);
+ ~GAMImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ Game* LoadGame(Game *newGame, int ver_override = 0);
+
+ int GetStoredFileSize(Game *game);
+ /* stores a gane in the savegame folder */
+ int PutGame(DataStream *stream, Game *game);
+private:
+ Actor* GetActor(Holder<ActorMgr> aM, bool is_in_party );
+ void GetPCStats(PCStatsStruct* ps, bool extended);
+ GAMJournalEntry* GetJournalEntry();
+
+ int PutHeader(DataStream *stream, Game *game);
+ int PutActor(DataStream *stream, Actor *ac, ieDword CRESize, ieDword CREOffset, ieDword version);
+ int PutPCs(DataStream *stream, Game *game);
+ int PutNPCs(DataStream *stream, Game *game);
+ int PutJournals(DataStream *stream, Game *game);
+ int PutVariables( DataStream *stream, Game *game);
+ int PutKillVars(DataStream *stream, Game *game);
+ void GetMazeHeader(void *memory);
+ void GetMazeEntry(void *memory);
+ void PutMazeHeader(DataStream *stream, void *memory);
+ void PutMazeEntry(DataStream *stream, void *memory);
+ int PutMaze(DataStream *stream, Game *game);
+ int PutFamiliars(DataStream *stream, Game *game);
+ int PutSavedLocations(DataStream *stream, Game *game);
+ int PutPlaneLocations(DataStream *stream, Game *game);
+};
+
+#endif
+
diff --git a/gemrb/plugins/GAMImporter/Makefile.am b/gemrb/plugins/GAMImporter/Makefile.am
new file mode 100644
index 0000000..79acf7e
--- /dev/null
+++ b/gemrb/plugins/GAMImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = GAMImporter.la
+GAMImporter_la_LDFLAGS = -module -avoid-version -shared
+GAMImporter_la_SOURCES = GAMImporter.cpp GAMImporter.h
diff --git a/gemrb/plugins/GUIScript/CMakeLists.txt b/gemrb/plugins/GUIScript/CMakeLists.txt
new file mode 100644
index 0000000..54131e9
--- /dev/null
+++ b/gemrb/plugins/GUIScript/CMakeLists.txt
@@ -0,0 +1,5 @@
+INCLUDE(FindPythonLibs)
+
+INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH})
+ADD_GEMRB_PLUGIN (GUIScript GUIScript.cpp PythonHelpers.cpp)
+TARGET_LINK_LIBRARIES(GUIScript ${PYTHON_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})
diff --git a/gemrb/plugins/GUIScript/GUIScript.cpp b/gemrb/plugins/GUIScript/GUIScript.cpp
new file mode 100644
index 0000000..f44184c
--- /dev/null
+++ b/gemrb/plugins/GUIScript/GUIScript.cpp
@@ -0,0 +1,10680 @@
+/* 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 "GUIScript.h"
+
+#include "PythonHelpers.h"
+
+#include "Audio.h"
+#include "ControlAnimation.h"
+#include "DataFileMgr.h"
+#include "DialogHandler.h"
+#include "DisplayMessage.h"
+#include "EffectQueue.h"
+#include "Game.h"
+#include "GameData.h"
+#include "ImageFactory.h"
+#include "ImageMgr.h"
+#include "Interface.h"
+#include "Item.h"
+#include "Map.h"
+#include "MusicMgr.h"
+#include "Palette.h"
+#include "PalettedImageMgr.h"
+#include "ResourceDesc.h"
+#include "SaveGameIterator.h"
+#include "Spell.h"
+#include "TileMap.h"
+#include "Video.h"
+#include "WorldMap.h"
+#include "GameScript/GSUtils.h" //checkvariable
+#include "GUI/Button.h"
+#include "GUI/GameControl.h"
+#include "GUI/Label.h"
+#include "GUI/MapControl.h"
+#include "GUI/TextEdit.h"
+#include "GUI/WorldMapControl.h"
+#include "Scriptable/Container.h"
+#include "Scriptable/Door.h"
+#include "Scriptable/InfoPoint.h"
+#include "System/VFS.h"
+
+#include <cstdio>
+
+GUIScript *gs = NULL;
+
+//this stuff is missing from Python 2.2
+#ifndef PyDoc_VAR
+#define PyDoc_VAR(name) static char name[]
+#endif
+
+#ifndef PyDoc_STR
+# ifdef WITH_DOC_STRINGS
+# define PyDoc_STR(str) str
+# else
+# define PyDoc_STR(str) ""
+# endif
+#endif
+
+#ifndef PyDoc_STRVAR
+#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str)
+#endif
+
+// a shorthand for declaring methods in method table
+#define METHOD(name, args) {#name, GemRB_ ## name, args, GemRB_ ## name ## __doc}
+
+static int SpecialItemsCount = -1;
+static int StoreSpellsCount = -1;
+static int UsedItemsCount = -1;
+static int ItemSoundsCount = -1;
+
+//Check removal/equip/swap of item based on item name and actor's scriptname
+#define CRI_REMOVE 0
+#define CRI_EQUIP 1
+#define CRI_SWAP 2
+
+//bit used in SetCreatureStat to access some fields
+#define EXTRASETTINGS 0x1000
+
+struct UsedItemType {
+ ieResRef itemname;
+ ieVariable username; //death variable
+ ieStrRef value;
+ int flags;
+};
+
+typedef char EventNameType[17];
+#define IS_DROP 0
+#define IS_GET 1
+
+typedef ieResRef ResRefPairs[2];
+
+#define UNINIT_IEDWORD 0xcccccccc
+
+static SpellDescType *SpecialItems = NULL;
+
+static SpellDescType *StoreSpells = NULL;
+static ItemExtHeader *ItemArray = NULL;
+static SpellExtHeader *SpellArray = NULL;
+static UsedItemType *UsedItems = NULL;
+static ResRefPairs *ItemSounds = NULL;
+
+static int ReputationIncrease[20]={(int) UNINIT_IEDWORD};
+static int ReputationDonation[20]={(int) UNINIT_IEDWORD};
+//4 action button indices are packed on a single ieDword, there are 32 actions max.
+//there are additional fake action buttons
+static ieDword GUIAction[MAX_ACT_COUNT]={UNINIT_IEDWORD};
+static ieDword GUITooltip[MAX_ACT_COUNT]={UNINIT_IEDWORD};
+static ieResRef GUIResRef[MAX_ACT_COUNT];
+static EventNameType GUIEvent[MAX_ACT_COUNT];
+static bool QuitOnError = false;
+
+// Natural screen size of currently loaded winpack
+static int CHUWidth = 0;
+static int CHUHeight = 0;
+
+static EffectRef fx_learn_spell_ref = { "Spell:Learn", -1 };
+
+// Like PyString_FromString(), but for ResRef
+inline PyObject* PyString_FromResRef(char* ResRef)
+{
+ unsigned int i = strnlen(ResRef,sizeof(ieResRef));
+ return PyString_FromStringAndSize( ResRef, i );
+}
+
+// Like PyString_FromString(), but for ResRef
+inline PyObject* PyString_FromAnimID(const char* AnimID)
+{
+ unsigned int i = strnlen(AnimID,2);
+ return PyString_FromStringAndSize( AnimID, i );
+}
+
+/* Sets RuntimeError exception and returns NULL, so this function
+ * can be called in `return'.
+ */
+inline PyObject* RuntimeError(const char* msg)
+{
+ printMessage( "GUIScript", "Runtime Error:\n", LIGHT_RED );
+ PyErr_SetString( PyExc_RuntimeError, msg );
+ if (QuitOnError) {
+ core->Quit();
+ }
+ return NULL;
+}
+
+/* Prints error msg for invalid function parameters and also the function's
+ * doc string (given as an argument). Then returns NULL, so this function
+ * can be called in `return'. The exception should be set by previous
+ * call to e.g. PyArg_ParseTuple()
+ */
+inline PyObject* AttributeError(const char* doc_string)
+{
+ printMessage( "GUIScript", "Syntax Error:\n", LIGHT_RED );
+ PyErr_SetString(PyExc_AttributeError, doc_string);
+ if (QuitOnError) {
+ core->Quit();
+ }
+ return NULL;
+}
+
+#define GET_GAME() \
+ Game *game = core->GetGame(); \
+ if (!game) { \
+ return RuntimeError( "No game loaded!\n" ); \
+ }
+
+#define GET_MAP() \
+ Map *map = game->GetCurrentArea(); \
+ if (!map) { \
+ return RuntimeError( "No current area!" ); \
+ }
+
+#define GET_GAMECONTROL() \
+ GameControl *gc = core->GetGameControl(); \
+ if (!gc) { \
+ return RuntimeError("Can't find GameControl!"); \
+ }
+
+inline Control *GetControl( int wi, int ci, int ct)
+{
+ char errorbuffer[256];
+
+ Window* win = core->GetWindow( wi );
+ if (win == NULL) {
+ snprintf(errorbuffer, sizeof(errorbuffer), "Cannot find window index #%d (unloaded?)", wi);
+ RuntimeError(errorbuffer);
+ return NULL;
+ }
+ Control* ctrl = win->GetControl( ci );
+ if (!ctrl) {
+ snprintf(errorbuffer, sizeof(errorbuffer), "Cannot find control #%d", ci);
+ RuntimeError(errorbuffer);
+ return NULL;
+ }
+ if ((ct>=0) && (ctrl->ControlType != ct)) {
+ snprintf(errorbuffer, sizeof(errorbuffer), "Invalid control type: %d!=%d", ctrl->ControlType, ct);
+ RuntimeError(errorbuffer);
+ return NULL;
+ }
+ return ctrl;
+}
+
+//sets tooltip with Fx key prepended
+static inline void SetFunctionTooltip(int WindowIndex, int ControlIndex, char *txt, int Function)
+{
+ if (txt) {
+ if (txt[0]) {
+ char *txt2 = (char *) malloc(strlen(txt)+10);
+ if (Function) {
+ sprintf(txt2,"F%d - %s",Function,txt);
+ } else {
+ sprintf(txt2,"F%d - %s",ControlIndex+1,txt);
+ }
+ core->FreeString(txt);
+ core->SetTooltip((ieWord) WindowIndex, (ieWord) ControlIndex, txt2);
+ free (txt2);
+ return;
+ }
+ core->FreeString(txt);
+ }
+ core->SetTooltip((ieWord) WindowIndex, (ieWord) ControlIndex, "");
+}
+
+static void ReadItemSounds()
+{
+ int table = gamedata->LoadTable( "itemsnd" );
+ if (table<0) {
+ ItemSoundsCount = 0;
+ ItemSounds = NULL;
+ return;
+ }
+ Holder<TableMgr> tab = gamedata->GetTable( table );
+ ItemSoundsCount = tab->GetRowCount();
+ ItemSounds = (ResRefPairs *) malloc( sizeof(ResRefPairs)*ItemSoundsCount);
+ for (int i = 0; i < ItemSoundsCount; i++) {
+ strnlwrcpy(ItemSounds[i][0], tab->QueryField(i,0), 8);
+ strnlwrcpy(ItemSounds[i][1], tab->QueryField(i,1), 8);
+ }
+ gamedata->DelTable( table );
+}
+
+static void GetItemSound(ieResRef &Sound, ieDword ItemType, const char *ID, ieDword Col)
+{
+ Sound[0]=0;
+ if (Col>1) {
+ return;
+ }
+
+ if (ItemSoundsCount<0) {
+ ReadItemSounds();
+ }
+
+ if (ID[1]=='A') {
+ //the last 4 item sounds are used for '1A', '2A', '3A' and '4A'
+ //item animation types
+ ItemType = ItemSoundsCount-4+ID[0]-'1';
+ }
+
+ if (ItemType>=(ieDword) ItemSoundsCount) {
+ return;
+ }
+ strnlwrcpy(Sound, ItemSounds[ItemType][Col], 8);
+}
+
+static int GetCreatureStrRef(Actor *actor, unsigned int Str)
+{
+ return actor->StrRefs[Str];
+}
+
+static inline bool CheckStat(Actor * actor, ieDword stat, ieDword value, int op)
+{
+ int dc = DiffCore(actor->GetBase(stat), value, op);
+ return dc;
+}
+
+static int GetCreatureStat(Actor *actor, unsigned int StatID, int Mod)
+{
+ //this is a hack, if more PCStats fields are needed, improve it
+ if (StatID&EXTRASETTINGS) {
+ PCStatsStruct *ps = actor->PCStats;
+ if (!ps) {
+ //the official invalid value in GetStat
+ return 0xdadadada;
+ }
+ StatID&=15;
+ return ps->ExtraSettings[StatID];
+ }
+ if (Mod) {
+ return actor->GetStat( StatID );
+ }
+ return actor->GetBase( StatID );
+}
+
+static int SetCreatureStat(Actor *actor, unsigned int StatID, int StatValue, bool pcf)
+{
+ //this is a hack, if more PCStats fields are needed, improve it
+ if (StatID&EXTRASETTINGS) {
+ PCStatsStruct *ps = actor->PCStats;
+ if (!ps) {
+ return 0;
+ }
+ StatID&=15;
+ ps->ExtraSettings[StatID] = StatValue;
+ return 1;
+ }
+
+ if (pcf) {
+ actor->SetBase( StatID, StatValue );
+ } else {
+ actor->SetBaseNoPCF( StatID, StatValue );
+ }
+ actor->CreateDerivedStats();
+ return 1;
+}
+
+/* create an item entry
+ TODO: this code snippet exists in many copies, maybe consolidate */
+static CREItem *CreateCreItem(const char *ItemResRef, int Charge0, int Charge1, int Charge2)
+{
+ CREItem *TmpItem = new CREItem();
+ strnlwrcpy(TmpItem->ItemResRef, ItemResRef, 8);
+ TmpItem->Expired=0;
+ TmpItem->Usages[0]=(ieWord) Charge0;
+ TmpItem->Usages[1]=(ieWord) Charge1;
+ TmpItem->Usages[2]=(ieWord) Charge2;
+ TmpItem->Flags=0;
+ if (core->ResolveRandomItem(TmpItem) && gamedata->Exists(TmpItem->ItemResRef, IE_ITM_CLASS_ID)) {
+ return TmpItem;
+ }
+ /* item couldn't be resolved */
+ delete TmpItem;
+ return NULL;
+}
+
+PyDoc_STRVAR( GemRB_SetInfoTextColor__doc,
+"SetInfoTextColor(red, green, blue, [alpha])\n\n"
+"Sets the color of Floating Messages in GameControl." );
+
+static PyObject* GemRB_SetInfoTextColor(PyObject*, PyObject* args)
+{
+ int r,g,b,a;
+
+ if (!PyArg_ParseTuple( args, "iii|i", &r, &g, &b, &a)) {
+ return AttributeError( GemRB_SetInfoTextColor__doc );
+ }
+ Color c = {r,g,b,a};
+ core->SetInfoTextColor( c );
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_UnhideGUI__doc,
+"UnhideGUI()\n\n"
+"Shows the Game GUI and redraws windows." );
+
+static PyObject* GemRB_UnhideGUI(PyObject*, PyObject* /*args*/)
+{
+ //this is not the usual gc retrieval
+ GameControl* gc = (GameControl *) GetControl( 0, 0, IE_GUI_GAMECONTROL);
+ if (!gc) {
+ return RuntimeError("No gamecontrol!");
+ }
+ gc->UnhideGUI();
+ //this enables mouse in dialogs, which is wrong
+ //gc->SetCutSceneMode( false );
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_HideGUI__doc,
+"HideGUI()=>returns 1 if it did something\n\n"
+"Hides the Game GUI." );
+
+static PyObject* GemRB_HideGUI(PyObject*, PyObject* /*args*/)
+{
+ //it is no problem if the gamecontrol couldn't be found here?
+ GameControl* gc = (GameControl *) GetControl( 0, 0, IE_GUI_GAMECONTROL);
+ if (!gc) {
+ return PyInt_FromLong( 0 );
+ }
+ int ret = gc->HideGUI();
+
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_GetGameString__doc,
+"GetGameString(Index) => string\n\n"
+"Returns various string attributes of the Game object, see the docs.");
+
+static PyObject* GemRB_GetGameString(PyObject*, PyObject* args)
+{
+ int Index;
+
+ if (!PyArg_ParseTuple( args, "i", &Index )) {
+ return AttributeError( GemRB_GetGameString__doc );
+ }
+ switch(Index&0xf0) {
+ case 0: //game strings
+ Game *game = core->GetGame();
+ if (!game) {
+ return PyString_FromString("");
+ }
+ switch(Index&15) {
+ case 0:
+ return PyString_FromString( game->LoadMos );
+ case 1:
+ return PyString_FromString( game->CurrentArea );
+ }
+ }
+
+ return AttributeError( GemRB_GetGameString__doc );
+}
+
+PyDoc_STRVAR( GemRB_LoadGame__doc,
+"LoadGame(Index [,version] )\n\n"
+"Loads and enters the Game." );
+
+static PyObject* GemRB_LoadGame(PyObject*, PyObject* args)
+{
+ PyObject *obj;
+ int VersionOverride = 0;
+
+ if (!PyArg_ParseTuple( args, "O|i", &obj, &VersionOverride )) {
+ return AttributeError( GemRB_LoadGame__doc );
+ }
+ CObject<SaveGame> save(obj);
+ core->SetupLoadGame(save, VersionOverride);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_EnterGame__doc,
+"EnterGame()\n\n"
+"Starts new game and enters it." );
+
+static PyObject* GemRB_EnterGame(PyObject*, PyObject* /*args*/)
+{
+ core->QuitFlag|=QF_ENTERGAME;
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_QuitGame__doc,
+"QuitGame()\n\n"
+"Stops the current game.");
+static PyObject* GemRB_QuitGame(PyObject*, PyObject* /*args*/)
+{
+ core->QuitFlag=QF_QUITGAME;
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_TextArea_MoveText__doc,
+"MoveTAText(srcWin, srcCtrl, dstWin, dstCtrl)\n\n"
+"Copies a TextArea content to another.");
+
+static PyObject* GemRB_TextArea_MoveText(PyObject * /*self*/, PyObject* args)
+{
+ int srcWin, srcCtrl, dstWin, dstCtrl;
+
+ if (!PyArg_ParseTuple( args, "iiii", &srcWin, &srcCtrl, &dstWin, &dstCtrl )) {
+ return AttributeError( GemRB_TextArea_MoveText__doc );
+ }
+
+ TextArea* SrcTA = ( TextArea* ) GetControl( srcWin, srcCtrl, IE_GUI_TEXTAREA);
+ if (!SrcTA) {
+ return NULL;
+ }
+
+ TextArea* DstTA = ( TextArea* ) GetControl( dstWin, dstCtrl, IE_GUI_TEXTAREA);
+ if (!DstTA) {
+ return NULL;
+ }
+
+ SrcTA->CopyTo( DstTA );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_TextArea_Rewind__doc,
+"RewindTA(Win, Ctrl, Ticks)\n\n"
+"Sets up a TextArea for scrolling. Ticks is the delay between the steps in scrolling.");
+
+static PyObject* GemRB_TextArea_Rewind(PyObject * /*self*/, PyObject* args)
+{
+ int Win, Ctrl, Ticks;
+
+ if (!PyArg_ParseTuple( args, "iii", &Win, &Ctrl, &Ticks)) {
+ return AttributeError( GemRB_TextArea_Rewind__doc );
+ }
+
+ TextArea* ctrl = ( TextArea* ) GetControl( Win, Ctrl, IE_GUI_TEXTAREA);
+ if (!ctrl) {
+ return NULL;
+ }
+
+ ctrl->SetupScroll(Ticks);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_TextArea_SetHistory__doc,
+"SetTAHistory(Win, Ctrl, KeepLines)\n\n"
+"Sets up a TextArea to expire scrolled out lines.");
+
+static PyObject* GemRB_TextArea_SetHistory(PyObject * /*self*/, PyObject* args)
+{
+ int Win, Ctrl, Keep;
+
+ if (!PyArg_ParseTuple( args, "iii", &Win, &Ctrl, &Keep)) {
+ return AttributeError( GemRB_TextArea_SetHistory__doc );
+ }
+
+ TextArea* ctrl = ( TextArea* ) GetControl( Win, Ctrl, IE_GUI_TEXTAREA);
+ if (!ctrl) {
+ return NULL;
+ }
+
+ ctrl->SetPreservedRow(Keep);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_StatComment__doc,
+"StatComment(Strref, X, Y) => string\n\n"
+"Replaces values X and Y into an strref in place of %%d." );
+
+static PyObject* GemRB_StatComment(PyObject * /*self*/, PyObject* args)
+{
+ ieStrRef Strref;
+ int X, Y;
+ PyObject* ret;
+
+ if (!PyArg_ParseTuple( args, "iii", &Strref, &X, &Y )) {
+ return AttributeError( GemRB_StatComment__doc );
+ }
+ char* text = core->GetString( Strref );
+ size_t bufflen = strlen( text ) + 12;
+ if (bufflen<12) {
+ return AttributeError( GemRB_StatComment__doc );
+ }
+ char* newtext = ( char* ) malloc( bufflen );
+ //this could be DANGEROUS, not anymore (snprintf is your friend)
+ snprintf( newtext, bufflen, text, X, Y );
+ core->FreeString( text );
+ ret = PyString_FromString( newtext );
+ free( newtext );
+ return ret;
+}
+
+PyDoc_STRVAR( GemRB_GetString__doc,
+"GetString(strref[,flags]) => string\n\n"
+"Returns string for given strref. " );
+
+static PyObject* GemRB_GetString(PyObject * /*self*/, PyObject* args)
+{
+ ieStrRef strref;
+ int flags = 0;
+ PyObject* ret;
+
+ if (!PyArg_ParseTuple( args, "i|i", &strref, &flags )) {
+ return AttributeError( GemRB_GetString__doc );
+ }
+
+ char *text = core->GetString( strref, flags );
+ ret=PyString_FromString( text );
+ core->FreeString( text );
+ return ret;
+}
+
+PyDoc_STRVAR( GemRB_EndCutSceneMode__doc,
+"EndCutSceneMode()\n\n"
+"Exits the CutScene Mode." );
+
+static PyObject* GemRB_EndCutSceneMode(PyObject * /*self*/, PyObject* /*args*/)
+{
+ core->SetCutSceneMode( false );
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_LoadWindowPack__doc,
+"LoadWindowPack(CHUIResRef, [Width=0, Height=0])\n\n"
+"Loads a WindowPack into the Window Manager Module. "
+"Width and Height set winpack's natural screen size if nonzero." );
+
+static PyObject* GemRB_LoadWindowPack(PyObject * /*self*/, PyObject* args)
+{
+ char* string;
+ int width = 0, height = 0;
+
+ if (!PyArg_ParseTuple( args, "s|ii", &string, &width, &height )) {
+ return AttributeError( GemRB_LoadWindowPack__doc );
+ }
+
+ if (!core->LoadWindowPack( string )) {
+ return RuntimeError("Can't find resource");
+ }
+
+ CHUWidth = width;
+ CHUHeight = height;
+
+ if ( (width && (width>core->Width)) ||
+ (height && (height>core->Height)) ) {
+ printMessage("GUIScript","Screen is too small!\n",LIGHT_RED);
+ printf("This window requires %d x %d resolution.\n",width,height);
+ return RuntimeError("Please change your settings.");
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_LoadWindow__doc,
+"LoadWindow(WindowID) => WindowIndex\n\n"
+"Returns a Window." );
+
+static PyObject* GemRB_LoadWindow(PyObject * /*self*/, PyObject* args)
+{
+ int WindowID;
+
+ if (!PyArg_ParseTuple( args, "i", &WindowID )) {
+ return AttributeError( GemRB_LoadWindow__doc );
+ }
+
+ int ret = core->LoadWindow( WindowID );
+ if (ret == -1) {
+ char buf[256];
+ snprintf( buf, sizeof( buf ), "Can't find window #%d!", WindowID );
+ return RuntimeError(buf);
+ }
+
+ // If the current winpack windows are placed for screen resolution
+ // other than the current one, reposition them
+ Window* win = core->GetWindow( ret );
+ if (CHUWidth && CHUWidth != core->Width)
+ win->XPos += (core->Width - CHUWidth) / 2;
+ if (CHUHeight && CHUHeight != core->Height)
+ win->YPos += (core->Height - CHUHeight) / 2;
+
+ return gs->ConstructObject("Window", ret);
+}
+
+PyDoc_STRVAR( GemRB_Window_SetSize__doc,
+"SetWindowSize(WindowIndex, Width, Height)\n\n"
+"Resizes a Window.");
+
+static PyObject* GemRB_Window_SetSize(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, Width, Height;
+
+ if (!PyArg_ParseTuple( args, "iii", &WindowIndex, &Width, &Height )) {
+ return AttributeError( GemRB_Window_SetSize__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!\n");
+ }
+
+ win->Width = Width;
+ win->Height = Height;
+ win->Invalidate();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_SetFrame__doc,
+"SetWindowFrame(WindowIndex)\n\n"
+"Sets Window frame used to fill screen on higher resolutions.");
+
+static PyObject* GemRB_Window_SetFrame(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex;
+
+ if (!PyArg_ParseTuple( args, "i", &WindowIndex )) {
+ return AttributeError( GemRB_Window_SetFrame__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!\n");
+ }
+
+ win->SetFrame();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_LoadWindowFrame__doc,
+"LoadWindowFrame(MOSResRef_Left, MOSResRef_Right, MOSResRef_Top, MOSResRef_Bottom))\n\n"
+"Load the parts of window frame used to decorate windows on higher resolutions." );
+
+static PyObject* GemRB_LoadWindowFrame(PyObject * /*self*/, PyObject* args)
+{
+ char* ResRef[4];
+
+ if (!PyArg_ParseTuple( args, "ssss", &ResRef[0], &ResRef[1], &ResRef[2], &ResRef[3] )) {
+ return AttributeError( GemRB_LoadWindowFrame__doc );
+ }
+
+
+ for (int i = 0; i < 4; i++) {
+ if (ResRef[i] == 0) {
+ return AttributeError( GemRB_LoadWindowFrame__doc );
+ }
+
+ ResourceHolder<ImageMgr> im(ResRef[i]);
+ if (im == NULL) {
+ return NULL;
+ }
+
+ Sprite2D* Picture = im->GetSprite2D();
+ if (Picture == NULL) {
+ return NULL;
+ }
+
+ // FIXME: delete previous WindowFrames
+ //core->WindowFrames[i] = Picture;
+ core->SetWindowFrame(i, Picture);
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_EnableCheatKeys__doc,
+"EnableCheatKeys(flag)\n\n"
+"Sets CheatFlags." );
+
+static PyObject* GemRB_EnableCheatKeys(PyObject * /*self*/, PyObject* args)
+{
+ int Flag;
+
+ if (!PyArg_ParseTuple( args, "i", &Flag )) {
+ return AttributeError( GemRB_EnableCheatKeys__doc );
+ }
+
+ core->EnableCheatKeys( Flag );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_SetPicture__doc,
+"SetWindowPicture(WindowIndex, MosResRef)\n\n"
+"Changes the background of a Window." );
+
+static PyObject* GemRB_Window_SetPicture(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex;
+ char* MosResRef;
+
+ if (!PyArg_ParseTuple( args, "is", &WindowIndex, &MosResRef )) {
+ return AttributeError( GemRB_Window_SetPicture__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!\n");
+ }
+
+ ResourceHolder<ImageMgr> mos(MosResRef);
+ if (mos != NULL) {
+ win->SetBackGround( mos->GetSprite2D(), true );
+ }
+ win->Invalidate();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_SetPos__doc,
+"SetWindowPos(WindowIndex, X, Y, [Flags=WINDOW_TOPLEFT])\n\n"
+"Moves a Window to pos. (X, Y).\n"
+"Flags is a bitmask of WINDOW_(TOPLEFT|CENTER|ABSCENTER|RELATIVE|SCALE|BOUNDED) and "
+"they are used to modify the meaning of X and Y.\n"
+"TOPLEFT: X, Y are coordinates of upper-left corner.\n"
+"CENTER: X, Y are coordinates of window's center.\n"
+"ABSCENTER: window is placed at screen center, moved by X, Y.\n"
+"RELATIVE: window is moved by X, Y.\n"
+"SCALE: window is moved by diff of screen size and X, Y, divided by 2.\n"
+"BOUNDED: the window is kept within screen boundaries." );
+
+static PyObject* GemRB_Window_SetPos(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, X, Y, Flags = WINDOW_TOPLEFT;
+
+ if (!PyArg_ParseTuple( args, "iii|i", &WindowIndex, &X, &Y, &Flags )) {
+ return AttributeError( GemRB_Window_SetPos__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!\n");
+ }
+
+ if (Flags & WINDOW_CENTER) {
+ X -= win->Width / 2;
+ Y -= win->Height / 2;
+ }
+ else if (Flags & WINDOW_ABSCENTER) {
+ X += (core->Width - win->Width) / 2;
+ Y += (core->Height - win->Height) / 2;
+ }
+ else if (Flags & WINDOW_RELATIVE) {
+ X += win->XPos;
+ Y += win->YPos;
+ }
+ else if (Flags & WINDOW_SCALE) {
+ X = win->XPos + (core->Width - X) / 2;
+ Y = win->YPos + (core->Height - Y) / 2;
+ }
+
+ // Keep window within screen
+ // FIXME: keep it within gamecontrol
+ if (Flags & WINDOW_BOUNDED) {
+ // FIXME: grrrr, should be < 0!!!
+ if (X > 32767 || X < 0)
+ X = 0;
+ if (Y > 32767 || Y < 0)
+ Y = 0;
+
+ if (X + win->Width >= core->Width)
+ X = core->Width - win->Width;
+ if (Y + win->Height >= core->Height)
+ Y = core->Height - win->Height;
+ }
+
+ win->XPos = X;
+ win->YPos = Y;
+ win->Invalidate();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_LoadTable__doc,
+"LoadTable(2DAResRef, [ignore_error=0]) => GTable\n\n"
+"Loads a 2DA Table." );
+
+static PyObject* GemRB_LoadTable(PyObject * /*self*/, PyObject* args)
+{
+ char* tablename;
+ int noerror = 0;
+
+ if (!PyArg_ParseTuple( args, "s|i", &tablename, &noerror )) {
+ return AttributeError( GemRB_LoadTable__doc );
+ }
+
+ int ind = gamedata->LoadTable( tablename );
+ if (!noerror && ind == -1) {
+ return RuntimeError("Can't find resource");
+ }
+ return gs->ConstructObject("Table", ind);
+}
+
+PyDoc_STRVAR( GemRB_Table_Unload__doc,
+"UnloadTable(TableIndex)\n\n"
+"Unloads a 2DA Table." );
+
+static PyObject* GemRB_Table_Unload(PyObject * /*self*/, PyObject* args)
+{
+ int ti;
+
+ if (!PyArg_ParseTuple( args, "i", &ti )) {
+ return AttributeError( GemRB_Table_Unload__doc );
+ }
+
+ int ind = gamedata->DelTable( ti );
+ if (ind == -1) {
+ return RuntimeError("Can't find resource");
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Table_GetValue__doc,
+"GetTableValue(TableIndex, RowIndex/RowString, ColIndex/ColString, type) => value\n\n"
+"Returns a field of a 2DA Table. If Type is omitted the return type is the autodetected, "
+"otherwise 0 means string, 1 means integer, 2 means stat symbol translation." );
+
+static PyObject* GemRB_Table_GetValue(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* ti, * row, * col;
+ PyObject* type = NULL;
+ int which = -1;
+
+ if (!PyArg_UnpackTuple( args, "ref", 3, 4, &ti, &row, &col, &type )) {
+ return AttributeError( GemRB_Table_GetValue__doc );
+ }
+ if (type!=NULL) {
+ if (!PyObject_TypeCheck( type, &PyInt_Type )) {
+ return AttributeError( GemRB_Table_GetValue__doc );
+ }
+ which = PyInt_AsLong( type );
+ }
+
+ if (!PyObject_TypeCheck( ti, &PyInt_Type )) {
+ return AttributeError( GemRB_Table_GetValue__doc );
+ }
+ long TableIndex = PyInt_AsLong( ti );
+ if (( !PyObject_TypeCheck( row, &PyInt_Type ) ) &&
+ ( !PyObject_TypeCheck( row, &PyString_Type ) )) {
+ return AttributeError( GemRB_Table_GetValue__doc );
+ }
+ if (( !PyObject_TypeCheck( col, &PyInt_Type ) ) &&
+ ( !PyObject_TypeCheck( col, &PyString_Type ) )) {
+ return AttributeError( GemRB_Table_GetValue__doc );
+ }
+ if (PyObject_TypeCheck( row, &PyInt_Type ) &&
+ ( !PyObject_TypeCheck( col, &PyInt_Type ) )) {
+ printMessage( "GUIScript",
+ "Type Error: RowIndex/RowString and ColIndex/ColString must be the same type\n",
+ LIGHT_RED );
+ return NULL;
+ }
+ if (PyObject_TypeCheck( row, &PyString_Type ) &&
+ ( !PyObject_TypeCheck( col, &PyString_Type ) )) {
+ printMessage( "GUIScript",
+ "Type Error: RowIndex/RowString and ColIndex/ColString must be the same type\n",
+ LIGHT_RED );
+ return NULL;
+ }
+ Holder<TableMgr> tm = gamedata->GetTable( TableIndex );
+ if (!tm) {
+ return RuntimeError("Can't find resource");
+ }
+ const char* ret;
+ if (PyObject_TypeCheck( row, &PyString_Type )) {
+ char* rows = PyString_AsString( row );
+ char* cols = PyString_AsString( col );
+ ret = tm->QueryField( rows, cols );
+ } else {
+ long rowi = PyInt_AsLong( row );
+ long coli = PyInt_AsLong( col );
+ ret = tm->QueryField( rowi, coli );
+ }
+ if (ret == NULL)
+ return NULL;
+
+ long val;
+ //if which = 0, then return string
+ if (!which) {
+ return PyString_FromString( ret );
+ }
+ //if which = 1 then return number
+ //if which = -1 (omitted) then return the best format
+ if (valid_number( ret, val ) || (which==1) ) {
+ return PyInt_FromLong( val );
+ }
+ if (which==2) {
+ val = core->TranslateStat(ret);
+ return PyInt_FromLong( val );
+ }
+ return PyString_FromString( ret );
+}
+
+PyDoc_STRVAR( GemRB_Table_FindValue__doc,
+"FindTableValue(TableIndex, ColumnIndex, Value[, StartRow]) => Row\n\n"
+"Returns the first rowcount of a field of a 2DA Table." );
+
+static PyObject* GemRB_Table_FindValue(PyObject * /*self*/, PyObject* args)
+{
+ int ti, col;
+ int start = 0;
+ long Value;
+
+ if (!PyArg_ParseTuple( args, "iil|i", &ti, &col, &Value, &start )) {
+ return AttributeError( GemRB_Table_FindValue__doc );
+ }
+
+ Holder<TableMgr> tm = gamedata->GetTable( ti );
+ if (tm == NULL) {
+ return RuntimeError("Can't find resource");
+ }
+ return PyInt_FromLong(tm->FindTableValue(col, Value, start));
+}
+
+PyDoc_STRVAR( GemRB_Table_GetRowIndex__doc,
+"GetTableRowIndex(TableIndex, RowName) => Row\n\n"
+"Returns the Index of a Row in a 2DA Table." );
+
+static PyObject* GemRB_Table_GetRowIndex(PyObject * /*self*/, PyObject* args)
+{
+ int ti;
+ char* rowname;
+
+ if (!PyArg_ParseTuple( args, "is", &ti, &rowname )) {
+ return AttributeError( GemRB_Table_GetRowIndex__doc );
+ }
+
+ Holder<TableMgr> tm = gamedata->GetTable( ti );
+ if (tm == NULL) {
+ return RuntimeError("Can't find resource");
+ }
+ int row = tm->GetRowIndex( rowname );
+ //no error if the row doesn't exist
+ return PyInt_FromLong( row );
+}
+
+PyDoc_STRVAR( GemRB_Table_GetRowName__doc,
+"GetTableRowName(TableIndex, RowIndex) => string\n\n"
+"Returns the Name of a Row in a 2DA Table." );
+
+static PyObject* GemRB_Table_GetRowName(PyObject * /*self*/, PyObject* args)
+{
+ int ti, row;
+
+ if (!PyArg_ParseTuple( args, "ii", &ti, &row )) {
+ return AttributeError( GemRB_Table_GetRowName__doc );
+ }
+
+ Holder<TableMgr> tm = gamedata->GetTable( ti );
+ if (tm == NULL) {
+ return RuntimeError("Can't find resource");
+ }
+ const char* str = tm->GetRowName( row );
+ if (str == NULL) {
+ return NULL;
+ }
+
+ return PyString_FromString( str );
+}
+
+PyDoc_STRVAR( GemRB_Table_GetColumnIndex__doc,
+"GetTableColumnIndex(TableIndex, ColumnName) => Column\n\n"
+"Returns the Index of a Column in a 2DA Table." );
+
+static PyObject* GemRB_Table_GetColumnIndex(PyObject * /*self*/, PyObject* args)
+{
+ int ti;
+ char* colname;
+
+ if (!PyArg_ParseTuple( args, "is", &ti, &colname )) {
+ return AttributeError( GemRB_Table_GetColumnIndex__doc );
+ }
+
+ Holder<TableMgr> tm = gamedata->GetTable( ti );
+ if (tm == NULL) {
+ return RuntimeError("Can't find resource");
+ }
+ int col = tm->GetColumnIndex( colname );
+ //no error if the column doesn't exist
+ return PyInt_FromLong( col );
+}
+
+PyDoc_STRVAR( GemRB_Table_GetColumnName__doc,
+"GetTableColumnName(TableIndex, ColumnIndex) => string\n\n"
+"Returns the Name of a Column in a 2DA Table." );
+
+static PyObject* GemRB_Table_GetColumnName(PyObject * /*self*/, PyObject* args)
+{
+ int ti, col;
+
+ if (!PyArg_ParseTuple( args, "ii", &ti, &col )) {
+ return AttributeError( GemRB_Table_GetColumnName__doc );
+ }
+
+ Holder<TableMgr> tm = gamedata->GetTable( ti );
+ if (tm == NULL) {
+ return RuntimeError("Can't find resource");
+ }
+ const char* str = tm->GetColumnName( col );
+ if (str == NULL) {
+ return NULL;
+ }
+
+ return PyString_FromString( str );
+}
+
+PyDoc_STRVAR( GemRB_Table_GetRowCount__doc,
+"GetTableRowCount(TableIndex) => RowCount\n\n"
+"Returns the number of rows in a 2DA Table." );
+
+static PyObject* GemRB_Table_GetRowCount(PyObject * /*self*/, PyObject* args)
+{
+ int ti;
+
+ if (!PyArg_ParseTuple( args, "i", &ti )) {
+ return AttributeError( GemRB_Table_GetRowCount__doc );
+ }
+
+ Holder<TableMgr> tm = gamedata->GetTable( ti );
+ if (tm == NULL) {
+ return RuntimeError("Can't find resource");
+ }
+
+ return PyInt_FromLong( tm->GetRowCount() );
+}
+
+PyDoc_STRVAR( GemRB_Table_GetColumnCount__doc,
+"GetTableColumnCount(TableIndex[, Row]) => ColumnCount\n\n"
+"Returns the number of columns in the given row of a 2DA Table. Row may be omitted." );
+
+static PyObject* GemRB_Table_GetColumnCount(PyObject * /*self*/, PyObject* args)
+{
+ int ti;
+ int row = 0;
+
+ if (!PyArg_ParseTuple( args, "i|i", &ti, &row )) {
+ return AttributeError( GemRB_Table_GetColumnCount__doc );
+ }
+
+ Holder<TableMgr> tm = gamedata->GetTable( ti );
+ if (tm == NULL) {
+ return RuntimeError("Can't find resource");
+ }
+
+ return PyInt_FromLong( tm->GetColumnCount(row) );
+}
+
+PyDoc_STRVAR( GemRB_LoadSymbol__doc,
+"LoadSymbol(IDSResRef) => SymbolIndex\n\n"
+"Loads an IDS Symbol Table." );
+
+static PyObject* GemRB_LoadSymbol(PyObject * /*self*/, PyObject* args)
+{
+ const char* string;
+
+ if (!PyArg_ParseTuple( args, "s", &string )) {
+ return AttributeError( GemRB_LoadSymbol__doc );
+ }
+
+ int ind = core->LoadSymbol( string );
+ if (ind == -1) {
+ return NULL;
+ }
+
+ return gs->ConstructObject("Symbol", ind);
+}
+
+PyDoc_STRVAR( GemRB_Symbol_Unload__doc,
+"UnloadSymbol(SymbolIndex)\n\n"
+"Unloads an IDS Symbol Table." );
+
+static PyObject* GemRB_Symbol_Unload(PyObject * /*self*/, PyObject* args)
+{
+ int si;
+
+ if (!PyArg_ParseTuple( args, "i", &si )) {
+ return AttributeError( GemRB_Symbol_Unload__doc );
+ }
+
+ int ind = core->DelSymbol( si );
+ if (ind == -1) {
+ return NULL;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Symbol_GetValue__doc,
+"GetSymbolValue(SymbolIndex, StringVal) => int\n"
+"GetSymbolValue(SymbolIndex, IntVal) => string\n\n"
+"Returns a field of an IDS Symbol Table." );
+
+static PyObject* GemRB_Symbol_GetValue(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* si, * sym;
+
+ if (PyArg_UnpackTuple( args, "ref", 2, 2, &si, &sym )) {
+ if (!PyObject_TypeCheck( si, &PyInt_Type )) {
+ return AttributeError( GemRB_Symbol_GetValue__doc );
+ }
+ long SymbolIndex = PyInt_AsLong( si );
+ if (PyObject_TypeCheck( sym, &PyString_Type )) {
+ char* syms = PyString_AsString( sym );
+ Holder<SymbolMgr> sm = core->GetSymbol( SymbolIndex );
+ if (!sm)
+ return NULL;
+ long val = sm->GetValue( syms );
+ return PyInt_FromLong( val );
+ }
+ if (PyObject_TypeCheck( sym, &PyInt_Type )) {
+ long symi = PyInt_AsLong( sym );
+ Holder<SymbolMgr> sm = core->GetSymbol( SymbolIndex );
+ if (!sm)
+ return NULL;
+ const char* str = sm->GetValue( symi );
+ return PyString_FromString( str );
+ }
+ }
+ return AttributeError( GemRB_Symbol_GetValue__doc );
+}
+
+PyDoc_STRVAR( GemRB_Window_GetControl__doc,
+"GetControlObject(WindowID, ControlID) => GControl, or\n"
+"Window.GetControl(ControlID) => GControl\n\n"
+"Returns a control as an object." );
+
+static PyObject* GemRB_Window_GetControl(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlID;
+
+ if (!PyArg_ParseTuple( args, "ii", &WindowIndex, &ControlID )) {
+ return AttributeError( GemRB_Window_GetControl__doc );
+ }
+
+ int ctrlindex = core->GetControl(WindowIndex, ControlID);
+ if (ctrlindex == -1) {
+ return RuntimeError( "Control is not found" );
+ }
+
+ PyObject* ctrltuple = PyTuple_New(2);
+ PyTuple_SET_ITEM(ctrltuple, 0, PyInt_FromLong(WindowIndex));
+ PyTuple_SET_ITEM(ctrltuple, 1, PyInt_FromLong(ctrlindex));
+
+ PyObject* ret = 0;
+ Control *ctrl = GetControl(WindowIndex, ctrlindex, -1);
+ if (!ctrl) {
+ return RuntimeError( "Control is not found" );
+ }
+ const char* type = "Control";
+ switch(ctrl->ControlType) {
+ case IE_GUI_LABEL:
+ type = "Label";
+ break;
+ case IE_GUI_EDIT:
+ type = "TextEdit";
+ break;
+ case IE_GUI_SCROLLBAR:
+ type = "ScrollBar";
+ break;
+ case IE_GUI_TEXTAREA:
+ type = "TextArea";
+ break;
+ case IE_GUI_BUTTON:
+ type = "Button";
+ break;
+ case IE_GUI_WORLDMAP:
+ type = "WorldMap";
+ break;
+ default:
+ break;
+ }
+ ret = gs->ConstructObject(type, ctrltuple);
+ Py_DECREF(ctrltuple);
+
+ if (!ret) {
+ char buf[256];
+ snprintf( buf, sizeof( buf ), "Couldn't construct Control object for control %d in window %d!", ControlID, WindowIndex );
+ return RuntimeError(buf);
+ }
+ return ret;
+}
+
+PyDoc_STRVAR( GemRB_Window_HasControl__doc,
+"HasControl(WindowIndex, ControlID[, ControlType]) => bool\n\n"
+"Returns true if the control exists." );
+
+static PyObject* GemRB_Window_HasControl(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlID;
+ int Type = -1;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &WindowIndex, &ControlID, &Type )) {
+ return AttributeError( GemRB_Window_HasControl__doc );
+ }
+ int ret = core->GetControl( WindowIndex, ControlID );
+ if (ret == -1) {
+ return PyInt_FromLong( 0 );
+ }
+
+ if (Type!=-1) {
+ Control *ctrl = GetControl(WindowIndex, ControlID, -1);
+ if (ctrl->ControlType!=Type) {
+ return PyInt_FromLong( 0 );
+ }
+ }
+ return PyInt_FromLong( 1 );
+}
+
+PyDoc_STRVAR( GemRB_Control_QueryText__doc,
+"QueryText(WindowIndex, ControlIndex) => string\n\n"
+"Returns the Text of a TextEdit control." );
+
+static PyObject* GemRB_Control_QueryText(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci;
+
+ if (!PyArg_ParseTuple( args, "ii", &wi, &ci )) {
+ return AttributeError( GemRB_Control_QueryText__doc );
+ }
+
+ Control *ctrl = GetControl(wi, ci, -1);
+ if (!ctrl) {
+ return NULL;
+ }
+ switch(ctrl->ControlType) {
+ case IE_GUI_LABEL:
+ return PyString_FromString(((Label *) ctrl)->QueryText() );
+ case IE_GUI_EDIT:
+ return PyString_FromString(((TextEdit *) ctrl)->QueryText() );
+ case IE_GUI_TEXTAREA:
+ return PyString_FromString(((TextArea *) ctrl)->QueryText() );
+ default:
+ return RuntimeError("Invalid control type");
+ }
+}
+
+PyDoc_STRVAR( GemRB_TextEdit_SetBufferLength__doc,
+"SetBufferLength(WindowIndex, ControlIndex, Length)\n\n"
+"Sets the maximum text length of a TextEdit Control. It cannot be more than 65535." );
+
+static PyObject* GemRB_TextEdit_SetBufferLength(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, Length;
+
+ if (!PyArg_ParseTuple( args, "iii", &WindowIndex, &ControlIndex, &Length)) {
+ return AttributeError( GemRB_TextEdit_SetBufferLength__doc );
+ }
+
+ TextEdit* te = (TextEdit *) GetControl( WindowIndex, ControlIndex, IE_GUI_EDIT );
+ if (!te)
+ return NULL;
+
+ if ((ieDword) Length>0xffff) {
+ return AttributeError( GemRB_Control_QueryText__doc );
+ }
+
+ te->SetBufferLength((ieWord) Length );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_TextArea_SelectText__doc,
+"SelectText(WindowIndex, ControlIndex, String|Strref) => void\n\n"
+"Tries to set the Variable of the TextArea control to the linenumber of the referenced string.");
+
+static PyObject* GemRB_TextArea_SelectText(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* wi, * ci, * str;
+ long WindowIndex, ControlIndex;
+ char* string;
+
+ if (!PyArg_UnpackTuple( args, "ref", 3, 3, &wi, &ci, &str )) {
+ return AttributeError( GemRB_TextArea_SelectText__doc );
+ }
+
+ if (!PyObject_TypeCheck( wi, &PyInt_Type ) ||
+ !PyObject_TypeCheck( ci, &PyInt_Type ) ||
+ ( !PyObject_TypeCheck( str, &PyString_Type ) &&
+ !PyObject_TypeCheck( str, &PyInt_Type ) )) {
+ return AttributeError( GemRB_TextArea_SelectText__doc );
+ }
+
+ WindowIndex = PyInt_AsLong( wi );
+ ControlIndex = PyInt_AsLong( ci );
+ if (PyObject_TypeCheck( str, &PyString_Type )) {
+ string = PyString_AsString( str );
+ if (string == NULL) {
+ return RuntimeError("Null string received");
+ }
+ TextArea* ta = (TextArea *) GetControl( WindowIndex, ControlIndex, IE_GUI_TEXTAREA );
+ if (!ta)
+ return NULL;
+ ta->SelectText( string );
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_SetText__doc,
+"SetText(WindowIndex, ControlIndex, String|Strref) => int\n\n"
+"Sets the Text of a control in a Window." );
+
+static PyObject* GemRB_Control_SetText(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* wi, * ci, * str;
+ long WindowIndex, ControlIndex, StrRef;
+ char* string;
+ int ret;
+
+ if (!PyArg_UnpackTuple( args, "ref", 3, 3, &wi, &ci, &str )) {
+ return AttributeError( GemRB_Control_SetText__doc );
+ }
+
+ if (!PyObject_TypeCheck( wi, &PyInt_Type ) ||
+ !PyObject_TypeCheck( ci, &PyInt_Type ) ||
+ ( !PyObject_TypeCheck( str, &PyString_Type ) &&
+ !PyObject_TypeCheck( str, &PyInt_Type ) )) {
+ return AttributeError( GemRB_Control_SetText__doc );
+ }
+
+ WindowIndex = PyInt_AsLong( wi );
+ ControlIndex = PyInt_AsLong( ci );
+ if (PyObject_TypeCheck( str, &PyString_Type )) {
+ string = PyString_AsString( str );
+ if (string == NULL) {
+ return RuntimeError("Null string received");
+ }
+ ret = core->SetText( (ieWord) WindowIndex, (ieWord) ControlIndex, string );
+ if (ret == -1) {
+ return RuntimeError("Cannot set text");
+ }
+ } else {
+ StrRef = PyInt_AsLong( str );
+ if (StrRef == -1) {
+ ret = core->SetText( (ieWord) WindowIndex, (ieWord) ControlIndex, GEMRB_STRING );
+ } else {
+ char *tmpstr = core->GetString( StrRef );
+ ret = core->SetText( (ieWord) WindowIndex, (ieWord) ControlIndex, tmpstr );
+ core->FreeString( tmpstr );
+ }
+ if (ret == -1) {
+ return RuntimeError("Cannot set text");
+ }
+ }
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_TextArea_Append__doc,
+"TextAreaAppend(WindowIndex, ControlIndex, String|Strref [, Row[, Flag]]) => int\n\n"
+"Appends the Text to the TextArea Control in the Window. "
+"If Row is given then it will insert the text after that row. "
+"If Flag is given, then it will use that value as a GetString flag.");
+
+static PyObject* GemRB_TextArea_Append(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* wi, * ci, * str;
+ PyObject* row = NULL;
+ PyObject* flag = NULL;
+ long WindowIndex, ControlIndex;
+ long StrRef, Row, Flag = 0;
+ char* string;
+ int ret;
+
+ if (!PyArg_UnpackTuple( args, "ref", 3, 5, &wi, &ci, &str, &row, &flag )) {
+ return AttributeError( GemRB_TextArea_Append__doc );
+ }
+ if (!PyObject_TypeCheck( wi, &PyInt_Type ) ||
+ !PyObject_TypeCheck( ci, &PyInt_Type ) ||
+ ( !PyObject_TypeCheck( str, &PyString_Type ) &&
+ !PyObject_TypeCheck( str, &PyInt_Type ) )) {
+ return AttributeError( GemRB_TextArea_Append__doc );
+ }
+ WindowIndex = PyInt_AsLong( wi );
+ ControlIndex = PyInt_AsLong( ci );
+
+ TextArea* ta = ( TextArea* ) GetControl( WindowIndex, ControlIndex, IE_GUI_TEXTAREA);
+ if (!ta) {
+ return NULL;
+ }
+ if (row) {
+ if (!PyObject_TypeCheck( row, &PyInt_Type )) {
+ printMessage( "GUIScript",
+ "Syntax Error: AppendText row must be integer\n", LIGHT_RED );
+ return NULL;
+ }
+ Row = PyInt_AsLong( row );
+ if (Row > ta->GetRowCount() - 1)
+ Row = -1;
+ } else
+ Row = ta->GetRowCount() - 1;
+
+ if (flag) {
+ if (!PyObject_TypeCheck( flag, &PyInt_Type )) {
+ printMessage( "GUIScript",
+ "Syntax Error: GetString flag must be integer\n", LIGHT_RED );
+ return NULL;
+ }
+ Flag = PyInt_AsLong( flag );
+ }
+
+ if (PyObject_TypeCheck( str, &PyString_Type )) {
+ string = PyString_AsString( str );
+ if (string == NULL)
+ return RuntimeError("Null string received");
+ ret = ta->AppendText( string, Row );
+ } else {
+ StrRef = PyInt_AsLong( str );
+ char* str = core->GetString( StrRef, Flag );
+ ret = ta->AppendText( str, Row );
+ core->FreeString( str );
+ }
+
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_TextArea_Clear__doc,
+"TextAreaClear(WindowIndex, ControlIndex)\n\n"
+"Clears the Text from the TextArea Control in the Window." );
+
+static PyObject* GemRB_TextArea_Clear(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* wi, * ci;
+ long WindowIndex, ControlIndex;
+
+ if (!PyArg_UnpackTuple( args, "ref", 2, 2, &wi, &ci )) {
+ return AttributeError( GemRB_TextArea_Clear__doc );
+ }
+ if (!PyObject_TypeCheck( wi, &PyInt_Type ) ||
+ !PyObject_TypeCheck( ci, &PyInt_Type )) {
+ return AttributeError( GemRB_TextArea_Clear__doc );
+ }
+ WindowIndex = PyInt_AsLong( wi );
+ ControlIndex = PyInt_AsLong( ci );
+ TextArea* ta = ( TextArea* ) GetControl( WindowIndex, ControlIndex, IE_GUI_TEXTAREA);
+ if (!ta) {
+ return NULL;
+ }
+ ta->Clear();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_TextArea_Scroll__doc,
+"TextAreaScroll(WindowIndex, ControlIndex, offset)\n\n"
+"Scrolls the textarea up or down by offset." );
+
+static PyObject* GemRB_TextArea_Scroll(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, offset;
+
+ if (!PyArg_ParseTuple( args, "iii", &WindowIndex, &ControlIndex, &offset)) {
+ return AttributeError( GemRB_TextArea_Scroll__doc );
+ }
+ TextArea* ta = ( TextArea* ) GetControl( WindowIndex, ControlIndex, IE_GUI_TEXTAREA);
+ if (!ta) {
+ return NULL;
+ }
+ int row = ta->GetTopIndex()+offset;
+ if (row<0) {
+ row = 0;
+ }
+ ta->SetRow( row );
+ core->RedrawAll();
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_SetTooltip__doc,
+"SetTooltip(WindowIndex, ControlIndex, String|Strref) => int\n\n"
+"Sets control's tooltip." );
+
+static PyObject* GemRB_Control_SetTooltip(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* wi, * ci, * str;
+ long WindowIndex, ControlIndex, StrRef;
+ char* string;
+ int ret;
+
+ if (!PyArg_UnpackTuple( args, "ref", 3, 3, &wi, &ci, &str )) {
+ return AttributeError( GemRB_Control_SetTooltip__doc );
+ }
+ if (!PyObject_TypeCheck( wi, &PyInt_Type ) ||
+ !PyObject_TypeCheck( ci, &PyInt_Type ) ||
+ ( !PyObject_TypeCheck( str, &PyString_Type ) &&
+ !PyObject_TypeCheck( str, &PyInt_Type ) )) {
+ return AttributeError( GemRB_Control_SetTooltip__doc );
+ }
+
+ WindowIndex = PyInt_AsLong( wi );
+ ControlIndex = PyInt_AsLong( ci );
+ if (PyObject_TypeCheck( str, &PyString_Type )) {
+ string = PyString_AsString( str );
+ if (string == NULL) {
+ return RuntimeError("Null string received");
+ }
+ ret = core->SetTooltip( (ieWord) WindowIndex, (ieWord) ControlIndex, string );
+ if (ret == -1) {
+ return RuntimeError("Cannot set tooltip");
+ }
+ } else {
+ StrRef = PyInt_AsLong( str );
+ if (StrRef == -1) {
+ ret = core->SetTooltip( (ieWord) WindowIndex, (ieWord) ControlIndex, GEMRB_STRING );
+ } else {
+ char* str = core->GetString( StrRef );
+ ret = core->SetTooltip( (ieWord) WindowIndex, (ieWord) ControlIndex, str );
+ core->FreeString( str );
+ }
+ if (ret == -1) {
+ return RuntimeError("Cannot set tooltip");
+ }
+ }
+
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_Window_SetVisible__doc,
+"SetVisible(WindowIndex, Visible)=>bool\n\n"
+"Sets the Visibility Flag of a Window." );
+
+static PyObject* GemRB_Window_SetVisible(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex;
+ int visible;
+
+ if (!PyArg_ParseTuple( args, "ii", &WindowIndex, &visible )) {
+ return AttributeError( GemRB_Window_SetVisible__doc );
+ }
+
+ int ret = core->SetVisible( WindowIndex, visible );
+ if (ret == -1) {
+ return RuntimeError("Invalid window in SetVisible");
+ }
+ if (!WindowIndex) {
+ core->SetEventFlag(EF_CONTROL);
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+//useful only for ToB and HoW, sets masterscript/worldmap name
+PyDoc_STRVAR( GemRB_SetMasterScript__doc,
+"SetMasterScript(ScriptResRef, WMPResRef)\n\n"
+"Sets the worldmap and masterscript names." );
+
+PyObject* GemRB_SetMasterScript(PyObject * /*self*/, PyObject* args)
+{
+ char* script;
+ char* worldmap1;
+ char* worldmap2 = NULL;
+
+ if (!PyArg_ParseTuple( args, "ss|s", &script, &worldmap1, &worldmap2 )) {
+ return AttributeError( GemRB_SetMasterScript__doc );
+ }
+ strnlwrcpy( core->GlobalScript, script, 8 );
+ strnlwrcpy( core->WorldMapName[0], worldmap1, 8 );
+ if (!worldmap2) {
+ memset(core->WorldMapName[1], 0, 8);
+ } else {
+ strnlwrcpy( core->WorldMapName[1], worldmap2, 8 );
+ }
+ core->UpdateMasterScript();
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_ShowModal__doc,
+"ShowModal(WindowIndex, [Shadow=MODAL_SHADOW_NONE])\n\n"
+"Show a Window on Screen setting the Modal Status. "
+"If Shadow is MODAL_SHADOW_GRAY, other windows are grayed. "
+"If Shadow is MODAL_SHADOW_BLACK, they are blacked out." );
+
+static PyObject* GemRB_Window_ShowModal(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, Shadow = MODAL_SHADOW_NONE;
+
+ if (!PyArg_ParseTuple( args, "i|i", &WindowIndex, &Shadow )) {
+ return AttributeError( GemRB_Window_ShowModal__doc );
+ }
+
+ int ret = core->ShowModal( WindowIndex, Shadow );
+ if (ret == -1) {
+ return NULL;
+ }
+
+ core->PlaySound(DS_WINDOW_OPEN);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetTimedEvent__doc,
+"SetTimedEvent(Function, Rounds)\n\n"
+"Sets a timed event, the timing is handled by the game object\n"
+"if the game object doesn't exist, this command is ignored." );
+
+static PyObject* GemRB_SetTimedEvent(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* function;
+ int rounds;
+
+ if (!PyArg_ParseTuple( args, "Oi", &function, &rounds )) {
+ return AttributeError( GemRB_SetTimedEvent__doc );
+ }
+
+ EventHandler handler;
+ if (function == Py_None) {
+ handler = new Callback();
+ } else if (PyCallable_Check(function)) {
+ handler = new PythonCallback(function);
+ } else {
+ char buf[256];
+ // TODO: Print function name. (func.__name__)
+ snprintf(buf, sizeof(buf), "Can't set timed event handler!");
+ return RuntimeError(buf);
+ }
+ Game *game = core->GetGame();
+ if (game) {
+ game->SetTimedEvent(handler, rounds);
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_SetEvent__doc,
+"Control.SetEvent(EventMask, Function)\n\n"
+"Sets an event of a control on a window to a script defined function." );
+
+static PyObject* GemRB_Control_SetEvent(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ int event;
+ PyObject* func;
+
+ if (!PyArg_ParseTuple(args, "iiiO", &WindowIndex, &ControlIndex,
+ &event, &func)) {
+ return AttributeError(GemRB_Control_SetEvent__doc);
+ }
+
+ Control* ctrl = GetControl(WindowIndex, ControlIndex, -1);
+ if (!ctrl)
+ return NULL;
+
+ EventHandler handler;
+ if (func == Py_None) {
+ handler = new Callback();
+ } else if (PyCallable_Check(func)) {
+ handler = new PythonCallback(func);
+ }
+ if (!handler || !ctrl->SetEvent(event, handler)) {
+ char buf[256];
+ // TODO: Print function name. (func.__name__)
+ snprintf(buf, sizeof(buf), "Can't set event handler!");
+ return RuntimeError(buf);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetNextScript__doc,
+"SetNextScript(GUIScriptName)\n\n"
+"Sets the Next Script File to be loaded." );
+
+static PyObject* GemRB_SetNextScript(PyObject * /*self*/, PyObject* args)
+{
+ const char* funcName;
+
+ if (!PyArg_ParseTuple( args, "s", &funcName )) {
+ return AttributeError( GemRB_SetNextScript__doc );
+ }
+
+ if (!strcmp(funcName, "")) {
+ return AttributeError( GemRB_SetNextScript__doc );
+ }
+
+ core->SetNextScript(funcName);
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_SetStatus__doc,
+"SetControlStatus(WindowIndex, ControlIndex, Status)\n\n"
+"Sets the status of a Control." );
+
+static PyObject* GemRB_Control_SetStatus(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ int status;
+
+ if (!PyArg_ParseTuple( args, "iii", &WindowIndex, &ControlIndex, &status )) {
+ return AttributeError( GemRB_Control_SetStatus__doc );
+ }
+
+ int ret = core->SetControlStatus( WindowIndex, ControlIndex, status );
+ if (ret == -1) {
+ return NULL;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_AttachScrollBar__doc,
+"AttachScrollBar(WindowIndex, ControlIndex, ScrollBarControlIndex)\n\n"
+"Attaches a ScrollBar to another control." );
+
+static PyObject* GemRB_Control_AttachScrollBar(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, ScbControlIndex;
+
+ if (!PyArg_ParseTuple( args, "iii", &WindowIndex, &ControlIndex, &ScbControlIndex )) {
+ return AttributeError( GemRB_Control_AttachScrollBar__doc );
+ }
+
+ Control *ctrl = GetControl(WindowIndex, ControlIndex, -1);
+ if (!ctrl) {
+ return NULL;
+ }
+
+ Control *scb = NULL;
+
+ if (ScbControlIndex != -1) {
+ scb = GetControl(WindowIndex, ScbControlIndex, IE_GUI_SCROLLBAR);
+ if (!scb) {
+ return NULL;
+ }
+ }
+
+ int ret = ctrl->SetScrollBar( scb );
+ if (ret == -1) {
+ return NULL;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_SetVarAssoc__doc,
+"SetVarAssoc(WindowIndex, ControlIndex, VariableName, LongValue)\n\n"
+"Sets the name of the Variable associated with a control." );
+
+static PyObject* GemRB_Control_SetVarAssoc(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ ieDword Value;
+ char* VarName;
+
+ if (!PyArg_ParseTuple( args, "iisi", &WindowIndex, &ControlIndex,
+ &VarName, &Value )) {
+ return AttributeError( GemRB_Control_SetVarAssoc__doc );
+ }
+
+ Control* ctrl = GetControl( WindowIndex, ControlIndex, -1 );
+ if (!ctrl) {
+ return NULL;
+ }
+
+ //max variable length is not 32, but 40 (in guiscripts), but that includes zero terminator!
+ strnlwrcpy( ctrl->VarName, VarName, MAX_VARIABLE_LENGTH-1 );
+ ctrl->Value = Value;
+ /** setting the correct state for this control */
+ /** it is possible to set up a default value, if Lookup returns false, use it */
+ Value = 0;
+ core->GetDictionary()->Lookup( VarName, Value );
+ Window* win = core->GetWindow( WindowIndex );
+ win->RedrawControls(VarName, Value);
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_Unload__doc,
+"UnloadWindow(WindowIndex)\n\n"
+"Unloads a previously Loaded Window." );
+
+static PyObject* GemRB_Window_Unload(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex;
+
+ if (!PyArg_ParseTuple( args, "i", &WindowIndex )) {
+ return AttributeError( GemRB_Window_Unload__doc );
+ }
+
+ unsigned short arg = (unsigned short) WindowIndex;
+ if (arg == 0xffff) {
+ return AttributeError( "Feature unsupported! ");
+ }
+
+ //Don't bug if the window wasn't loaded
+ if (core->GetWindow(arg) ) {
+ int ret = core->DelWindow( arg );
+ if (ret == -1) {
+ return RuntimeError( "Can't unload window!" );
+ }
+
+ core->PlaySound(DS_WINDOW_CLOSE);
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_Invalidate__doc,
+"InvalidateWindow(WindowIndex)\n\n"
+"Invalidates the given Window." );
+
+static PyObject* GemRB_Window_Invalidate(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex;
+
+ if (!PyArg_ParseTuple( args, "i", &WindowIndex )) {
+ return AttributeError( GemRB_Window_Invalidate__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+ win->Invalidate();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_CreateWindow__doc,
+"CreateWindow(WindowID, X, Y, Width, Height, MosResRef) => WindowIndex\n\n"
+"Creates a new empty window and returns its index.");
+
+static PyObject* GemRB_CreateWindow(PyObject * /*self*/, PyObject* args)
+{
+ int WindowID, x, y, w, h;
+ char* Background;
+
+ if (!PyArg_ParseTuple( args, "iiiiis", &WindowID, &x, &y,
+ &w, &h, &Background )) {
+ return AttributeError( GemRB_CreateWindow__doc );
+ }
+ int WindowIndex = core->CreateWindow( WindowID, x, y, w, h, Background );
+ if (WindowIndex == -1) {
+ return RuntimeError( "Can't create window" );
+ }
+
+ return PyInt_FromLong( WindowIndex );
+}
+
+PyDoc_STRVAR( GemRB_Button_CreateLabelOnButton__doc,
+"CreateLabelOnButton(WindowIndex, ControlIndex, NewControlID, font, align)"
+"Creates a label on top of a button, copying the button's size and position." );
+
+static PyObject* GemRB_Button_CreateLabelOnButton(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, ControlID, align;
+ char *font;
+
+ if (!PyArg_ParseTuple( args, "iiisi", &WindowIndex, &ControlIndex,
+ &ControlID, &font, &align )) {
+ return AttributeError( GemRB_Button_CreateLabelOnButton__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+ Control *btn = GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+ Label* lbl = new Label( core->GetFont( font ) );
+ lbl->XPos = btn->XPos;
+ lbl->YPos = btn->YPos;
+ lbl->Width = btn->Width;
+ lbl->Height = btn->Height;
+ lbl->ControlID = ControlID;
+ lbl->ControlType = IE_GUI_LABEL;
+ lbl->Owner = win;
+ lbl->SetAlignment( align );
+ win->AddControl( lbl );
+
+ int ret = core->GetControl( WindowIndex, ControlID );
+
+ if (ret<0) {
+ return NULL;
+ }
+ return PyInt_FromLong( ret );
+ //Py_INCREF( Py_None );
+ //return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_CreateLabel__doc,
+"CreateLabel(WindowIndex, ControlID, x, y, w, h, font, text, align)\n\n"
+"Creates and adds a new Label to a Window." );
+
+static PyObject* GemRB_Window_CreateLabel(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlID, x, y, w, h, align;
+ char *font, *text;
+
+ if (!PyArg_ParseTuple( args, "iiiiiissi", &WindowIndex, &ControlID, &x,
+ &y, &w, &h, &font, &text, &align )) {
+ return AttributeError( GemRB_Window_CreateLabel__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+ Label* lbl = new Label( core->GetFont( font ) );
+ lbl->XPos = x;
+ lbl->YPos = y;
+ lbl->Width = w;
+ lbl->Height = h;
+ lbl->ControlID = ControlID;
+ lbl->ControlType = IE_GUI_LABEL;
+ lbl->Owner = win;
+ lbl->SetText( text );
+ lbl->SetAlignment( align );
+ win->AddControl( lbl );
+
+ int ret = core->GetControl( WindowIndex, ControlID );
+
+ if (ret<0) {
+ return NULL;
+ }
+ return PyInt_FromLong( ret );
+ //Py_INCREF( Py_None );
+ //return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Label_SetTextColor__doc,
+"SetLabelTextColor(WindowIndex, ControlIndex, red, green, blue)\n\n"
+"Sets the Text Color of a Label Control." );
+
+static PyObject* GemRB_Label_SetTextColor(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, r, g, b;
+
+ if (!PyArg_ParseTuple( args, "iiiii", &WindowIndex, &ControlIndex, &r, &g,
+ &b )) {
+ return AttributeError( GemRB_Label_SetTextColor__doc );
+ }
+
+ Label* lab = ( Label* ) GetControl(WindowIndex, ControlIndex, IE_GUI_LABEL);
+ if (!lab) {
+ return NULL;
+ }
+
+ Color fore = {r,g, b, 0}, back = {0, 0, 0, 0};
+ lab->SetColor( fore, back );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_CreateTextEdit__doc,
+"CreateTextEdit(WindowIndex, ControlID, x, y, w, h, font, text)\n\n"
+"Creates and adds a new TextEdit to a Window." );
+
+static PyObject* GemRB_Window_CreateTextEdit(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlID, x, y, w, h;
+ char *font, *text;
+
+ if (!PyArg_ParseTuple( args, "iiiiiiss", &WindowIndex, &ControlID, &x,
+ &y, &w, &h, &font, &text )) {
+ return AttributeError( GemRB_Window_CreateTextEdit__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+ //there is no need to set these differently, currently
+ TextEdit* edit = new TextEdit( 500, 0, 0);
+ edit->SetFont( core->GetFont( font ) );
+ edit->XPos = x;
+ edit->YPos = y;
+ edit->Width = w;
+ edit->Height = h;
+ edit->ControlID = ControlID;
+ edit->ControlType = IE_GUI_EDIT;
+ edit->Owner = win;
+ edit->SetText( text );
+
+ Sprite2D* spr = core->GetCursorSprite();
+ if (spr)
+ edit->SetCursor( spr );
+ else
+ return RuntimeError( "Cursor BAM not found" );
+
+ win->AddControl( edit );
+
+ int ret = core->GetControl( WindowIndex, ControlID );
+
+ if (ret<0) {
+ return NULL;
+ }
+ return PyInt_FromLong( ret );
+ //Py_INCREF( Py_None );
+ //return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_CreateScrollBar__doc,
+"CreateScrollBar(WindowIndex, ControlID, x, y, w, h) => ControlIndex\n\n"
+"Creates and adds a new ScrollBar to a Window.");
+
+static PyObject* GemRB_Window_CreateScrollBar(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlID, x, y, w, h;
+
+ if (!PyArg_ParseTuple( args, "iiiiii", &WindowIndex, &ControlID, &x, &y,
+ &w, &h )) {
+ return AttributeError( GemRB_Window_CreateScrollBar__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+
+ ScrollBar* sb = new ScrollBar( );
+ sb->XPos = x;
+ sb->YPos = y;
+ sb->Width = w;
+ sb->Height = h;
+ sb->ControlID = ControlID;
+ sb->ControlType = IE_GUI_SCROLLBAR;
+ sb->Owner = win;
+ win->AddControl( sb );
+
+ int ret = core->GetControl( WindowIndex, ControlID );
+
+ if (ret<0) {
+ return NULL;
+ }
+ return PyInt_FromLong( ret );
+ //Py_INCREF( Py_None );
+ //return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_Window_CreateButton__doc,
+"CreateButton(WindowIndex, ControlID, x, y, w, h) => ControlIndex\n\n"
+"Creates and adds a new Button to a Window." );
+
+static PyObject* GemRB_Window_CreateButton(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlID, x, y, w, h;
+
+ if (!PyArg_ParseTuple( args, "iiiiii", &WindowIndex, &ControlID, &x, &y,
+ &w, &h )) {
+ return AttributeError( GemRB_Window_CreateButton__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+
+ Button* btn = new Button( );
+ btn->XPos = x;
+ btn->YPos = y;
+ btn->Width = w;
+ btn->Height = h;
+ btn->ControlID = ControlID;
+ btn->ControlType = IE_GUI_BUTTON;
+ btn->Owner = win;
+ win->AddControl( btn );
+
+ int ret = core->GetControl( WindowIndex, ControlID );
+
+ if (ret<0) {
+ return NULL;
+ }
+ return PyInt_FromLong( ret );
+ //Py_INCREF( Py_None );
+ //return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_TextEdit_ConvertEdit__doc,
+"ConvertEdit(WindowIndex, ControlIndex, ScrollBarID) => ControlIndex\n\n"
+"Converts a simple Edit Control to a TextArea, keeping its ControlID." );
+
+static PyObject* GemRB_TextEdit_ConvertEdit(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ Color fore={255,255,255,0};
+ Color init={255,255,255,0};
+ Color back={0,0,0,0};
+ int ScrollBarID=0;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &WindowIndex, &ControlIndex, &ScrollBarID)) {
+ return AttributeError( GemRB_TextEdit_ConvertEdit__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+
+ TextEdit *ctrl = (TextEdit *) GetControl(WindowIndex, ControlIndex, IE_GUI_EDIT);
+ if (!ctrl) {
+ return NULL;
+ }
+ TextArea* ta = new TextArea( fore, init, back );
+ ta->XPos = ctrl->XPos;
+ ta->YPos = ctrl->YPos;
+ ta->Width = ctrl->Width;
+ ta->Height = ctrl->Height;
+ ta->ControlID = ctrl->ControlID;
+ ta->ControlType = IE_GUI_TEXTAREA;
+ ta->Owner = win;
+ ta->SetFonts (ctrl->GetFont(), ctrl->GetFont() );
+ ta->Flags |= IE_GUI_TEXTAREA_EDITABLE;
+ win->AddControl( ta );
+ win->Link( ScrollBarID, ( unsigned short ) ta->ControlID );
+
+ int ret = core->GetControl( WindowIndex, ta->ControlID );
+
+ if (ret<0) {
+ return NULL;
+ }
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_ScrollBar_SetSprites__doc,
+"SetScrollBarSprites(WindowIndex, ControlIndex, ResRef, Cycle, UpUnpressedFrame, UpPressedFrame, DownUnpressedFrame, DownPressedFrame, TroughFrame, SliderFrame)\n\n"
+"Sets a ScrollBar Sprites Images." );
+
+static PyObject* GemRB_ScrollBar_SetSprites(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, cycle, upunpressed, uppressed;
+ int downunpressed, downpressed, trough, knob;
+ char *ResRef;
+
+ if (!PyArg_ParseTuple( args, "iisiiiiiii", &WindowIndex, &ControlIndex,
+ &ResRef, &cycle, &upunpressed, &uppressed, &downunpressed, &downpressed, &trough, &knob )) {
+ return AttributeError( GemRB_ScrollBar_SetSprites__doc );
+ }
+
+ ScrollBar* sb = ( ScrollBar* ) GetControl(WindowIndex, ControlIndex, IE_GUI_SCROLLBAR);
+ if (!sb) {
+ return NULL;
+ }
+
+ if (ResRef[0] == 0) {
+ sb->SetImage( IE_GUI_SCROLLBAR_UP_UNPRESSED, 0 );
+ sb->SetImage( IE_GUI_SCROLLBAR_UP_PRESSED, 0 );
+ sb->SetImage( IE_GUI_SCROLLBAR_DOWN_UNPRESSED, 0 );
+ sb->SetImage( IE_GUI_SCROLLBAR_DOWN_PRESSED, 0 );
+ sb->SetImage( IE_GUI_SCROLLBAR_TROUGH, 0 );
+ sb->SetImage( IE_GUI_SCROLLBAR_SLIDER, 0 );
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( ResRef,
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (!af) {
+ char tmpstr[24];
+
+ snprintf(tmpstr,sizeof(tmpstr),"%s BAM not found", ResRef);
+ return RuntimeError( tmpstr );
+ }
+ Sprite2D *tspr = af->GetFrame(upunpressed, (unsigned char)cycle);
+ sb->SetImage( IE_GUI_SCROLLBAR_UP_UNPRESSED, tspr );
+ tspr = af->GetFrame( uppressed, (unsigned char) cycle);
+ sb->SetImage( IE_GUI_SCROLLBAR_UP_PRESSED, tspr );
+ tspr = af->GetFrame( downunpressed, (unsigned char)cycle);
+ sb->SetImage( IE_GUI_SCROLLBAR_DOWN_UNPRESSED, tspr );
+ tspr = af->GetFrame( downpressed, (unsigned char) cycle);
+ sb->SetImage( IE_GUI_SCROLLBAR_DOWN_PRESSED, tspr );
+ tspr = af->GetFrame( trough, (unsigned char) cycle);
+ sb->SetImage( IE_GUI_SCROLLBAR_TROUGH, tspr );
+ tspr = af->GetFrame( knob, (unsigned char) cycle);
+ sb->SetImage( IE_GUI_SCROLLBAR_SLIDER, tspr );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_Button_SetSprites__doc,
+"SetButtonSprites(WindowIndex, ControlIndex, ResRef, Cycle, UnpressedFrame, PressedFrame, SelectedFrame, DisabledFrame)\n\n"
+"Sets a Button Sprites Images." );
+
+static PyObject* GemRB_Button_SetSprites(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, cycle, unpressed, pressed, selected,
+ disabled;
+ char *ResRef;
+
+ if (!PyArg_ParseTuple( args, "iisiiiii", &WindowIndex, &ControlIndex,
+ &ResRef, &cycle, &unpressed, &pressed, &selected, &disabled )) {
+ return AttributeError( GemRB_Button_SetSprites__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ if (ResRef[0] == 0) {
+ btn->SetImage( IE_GUI_BUTTON_UNPRESSED, 0 );
+ btn->SetImage( IE_GUI_BUTTON_PRESSED, 0 );
+ btn->SetImage( IE_GUI_BUTTON_SELECTED, 0 );
+ btn->SetImage( IE_GUI_BUTTON_DISABLED, 0 );
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( ResRef,
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (!af) {
+ char tmpstr[24];
+
+ snprintf(tmpstr,sizeof(tmpstr),"%s BAM not found", ResRef);
+ return RuntimeError( tmpstr );
+ }
+ Sprite2D *tspr = af->GetFrame(unpressed, (unsigned char)cycle);
+ btn->SetImage( IE_GUI_BUTTON_UNPRESSED, tspr );
+ tspr = af->GetFrame( pressed, (unsigned char) cycle);
+ btn->SetImage( IE_GUI_BUTTON_PRESSED, tspr );
+ tspr = af->GetFrame( selected, (unsigned char) cycle);
+ btn->SetImage( IE_GUI_BUTTON_SELECTED, tspr );
+ tspr = af->GetFrame( disabled, (unsigned char) cycle);
+ btn->SetImage( IE_GUI_BUTTON_DISABLED, tspr );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetOverlay__doc,
+"SetButtonOverlay(WindowIndex, ControlIndex, Current, Max, r,g,b,a, r,g,b,a)\n\n"
+"Sets up a portrait button for hitpoint overlay" );
+
+static PyObject* GemRB_Button_SetOverlay(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ double Clipping;
+ int r1,g1,b1,a1;
+ int r2,g2,b2,a2;
+
+ if (!PyArg_ParseTuple( args, "iidiiiiiiii", &WindowIndex, &ControlIndex,
+ &Clipping, &r1, &g1, &b1, &a1, &r2, &g2, &b2, &a2)) {
+ return AttributeError( GemRB_Button_SetOverlay__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ const Color src = { r1, g1, b1, a1 };
+ const Color dest = { r2, g2, b2, a2 };
+
+ if (Clipping<0.0) Clipping = 0.0;
+ else if (Clipping>1.0) Clipping = 1.0;
+ //can't call clipping, because the change of ratio triggers color change
+ btn->SetHorizontalOverlay(Clipping, src, dest);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetBorder__doc,
+"SetButtonBorder(WindowIndex, ControlIndex, BorderIndex, dx1, dy1, dx2, dy2, R, G, B, A, [enabled, filled])\n\n"
+"Sets border/frame parameters for a button." );
+
+static PyObject* GemRB_Button_SetBorder(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, BorderIndex, dx1, dy1, dx2, dy2, r, g, b, a, enabled = 0, filled = 0;
+
+ if (!PyArg_ParseTuple( args, "iiiiiiiiiii|ii", &WindowIndex, &ControlIndex,
+ &BorderIndex, &dx1, &dy1, &dx2, &dy2, &r, &g, &b, &a, &enabled, &filled)) {
+ return AttributeError( GemRB_Button_SetBorder__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ const Color color = { r, g, b, a };
+ btn->SetBorder( BorderIndex, dx1, dy1, dx2, dy2, color, (bool)enabled, (bool)filled );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_EnableBorder__doc,
+"EnableButtonBorder(WindowIndex, ControlIndex, BorderIndex, enabled)\n\n"
+"Enable or disable specified border/frame." );
+
+static PyObject* GemRB_Button_EnableBorder(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, BorderIndex, enabled;
+
+ if (!PyArg_ParseTuple( args, "iiii", &WindowIndex, &ControlIndex,
+ &BorderIndex, &enabled)) {
+ return AttributeError( GemRB_Button_EnableBorder__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ btn->EnableBorder( BorderIndex, (bool)enabled );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetFont__doc,
+"SetButtonFont(WindowIndex, ControlIndex, FontResRef)\n\n"
+"Sets font used for drawing button label." );
+
+static PyObject* GemRB_Button_SetFont(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ char *FontResRef;
+
+ if (!PyArg_ParseTuple( args, "iis", &WindowIndex, &ControlIndex,
+ &FontResRef)) {
+ return AttributeError( GemRB_Button_SetFont__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ btn->SetFont( core->GetFont( FontResRef ));
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetTextColor__doc,
+"SetButtonTextColor(WindowIndex, ControlIndex, red, green, blue[, invert=false])\n\n"
+"Sets the Text Color of a Button Control. Invert is used for fonts with swapped background and text colors." );
+
+static PyObject* GemRB_Button_SetTextColor(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, r, g, b, swap = 0;
+
+ if (!PyArg_ParseTuple( args, "iiiii|i", &WindowIndex, &ControlIndex, &r, &g, &b, &swap )) {
+ return AttributeError( GemRB_Button_SetTextColor__doc );
+ }
+
+ Button* but = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!but) {
+ return NULL;
+ }
+
+ Color fore = {r,g, b, 0}, back = {0, 0, 0, 0};
+
+ // FIXME: swap is a hack for fonts which apparently have swapped f & B
+ // colors. Maybe it depends on need_palette?
+ if (! swap)
+ but->SetTextColor( fore, back );
+ else
+ but->SetTextColor( back, fore );
+
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_DeleteControl__doc,
+"DeleteControl(WindowIndex, ControlID)\n\n"
+"Deletes a control from a Window." );
+
+static PyObject* GemRB_Window_DeleteControl(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlID;
+
+ if (!PyArg_ParseTuple( args, "ii", &WindowIndex, &ControlID)) {
+ return AttributeError( GemRB_Window_DeleteControl__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+ int CtrlIndex = core->GetControl( WindowIndex, ControlID );
+ if (CtrlIndex == -1) {
+ return RuntimeError( "Control is not found" );
+ }
+ win -> DelControl( CtrlIndex );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_AddNewArea__doc,
+"AddNewArea(_2daresref)\n\n"
+"Adds the extension areas to the game.");
+
+static PyObject* GemRB_AddNewArea(PyObject * /*self*/, PyObject* args)
+{
+ const char *resref;
+
+ if (!PyArg_ParseTuple( args, "s", &resref)) {
+ return AttributeError( GemRB_AddNewArea__doc );
+ }
+
+ AutoTable newarea(resref);
+ if (!newarea) {
+ return RuntimeError( "2da not found!\n");
+ }
+
+ WorldMap *wmap = core->GetWorldMap();
+ if (!wmap) {
+ return RuntimeError( "no worldmap loaded!");
+ }
+
+ const char *enc[5];
+ int k;
+ int links[4];
+ int indices[4];
+ int rows = newarea->GetRowCount();
+ for(int i=0;i<rows;i++) {
+ const char *area = newarea->QueryField(i,0);
+ const char *script = newarea->QueryField(i,1);
+ int flags = atoi(newarea->QueryField(i,2));
+ int icon = atoi(newarea->QueryField(i,3));
+ int locx = atoi(newarea->QueryField(i,4));
+ int locy = atoi(newarea->QueryField(i,5));
+ int label = atoi(newarea->QueryField(i,6));
+ int name = atoi(newarea->QueryField(i,7));
+ const char *ltab = newarea->QueryField(i,8);
+ links[WMP_NORTH] = atoi(newarea->QueryField(i,9));
+ links[WMP_EAST] = atoi(newarea->QueryField(i,10));
+ links[WMP_SOUTH] = atoi(newarea->QueryField(i,11));
+ links[WMP_WEST] = atoi(newarea->QueryField(i,12));
+ //this is the number of links in the 2da, we don't need it
+ int linksto = atoi(newarea->QueryField(i,13));
+
+ unsigned int local = 0;
+ int linkcnt = wmap->GetLinkCount();
+ for (k=0;k<4;k++) {
+ indices[k] = linkcnt;
+ linkcnt += links[k];
+ local += links[k];
+ }
+ unsigned int total = linksto+local;
+
+ AutoTable newlinks(ltab);
+ if (!newlinks || total != newlinks->GetRowCount() ) {
+ return RuntimeError( "invalid links 2da!");
+ }
+
+ WMPAreaEntry *entry = wmap->GetNewAreaEntry();
+ strnuprcpy(entry->AreaName, area, 8);
+ strnuprcpy(entry->AreaResRef, area, 8);
+ strnuprcpy(entry->AreaLongName, script, 32);
+ entry->SetAreaStatus(flags, BM_SET);
+ entry->IconSeq = icon;
+ entry->X = locx;
+ entry->Y = locy;
+ entry->LocCaptionName = label;
+ entry->LocTooltipName = name;
+ memset(entry->LoadScreenResRef, 0, sizeof(ieResRef));
+ memcpy(entry->AreaLinksIndex, indices, sizeof(entry->AreaLinksIndex) );
+ memcpy(entry->AreaLinksCount, links, sizeof(entry->AreaLinksCount) );
+
+ int thisarea = wmap->GetEntryCount();
+ wmap->AddAreaEntry(entry);
+ wmap->AreaEntriesCount++;
+ for (unsigned int j=0;j<total;j++) {
+ WMPAreaLink *link = new WMPAreaLink();
+ const char *larea = newlinks->QueryField(j,0);
+ int lflags = atoi(newlinks->QueryField(j,1));
+ const char *ename = newlinks->QueryField(j,2);
+ int distance = atoi(newlinks->QueryField(j,3));
+ int encprob = atoi(newlinks->QueryField(j,4));
+ for(k=0;k<5;k++) {
+ enc[k] = newlinks->QueryField(i,5+k);
+ }
+ int linktodir = atoi(newlinks->QueryField(j,10));
+
+ unsigned int areaindex;
+ WMPAreaEntry *oarea = wmap->GetArea(larea, areaindex);
+ if (!oarea) {
+ //blabla
+ return RuntimeError("cannot establish area link!");
+ }
+ strnuprcpy(link->DestEntryPoint, ename, 32);
+ link->DistanceScale = distance;
+ link->DirectionFlags = lflags;
+ link->EncounterChance = encprob;
+ for(k=0;k<5;k++) {
+ if (enc[k][0]=='*') {
+ memset(link->EncounterAreaResRef[k],0,sizeof(ieResRef));
+ } else {
+ strnuprcpy(link->EncounterAreaResRef[k], enc[k], 8);
+ }
+ }
+
+ //first come the local links, then 'links to' this area
+ //local is total-linksto
+ if (j<local) {
+ link->AreaIndex = thisarea;
+ //linktodir may need translation
+ wmap->InsertAreaLink(areaindex, linktodir, link);
+ } else {
+ link->AreaIndex = areaindex;
+ wmap->AddAreaLink(link);
+ }
+
+ }
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_WorldMap_AdjustScrolling__doc,
+"AdjustScrolling(WindowIndex, ControlIndex, x, y)\n\n"
+"Sets the scrolling offset of a WorldMapControl.");
+
+static PyObject* GemRB_WorldMap_AdjustScrolling(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, x, y;
+
+ if (!PyArg_ParseTuple( args, "iiii", &WindowIndex, &ControlIndex, &x, &y )) {
+ return AttributeError( GemRB_WorldMap_AdjustScrolling__doc );
+ }
+
+ core->AdjustScrolling( WindowIndex, ControlIndex, x, y );
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_CreateMovement__doc,
+"CreateMovement(Area, Entrance, Direction)\n\n"
+"Moves actors to a new area." );
+
+static PyObject* GemRB_CreateMovement(PyObject * /*self*/, PyObject* args)
+{
+ int everyone;
+ char *area;
+ char *entrance;
+ int direction = 0;
+
+ if (!PyArg_ParseTuple( args, "ss|i", &area, &entrance, &direction)) {
+ return AttributeError( GemRB_CreateMovement__doc );
+ }
+ if (core->HasFeature(GF_TEAM_MOVEMENT) ) {
+ everyone = CT_WHOLE;
+ } else {
+ everyone = CT_GO_CLOSER;
+ }
+ GET_GAME();
+
+ GET_MAP();
+
+ map->MoveToNewArea(area, entrance, (unsigned int)direction, everyone, NULL);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_WorldMap_GetDestinationArea__doc,
+"GetDestinationArea(WindowIndex, ControlID[, RndEncounter]) => WorldMap entry\n\n"
+"Returns the last area pointed on the worldmap.\n"
+"If the random encounter flag is set, the random encounters will be evaluated too." );
+
+static PyObject* GemRB_WorldMap_GetDestinationArea(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ int eval = 0;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &WindowIndex, &ControlIndex, &eval)) {
+ return AttributeError( GemRB_WorldMap_GetDestinationArea__doc );
+ }
+
+ WorldMapControl* wmc = (WorldMapControl *) GetControl(WindowIndex, ControlIndex, IE_GUI_WORLDMAP);
+ if (!wmc) {
+ return NULL;
+ }
+ //no area was pointed on
+ if (!wmc->Area) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ WorldMap *wm = core->GetWorldMap();
+ PyObject* dict = PyDict_New();
+ //the area the user clicked on
+ PyDict_SetItemString(dict, "Target", PyString_FromString (wmc->Area->AreaName) );
+ bool encounter;
+ WMPAreaLink *wal = wm->GetEncounterLink(wmc->Area->AreaName, encounter);
+ if (!wal) {
+ PyDict_SetItemString(dict, "Distance", PyInt_FromLong (-1) );
+ return dict;
+ }
+ PyDict_SetItemString(dict, "Destination", PyString_FromString (wmc->Area->AreaName) );
+ PyDict_SetItemString(dict, "Entrance", PyString_FromString (wal->DestEntryPoint) );
+ PyDict_SetItemString(dict, "Direction", PyInt_FromLong (wal->DirectionFlags) );
+ //the area the user will fall on
+ if (encounter && eval) {
+ int i=rand()%5;
+
+ displaymsg->DisplayConstantString(STR_AMBUSH,0xbcefbc);
+
+ if(wal->EncounterChance>=100) {
+ wal->EncounterChance-=100;
+ }
+ for(int j=0;j<5;j++) {
+ if (wal->EncounterAreaResRef[(i+j)%5][0]) {
+ PyDict_SetItemString(dict, "Destination", PyString_FromString (wal->EncounterAreaResRef[(i+j)%5]) );
+ PyDict_SetItemString(dict, "Entrance", PyString_FromString ("") );
+ // do we need to change Direction here?
+ break;
+ }
+ }
+ }
+
+ //the entrance the user will fall on
+ PyDict_SetItemString(dict, "Distance", PyInt_FromLong (wm->GetDistance(wmc->Area->AreaName)) );
+ return dict;
+}
+
+PyDoc_STRVAR( GemRB_Window_CreateWorldMapControl__doc,
+"CreateWorldMapControl(WindowIndex, ControlID, x, y, w, h, direction[, font])\n\n"
+"Creates and adds a new WorldMap control to a Window." );
+
+static PyObject* GemRB_Window_CreateWorldMapControl(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlID, x, y, w, h, direction;
+ char *font=NULL;
+
+ if (!PyArg_ParseTuple( args, "iiiiiii|s", &WindowIndex, &ControlID, &x,
+ &y, &w, &h, &direction, &font )) {
+ return AttributeError( GemRB_Window_CreateWorldMapControl__doc );
+ }
+
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+ int CtrlIndex = core->GetControl( WindowIndex, ControlID );
+ if (CtrlIndex != -1) {
+ Control *ctrl = win->GetControl( CtrlIndex );
+ x = ctrl->XPos;
+ y = ctrl->YPos;
+ w = ctrl->Width;
+ h = ctrl->Height;
+ //flags = ctrl->Value;
+ win->DelControl( CtrlIndex );
+ }
+ WorldMapControl* wmap = new WorldMapControl( font?font:"", direction );
+ wmap->XPos = x;
+ wmap->YPos = y;
+ wmap->Width = w;
+ wmap->Height = h;
+ wmap->ControlID = ControlID;
+ wmap->ControlType = IE_GUI_WORLDMAP;
+ wmap->Owner = win;
+ win->AddControl( wmap );
+
+ int ret = core->GetControl( WindowIndex, ControlID );
+
+ if (ret<0) {
+ return NULL;
+ }
+ return PyInt_FromLong( ret );
+ //Py_INCREF( Py_None );
+ //return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_WorldMap_SetTextColor__doc,
+"SetWorldMapTextColor(WindowIndex, ControlIndex, which, red, green, blue)\n\n"
+"Sets the label colors of a WorldMap Control. WHICH selects color affected"
+"and is one of IE_GUI_WMAP_COLOR_(BACKGROUND|NORMAL|SELECTED|NOTVISITED)." );
+
+static PyObject* GemRB_WorldMap_SetTextColor(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, which, r, g, b, a;
+
+ if (!PyArg_ParseTuple( args, "iiiiiii", &WindowIndex, &ControlIndex, &which, &r, &g, &b, &a )) {
+ return AttributeError( GemRB_WorldMap_SetTextColor__doc );
+ }
+
+ WorldMapControl* wmap = ( WorldMapControl* ) GetControl( WindowIndex, ControlIndex, IE_GUI_WORLDMAP);
+ if (!wmap) {
+ return NULL;
+ }
+
+ Color color = {r, g, b, a};
+ wmap->SetColor( which, color );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_Window_CreateMapControl__doc,
+"CreateMapControl(WindowIndex, ControlID, x, y, w, h, "
+"[LabelID, FlagResRef[, Flag2ResRef]])\n\n"
+"Creates and adds a new Area Map Control to a Window.\n"
+"Note: LabelID is an ID, not an index. "
+"If there are two flags given, they will be considered a BMP.");
+
+static PyObject* GemRB_Window_CreateMapControl(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlID, x, y, w, h;
+ int LabelID;
+ char *Flag=NULL;
+ char *Flag2=NULL;
+
+ if (!PyArg_ParseTuple( args, "iiiiiiis|s", &WindowIndex, &ControlID,
+ &x, &y, &w, &h, &LabelID, &Flag, &Flag2)) {
+ Flag=NULL;
+ PyErr_Clear(); //clearing the exception
+ if (!PyArg_ParseTuple( args, "iiiiii", &WindowIndex, &ControlID,
+ &x, &y, &w, &h)) {
+ return AttributeError( GemRB_Window_CreateMapControl__doc );
+ }
+ }
+ Window* win = core->GetWindow( WindowIndex );
+ if (win == NULL) {
+ return RuntimeError("Cannot find window!");
+ }
+ int CtrlIndex = core->GetControl( WindowIndex, ControlID );
+ if (CtrlIndex != -1) {
+ Control *ctrl = win->GetControl( CtrlIndex );
+ x = ctrl->XPos;
+ y = ctrl->YPos;
+ w = ctrl->Width;
+ h = ctrl->Height;
+ // do *not* delete the existing control, we want to replace
+ // it in the sort order!
+ //win->DelControl( CtrlIndex );
+ }
+
+ MapControl* map = new MapControl( );
+ map->XPos = x;
+ map->YPos = y;
+ map->Width = w;
+ map->Height = h;
+ map->ControlID = ControlID;
+ map->ControlType = IE_GUI_MAP;
+ map->Owner = win;
+ if (Flag2) { //pst flavour
+ map->convertToGame = false;
+ CtrlIndex = core->GetControl( WindowIndex, LabelID );
+ Control *lc = win->GetControl( CtrlIndex );
+ map->LinkedLabel = lc;
+ ResourceHolder<ImageMgr> anim(Flag);
+ if (anim) {
+ map->Flag[0] = anim->GetSprite2D();
+ }
+ ResourceHolder<ImageMgr> anim2(Flag2);
+ if (anim2) {
+ map->Flag[1] = anim2->GetSprite2D();
+ }
+ goto setup_done;
+ }
+ if (Flag) {
+ CtrlIndex = core->GetControl( WindowIndex, LabelID );
+ Control *lc = win->GetControl( CtrlIndex );
+ map->LinkedLabel = lc;
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( Flag,
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (af) {
+ for (int i=0;i<8;i++) {
+ map->Flag[i] = af->GetFrame(0,i);
+ }
+
+ }
+ }
+setup_done:
+ win->AddControl( map );
+
+ int ret = core->GetControl( WindowIndex, ControlID );
+
+ if (ret<0) {
+ return NULL;
+ }
+ return PyInt_FromLong( ret );
+ //Py_INCREF( Py_None );
+ //return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_SetPos__doc,
+"SetControlPos(WindowIndex, ControlIndex, X, Y)\n\n"
+"Moves a Control." );
+
+static PyObject* GemRB_Control_SetPos(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, X, Y;
+
+ if (!PyArg_ParseTuple( args, "iiii", &WindowIndex, &ControlIndex, &X, &Y )) {
+ return AttributeError( GemRB_Control_SetPos__doc );
+ }
+
+ Control* ctrl = GetControl(WindowIndex, ControlIndex, -1);
+ if (!ctrl) {
+ return NULL;
+ }
+
+ ctrl->XPos = X;
+ ctrl->YPos = Y;
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_SetSize__doc,
+"SetControlSize(WindowIndex, ControlIndex, Width, Height)\n\n"
+"Resizes a Control." );
+
+static PyObject* GemRB_Control_SetSize(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, Width, Height;
+
+ if (!PyArg_ParseTuple( args, "iiii", &WindowIndex, &ControlIndex, &Width,
+ &Height )) {
+ return AttributeError( GemRB_Control_SetSize__doc );
+ }
+
+ Control* ctrl = GetControl(WindowIndex, ControlIndex, -1);
+ if (!ctrl) {
+ return NULL;
+ }
+
+ ctrl->Width = Width;
+ ctrl->Height = Height;
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Label_SetUseRGB__doc,
+"SetLabelUseRGB(WindowIndex, ControlIndex, status)\n\n"
+"Tells a Label to use the RGB colors with the text." );
+
+static PyObject* GemRB_Label_SetUseRGB(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, status;
+
+ if (!PyArg_ParseTuple( args, "iii", &WindowIndex, &ControlIndex, &status )) {
+ return AttributeError( GemRB_Label_SetUseRGB__doc );
+ }
+
+ Label* lab = (Label *) GetControl(WindowIndex, ControlIndex, IE_GUI_LABEL);
+ if (!lab) {
+ return NULL;
+ }
+
+ lab->useRGB = ( status != 0 );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GameSetPartySize__doc,
+"GameSetPartySize(size)\n\n"
+"Sets the maximum party size." );
+
+static PyObject* GemRB_GameSetPartySize(PyObject * /*self*/, PyObject* args)
+{
+ int Flags;
+
+ if (!PyArg_ParseTuple( args, "i", &Flags )) {
+ return AttributeError( GemRB_GameSetPartySize__doc );
+ }
+
+ GET_GAME();
+
+ game->SetPartySize( Flags );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GameSetProtagonistMode__doc,
+"GameSetProtagonistMode(PM)\n\n"
+"Sets the protagonist mode, 0-no check, 1-protagonist, 2-team." );
+
+static PyObject* GemRB_GameSetProtagonistMode(PyObject * /*self*/, PyObject* args)
+{
+ int Flags;
+
+ if (!PyArg_ParseTuple( args, "i", &Flags )) {
+ return AttributeError( GemRB_GameSetProtagonistMode__doc );
+ }
+
+ GET_GAME();
+
+ game->SetProtagonistMode( Flags );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GameGetExpansion__doc,
+"GameGetExpansion()=>int\n\n"
+"Gets the expansion mode." );
+static PyObject* GemRB_GameGetExpansion(PyObject * /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ return PyInt_FromLong( game->Expansion );
+}
+
+PyDoc_STRVAR( GemRB_GameSetExpansion__doc,
+"GameSetExpansion(value)=>bool\n\n"
+"Sets the expansion mode, returns false if it was already set." );
+
+static PyObject* GemRB_GameSetExpansion(PyObject * /*self*/, PyObject* args)
+{
+ int value;
+
+ if (!PyArg_ParseTuple( args, "i", &value )) {
+ return AttributeError( GemRB_GameSetExpansion__doc );
+ }
+
+ GET_GAME();
+
+ if ((unsigned int) value<=game->Expansion) {
+ Py_INCREF( Py_False );
+ return Py_False;
+ }
+ game->SetExpansion(value);
+ Py_INCREF( Py_True );
+ return Py_True;
+}
+
+PyDoc_STRVAR( GemRB_GameSetScreenFlags__doc,
+"GameSetScreenFlags(Flags, Operation)\n\n"
+"Sets the Display Flags of the main game screen (pane status, dialog textarea size)." );
+
+static PyObject* GemRB_GameSetScreenFlags(PyObject * /*self*/, PyObject* args)
+{
+ int Flags, Operation;
+
+ if (!PyArg_ParseTuple( args, "ii", &Flags, &Operation )) {
+ return AttributeError( GemRB_GameSetScreenFlags__doc );
+ }
+ if (Operation < BM_SET || Operation > BM_NAND) {
+ printMessage( "GUIScript",
+ "Syntax Error: operation must be 0-4\n", LIGHT_RED );
+ return NULL;
+ }
+
+ GET_GAME();
+
+ game->SetControlStatus( Flags, Operation );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GameControlSetScreenFlags__doc,
+"GameControlSetScreenFlags(Flags, Operation)\n\n"
+"Sets the Display Flags of the main game screen control (centeronactor,...)." );
+
+static PyObject* GemRB_GameControlSetScreenFlags(PyObject * /*self*/, PyObject* args)
+{
+ int Flags, Operation;
+
+ if (!PyArg_ParseTuple( args, "ii", &Flags, &Operation )) {
+ return AttributeError( GemRB_GameControlSetScreenFlags__doc );
+ }
+ if (Operation < BM_SET || Operation > BM_NAND) {
+ return AttributeError("Operation must be 0-4\n");
+ }
+
+ GET_GAMECONTROL();
+
+ gc->SetScreenFlags( Flags, Operation );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_GameControlSetTargetMode__doc,
+"GameControlSetTargetMode(Mode, Types)\n\n"
+"Sets the targetting mode of the main game screen control (attack, cast spell,...) and type of target (ally, enemy and/or neutral; all by default)" );
+
+static PyObject* GemRB_GameControlSetTargetMode(PyObject * /*self*/, PyObject* args)
+{
+ int Mode;
+ int Types = GA_SELECT | GA_NO_DEAD | GA_NO_HIDDEN;
+
+ if (!PyArg_ParseTuple( args, "i|i", &Mode, &Types )) {
+ return AttributeError( GemRB_GameControlSetTargetMode__doc );
+ }
+
+ GET_GAMECONTROL();
+
+ //target mode is only the low bits (which is a number)
+ gc->SetTargetMode(Mode&GA_ACTION);
+ //target type is all the bits
+ gc->target_types = (Mode&GA_ACTION)|Types;
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GameControlGetTargetMode__doc,
+"GameControlGetTargetMode() => Mode\n\n"
+"Returns the targetting mode of the main game screen control (attack, cast spell,...)." );
+
+static PyObject* GemRB_GameControlGetTargetMode(PyObject * /*self*/, PyObject* /*args*/)
+{
+ GET_GAMECONTROL();
+
+ return PyInt_FromLong(gc->GetTargetMode());
+}
+
+PyDoc_STRVAR( GemRB_Button_SetFlags__doc,
+"SetButtonFlags(WindowIndex, ControlIndex, Flags, Operation)\n\n"
+"Sets the Display Flags of a Button." );
+
+static PyObject* GemRB_Button_SetFlags(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, Flags, Operation;
+
+ if (!PyArg_ParseTuple( args, "iiii", &WindowIndex, &ControlIndex, &Flags, &Operation )) {
+ return AttributeError( GemRB_Button_SetFlags__doc );
+ }
+ if (Operation < BM_SET || Operation > BM_NAND) {
+ printMessage( "GUIScript",
+ "Syntax Error: operation must be 0-4\n", LIGHT_RED );
+ return NULL;
+ }
+
+ Control* btn = ( Control* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ if (btn->SetFlags( Flags, Operation ) ) {
+ printMessage ("GUIScript", "Flag cannot be set!\n",LIGHT_RED);
+ return NULL;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_TextArea_SetFlags__doc,
+"SetTextAreaFlags(WindowIndex, ControlIndex, Flags, Operation)\n\n"
+"Sets the Display Flags of a TextArea. Flags are: IE_GUI_TA_SELECTABLE, IE_GUI_TA_AUTOSCROLL, IE_GUI_TA_SMOOTHSCROLL. Operation defaults to OP_SET." );
+
+static PyObject* GemRB_Control_TextArea_SetFlags(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, Flags;
+ int Operation=0;
+
+ if (!PyArg_ParseTuple( args, "iii|i", &WindowIndex, &ControlIndex, &Flags, &Operation )) {
+ return AttributeError( GemRB_Control_TextArea_SetFlags__doc );
+ }
+ if (Operation < BM_SET || Operation > BM_NAND) {
+ printMessage( "GUIScript",
+ "Syntax Error: operation must be 0-4\n", LIGHT_RED );
+ return NULL;
+ }
+
+ Control* ta = ( Control* ) GetControl(WindowIndex, ControlIndex, IE_GUI_TEXTAREA);
+ if (!ta) {
+ return NULL;
+ }
+
+ if (ta->SetFlags( Flags, Operation )) {
+ printMessage ("GUIScript", "Flag cannot be set!\n",LIGHT_RED);
+ return NULL;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_ScrollBar_SetDefaultScrollBar__doc,
+"SetDefaultScrollBar(WindowIndex, ControlIndex)\n\n"
+"Sets the ScrollBar control as default." );
+
+static PyObject* GemRB_ScrollBar_SetDefaultScrollBar(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+
+ if (!PyArg_ParseTuple( args, "ii", &WindowIndex, &ControlIndex)) {
+ return AttributeError( GemRB_ScrollBar_SetDefaultScrollBar__doc );
+ }
+
+ Control* sb = ( Control* ) GetControl(WindowIndex, ControlIndex, IE_GUI_SCROLLBAR);
+ if (!sb) {
+ return NULL;
+ }
+
+ sb->SetFlags( (IE_GUI_SCROLLBAR<<24) | IE_GUI_SCROLLBAR_DEFAULT, BM_OR );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetState__doc,
+"SetButtonState(WindowIndex, ControlIndex, State)\n\n"
+"Sets the state of a Button Control." );
+
+static PyObject* GemRB_Button_SetState(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, state;
+
+ if (!PyArg_ParseTuple( args, "iii", &WindowIndex, &ControlIndex, &state )) {
+ return AttributeError( GemRB_Button_SetState__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ btn->SetState( state );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetPictureClipping__doc,
+"SetButtonPictureClipping(Window, Button, ClippingPercent)\n\n"
+"Sets percent (0-1.0) of width to which button picture will be clipped." );
+
+static PyObject* GemRB_Button_SetPictureClipping(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ double Clipping;
+
+ if (!PyArg_ParseTuple( args, "iid", &WindowIndex, &ControlIndex, &Clipping )) {
+ return AttributeError( GemRB_Button_SetPictureClipping__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ if (Clipping<0.0) Clipping = 0.0;
+ else if (Clipping>1.0) Clipping = 1.0;
+ btn->SetPictureClipping( Clipping );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetPicture__doc,
+"SetButtonPicture(WindowIndex, ControlIndex, PictureResRef, DefaultResRef)\n\n"
+"Sets the Picture of a Button Control from a BMP file. You can also supply a default picture." );
+
+static PyObject* GemRB_Button_SetPicture(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ char *ResRef;
+ char *DefResRef = NULL;
+
+ if (!PyArg_ParseTuple( args, "iis|s", &WindowIndex, &ControlIndex, &ResRef, &DefResRef )) {
+ return AttributeError( GemRB_Button_SetPicture__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return RuntimeError("Cannot find the button!\n");
+ }
+
+ if (ResRef[0] == 0) {
+ btn->SetPicture( NULL );
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ ImageFactory* fact = ( ImageFactory* )
+ gamedata->GetFactoryResource( ResRef, IE_BMP_CLASS_ID, IE_NORMAL );
+
+ //if the resource doesn't exist, but we have a default resource
+ //use this resource
+ if (!fact && DefResRef) {
+ fact = ( ImageFactory* )
+ gamedata->GetFactoryResource( DefResRef, IE_BMP_CLASS_ID, IE_NORMAL );
+ }
+
+ if (!fact) {
+ return RuntimeError("Picture resource not found!\n");
+ }
+
+ Sprite2D* Picture = fact->GetSprite2D();
+ if (Picture == NULL) {
+ return RuntimeError("Failed to acquire the picture!\n");
+ }
+
+ btn->SetPicture( Picture );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetSprite2D__doc,
+"Button.SetSprite2D(WindowIndex, ControlIndex, Sprite2D)\n\n"
+"Sets a Sprite2D onto a button as picture." );
+
+static PyObject* GemRB_Button_SetSprite2D(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci;
+ PyObject *obj;
+
+ if (!PyArg_ParseTuple( args, "iiO", &wi, &ci, &obj )) {
+ return AttributeError( GemRB_Button_SetSprite2D__doc );
+ }
+ Button* btn = ( Button* ) GetControl( wi, ci, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ CObject<Sprite2D> spr(obj);
+
+ btn->SetPicture( spr.get() );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetMOS__doc,
+"SetButtonMOS(WindowIndex, ControlIndex, MOSResRef)\n\n"
+"Sets the Picture of a Button Control from a MOS file." );
+
+static PyObject* GemRB_Button_SetMOS(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ char *ResRef;
+
+ if (!PyArg_ParseTuple( args, "iis", &WindowIndex, &ControlIndex, &ResRef )) {
+ return AttributeError( GemRB_Button_SetMOS__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ if (ResRef[0] == 0) {
+ btn->SetPicture( NULL );
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ ResourceHolder<ImageMgr> im(ResRef);
+ if (im == NULL) {
+ return RuntimeError("Picture resource not found!\n");
+ }
+
+ Sprite2D* Picture = im->GetSprite2D();
+ if (Picture == NULL) {
+ return RuntimeError("Failed to acquire the picture!\n");
+ }
+
+ btn->SetPicture( Picture );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetPLT__doc,
+"SetButtonPLT(WindowIndex, ControlIndex, PLTResRef, col1, col2, col3, col4, col5, col6, col7, col8, type)\n\n"
+"Sets the Picture of a Button Control from a PLT file." );
+
+static PyObject* GemRB_Button_SetPLT(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex;
+ ieDword col[8];
+ int type = 0;
+ char *ResRef;
+
+ memset(col,-1,sizeof(col));
+ if (!PyArg_ParseTuple( args, "iisiiiiiiii|i", &WindowIndex, &ControlIndex,
+ &ResRef, &(col[0]), &(col[1]), &(col[2]), &(col[3]),
+ &(col[4]), &(col[5]), &(col[6]), &(col[7]), &type) ) {
+ return AttributeError( GemRB_Button_SetPLT__doc );
+ }
+
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ //empty image
+ if (ResRef[0] == 0 || ResRef[0]=='*') {
+ btn->SetPicture( NULL );
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ Sprite2D *Picture;
+ Sprite2D *Picture2=NULL;
+
+ ResourceHolder<PalettedImageMgr> im(ResRef);
+
+ if (im == NULL ) {
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( ResRef,
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (!af) {
+ printMessage("GUISCript","PLT/BAM not found for ref: ",YELLOW);
+ printf("%s\n", ResRef);
+ textcolor(WHITE);
+ if (type == 0)
+ return NULL;
+ else {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ }
+
+ Picture = af->GetPaperdollImage(col[0]==0xFFFFFFFF?0:col, Picture2,(unsigned int)type);
+ if (Picture == NULL) {
+ printf ("Picture == NULL\n");
+ return NULL;
+ }
+ } else {
+ Picture = im->GetSprite2D(type, col);
+ if (Picture == NULL) {
+ printf ("Picture == NULL\n");
+ return NULL;
+ }
+ }
+
+ if (type == 0)
+ btn->ClearPictureList();
+ btn->StackPicture(Picture);
+ if (Picture2) {
+ btn->SetFlags ( IE_GUI_BUTTON_BG1_PAPERDOLL, BM_OR );
+ btn->StackPicture( Picture2 );
+ } else if (type == 0) {
+ btn->SetFlags ( ~IE_GUI_BUTTON_BG1_PAPERDOLL, BM_AND );
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetBAM__doc,
+"SetButtonBAM(WindowIndex, ControlIndex, BAMResRef, CycleIndex, FrameIndex, col1)\n\n"
+"Sets the Picture of a Button Control from a BAM file. If col1 is >= 0, changes palette picture's palette to one specified by col1. Since it uses 12 colors palette, it has issues in PST." );
+
+static PyObject* SetButtonBAM(int wi, int ci, const char *ResRef, int CycleIndex, int FrameIndex, int col1)
+{
+ Button* btn = ( Button* ) GetControl(wi, ci, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ if (ResRef[0] == 0) {
+ btn->SetPicture( NULL );
+ //no incref! (happens in caller if necessary)
+ return Py_None;
+ }
+
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( ResRef,
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (!af)
+ return NULL;
+ Sprite2D* Picture = af->GetFrame ( FrameIndex, CycleIndex );
+
+ if (Picture == NULL) {
+ return NULL;
+ }
+
+ if (col1 >= 0) {
+ Sprite2D* old = Picture;
+ Picture = core->GetVideoDriver()->DuplicateSprite(old);
+ core->GetVideoDriver()->FreeSprite(old);
+
+ Palette* newpal = Picture->GetPalette()->Copy();
+ core->GetPalette( col1, 12, &newpal->col[4]);
+ Picture->SetPalette( newpal );
+ newpal->Release();
+ }
+
+ btn->SetPicture( Picture );
+
+ //no incref! (happens in caller if necessary)
+ return Py_None;
+}
+
+static PyObject* GemRB_Button_SetBAM(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci, CycleIndex, FrameIndex, col1 = -1;
+ char *ResRef;
+
+ if (!PyArg_ParseTuple( args, "iisii|i", &wi, &ci,
+ &ResRef, &CycleIndex, &FrameIndex, &col1 )) {
+ return AttributeError( GemRB_Button_SetBAM__doc );
+ }
+
+ PyObject *ret = SetButtonBAM(wi,ci, ResRef, CycleIndex, FrameIndex,col1);
+ if (ret) {
+ Py_INCREF(ret);
+ }
+ return ret;
+}
+
+PyDoc_STRVAR( GemRB_Control_SetAnimationPalette__doc,
+"SetAnimationPalette(WindowIndex, ControlIndex, col1, col2, col3, col4, col5, col6, col7, col8)\n\n"
+"Sets the palette of an animation already assigned to the button.");
+
+static PyObject* GemRB_Control_SetAnimationPalette(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci;
+ ieDword col[8];
+
+ memset(col,-1,sizeof(col));
+ if (!PyArg_ParseTuple( args, "iiiiiiiiii", &wi, &ci,
+ &(col[0]), &(col[1]), &(col[2]), &(col[3]),
+ &(col[4]), &(col[5]), &(col[6]), &(col[7])) ) {
+ return AttributeError( GemRB_Control_SetAnimationPalette__doc );
+ }
+
+ Control* ctl = GetControl(wi, ci, -1);
+ if (!ctl) {
+ return NULL;
+ }
+
+ ControlAnimation* anim = ctl->animation;
+ if (!anim) {
+ return RuntimeError("No animation!");
+ }
+
+ anim->SetPaletteGradients(col);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Control_SetAnimation__doc,
+"SetAnimation(WindowIndex, ControlIndex, BAMResRef[, Cycle])\n\n"
+"Sets the animation of a Control (usually a Button) from a BAM file. Optionally an animation cycle could be set too.");
+
+static PyObject* GemRB_Control_SetAnimation(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci;
+ char *ResRef;
+ int Cycle = 0;
+
+ if (!PyArg_ParseTuple( args, "iis|i", &wi, &ci, &ResRef, &Cycle )) {
+ return AttributeError( GemRB_Control_SetAnimation__doc );
+ }
+
+ Control* ctl = GetControl(wi, ci, -1);
+ if (!ctl) {
+ return NULL;
+ }
+
+ //who knows, there might have been an active animation lurking
+ if (ctl->animation) {
+ //if this control says the resource is the same
+ //we wanted to set, we don't reset it
+ if(ctl->animation->SameResource(ResRef, Cycle)) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ delete ctl->animation;
+ ctl->animation = NULL;
+ }
+
+ if (ResRef[0] == 0) {
+ ctl->SetAnimPicture( NULL );
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ ControlAnimation* anim = new ControlAnimation( ctl, ResRef, Cycle );
+
+ anim->UpdateAnimation();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_VerbalConstant__doc,
+"VerbalConstant(PartyID, str)\n\n"
+"Plays a Character's SoundSet entry." );
+
+static PyObject* GemRB_VerbalConstant(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, str;
+ char Sound[_MAX_PATH];
+
+ if (!PyArg_ParseTuple( args, "ii", &PartyID, &str )) {
+ return AttributeError( GemRB_VerbalConstant__doc );
+ }
+
+ GET_GAME();
+
+ Actor *actor = game->FindPC(PartyID);
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ if (str<0 || str>=VCONST_COUNT) {
+ return AttributeError( "SoundSet Entry is too large" );
+ }
+
+ //get soundset based string constant
+ snprintf(Sound, _MAX_PATH, "%s/%s%02d",
+ actor->PCStats->SoundFolder, actor->PCStats->SoundSet, str);
+ core->GetAudioDrv()->Play( Sound, 0, 0, GEM_SND_RELATIVE|GEM_SND_SPEECH);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_PlaySound__doc,
+"PlaySound(SoundResource, xpos, ypos, type)\n\n"
+"Plays a Sound." );
+
+static PyObject* GemRB_PlaySound(PyObject * /*self*/, PyObject* args)
+{
+ char *ResRef;
+ int xpos = 0;
+ int ypos = 0;
+ unsigned int flags = 1; //GEM_SND_RELATIVE
+
+ if (!PyArg_ParseTuple( args, "z|iii", &ResRef, &xpos, &ypos, &flags )) {
+ return AttributeError( GemRB_PlaySound__doc );
+ }
+
+ core->GetAudioDrv()->Play( ResRef, xpos, ypos, flags );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_DrawWindows__doc,
+"DrawWindows()\n\n"
+"Refreshes the User Interface." );
+
+static PyObject* GemRB_DrawWindows(PyObject * /*self*/, PyObject * /*args*/)
+{
+ core->DrawWindows();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Quit__doc,
+"Quit()\n\n"
+"Quits GemRB." );
+
+static PyObject* GemRB_Quit(PyObject * /*self*/, PyObject * /*args*/)
+{
+ bool ret = core->Quit();
+ if (!ret) {
+ return NULL;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_LoadMusicPL__doc,
+"LoadMusicPL(MusicPlayListResource, HardEnd)\n\n"
+"Loads and starts a Music PlayList." );
+
+static PyObject* GemRB_LoadMusicPL(PyObject * /*self*/, PyObject* args)
+{
+ char *ResRef;
+ int HardEnd = 0;
+
+ if (!PyArg_ParseTuple( args, "s|i", &ResRef, &HardEnd )) {
+ return AttributeError( GemRB_LoadMusicPL__doc );
+ }
+
+ core->GetMusicMgr()->SwitchPlayList( ResRef, (bool) HardEnd );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SoftEndPL__doc,
+"SoftEndPL()\n\n"
+"Ends a Music Playlist softly." );
+
+static PyObject* GemRB_SoftEndPL(PyObject * /*self*/, PyObject * /*args*/)
+{
+ core->GetMusicMgr()->End();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_HardEndPL__doc,
+"HardEndPL()\n\n"
+"Ends a Music Playlist immediately." );
+
+static PyObject* GemRB_HardEndPL(PyObject * /*self*/, PyObject * /*args*/)
+{
+ core->GetMusicMgr()->HardEnd();
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetToken__doc,
+"SetToken(VariableName, Value)\n\n"
+"Set/Create a token to be replaced in StrRefs." );
+
+static PyObject* GemRB_SetToken(PyObject * /*self*/, PyObject* args)
+{
+ char *Variable;
+ char *value;
+
+ if (!PyArg_ParseTuple( args, "ss", &Variable, &value )) {
+ return AttributeError( GemRB_SetToken__doc );
+ }
+ core->GetTokenDictionary()->SetAtCopy( Variable, value );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetVar__doc,
+"SetVar(VariableName, Value)\n\n"
+"Set/Create a Variable in the Global Dictionary." );
+
+static PyObject* GemRB_SetVar(PyObject * /*self*/, PyObject* args)
+{
+ char *Variable;
+ //this should be 32 bits, always, but i cannot tell that to Python
+ unsigned long value;
+
+ if (!PyArg_ParseTuple( args, "sl", &Variable, &value )) {
+ return AttributeError( GemRB_SetVar__doc );
+ }
+
+ core->GetDictionary()->SetAt( Variable, (ieDword) value );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetMessageWindowSize__doc,
+"GetMessageWindowSize() => int\n\n"
+"Returns current MessageWindowSize, it works only when a game is loaded." );
+
+static PyObject* GemRB_GetMessageWindowSize(PyObject * /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ return PyInt_FromLong( game->ControlStatus );
+}
+
+PyDoc_STRVAR( GemRB_GetToken__doc,
+"GetToken(VariableName) => string\n\n"
+"Get a Variable value from the Token Dictionary." );
+
+static PyObject* GemRB_GetToken(PyObject * /*self*/, PyObject* args)
+{
+ const char *Variable;
+ char *value;
+
+ if (!PyArg_ParseTuple( args, "s", &Variable )) {
+ return AttributeError( GemRB_GetToken__doc );
+ }
+
+ //returns only the pointer
+ if (!core->GetTokenDictionary()->Lookup( Variable, value )) {
+ return PyString_FromString( "" );
+ }
+
+ return PyString_FromString( value );
+}
+
+PyDoc_STRVAR( GemRB_GetVar__doc,
+"GetVar(VariableName) => int\n\n"
+"Get a Variable value from the Global Dictionary." );
+
+static PyObject* GemRB_GetVar(PyObject * /*self*/, PyObject* args)
+{
+ const char *Variable;
+ ieDword value;
+
+ if (!PyArg_ParseTuple( args, "s", &Variable )) {
+ return AttributeError( GemRB_GetVar__doc );
+ }
+
+ if (!core->GetDictionary()->Lookup( Variable, value )) {
+ return PyInt_FromLong( 0 );
+ }
+
+ // A PyInt is internally (probably) a long. Since we sometimes set
+ // variables to -1, cast value to a signed integer first, so it is
+ // sign-extended into a long if long is larger than int.
+ return PyInt_FromLong( (int)value );
+}
+
+PyDoc_STRVAR( GemRB_CheckVar__doc,
+"CheckVar(VariableName, Context) => long\n\n"
+"Return (and output on terminal) the value of a Game Variable." );
+
+static PyObject* GemRB_CheckVar(PyObject * /*self*/, PyObject* args)
+{
+ char *Variable;
+ char *Context;
+
+ if (!PyArg_ParseTuple( args, "ss", &Variable, &Context )) {
+ return AttributeError( GemRB_CheckVar__doc );
+ }
+ GET_GAMECONTROL();
+
+ Scriptable *Sender = (Scriptable *) gc->GetLastActor();
+ if (!Sender) {
+ GET_GAME();
+
+ Sender = (Scriptable *) game->GetCurrentArea();
+ }
+
+ if (!Sender) {
+ printMessage("GUIScript","No Sender!\n", LIGHT_RED);
+ return NULL;
+ }
+ long value =(long) CheckVariable(Sender, Variable, Context);
+ printMessage("GUISCript"," ",YELLOW);
+ printf("%s %s=%ld\n",Context, Variable, value);
+ textcolor(WHITE);
+ return PyInt_FromLong( value );
+}
+
+PyDoc_STRVAR( GemRB_SetGlobal__doc,
+"SetGlobal(VariableName, Context, Value)\n\n"
+"Sets a gamescript variable to the specificed numeric value." );
+
+static PyObject* GemRB_SetGlobal(PyObject * /*self*/, PyObject* args)
+{
+ char *Variable;
+ char *Context;
+ int Value;
+
+ if (!PyArg_ParseTuple( args, "ssi", &Variable, &Context, &Value )) {
+ return AttributeError( GemRB_SetGlobal__doc );
+ }
+
+ Scriptable *Sender = NULL;
+
+ GET_GAME();
+
+ if (!strnicmp(Context, "MYAREA", 6) || !strnicmp(Context, "LOCALS", 6)) {
+ GET_GAMECONTROL();
+
+ Sender = (Scriptable *) gc->GetLastActor();
+ if (!Sender) {
+ Sender = (Scriptable *) game->GetCurrentArea();
+ }
+ if (!Sender) {
+ printMessage("GUIScript","No Sender!\n", LIGHT_RED);
+ return NULL;
+ }
+ } // else GLOBAL, area name or KAPUTZ
+
+ SetVariable(Sender, Variable, Context, (ieDword) Value);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetGameVar__doc,
+"GetGameVar(VariableName) => long\n\n"
+"Get a Variable value from the Game Global Dictionary." );
+
+static PyObject* GemRB_GetGameVar(PyObject * /*self*/, PyObject* args)
+{
+ const char *Variable;
+ ieDword value;
+
+ if (!PyArg_ParseTuple( args, "s", &Variable )) {
+ return AttributeError( GemRB_GetGameVar__doc );
+ }
+
+ GET_GAME();
+
+ if (!game->locals->Lookup( Variable, value )) {
+ return PyInt_FromLong( ( unsigned long ) 0 );
+ }
+
+ return PyInt_FromLong( (unsigned long) value );
+}
+
+PyDoc_STRVAR( GemRB_PlayMovie__doc,
+"PlayMovie(MOVResRef[, flag]) => int\n\n"
+"Plays the movie named. If flag is set to 1, it won't play it if it was already played once (set in .ini). It returns 0 if it played the movie." );
+
+static PyObject* GemRB_PlayMovie(PyObject * /*self*/, PyObject* args)
+{
+ const char *string;
+ int flag = 0;
+
+ if (!PyArg_ParseTuple( args, "s|i", &string, &flag )) {
+ return AttributeError( GemRB_PlayMovie__doc );
+ }
+
+ ieDword ind = 0;
+
+ //Lookup will leave the flag untouched if it doesn't exist yet
+ core->GetDictionary()->Lookup(string, ind);
+ if (flag)
+ ind = 0;
+ if (!ind) {
+ ind = core->PlayMovie( string );
+ }
+ //don't return NULL
+ return PyInt_FromLong( ind );
+}
+
+PyDoc_STRVAR( GemRB_SaveCharacter__doc,
+"SaveCharacter(PartyID, CharName)\n\n"
+"Exports the character from party.");
+
+static PyObject* GemRB_SaveCharacter(PyObject * /*self*/, PyObject * args)
+{
+ int PartyID;
+ const char *name;
+
+ if (!PyArg_ParseTuple( args, "is", &PartyID, &name )) {
+ return AttributeError( GemRB_SaveCharacter__doc );
+ }
+ if (!name[0]) {
+ return AttributeError( GemRB_SaveCharacter__doc );
+ }
+
+ GET_GAME();
+
+ Actor *actor = game->FindPC(PartyID);
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ return PyInt_FromLong(core->WriteCharacter(name, actor) );
+}
+
+PyDoc_STRVAR( GemRB_SaveGame__doc,
+"SaveGame(SlotCount, GameName[,version])\n\n"
+"Dumps the save game, if version is given, it will save a specific version.");
+
+static PyObject* GemRB_SaveGame(PyObject * /*self*/, PyObject * args)
+{
+ PyObject *obj;
+ int Version = -1;
+ const char *folder;
+
+ if (!PyArg_ParseTuple( args, "Os|i", &obj, &folder, &Version )) {
+ return AttributeError( GemRB_SaveGame__doc );
+ }
+
+ CObject<SaveGame> save(obj);
+
+ GET_GAME();
+
+ SaveGameIterator *sgi = core->GetSaveGameIterator();
+ if (!sgi) {
+ return RuntimeError("No savegame iterator");
+ }
+
+ if (Version>0) {
+ game->version = Version;
+ }
+ return PyInt_FromLong(sgi->CreateSaveGame(save, folder) );
+}
+
+PyDoc_STRVAR( GemRB_GetSaveGames__doc,
+"GetSaveGameCount() => int\n\n"
+"Returns the list of saved games." );
+
+static PyObject* GemRB_GetSaveGames(PyObject * /*self*/, PyObject * /*args*/)
+{
+ return MakePyList<SaveGame>(core->GetSaveGameIterator()->GetSaveGames());
+}
+
+PyDoc_STRVAR( GemRB_DeleteSaveGame__doc,
+"DeleteSaveGame(Slot)\n\n"
+"Deletes a saved game folder completely." );
+
+static PyObject* GemRB_DeleteSaveGame(PyObject * /*self*/, PyObject* args)
+{
+ PyObject *Slot;
+
+ if (!PyArg_ParseTuple( args, "O", &Slot )) {
+ return AttributeError( GemRB_DeleteSaveGame__doc );
+ }
+
+ CObject<SaveGame> game(Slot);
+ core->GetSaveGameIterator()->DeleteSaveGame( game );
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SaveGame_GetName__doc,
+"SaveGame.GetName() => string/int\n\n"
+"Returns name of the saved game." );
+
+static PyObject* GemRB_SaveGame_GetName(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* Slot;
+
+ if (!PyArg_ParseTuple( args, "O", &Slot )) {
+ return AttributeError( GemRB_SaveGame_GetName__doc );
+ }
+
+ CObject<SaveGame> save(Slot);
+ return PyString_FromString(save->GetName());
+}
+
+PyDoc_STRVAR( GemRB_SaveGame_GetDate__doc,
+"SaveGame.GetDate() => string/int\n\n"
+"Returns date of the saved game." );
+
+static PyObject* GemRB_SaveGame_GetDate(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* Slot;
+
+ if (!PyArg_ParseTuple( args, "O", &Slot )) {
+ return AttributeError( GemRB_SaveGame_GetDate__doc );
+ }
+
+ CObject<SaveGame> save(Slot);
+ return PyString_FromString(save->GetDate());
+}
+
+PyDoc_STRVAR( GemRB_SaveGame_GetGameDate__doc,
+"SaveGame.GetGameDate() => string/int\n\n"
+"Returns game date of the saved game." );
+
+static PyObject* GemRB_SaveGame_GetGameDate(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* Slot;
+
+ if (!PyArg_ParseTuple( args, "O", &Slot )) {
+ return AttributeError( GemRB_SaveGame_GetGameDate__doc );
+ }
+
+ CObject<SaveGame> save(Slot);
+ return PyString_FromString(save->GetGameDate());
+}
+
+PyDoc_STRVAR( GemRB_SaveGame_GetSaveID__doc,
+"SaveGame.GetSaveID() => string/int\n\n"
+"Returns ID of the saved game." );
+
+static PyObject* GemRB_SaveGame_GetSaveID(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* Slot;
+
+ if (!PyArg_ParseTuple( args, "O", &Slot )) {
+ return AttributeError( GemRB_SaveGame_GetSaveID__doc );
+ }
+
+ CObject<SaveGame> save(Slot);
+ return PyInt_FromLong(save->GetSaveID());
+}
+
+PyDoc_STRVAR( GemRB_SaveGame_GetPreview__doc,
+"SaveGame.GetPreview() => string/int\n\n"
+"Returns preview of the saved game." );
+
+static PyObject* GemRB_SaveGame_GetPreview(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* Slot;
+
+ if (!PyArg_ParseTuple( args, "O", &Slot )) {
+ return AttributeError( GemRB_SaveGame_GetPreview__doc );
+ }
+
+ CObject<SaveGame> save(Slot);
+ return CObject<Sprite2D>(save->GetPreview());
+}
+
+PyDoc_STRVAR( GemRB_SaveGame_GetPortrait__doc,
+"SaveGame.GetPortrait(int index) => string/int\n\n"
+"Returns portrait of the saved game." );
+
+static PyObject* GemRB_SaveGame_GetPortrait(PyObject * /*self*/, PyObject* args)
+{
+ PyObject* Slot;
+ int index;
+
+ if (!PyArg_ParseTuple( args, "Oi", &Slot, &index )) {
+ return AttributeError( GemRB_SaveGame_GetPortrait__doc );
+ }
+
+ CObject<SaveGame> save(Slot);
+ return CObject<Sprite2D>(save->GetPortrait(index));
+}
+
+PyDoc_STRVAR( GemRB_GetGamePreview__doc,
+"GetGamePreview()\n\n"
+"Gets current game area preview." );
+
+static PyObject* GemRB_GetGamePreview(PyObject * /*self*/, PyObject* args)
+{
+ if (!PyArg_ParseTuple( args, "" )) {
+ return AttributeError( GemRB_GetGamePreview__doc );
+ }
+
+ GET_GAMECONTROL();
+ return CObject<Sprite2D>(gc->GetPreview());
+}
+
+PyDoc_STRVAR( GemRB_GetGamePortraitPreview__doc,
+"GetGamePortraitPreview(PCSlotCount)\n\n"
+"Gets a current game PC portrait." );
+
+static PyObject* GemRB_GetGamePortraitPreview(PyObject * /*self*/, PyObject* args)
+{
+ int PCSlotCount;
+
+ if (!PyArg_ParseTuple( args, "i", &PCSlotCount )) {
+ return AttributeError( GemRB_GetGamePreview__doc );
+ }
+
+ GET_GAMECONTROL();
+ return CObject<Sprite2D>(gc->GetPortraitPreview(PCSlotCount));
+}
+
+PyDoc_STRVAR( GemRB_Roll__doc,
+"Roll(Dice, Size, Add) => int\n\n"
+"Calls traditional dice roll." );
+
+static PyObject* GemRB_Roll(PyObject * /*self*/, PyObject* args)
+{
+ int Dice, Size, Add;
+
+ if (!PyArg_ParseTuple( args, "iii", &Dice, &Size, &Add )) {
+ return AttributeError( GemRB_Roll__doc );
+ }
+ return PyInt_FromLong( core->Roll( Dice, Size, Add ) );
+}
+
+PyDoc_STRVAR( GemRB_TextArea_GetPortraits__doc,
+"GetPortraits(WindowIndex, ControlIndex, SmallOrLarge) => int\n\n"
+"Reads in the contents of the portraits subfolder." );
+
+static PyObject* GemRB_TextArea_GetPortraits(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci;
+ int suffix;
+
+ if (!PyArg_ParseTuple( args, "iii", &wi, &ci, &suffix )) {
+ return AttributeError( GemRB_TextArea_GetPortraits__doc );
+ }
+ TextArea* ta = ( TextArea* ) GetControl( wi, ci, IE_GUI_TEXTAREA );
+ if (!ta) {
+ return NULL;
+ }
+ return PyInt_FromLong( core->GetPortraits( ta, suffix ) );
+}
+
+PyDoc_STRVAR( GemRB_TextArea_GetCharSounds__doc,
+"GetCharSounds(WindowIndex, ControlIndex) => int\n\n"
+"Reads in the contents of the sounds subfolder." );
+
+static PyObject* GemRB_TextArea_GetCharSounds(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci;
+
+ if (!PyArg_ParseTuple( args, "ii", &wi, &ci )) {
+ return AttributeError( GemRB_TextArea_GetCharSounds__doc );
+ }
+ TextArea* ta = ( TextArea* ) GetControl( wi, ci, IE_GUI_TEXTAREA );
+ if (!ta) {
+ return NULL;
+ }
+ return PyInt_FromLong( core->GetCharSounds( ta ) );
+}
+
+PyDoc_STRVAR( GemRB_TextArea_GetCharacters__doc,
+"GetCharacters(WindowIndex, ControlIndex) => int\n\n"
+"Reads in the contents of the characters subfolder." );
+
+static PyObject* GemRB_TextArea_GetCharacters(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci;
+
+ if (!PyArg_ParseTuple( args, "ii", &wi, &ci )) {
+ return AttributeError( GemRB_TextArea_GetCharacters__doc );
+ }
+ TextArea* ta = ( TextArea* ) GetControl( wi, ci, IE_GUI_TEXTAREA );
+ if (!ta) {
+ return NULL;
+ }
+ return PyInt_FromLong( core->GetCharacters( ta ) );
+}
+
+PyDoc_STRVAR( GemRB_GetPartySize__doc,
+"GetPartySize() => int\n\n"
+"Returns the number of PCs." );
+
+static PyObject* GemRB_GetPartySize(PyObject * /*self*/, PyObject * /*args*/)
+{
+ GET_GAME();
+
+ return PyInt_FromLong( game->GetPartySize(0) );
+}
+
+PyDoc_STRVAR( GemRB_GetGameTime__doc,
+"GetGameTime() => int\n\n"
+"Returns current game time." );
+
+static PyObject* GemRB_GetGameTime(PyObject * /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ unsigned long GameTime = game->GameTime/AI_UPDATE_TIME;
+ return PyInt_FromLong( GameTime );
+}
+
+PyDoc_STRVAR( GemRB_GameGetReputation__doc,
+"GameGetReputation() => int\n\n"
+"Returns party reputation." );
+
+static PyObject* GemRB_GameGetReputation(PyObject * /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ int Reputation = (int) game->Reputation;
+ return PyInt_FromLong( Reputation );
+}
+
+PyDoc_STRVAR( GemRB_GameSetReputation__doc,
+"GameSetReputation(Reputation)\n\n"
+"Sets current party reputation." );
+
+static PyObject* GemRB_GameSetReputation(PyObject * /*self*/, PyObject* args)
+{
+ int Reputation;
+
+ if (!PyArg_ParseTuple( args, "i", &Reputation )) {
+ return AttributeError( GemRB_GameSetReputation__doc );
+ }
+ GET_GAME();
+
+ game->SetReputation( (unsigned int) Reputation );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_IncreaseReputation__doc,
+"IncreaseReputation( donation ) => int\n\n"
+"Increases party reputation according to the donation. (See reputati.2da)" );
+
+static PyObject* GemRB_IncreaseReputation(PyObject * /*self*/, PyObject* args)
+{
+ int Donation;
+ int Increase = 0;
+
+ if (!PyArg_ParseTuple( args, "i", &Donation )) {
+ return AttributeError( GemRB_IncreaseReputation__doc );
+ }
+
+ GET_GAME();
+
+ int Limit = core->GetReputationMod(8);
+ if (Limit<=Donation) {
+ Increase = core->GetReputationMod(4);
+ if (Increase) {
+ game->SetReputation( game->Reputation + Increase );
+ }
+ }
+ return PyInt_FromLong ( Increase );
+}
+
+PyDoc_STRVAR( GemRB_GameGetPartyGold__doc,
+"GameGetPartyGold() => int\n\n"
+"Returns current party gold." );
+
+static PyObject* GemRB_GameGetPartyGold(PyObject * /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ int Gold = game->PartyGold;
+ return PyInt_FromLong( Gold );
+}
+
+PyDoc_STRVAR( GemRB_GameSetPartyGold__doc,
+"GameSetPartyGold(Gold)\n\n"
+"Sets current party gold." );
+
+static PyObject* GemRB_GameSetPartyGold(PyObject * /*self*/, PyObject* args)
+{
+ int Gold, flag = 0;
+
+ if (!PyArg_ParseTuple( args, "i|i", &Gold, &flag )) {
+ return AttributeError( GemRB_GameSetPartyGold__doc );
+ }
+ GET_GAME();
+
+ if (flag) {
+ game->AddGold((ieDword) Gold);
+ } else {
+ game->PartyGold=Gold;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GameGetFormation__doc,
+"GameGetFormation([Which]) => int\n\n"
+"Returns current party formation. If Which was supplied, it returns one of the preset formations." );
+
+static PyObject* GemRB_GameGetFormation(PyObject * /*self*/, PyObject* args)
+{
+ int Which = -1;
+ int Formation;
+
+ if (!PyArg_ParseTuple( args, "|i", &Which )) {
+ return AttributeError( GemRB_GameGetFormation__doc );
+ }
+ GET_GAME();
+
+ if (Which<0) {
+ Formation = game->WhichFormation;
+ } else {
+ if (Which>4) {
+ return AttributeError( GemRB_GameGetFormation__doc );
+ }
+ Formation = game->Formations[Which];
+ }
+ return PyInt_FromLong( Formation );
+}
+
+PyDoc_STRVAR( GemRB_GameSetFormation__doc,
+"GameSetFormation(Formation[, Which])\n\n"
+"Sets party formation. If Which was supplied, it sets one of the preset formations." );
+
+static PyObject* GemRB_GameSetFormation(PyObject * /*self*/, PyObject* args)
+{
+ int Formation, Which=-1;
+
+ if (!PyArg_ParseTuple( args, "i|i", &Formation, &Which )) {
+ return AttributeError( GemRB_GameSetFormation__doc );
+ }
+ GET_GAME();
+
+ if (Which<0) {
+ game->WhichFormation = Formation;
+ } else {
+ if (Which>4) {
+ return AttributeError( GemRB_GameSetFormation__doc );
+ }
+ game->Formations[Which] = Formation;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetJournalSize__doc,
+"GetJournalSize(chapter[, section]) => int\n\n"
+"Returns the number of entries in the given section of journal." );
+
+static PyObject* GemRB_GetJournalSize(PyObject * /*self*/, PyObject * args)
+{
+ int chapter;
+ int section = -1;
+
+ if (!PyArg_ParseTuple( args, "i|i", &chapter, §ion )) {
+ return AttributeError( GemRB_GetJournalSize__doc );
+ }
+
+ GET_GAME();
+
+ int count = 0;
+ for (unsigned int i = 0; i < game->GetJournalCount(); i++) {
+ GAMJournalEntry* je = game->GetJournalEntry( i );
+ if ((section == -1 || section == je->Section) && (chapter==je->Chapter) )
+ count++;
+ }
+
+ return PyInt_FromLong( count );
+}
+
+PyDoc_STRVAR( GemRB_GetJournalEntry__doc,
+"GetJournalEntry(chapter, index[, section]) => JournalEntry\n\n"
+"Returns dictionary representing journal entry w/ given chapter, section and index." );
+
+static PyObject* GemRB_GetJournalEntry(PyObject * /*self*/, PyObject * args)
+{
+ int section=-1, index, chapter;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &chapter, &index, §ion )) {
+ return AttributeError( GemRB_GetJournalEntry__doc );
+ }
+
+ GET_GAME();
+
+ int count = 0;
+ for (unsigned int i = 0; i < game->GetJournalCount(); i++) {
+ GAMJournalEntry* je = game->GetJournalEntry( i );
+ if ((section == -1 || section == je->Section) && (chapter == je->Chapter)) {
+ if (index == count) {
+ PyObject* dict = PyDict_New();
+ PyDict_SetItemString(dict, "Text", PyInt_FromLong ((signed) je->Text));
+ PyDict_SetItemString(dict, "GameTime", PyInt_FromLong (je->GameTime));
+ PyDict_SetItemString(dict, "Section", PyInt_FromLong (je->Section));
+ PyDict_SetItemString(dict, "Chapter", PyInt_FromLong (je->Chapter));
+
+ return dict;
+ }
+ count++;
+ }
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetJournalEntry__doc,
+"SetJournalEntry(strref[, section, chapter])\n\n"
+"Sets a journal journal entry w/ given chapter and section, if section was not given, then it will delete the entry.\n"
+"Chapter is optional, if it is omitted, then the current chapter will be used.\n"
+"If strref is -1, then it will delete the whole journal." );
+
+static PyObject* GemRB_SetJournalEntry(PyObject * /*self*/, PyObject * args)
+{
+ int section=-1, chapter = -1, strref;
+
+ if (!PyArg_ParseTuple( args, "i|ii", &strref, §ion, &chapter )) {
+ return AttributeError( GemRB_SetJournalEntry__doc );
+ }
+
+ GET_GAME();
+
+ if (strref == -1) {
+ //delete the whole journal
+ section = -1;
+ }
+
+ if (section==-1) {
+ //delete one or all entries
+ game->DeleteJournalEntry( strref);
+ } else {
+ if (chapter == -1) {
+ ieDword tmp = -1;
+ game->locals->Lookup("CHAPTER", tmp);
+ chapter = (int) tmp;
+ }
+ game->AddJournalEntry( chapter, section, strref);
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GameIsBeastKnown__doc,
+"GameIsBeastKnown(index) => int\n\n"
+"Returns whether beast with given index is known to PCs (works only on PST)." );
+
+static PyObject* GemRB_GameIsBeastKnown(PyObject * /*self*/, PyObject * args)
+{
+ int index;
+ if (!PyArg_ParseTuple( args, "i", &index )) {
+ return AttributeError( GemRB_GameIsBeastKnown__doc );
+ }
+
+ GET_GAME();
+
+ return PyInt_FromLong( game->IsBeastKnown( index ));
+}
+
+PyDoc_STRVAR( GemRB_GetINIPartyCount__doc,
+"GetINIPartyCount() =>int\n\n"
+"Returns the Number of Party defined in Party.ini (works only on IWD2)." );
+
+static PyObject* GemRB_GetINIPartyCount(PyObject * /*self*/,
+ PyObject * /*args*/)
+{
+ if (!core->GetPartyINI()) {
+ return RuntimeError( "INI resource not found!\n" );
+ }
+ return PyInt_FromLong( core->GetPartyINI()->GetTagsCount() );
+}
+
+PyDoc_STRVAR( GemRB_GetINIQuestsKey__doc,
+"GetINIQuestsKey(Tag, Key, Default) => string\n\n"
+"Returns a Value from the quests.ini File (works only on PST)." );
+
+static PyObject* GemRB_GetINIQuestsKey(PyObject * /*self*/, PyObject* args)
+{
+ char *Tag, *Key, *Default;
+
+ if (!PyArg_ParseTuple( args, "sss", &Tag, &Key, &Default )) {
+ return AttributeError( GemRB_GetINIQuestsKey__doc );
+ }
+ if (!core->GetQuestsINI()) {
+ return RuntimeError( "INI resource not found!\n" );
+ }
+ return PyString_FromString(
+ core->GetQuestsINI()->GetKeyAsString( Tag, Key, Default ) );
+}
+
+PyDoc_STRVAR( GemRB_GetINIBeastsKey__doc,
+"GetINIBeastsKey(Tag, Key, Default) => string\n\n"
+"Returns a Value from the beasts.ini File (works only on PST)." );
+
+static PyObject* GemRB_GetINIBeastsKey(PyObject * /*self*/, PyObject* args)
+{
+ char *Tag, *Key, *Default;
+
+ if (!PyArg_ParseTuple( args, "sss", &Tag, &Key, &Default )) {
+ return AttributeError( GemRB_GetINIBeastsKey__doc );
+ }
+ if (!core->GetBeastsINI()) {
+ return NULL;
+ }
+ return PyString_FromString(
+ core->GetBeastsINI()->GetKeyAsString( Tag, Key, Default ) );
+}
+
+PyDoc_STRVAR( GemRB_GetINIPartyKey__doc,
+"GetINIPartyKey(Tag, Key, Default) => string\n\n"
+"Returns a Value from the Party.ini File (works only on IWD2)." );
+
+static PyObject* GemRB_GetINIPartyKey(PyObject * /*self*/, PyObject* args)
+{
+ const char *Tag, *Key, *Default;
+
+ if (!PyArg_ParseTuple( args, "sss", &Tag, &Key, &Default )) {
+ return AttributeError( GemRB_GetINIPartyKey__doc );
+ }
+ if (!core->GetPartyINI()) {
+ return RuntimeError( "INI resource not found!\n" );
+ }
+ return PyString_FromString(
+ core->GetPartyINI()->GetKeyAsString( Tag, Key, Default ) );
+}
+
+PyDoc_STRVAR( GemRB_CreatePlayer__doc,
+"CreatePlayer(CREResRef, Slot [,Import, VersionOverride] ) => PlayerSlot\n\n"
+"Creates or removes a player character in a given party slot. If import is nonzero, then reads a CHR instead of a CRE." );
+
+static PyObject* GemRB_CreatePlayer(PyObject * /*self*/, PyObject* args)
+{
+ const char *CreResRef;
+ int PlayerSlot, Slot;
+ int Import=0;
+ int VersionOverride = -1;
+
+ if (!PyArg_ParseTuple( args, "si|ii", &CreResRef, &PlayerSlot, &Import, &VersionOverride)) {
+ return AttributeError( GemRB_CreatePlayer__doc );
+ }
+ //PlayerSlot is zero based
+ Slot = ( PlayerSlot & 0x7fff );
+ GET_GAME();
+
+ //FIXME:overwriting original slot
+ //is dangerous if the game is already loaded
+ //maybe the actor should be removed from the area first
+ if (PlayerSlot & 0x8000) {
+ PlayerSlot = game->FindPlayer( Slot );
+ if (PlayerSlot >= 0) {
+ game->DelPC(PlayerSlot, true);
+ }
+ } else {
+ PlayerSlot = game->FindPlayer( PlayerSlot );
+ if (PlayerSlot >= 0) {
+ return RuntimeError("Slot is already filled!\n");
+ }
+ }
+ if (CreResRef[0]) {
+ PlayerSlot = gamedata->LoadCreature( CreResRef, Slot, (bool) Import, VersionOverride );
+ } else {
+ //just destroyed the previous actor, not going to create one
+ PlayerSlot = 0;
+ }
+ if (PlayerSlot < 0) {
+ return RuntimeError("File not found!\n");
+ }
+ return PyInt_FromLong( PlayerSlot );
+}
+
+PyDoc_STRVAR( GemRB_GetPlayerStates__doc,
+"GetPlayerStates(PartyID) => string\n\n"
+"Returns the state string for the player.");
+
+static PyObject* GemRB_GetPlayerStates(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+
+ if (!PyArg_ParseTuple( args, "i", &PartyID )) {
+ return AttributeError( GemRB_GetPlayerStates__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PartyID );
+ if (!MyActor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ return PyString_FromString((const char *) MyActor->GetStateString() );
+}
+
+PyDoc_STRVAR( GemRB_GetPlayerName__doc,
+"GetPlayerName(PartyID[, LongOrShort]) => string\n\n"
+"Queries the player character's [script]name." );
+
+static PyObject* GemRB_GetPlayerName(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, Which;
+
+ Which = 0;
+ if (!PyArg_ParseTuple( args, "i|i", &PartyID, &Which )) {
+ return AttributeError( GemRB_GetPlayerName__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PartyID );
+ if (!MyActor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ if (Which == 2) {
+ return PyString_FromString( MyActor->GetScriptName() );
+ }
+
+ return PyString_FromString( MyActor->GetName(Which) );
+}
+
+PyDoc_STRVAR( GemRB_SetPlayerName__doc,
+"SetPlayerName(Slot, Name[, LongOrShort])\n\n"
+"Sets the player name." );
+
+static PyObject* GemRB_SetPlayerName(PyObject * /*self*/, PyObject* args)
+{
+ const char *Name=NULL;
+ int PlayerSlot, Which;
+
+ Which = 0;
+ if (!PyArg_ParseTuple( args, "is|i", &PlayerSlot, &Name, &Which )) {
+ return AttributeError( GemRB_SetPlayerName__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ MyActor->SetName(Name, Which);
+ MyActor->SetMCFlag(MC_EXPORTABLE,BM_OR);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_CreateString__doc,
+"CreateString( Text )->StrRef\n\n"
+"Creates a custom string." );
+
+static PyObject* GemRB_CreateString(PyObject * /*self*/, PyObject* args)
+{
+ const char *Text;
+ ieStrRef strref;
+
+ if (!PyArg_ParseTuple( args, "is", &strref, &Text )) {
+ return AttributeError( GemRB_CreateString__doc );
+ }
+ GET_GAME();
+
+ strref = core->UpdateString(strref, Text);
+ return PyInt_FromLong( strref );
+}
+
+PyDoc_STRVAR( GemRB_SetPlayerString__doc,
+"SetPlayerString(PlayerSlot, StringSlot, StrRef)\n\n"
+"Sets one of the player character's verbal constants. Mostly useful for setting biography." );
+
+static PyObject* GemRB_SetPlayerString(PyObject * /*self*/, PyObject* args)
+{
+ int PlayerSlot, StringSlot, StrRef;
+
+ if (!PyArg_ParseTuple( args, "iii", &PlayerSlot, &StringSlot, &StrRef )) {
+ return AttributeError( GemRB_SetPlayerString__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ if (StringSlot>=VCONST_COUNT) {
+ return AttributeError( "StringSlot is out of range!\n" );
+ }
+
+ MyActor->StrRefs[StringSlot]=StrRef;
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetPlayerSound__doc,
+"SetPlayerSound(Slot, SoundFolder)\n\n"
+"Sets the player character's sound set." );
+
+static PyObject* GemRB_SetPlayerSound(PyObject * /*self*/, PyObject* args)
+{
+ const char *Sound=NULL;
+ int PlayerSlot;
+
+ if (!PyArg_ParseTuple( args, "is", &PlayerSlot, &Sound )) {
+ return AttributeError( GemRB_SetPlayerSound__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ MyActor->SetSoundFolder(Sound);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetPlayerSound__doc,
+"SetPlayerSound(Slot)\n\n"
+"Gets the player character's sound set." );
+
+static PyObject* GemRB_GetPlayerSound(PyObject * /*self*/, PyObject* args)
+{
+ char Sound[42];
+ int PlayerSlot;
+ int flag = 0;
+
+ if (!PyArg_ParseTuple( args, "i|i", &PlayerSlot, &flag )) {
+ return AttributeError( GemRB_GetPlayerSound__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ MyActor->GetSoundFolder(Sound, flag);
+ return PyString_FromString(Sound);
+}
+
+PyDoc_STRVAR( GemRB_GetSlotType__doc,
+"GetSlotType(idx[, PartyID]) => dict\n\n"
+"Returns dictionary of an itemslot type (slottype.2da).");
+
+static PyObject* GemRB_GetSlotType(PyObject * /*self*/, PyObject* args)
+{
+ int idx;
+ int PartyID = 0;
+ Actor *actor = NULL;
+
+ if (!PyArg_ParseTuple( args, "i|i", &idx, &PartyID )) {
+ return AttributeError( GemRB_GetSlotType__doc );
+ }
+
+ if (PartyID) {
+ GET_GAME();
+
+ actor = game->FindPC( PartyID );
+ }
+
+ PyObject* dict = PyDict_New();
+ if (idx==-1) {
+ PyDict_SetItemString(dict, "Count", PyInt_FromLong(core->GetInventorySize()));
+ return dict;
+ }
+ int tmp = core->QuerySlot(idx);
+ if (core->QuerySlotEffects(idx)==0xffffffffu) {
+ tmp=idx;
+ }
+
+ PyDict_SetItemString(dict, "Slot", PyInt_FromLong(tmp));
+ PyDict_SetItemString(dict, "Type", PyInt_FromLong((int)core->QuerySlotType(tmp)));
+ PyDict_SetItemString(dict, "ID", PyInt_FromLong((int)core->QuerySlotID(tmp)));
+ PyDict_SetItemString(dict, "Tip", PyInt_FromLong((int)core->QuerySlottip(tmp)));
+ //see if the actor shouldn't have some slots displayed
+ if (!actor || !actor->PCStats) {
+ goto has_slot;
+ }
+ //WARNING:idx isn't used any more, recycling it
+ idx = actor->inventory.GetWeaponSlot();
+ if (tmp<idx || tmp>idx+3) {
+ goto has_slot;
+ }
+ if (actor->GetQuickSlot(tmp-idx)==0xffff) {
+ PyDict_SetItemString(dict, "ResRef", PyString_FromString (""));
+ goto continue_quest;
+ }
+has_slot:
+ PyDict_SetItemString(dict, "ResRef", PyString_FromString (core->QuerySlotResRef(tmp)));
+continue_quest:
+ PyDict_SetItemString(dict, "Effects", PyInt_FromLong (core->QuerySlotEffects(tmp)));
+ return dict;
+}
+
+PyDoc_STRVAR( GemRB_GetPCStats__doc,
+"GetPCStats(PartyID) => dict\n\n"
+"Returns dictionary of PC's performance stats." );
+
+static PyObject* GemRB_GetPCStats(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+
+ if (!PyArg_ParseTuple( args, "i", &PartyID )) {
+ return AttributeError( GemRB_GetPCStats__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PartyID );
+ if (!MyActor || !MyActor->PCStats) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ PyObject* dict = PyDict_New();
+ PCStatsStruct* ps = MyActor->PCStats;
+
+ PyDict_SetItemString(dict, "BestKilledName", PyInt_FromLong ((signed) ps->BestKilledName));
+ PyDict_SetItemString(dict, "BestKilledXP", PyInt_FromLong (ps->BestKilledXP));
+ PyDict_SetItemString(dict, "AwayTime", PyInt_FromLong (ps->AwayTime));
+ PyDict_SetItemString(dict, "JoinDate", PyInt_FromLong (ps->JoinDate));
+ PyDict_SetItemString(dict, "KillsChapterXP", PyInt_FromLong (ps->KillsChapterXP));
+ PyDict_SetItemString(dict, "KillsChapterCount", PyInt_FromLong (ps->KillsChapterCount));
+ PyDict_SetItemString(dict, "KillsTotalXP", PyInt_FromLong (ps->KillsTotalXP));
+ PyDict_SetItemString(dict, "KillsTotalCount", PyInt_FromLong (ps->KillsTotalCount));
+
+ // FIXME!!!
+ if (ps->FavouriteSpells[0][0]) {
+ int largest = 0;
+
+ for (int i = 1; i < 4; ++i) {
+ if (ps->FavouriteSpellsCount[i] > ps->FavouriteSpellsCount[largest]) {
+ largest = i;
+ }
+ }
+
+ Spell* spell = gamedata->GetSpell(ps->FavouriteSpells[largest]);
+ if (spell == NULL) {
+ return NULL;
+ }
+
+ PyDict_SetItemString(dict, "FavouriteSpell", PyInt_FromLong ((signed) spell->SpellName));
+
+ gamedata->FreeSpell( spell, ps->FavouriteSpells[largest], false );
+ } else {
+ PyDict_SetItemString(dict, "FavouriteSpell", PyString_FromString (""));
+ }
+
+
+
+ // FIXME!!!
+ if (ps->FavouriteWeapons[0][0]) {
+ int largest = 0;
+
+ for (int i = 1; i < 4; ++i) {
+ if (ps->FavouriteWeaponsCount[i] > ps->FavouriteWeaponsCount[largest]) {
+ largest = i;
+ }
+ }
+
+ Item* item = gamedata->GetItem(ps->FavouriteWeapons[largest]);
+ if (item == NULL) {
+ return RuntimeError( "Item not found!\n" );
+ }
+
+ PyDict_SetItemString(dict, "FavouriteWeapon", PyInt_FromLong ((signed) item->GetItemName(false)));
+
+ gamedata->FreeItem( item, ps->FavouriteWeapons[largest], false );
+ } else {
+ PyDict_SetItemString(dict, "FavouriteWeapon", PyString_FromString (""));
+ }
+
+ return dict;
+}
+
+
+PyDoc_STRVAR( GemRB_GameSelectPC__doc,
+"GameSelectPC(PartyID, Selected, [Flags = SELECT_NORMAL])\n\n"
+"Selects or deselects PC."
+"if PartyID=0, (De)selects all PC."
+"Flags is combination of SELECT_REPLACE and SELECT_QUIET."
+"SELECT_REPLACE: when selecting other party members, unselect the others." );
+
+static PyObject* GemRB_GameSelectPC(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, Select;
+ int Flags = SELECT_NORMAL;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &PartyID, &Select, &Flags )) {
+ return AttributeError( GemRB_GameSelectPC__doc );
+ }
+ GET_GAME();
+
+ Actor* actor;
+ if (PartyID > 0) {
+ actor = game->FindPC( PartyID );
+ if (!actor) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ } else {
+ actor = NULL;
+ }
+
+ game->SelectActor( actor, (bool) Select, Flags );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GameIsPCSelected__doc,
+"GameIsPCSelected(Slot) => bool\n\n"
+"Returns true if the PC is selected." );
+
+static PyObject* GemRB_GameIsPCSelected(PyObject * /*self*/, PyObject* args)
+{
+ int PlayerSlot;
+
+ if (!PyArg_ParseTuple( args, "i", &PlayerSlot )) {
+ return AttributeError( GemRB_GameIsPCSelected__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return PyInt_FromLong( 0 );
+ }
+ return PyInt_FromLong( MyActor->IsSelected() );
+}
+
+
+PyDoc_STRVAR( GemRB_GameSelectPCSingle__doc,
+"GameSelectPCSingle(index)\n\n"
+"Selects one PC in non-walk environment (i.e. in shops, inventory,...)"
+"Index must be greater than zero." );
+
+static PyObject* GemRB_GameSelectPCSingle(PyObject * /*self*/, PyObject* args)
+{
+ int index;
+
+ if (!PyArg_ParseTuple( args, "i", &index )) {
+ return AttributeError( GemRB_GameSelectPCSingle__doc );
+ }
+
+ GET_GAME();
+
+ game->SelectPCSingle( index );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GameGetSelectedPCSingle__doc,
+"GameGetSelectedPCSingle(flag) => int\n\n"
+"Returns index of the selected PC in non-walk environment (i.e. in shops, inventory,...). Index should be greater than zero. If flag is set, then this function will return the PC currently talking." );
+
+static PyObject* GemRB_GameGetSelectedPCSingle(PyObject * /*self*/, PyObject* args)
+{
+ int flag = 0;
+
+ if (!PyArg_ParseTuple( args, "|i", &flag )) {
+ return AttributeError( GemRB_GameGetSelectedPCSingle__doc );
+ }
+ GET_GAME();
+
+ if (flag) {
+ GET_GAMECONTROL();
+
+ Actor *ac = gc->dialoghandler->GetSpeaker();
+ int ret = 0;
+ if (ac) {
+ ret = ac->InParty;
+ }
+ return PyInt_FromLong( ret );
+ }
+ return PyInt_FromLong( game->GetSelectedPCSingle() );
+}
+
+PyDoc_STRVAR( GemRB_GameGetFirstSelectedPC__doc,
+"GameGetFirstSelectedPC() => int\n\n"
+"Returns index of the first selected PC or 0 if none." );
+
+static PyObject* GemRB_GameGetFirstSelectedPC(PyObject * /*self*/, PyObject* /*args*/)
+{
+ Actor *actor = core->GetFirstSelectedPC(false);
+ if (actor) {
+ return PyInt_FromLong( actor->InParty);
+ }
+
+ return PyInt_FromLong( 0 );
+}
+
+PyDoc_STRVAR( GemRB_GameGetFirstSelectedActor__doc,
+"GameGetFirstSelectedActor() => int\n\n"
+"Returns the global ID of the first selected actor or 0 if none." );
+
+static PyObject* GemRB_GameGetFirstSelectedActor(PyObject * /*self*/, PyObject* /*args*/)
+{
+ Actor *actor = core->GetFirstSelectedActor();
+ if (actor) {
+ return PyInt_FromLong( actor->GetGlobalID() );
+ }
+
+ return PyInt_FromLong( 0 );
+}
+
+PyDoc_STRVAR( GemRB_GameControlSetLastActor__doc,
+"GameControlSetLastActor() => int\n\n"
+"Sets the last actor that was hovered over by the mouse." );
+
+static PyObject* GemRB_GameControlSetLastActor(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID = 0;
+
+ if (!PyArg_ParseTuple( args, "|i", &PartyID )) {
+ return AttributeError( GemRB_GameControlSetLastActor__doc );
+ }
+
+ GET_GAME();
+
+ GET_GAMECONTROL();
+
+ Actor* actor = game->FindPC( PartyID );
+ gc->SetLastActor(actor, gc->GetLastActor() );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_ActOnPC__doc,
+"ActOnPC(player)\n\n"
+"Targets the selected PC for an action (cast spell, attack, ...)" );
+
+static PyObject* GemRB_ActOnPC(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+
+ if (!PyArg_ParseTuple( args, "i", &PartyID )) {
+ return AttributeError( GemRB_ActOnPC__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PartyID );
+ if (MyActor) {
+ GameControl* gc = core->GetGameControl();
+ if(gc) {
+ gc->PerformActionOn(MyActor);
+ }
+ }
+ Py_INCREF(Py_None) ;
+ return Py_None ;
+}
+
+PyDoc_STRVAR( GemRB_GetPlayerPortrait__doc,
+"GetPlayerPortrait(Slot[, SmallOrLarge]) => string\n\n"
+"Queries the player portrait." );
+
+static PyObject* GemRB_GetPlayerPortrait(PyObject * /*self*/, PyObject* args)
+{
+ int PlayerSlot, Which;
+
+ Which = 0;
+ if (!PyArg_ParseTuple( args, "i|i", &PlayerSlot, &Which )) {
+ return AttributeError( GemRB_GetPlayerPortrait__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return PyString_FromString( "");
+ }
+ return PyString_FromString( MyActor->GetPortrait(Which) );
+}
+
+PyDoc_STRVAR( GemRB_GetPlayerString__doc,
+"GetPlayerString(Slot, ID) => int\n\n"
+"Queries a string reference (verbal constant) from the actor." );
+
+static PyObject* GemRB_GetPlayerString(PyObject * /*self*/, PyObject* args)
+{
+ int PlayerSlot, Index, StatValue;
+
+ if (!PyArg_ParseTuple( args, "ii", &PlayerSlot, &Index)) {
+ return AttributeError( GemRB_GetPlayerString__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return RuntimeError("Cannot find actor!\n");
+ }
+ if (Index>=VCONST_COUNT) {
+ return RuntimeError("String reference too high!\n");
+ }
+ StatValue = GetCreatureStrRef( MyActor, Index );
+ return PyInt_FromLong( StatValue );
+}
+
+PyDoc_STRVAR( GemRB_GetPlayerStat__doc,
+"GetPlayerStat(Slot, ID[, BaseStat]) => int\n\n"
+"Queries a stat." );
+
+static PyObject* GemRB_GetPlayerStat(PyObject * /*self*/, PyObject* args)
+{
+ int PlayerSlot, StatID, StatValue, BaseStat;
+
+ BaseStat = 0;
+ if (!PyArg_ParseTuple( args, "ii|i", &PlayerSlot, &StatID, &BaseStat )) {
+ return AttributeError( GemRB_GetPlayerStat__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return RuntimeError("Cannot find actor!\n");
+ }
+ //returning the modified stat if BaseStat was 0 (default)
+ StatValue = GetCreatureStat( MyActor, StatID, !BaseStat );
+ return PyInt_FromLong( StatValue );
+}
+
+PyDoc_STRVAR( GemRB_SetPlayerStat__doc,
+"SetPlayerStat(Slot, ID, Value[, pcf])\n\n"
+"Changes a stat." );
+
+static PyObject* GemRB_SetPlayerStat(PyObject * /*self*/, PyObject* args)
+{
+ int PlayerSlot, StatID, StatValue;
+ int pcf = 1;
+
+ if (!PyArg_ParseTuple( args, "iii|i", &PlayerSlot, &StatID, &StatValue, &pcf )) {
+ return AttributeError( GemRB_SetPlayerStat__doc );
+ }
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return RuntimeError("Cannot find actor!\n");
+ }
+ //Setting the creature's base stat
+ SetCreatureStat( MyActor, StatID, StatValue, pcf);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetPlayerScript__doc,
+"GetPlayerScript(Slot[, Index])\n\n"
+"Retrieves the script resource for a player. If index is omitted, it will default to "
+"the class script slot (customisable by players)." );
+
+static PyObject* GemRB_GetPlayerScript(PyObject * /*self*/, PyObject* args)
+{
+ //class script is the custom slot for player scripts
+ int PlayerSlot, Index = SCR_CLASS;
+
+ if (!PyArg_ParseTuple( args, "i|i", &PlayerSlot, &Index )) {
+ return AttributeError( GemRB_GetPlayerScript__doc );
+ }
+ GET_GAME();
+
+ Actor *actor = game->FindPC(PlayerSlot);
+ if (!actor) {
+ return RuntimeError("Cannot find actor!\n");
+ }
+ const char *scr = actor->GetScript(Index);
+ if (scr[0]==0) {
+ scr="None";
+ }
+ return PyString_FromString( scr );
+}
+
+PyDoc_STRVAR( GemRB_SetPlayerScript__doc,
+"SetPlayerScript(Slot, Resource[, Index])\n\n"
+"Sets the script resource for a player. If index is omitted, it will default to "
+"the class script slot (customisable by players)." );
+
+static PyObject* GemRB_SetPlayerScript(PyObject * /*self*/, PyObject* args)
+{
+ const char *ScriptName;
+ int PlayerSlot, Index = SCR_CLASS;
+
+ if (!PyArg_ParseTuple( args, "is|i", &PlayerSlot, &ScriptName, &Index )) {
+ return AttributeError( GemRB_SetPlayerScript__doc );
+ }
+ GET_GAME();
+
+ Actor *actor = game->FindPC(PlayerSlot);
+ if (!actor) {
+ return RuntimeError("Cannot find actor!\n");
+ }
+ actor->SetScript(ScriptName, Index, true);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_FillPlayerInfo__doc,
+"FillPlayerInfo(Slot[, Portrait1, Portrait2])\n\n"
+"Fills basic character info, that is not stored in stats." );
+
+static PyObject* GemRB_FillPlayerInfo(PyObject * /*self*/, PyObject* args)
+{
+ int PlayerSlot;
+ const char *Portrait1=NULL, *Portrait2=NULL;
+
+ if (!PyArg_ParseTuple( args, "i|ss", &PlayerSlot, &Portrait1, &Portrait2)) {
+ return AttributeError( GemRB_FillPlayerInfo__doc );
+ }
+ // here comes some code to transfer icon/name to the PC sheet
+ //
+ //
+ GET_GAME();
+
+ Actor* MyActor = game->FindPC( PlayerSlot );
+ if (!MyActor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ if (Portrait1) {
+ MyActor->SetPortrait( Portrait1, 1);
+ }
+ if (Portrait2) {
+ MyActor->SetPortrait( Portrait2, 2);
+ }
+ int mastertable = gamedata->LoadTable( "avprefix" );
+ Holder<TableMgr> mtm = gamedata->GetTable( mastertable );
+ int count = mtm->GetRowCount();
+ if (count< 1 || count>8) {
+ return RuntimeError("Table is invalid." );
+ }
+ const char *poi = mtm->QueryField( 0 );
+ // the base animation id
+ int AnimID = strtoul( poi, NULL, 0 );
+ // tables for additive modifiers of the animation id (race, gender, class)
+ for (int i = 1; i < count; i++) {
+ poi = mtm->QueryField( i );
+ int table = gamedata->LoadTable( poi );
+ Holder<TableMgr> tm = gamedata->GetTable( table );
+ int StatID = atoi( tm->QueryField() );
+ StatID = MyActor->GetBase( StatID );
+ poi = tm->QueryField( StatID );
+ AnimID += strtoul( poi, NULL, 0 );
+ gamedata->DelTable( table );
+ }
+ MyActor->SetBase(IE_ANIMATION_ID, AnimID);
+ //setting PST's starting stance to 18
+ poi = mtm->QueryField( 0, 1 );
+ if (*poi != '*') {
+ MyActor->SetStance( atoi( poi ) );
+ }
+ gamedata->DelTable( mastertable );
+ MyActor->SetOver( false );
+ MyActor->InitButtons(MyActor->GetStat(IE_CLASS), true); //force re-init of actor's buttons
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Button_SetSpellIcon__doc,
+"SetSpellIcon(WindowIndex, ControlIndex, SPLResRef[, type, tooltip, function])\n\n"
+"Sets Spell icon image on a button. Type is the icon's type." );
+
+PyObject *SetSpellIcon(int wi, int ci, const ieResRef SpellResRef, int type, int tooltip, int Function)
+{
+ Button* btn = (Button *) GetControl( wi, ci, IE_GUI_BUTTON );
+ if (!btn) {
+ return NULL;
+ }
+
+ if (!SpellResRef[0]) {
+ btn->SetPicture( NULL );
+ //no incref here!
+ return Py_None;
+ }
+
+ Spell* spell = gamedata->GetSpell( SpellResRef, 1 );
+ if (spell == NULL) {
+ btn->SetPicture( NULL );
+ printMessage( "GUIScript", " ", LIGHT_RED);
+ printf("Spell not found :%.8s", SpellResRef );
+ //no incref here!
+ return Py_None;
+ }
+
+ const char* IconResRef;
+ if (type) {
+ IconResRef = spell->ext_headers[0].MemorisedIcon;
+ }
+ else {
+ IconResRef = spell->SpellbookIcon;
+ }
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( IconResRef,
+ IE_BAM_CLASS_ID, IE_NORMAL, 1 );
+ if (!af) {
+ char tmpstr[24];
+
+ snprintf(tmpstr,sizeof(tmpstr),"%s BAM not found", IconResRef);
+ return RuntimeError( tmpstr );
+ }
+ //small difference between pst and others
+ if (af->GetCycleSize(0)!=4) { //non-pst
+ btn->SetPicture( af->GetFrame(0, 0));
+ }
+ else { //pst
+ btn->SetImage( IE_GUI_BUTTON_UNPRESSED, af->GetFrame(0, 0));
+ btn->SetImage( IE_GUI_BUTTON_PRESSED, af->GetFrame(1, 0));
+ btn->SetImage( IE_GUI_BUTTON_SELECTED, af->GetFrame(2, 0));
+ btn->SetImage( IE_GUI_BUTTON_DISABLED, af->GetFrame(3, 0));
+ }
+ if (tooltip) {
+ char *str = core->GetString(spell->SpellName,0);
+ SetFunctionTooltip(wi, ci, str, Function); //will free str
+ }
+ gamedata->FreeSpell( spell, SpellResRef, false );
+ //no incref here!
+ return Py_None;
+}
+
+static PyObject* GemRB_Button_SetSpellIcon(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci;
+ const char *SpellResRef;
+ int type=0;
+ int tooltip=0;
+ int Function=0;
+
+ if (!PyArg_ParseTuple( args, "iis|iii", &wi, &ci, &SpellResRef, &type, &tooltip, &Function )) {
+ return AttributeError( GemRB_Button_SetSpellIcon__doc );
+ }
+ PyObject *ret = SetSpellIcon(wi, ci, SpellResRef, type, tooltip, Function);
+ if (ret) {
+ Py_INCREF(ret);
+ }
+ return ret;
+}
+
+
+static Sprite2D* GetUsedWeaponIcon(Item *item, int which)
+{
+ ITMExtHeader *ieh = item->GetWeaponHeader(false);
+ if (!ieh) {
+ ieh = item->GetWeaponHeader(true);
+ }
+ if (ieh) {
+ return gamedata->GetBAMSprite(ieh->UseIcon, -1, which);
+ }
+ return gamedata->GetBAMSprite(item->ItemIcon, -1, which);
+}
+
+static void SetItemText(int wi, int ci, int charges, bool oneisnone)
+{
+ Button* btn = (Button *) GetControl( wi, ci, IE_GUI_BUTTON );
+ if (!btn) {
+ return;
+ }
+ char tmp[10];
+
+ if (charges && (charges>1 || !oneisnone) ) {
+ sprintf(tmp,"%d",charges);
+ } else {
+ tmp[0]=0;
+ }
+ btn->SetText(tmp);
+}
+
+PyDoc_STRVAR( GemRB_Button_SetItemIcon__doc,
+"SetItemIcon(WindowIndex, ControlIndex, ITMResRef[, type, tooltip, Function, ITM2ResRef])\n\n"
+"Sets Item icon image on a button. 0/1 - Inventory Icons, 2 - Description Icon, 3 - No icon,\n"
+" 4/5 - Weapon icons, 6 and above - Extended header icons." );
+
+PyObject *SetItemIcon(int wi, int ci, const char *ItemResRef, int Which, int tooltip, int Function, const char *Item2ResRef)
+{
+ Button* btn = (Button *) GetControl( wi, ci, IE_GUI_BUTTON );
+ if (!btn) {
+ return NULL;
+ }
+
+ if (!ItemResRef[0]) {
+ btn->SetPicture( NULL );
+ //no incref here!
+ return Py_None;
+ }
+ Item* item = gamedata->GetItem(ItemResRef);
+ if (item == NULL) {
+ btn->SetPicture(NULL);
+ //no incref here!
+ return Py_None;
+ }
+
+ btn->SetFlags( IE_GUI_BUTTON_PICTURE, BM_OR );
+ Sprite2D* Picture;
+ bool setpicture = true;
+ int i;
+ switch (Which) {
+ case 0: case 1:
+ Picture = gamedata->GetBAMSprite(item->ItemIcon, -1, Which);
+ break;
+ case 2:
+ btn->SetPicture( NULL ); // also calls ClearPictureList
+ for (i=0;i<4;i++) {
+ Picture = gamedata->GetBAMSprite(item->DescriptionIcon, -1, i);
+ if (Picture)
+ btn->StackPicture(Picture);
+ }
+ //fallthrough
+ case 3:
+ setpicture = false;
+ Picture = NULL;
+ break;
+ case 4: case 5:
+ Picture = GetUsedWeaponIcon(item, Which-4);
+ if (Item2ResRef) {
+ btn->SetPicture( NULL ); // also calls ClearPictureList
+ Item* item2 = gamedata->GetItem(Item2ResRef);
+ if (item2) {
+ Sprite2D* Picture2;
+ Picture2 = gamedata->GetBAMSprite(item2->ItemIcon, -1, Which-4);
+ if (Picture2) btn->StackPicture(Picture2);
+ gamedata->FreeItem( item2, Item2ResRef, false );
+ }
+ if (Picture) btn->StackPicture(Picture);
+ setpicture = false;
+ }
+ break;
+ default:
+ ITMExtHeader *eh = item->GetExtHeader(Which-6);
+ if (eh) {
+ Picture = gamedata->GetBAMSprite(eh->UseIcon, -1, 0);
+ }
+ else {
+ Picture = NULL;
+ }
+ }
+
+ if (setpicture)
+ btn->SetPicture( Picture );
+ if (tooltip) {
+ //later getitemname could also return tooltip stuff
+ char *str = core->GetString(item->GetItemName(tooltip==2),0);
+ //this will free str, no need of freeing it
+ SetFunctionTooltip(wi, ci, str, Function);
+ }
+
+ gamedata->FreeItem( item, ItemResRef, false );
+ //no incref here!
+ return Py_None;
+}
+
+static PyObject* GemRB_Button_SetItemIcon(PyObject * /*self*/, PyObject* args)
+{
+ int wi, ci;
+ const char *ItemResRef;
+ int Which = 0;
+ int tooltip = 0;
+ int Function = 0;
+ const char *Item2ResRef = NULL;
+
+ if (!PyArg_ParseTuple( args, "iis|iiis", &wi, &ci, &ItemResRef, &Which, &tooltip, &Function, &Item2ResRef )) {
+ return AttributeError( GemRB_Button_SetItemIcon__doc );
+ }
+
+ PyObject *ret = SetItemIcon(wi, ci, ItemResRef, Which, tooltip, Function, Item2ResRef);
+ if (ret) {
+ Py_INCREF(ret);
+ }
+ return ret;
+}
+
+PyDoc_STRVAR( GemRB_EnterStore__doc,
+"EnterStore(STOResRef)\n\n"
+"Loads the store referenced and opens the store window." );
+
+static PyObject* GemRB_EnterStore(PyObject * /*self*/, PyObject* args)
+{
+ const char* StoreResRef;
+
+ if (!PyArg_ParseTuple( args, "s", &StoreResRef )) {
+ return AttributeError( GemRB_EnterStore__doc );
+ }
+
+ //stores are cached, bags could be opened while in shops
+ //so better just switch to the requested store silently
+ //the core will be intelligent enough to not do excess work
+ core->SetCurrentStore( StoreResRef, 0 );
+
+ core->SetEventFlag(EF_OPENSTORE);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_LeaveStore__doc,
+"LeaveStore(STOResRef)\n\n"
+"Saves the current store to the Cache folder and frees it from memory." );
+
+static PyObject* GemRB_LeaveStore(PyObject * /*self*/, PyObject* /*args*/)
+{
+ if (core->CloseCurrentStore() ) {
+ return RuntimeError("Cannot save store!");
+ }
+ core->ResetEventFlag(EF_OPENSTORE);
+ core->SetEventFlag(EF_PORTRAIT);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_LeaveContainer__doc,
+"LeaveContainer()\n\n"
+"Clears the current container variable and initiates the 'CloseContainerWindow' guiscript call in the next window update cycle.");
+
+static PyObject* GemRB_LeaveContainer(PyObject * /*self*/, PyObject* /*args*/)
+{
+ core->CloseCurrentContainer();
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetContainer__doc,
+"GetContainer( PartyID, autoselect ) => dictionary\n\n"
+"Returns relevant data of the container used by the selected actor. Use autoselect if the container is an item pile at the feet of the actor. It will create the container if required." );
+
+static PyObject* GemRB_GetContainer(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ int autoselect=0;
+
+ if (!PyArg_ParseTuple( args, "i|i", &PartyID, &autoselect )) {
+ return AttributeError( GemRB_GetContainer__doc );
+ }
+
+ Actor *actor;
+
+ GET_GAME();
+
+ if (PartyID) {
+ actor = game->FindPC( PartyID );
+ } else {
+ actor = core->GetFirstSelectedPC(false);
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ Container *container = NULL;
+ if (autoselect) { //autoselect works only with piles
+ Map *map = actor->GetCurrentArea();
+ //GetContainer should create an empty container
+ container = map->GetPile(actor->Pos);
+ } else {
+ container = core->GetCurrentContainer();
+ }
+ if (!container) {
+ return RuntimeError("No current container!");
+ }
+
+ PyObject* dict = PyDict_New();
+ PyDict_SetItemString(dict, "Type", PyInt_FromLong( container->Type ));
+ PyDict_SetItemString(dict, "ItemCount", PyInt_FromLong( container->inventory.GetSlotCount() ));
+
+ return dict;
+}
+
+PyDoc_STRVAR( GemRB_GetContainerItem__doc,
+"GetContainerItem(PartyID, idx) => dictionary\n\n"
+"Returns the container item referenced by the index. If PartyID is 0 then the container was opened manually and should be the current container. If PartyID is not 0 then the container is autoselected and should be at the feet of the player." );
+
+static PyObject* GemRB_GetContainerItem(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ int index;
+
+ if (!PyArg_ParseTuple( args, "ii", &PartyID, &index )) {
+ return AttributeError( GemRB_GetContainerItem__doc );
+ }
+ Container *container;
+
+ if (PartyID) {
+ GET_GAME();
+
+ Actor *actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ Map *map = actor->GetCurrentArea();
+ container = map->TMap->GetContainer(actor->Pos, IE_CONTAINER_PILE);
+ } else {
+ container = core->GetCurrentContainer();
+ }
+ if (!container) {
+ return RuntimeError("No current container!");
+ }
+ if (index>=(int) container->inventory.GetSlotCount()) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ PyObject* dict = PyDict_New();
+
+ CREItem *ci=container->inventory.GetSlotItem( index );
+
+ PyDict_SetItemString(dict, "ItemResRef", PyString_FromResRef( ci->ItemResRef ));
+ PyDict_SetItemString(dict, "Usages0", PyInt_FromLong (ci->Usages[0]));
+ PyDict_SetItemString(dict, "Usages1", PyInt_FromLong (ci->Usages[1]));
+ PyDict_SetItemString(dict, "Usages2", PyInt_FromLong (ci->Usages[2]));
+ PyDict_SetItemString(dict, "Flags", PyInt_FromLong (ci->Flags));
+
+ Item *item = gamedata->GetItem( ci->ItemResRef );
+
+ bool identified = ci->Flags & IE_INV_ITEM_IDENTIFIED;
+ PyDict_SetItemString(dict, "ItemName", PyInt_FromLong( (signed) item->GetItemName( identified )) );
+ PyDict_SetItemString(dict, "ItemDesc", PyInt_FromLong( (signed) item->GetItemDesc( identified )) );
+ gamedata->FreeItem( item, ci->ItemResRef, false );
+ return dict;
+}
+
+PyDoc_STRVAR( GemRB_ChangeContainerItem__doc,
+"ChangeContainerItem(PartyID, slot, action)\n\n"
+"Takes an item from a container, or puts it there. "
+"If PC is 0 then it uses the first selected PC and the current container, "
+"if it is not 0 then it autoselects the container. "
+"action=0: move item from PC to container."
+"action=1: move item from container to PC.");
+
+static PyObject* GemRB_ChangeContainerItem(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, Slot;
+ int action;
+
+ if (!PyArg_ParseTuple( args, "iii", &PartyID, &Slot, &action)) {
+ return AttributeError( GemRB_ChangeContainerItem__doc );
+ }
+ GET_GAME();
+
+ Actor* actor;
+ Container *container;
+ if (PartyID) {
+ actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ Map *map = actor->GetCurrentArea();
+ container = map->TMap->GetContainer(actor->Pos, IE_CONTAINER_PILE);
+ } else {
+ actor = core->GetFirstSelectedPC(false);
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ container = core->GetCurrentContainer();
+ }
+ if (!container) {
+ return RuntimeError("No current container!");
+ }
+
+ ieResRef Sound;
+ CREItem *si;
+ int res;
+
+ Sound[0]=0;
+ if (action) { //get stuff from container
+ if (Slot<0 || Slot>=(int) container->inventory.GetSlotCount()) {
+ return RuntimeError("Invalid Container slot!");
+ }
+
+ res = core->CanMoveItem(container->inventory.GetSlotItem(Slot) );
+ if (!res) { //cannot move
+ printMessage("GUIScript","Cannot move item, it is undroppable!\n", GREEN);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ //this will update the container
+ si = container->RemoveItem(Slot,0);
+ if (!si) {
+ printMessage("GUIScript","Cannot move item, there is something weird!\n", YELLOW);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ Item *item = gamedata->GetItem(si->ItemResRef);
+ if (item) {
+ if (core->HasFeature(GF_HAS_PICK_SOUND) && item->ReplacementItem[0]) {
+ memcpy(Sound,item->ReplacementItem,sizeof(ieResRef));
+ } else {
+ GetItemSound(Sound, item->ItemType, item->AnimationType, IS_DROP);
+ }
+ gamedata->FreeItem(item, si->ItemResRef,0);
+ }
+ if (res!=-1) { //it is gold!
+ game->PartyGold += res;
+ delete si;
+ } else {
+ res = actor->inventory.AddSlotItem(si, SLOT_ONLYINVENTORY);
+ if (res !=ASI_SUCCESS) { //putting it back
+ container->AddItem(si);
+ }
+ }
+ } else { //put stuff in container, simple!
+ res = core->CanMoveItem(actor->inventory.GetSlotItem(core->QuerySlot(Slot) ) );
+ if (!res) { //cannot move
+ printMessage("GUIScript","Cannot move item, it is undroppable!\n", GREEN);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ si = actor->inventory.RemoveItem(core->QuerySlot(Slot));
+ if (!si) {
+ printMessage("GUIScript","Cannot move item, there is something weird!\n", YELLOW);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ Item *item = gamedata->GetItem(si->ItemResRef);
+ if (item) {
+ if (core->HasFeature(GF_HAS_PICK_SOUND) && item->DescriptionIcon[0]) {
+ memcpy(Sound,item->DescriptionIcon,sizeof(ieResRef));
+ } else {
+ GetItemSound(Sound, item->ItemType, item->AnimationType, IS_GET);
+ }
+ gamedata->FreeItem(item, si->ItemResRef,0);
+ }
+ actor->ReinitQuickSlots();
+
+ if (res!=-1) { //it is gold!
+ game->PartyGold += res;
+ delete si;
+ } else {
+ container->AddItem(si);
+ }
+ }
+
+ if (Sound[0]) {
+ core->GetAudioDrv()->Play(Sound);
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetStore__doc,
+"GetStore() => dictionary\n\n"
+"Returns relevant data of the current store." );
+
+#define STORETYPE_COUNT 7
+static int storebuttons[STORETYPE_COUNT][4]={
+//store
+{STA_BUYSELL,STA_IDENTIFY|STA_OPTIONAL,STA_STEAL|STA_OPTIONAL,STA_CURE|STA_OPTIONAL},
+//tavern
+{STA_DRINK,STA_BUYSELL|STA_OPTIONAL,STA_IDENTIFY|STA_OPTIONAL,STA_STEAL|STA_OPTIONAL},
+//inn
+{STA_ROOMRENT,STA_BUYSELL|STA_OPTIONAL,STA_DRINK|STA_OPTIONAL,STA_STEAL|STA_OPTIONAL},
+//temple
+{STA_CURE, STA_DONATE|STA_OPTIONAL,STA_BUYSELL|STA_OPTIONAL,STA_IDENTIFY|STA_OPTIONAL},
+//iwd container
+{STA_BUYSELL,-1,-1,-1,},
+//no need to steal from your own container (original engine had STEAL instead of DRINK)
+{STA_BUYSELL,STA_IDENTIFY|STA_OPTIONAL,STA_DRINK|STA_OPTIONAL,STA_CURE|STA_OPTIONAL},
+//gemrb specific store type: (temple 2), added steal, removed identify
+{STA_BUYSELL,STA_STEAL|STA_OPTIONAL,STA_DONATE|STA_OPTIONAL,STA_CURE|STA_OPTIONAL} };
+
+//buy/sell, identify, steal, cure, donate, drink, rent
+static int storebits[7]={IE_STORE_BUY|IE_STORE_SELL,IE_STORE_ID,IE_STORE_STEAL,
+IE_STORE_CURE,IE_STORE_DONATE,IE_STORE_DRINK,IE_STORE_RENT};
+
+static PyObject* GemRB_GetStore(PyObject * /*self*/, PyObject* args)
+{
+ if (!PyArg_ParseTuple( args, "" )) {
+ return AttributeError( GemRB_GetStore__doc );
+ }
+
+ Store *store = core->GetCurrentStore();
+ if (!store) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ if (store->Type>STORETYPE_COUNT-1) {
+ store->Type=STORETYPE_COUNT-1;
+ }
+ PyObject* dict = PyDict_New();
+ PyDict_SetItemString(dict, "StoreType", PyInt_FromLong( store->Type ));
+ PyDict_SetItemString(dict, "StoreName", PyInt_FromLong( (signed) store->StoreName ));
+ PyDict_SetItemString(dict, "StoreDrinkCount", PyInt_FromLong( store->DrinksCount ));
+ PyDict_SetItemString(dict, "StoreCureCount", PyInt_FromLong( store->CuresCount ));
+ PyDict_SetItemString(dict, "StoreItemCount", PyInt_FromLong( store->GetRealStockSize() ));
+ PyDict_SetItemString(dict, "StoreCapacity", PyInt_FromLong( store->Capacity ));
+ PyDict_SetItemString(dict, "StoreOwner", PyInt_FromLong( store->GetOwnerID() ));
+ PyObject* p = PyTuple_New( 4 );
+
+ int i;
+ int j=1;
+ int k;
+ for (i = 0; i < 4; i++) {
+ if (store->AvailableRooms&j) {
+ k = store->RoomPrices[i];
+ }
+ else k=-1;
+ PyTuple_SetItem( p, i, PyInt_FromLong( k ) );
+ j<<=1;
+ }
+ PyDict_SetItemString(dict, "StoreRoomPrices", p);
+
+ p = PyTuple_New( 4 );
+ j=0;
+ for (i = 0; i < 4; i++) {
+ k = storebuttons[store->Type][i];
+ if (k&STA_OPTIONAL) {
+ k&=~STA_OPTIONAL;
+ //check if the type was disabled
+ if (!(store->Flags & storebits[k]) ) {
+ continue;
+ }
+ }
+ PyTuple_SetItem( p, j++, PyInt_FromLong( k ) );
+ }
+ for (;j<4;j++) {
+ PyTuple_SetItem( p, j, PyInt_FromLong( -1 ) );
+ }
+ PyDict_SetItemString(dict, "StoreButtons", p);
+ PyDict_SetItemString(dict, "StoreFlags", PyInt_FromLong( store->Flags ) );
+ PyDict_SetItemString(dict, "TavernRumour", PyString_FromResRef( store->RumoursTavern ));
+ PyDict_SetItemString(dict, "TempleRumour", PyString_FromResRef( store->RumoursTemple ));
+ PyDict_SetItemString(dict, "IDPrice", PyInt_FromLong( store->IDPrice ) );
+ PyDict_SetItemString(dict, "Lore", PyInt_FromLong( store->Lore ) );
+ PyDict_SetItemString(dict, "Depreciation", PyInt_FromLong( store->DepreciationRate ) );
+ PyDict_SetItemString(dict, "SellMarkup", PyInt_FromLong( store->SellMarkup ) );
+ PyDict_SetItemString(dict, "BuyMarkup", PyInt_FromLong( store->BuyMarkup ) );
+ PyDict_SetItemString(dict, "StealFailure", PyInt_FromLong( store->StealFailureChance ) );
+
+ return dict;
+}
+
+
+PyDoc_STRVAR( GemRB_IsValidStoreItem__doc,
+"IsValidStoreItem(pc, idx[, type]) => int\n\n"
+"Returns if a pc's inventory item or a store item is valid for buying, selling, identifying or stealing. It also has a flag for selected items. "
+"Type is 1 for store items and 0 for PC items." );
+
+static PyObject* GemRB_IsValidStoreItem(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, Slot, ret;
+ int type = 0;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &PartyID, &Slot, &type)) {
+ return AttributeError( GemRB_IsValidStoreItem__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ Store *store = core->GetCurrentStore();
+ if (!store) {
+ return RuntimeError("No current store!");
+ }
+
+ const char *ItemResRef;
+ ieDword Flags;
+
+ if (type) {
+ STOItem* si = store->GetItem( Slot );
+ if (!si) {
+ return PyInt_FromLong(0);
+ }
+ ItemResRef = si->ItemResRef;
+ Flags = si->Flags;
+ } else {
+ CREItem* si = actor->inventory.GetSlotItem( core->QuerySlot(Slot) );
+ if (!si) {
+ return PyInt_FromLong(0);
+ }
+ ItemResRef = si->ItemResRef;
+ Flags = si->Flags;
+ }
+ Item *item = gamedata->GetItem( ItemResRef );
+ if (!item) {
+ printMessage("GUIScript", " ", LIGHT_RED);
+ printf("Invalid resource reference: %s\n", ItemResRef);
+ return PyInt_FromLong(0);
+ }
+
+ ret = store->AcceptableItemType( item->ItemType, Flags, !type );
+
+ //don't allow putting a bag into itself
+ if (!strnicmp(ItemResRef, store->Name, sizeof(ieResRef)) ) {
+ ret &= ~IE_STORE_SELL;
+ }
+ //this is a hack to report on selected items
+ if (Flags & IE_INV_ITEM_SELECTED) {
+ ret |= IE_STORE_SELECT;
+ }
+
+ //don't allow overstuffing bags
+ if (store->Capacity && store->Capacity<=store->GetRealStockSize()) {
+ ret &= ~IE_STORE_SELL;
+ }
+
+ gamedata->FreeItem( item, ItemResRef, false );
+ return PyInt_FromLong(ret);
+}
+
+PyDoc_STRVAR( GemRB_FindStoreItem__doc,
+"FindStoreItem(resref)\n\n"
+"Returns the amount of the specified items in the open store."
+"0 is also returned for an infinite ammount.");
+
+static PyObject* GemRB_FindStoreItem(PyObject * /*self*/, PyObject* args)
+{
+ char *resref;
+
+ if (!PyArg_ParseTuple( args, "s", &resref)) {
+ return AttributeError( GemRB_FindStoreItem__doc );
+ }
+
+ Store *store = core->GetCurrentStore();
+ if (!store) {
+ return RuntimeError("No current store!");
+ }
+
+ int Slot = store->FindItem(resref, false);
+ if (Slot == -1) {
+ return PyInt_FromLong(0);
+ }
+ STOItem* si = store->GetItem( Slot );
+ if (!si) {
+ // shouldn't be possible, item vanished
+ return PyInt_FromLong(0);
+ }
+
+ if (si->InfiniteSupply == -1) {
+ // change this if it is ever needed for something else than depreciation calculation
+ return PyInt_FromLong(0);
+ } else {
+ return PyInt_FromLong(si->AmountInStock);
+ }
+}
+
+PyDoc_STRVAR( GemRB_SetPurchasedAmount__doc,
+"SetPurchasedAmount(idx, amount)\n\n"
+"Sets the amount of purchased items of a type.");
+
+static PyObject* GemRB_SetPurchasedAmount(PyObject * /*self*/, PyObject* args)
+{
+ int Slot, tmp;
+ ieDword amount;
+
+ if (!PyArg_ParseTuple( args, "ii", &Slot, &tmp)) {
+ return AttributeError( GemRB_SetPurchasedAmount__doc );
+ }
+ amount = (ieDword) tmp;
+ Store *store = core->GetCurrentStore();
+ if (!store) {
+ return RuntimeError("No current store!");
+ }
+ STOItem* si = store->GetItem( Slot );
+ if (!si) {
+ return RuntimeError("Store item not found!");
+ }
+
+ if (si->InfiniteSupply != -1) {
+ if (si->AmountInStock<amount) {
+ amount=si->AmountInStock;
+ }
+ }
+ si->PurchasedAmount=amount;
+ if (amount) {
+ si->Flags |= IE_INV_ITEM_SELECTED;
+ } else {
+ si->Flags &= ~IE_INV_ITEM_SELECTED;
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_ChangeStoreItem__doc,
+"ChangeStoreItem(PartyID, Slot, action)=>int\n\n"
+"Performs an action of buying, selling, identifying or stealing in a store. "
+"It can also toggle the selection of an item." );
+
+static PyObject* GemRB_ChangeStoreItem(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, Slot;
+ int action;
+ int res = ASI_FAILED;
+
+ if (!PyArg_ParseTuple( args, "iii", &PartyID, &Slot, &action)) {
+ return AttributeError( GemRB_ChangeStoreItem__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ Store *store = core->GetCurrentStore();
+ if (!store) {
+ return RuntimeError("No current store!");
+ }
+ switch (action) {
+ case IE_STORE_STEAL:
+ case IE_STORE_BUY:
+ {
+ STOItem* si = store->GetItem( Slot );
+ if (!si) {
+ return RuntimeError("Store item not found!");
+ }
+ //always stealing only one item
+ if (action == IE_STORE_STEAL) {
+ si->PurchasedAmount=1;
+ }
+ //the amount of items is stored in si->PurchasedAmount
+ //it will adjust AmountInStock/PurchasedAmount
+ actor->inventory.AddStoreItem(si, action);
+ if (si->PurchasedAmount) {
+ //was not able to buy it due to lack of space
+ res = ASI_FAILED;
+ break;
+ }
+ //if no item remained, remove it
+ if (si->AmountInStock) {
+ si->Flags &= ~IE_INV_ITEM_SELECTED;
+ } else {
+ store->RemoveItem( Slot );
+ }
+ res = ASI_SUCCESS;
+ break;
+ }
+ case IE_STORE_ID:
+ {
+ CREItem* si = actor->inventory.GetSlotItem( core->QuerySlot(Slot) );
+ if (!si) {
+ return RuntimeError( "Item not found!" );
+ }
+ si->Flags |= IE_INV_ITEM_IDENTIFIED;
+ res = ASI_SUCCESS;
+ break;
+ }
+ case IE_STORE_SELECT|IE_STORE_BUY:
+ {
+ STOItem* si = store->GetItem( Slot );
+ if (!si) {
+ return RuntimeError("Store item not found!");
+ }
+ si->Flags ^= IE_INV_ITEM_SELECTED;
+ if (si->Flags & IE_INV_ITEM_SELECTED) {
+ si->PurchasedAmount=1;
+ } else {
+ si->PurchasedAmount=0;
+ }
+ res = ASI_SUCCESS;
+ break;
+ }
+
+ case IE_STORE_SELECT|IE_STORE_SELL:
+ case IE_STORE_SELECT|IE_STORE_ID:
+ {
+ //this is not removeitem, because the item is just marked
+ CREItem* si = actor->inventory.GetSlotItem( core->QuerySlot(Slot) );
+ if (!si) {
+ return RuntimeError( "Item not found!" );
+ }
+ si->Flags ^= IE_INV_ITEM_SELECTED;
+ res = ASI_SUCCESS;
+ break;
+ }
+ case IE_STORE_SELL:
+ {
+ //store/bag is at full capacity
+ if (store->Capacity && (store->Capacity <= store->GetRealStockSize()) ) {
+ printMessage("GUIScript", "Store is full.\n", GREEN);
+ res = ASI_FAILED;
+ break;
+ }
+ //this is removeitem, because the item leaves our inventory
+ CREItem* si = actor->inventory.RemoveItem( core->QuerySlot(Slot) );
+ if (!si) {
+ return RuntimeError( "Item not found!" );
+ }
+ //well, it shouldn't be sold at all, but if it is here
+ //it will vanish!!!
+ if (!si->Expired && (si->Flags& IE_INV_ITEM_RESELLABLE)) {
+ si->Flags &= ~IE_INV_ITEM_SELECTED;
+ store->AddItem( si );
+ STOItem *s = store->GetItem(store->FindItem(si->ItemResRef, true));
+ s->AmountInStock++;
+ }
+ delete si;
+ res = ASI_SUCCESS;
+ break;
+ }
+ }
+ return PyInt_FromLong(res);
+}
+
+PyDoc_STRVAR( GemRB_GetStoreItem__doc,
+"GetStoreItem(idx) => dictionary\n\n"
+"Returns the store item referenced by the index." );
+
+static PyObject* GemRB_GetStoreItem(PyObject * /*self*/, PyObject* args)
+{
+ int index;
+
+ if (!PyArg_ParseTuple( args, "i", &index )) {
+ return AttributeError( GemRB_GetStoreItem__doc );
+ }
+ Store *store = core->GetCurrentStore();
+ if (!store) {
+ return RuntimeError("No current store!");
+ }
+ if (index>=(int) store->GetRealStockSize()) {
+ printMessage("GUIScript","Item is not available???",YELLOW);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ PyObject* dict = PyDict_New();
+ STOItem *si=store->GetItem( index );
+ if (!si) {
+ printMessage("GUIScript","Item is not available???",YELLOW);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ PyDict_SetItemString(dict, "ItemResRef", PyString_FromResRef( si->ItemResRef ));
+ PyDict_SetItemString(dict, "Usages0", PyInt_FromLong (si->Usages[0]));
+ PyDict_SetItemString(dict, "Usages1", PyInt_FromLong (si->Usages[1]));
+ PyDict_SetItemString(dict, "Usages2", PyInt_FromLong (si->Usages[2]));
+ PyDict_SetItemString(dict, "Flags", PyInt_FromLong (si->Flags));
+ PyDict_SetItemString(dict, "Purchased", PyInt_FromLong (si->PurchasedAmount) );
+
+ int amount;
+ if (si->InfiniteSupply==-1) {
+ PyDict_SetItemString(dict, "Amount", PyInt_FromLong( -1 ) );
+ amount = 100;
+ } else {
+ amount = si->AmountInStock;
+ PyDict_SetItemString(dict, "Amount", PyInt_FromLong( amount ) );
+ }
+
+ Item *item = gamedata->GetItem( si->ItemResRef );
+
+ if (!item) {
+ printMessage("GUIScript","Item is not available???",YELLOW);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ int identified = !!(si->Flags & IE_INV_ITEM_IDENTIFIED);
+ PyDict_SetItemString(dict, "ItemName", PyInt_FromLong( (signed) item->GetItemName( (bool) identified )) );
+ PyDict_SetItemString(dict, "ItemDesc", PyInt_FromLong( (signed) item->GetItemDesc( (bool) identified )) );
+
+ int price = item->Price * store->SellMarkup / 100;
+ //calculate depreciation too
+ //store->DepreciationRate, mount
+
+ if (item->StackAmount>1) {
+ price *= si->Usages[0];
+ }
+ //is this correct?
+ if (price<1) {
+ price = 1;
+ }
+ PyDict_SetItemString(dict, "Price", PyInt_FromLong( price ) );
+
+ gamedata->FreeItem( item, si->ItemResRef, false );
+ return dict;
+}
+
+PyDoc_STRVAR( GemRB_GetStoreDrink__doc,
+"GetStoreDrink(idx) => dictionary\n\n"
+"Returns the drink structure indexed. Returns None if the index is wrong." );
+
+static PyObject* GemRB_GetStoreDrink(PyObject * /*self*/, PyObject* args)
+{
+ int index;
+
+ if (!PyArg_ParseTuple( args, "i", &index )) {
+ return AttributeError( GemRB_GetStoreDrink__doc );
+ }
+ Store *store = core->GetCurrentStore();
+ if (!store) {
+ return RuntimeError("No current store!");
+ }
+ if (index>=(int) store->DrinksCount) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ PyObject* dict = PyDict_New();
+ STODrink *drink=store->GetDrink(index);
+ PyDict_SetItemString(dict, "DrinkName", PyInt_FromLong( (signed) drink->DrinkName ));
+ PyDict_SetItemString(dict, "Price", PyInt_FromLong( drink->Price ));
+ PyDict_SetItemString(dict, "Strength", PyInt_FromLong( drink->Strength ));
+ return dict;
+}
+
+static void ReadUsedItems()
+{
+ int i;
+
+ UsedItemsCount = 0;
+ int table = gamedata->LoadTable("item_use");
+ if (table>=0) {
+ Holder<TableMgr> tab = gamedata->GetTable(table);
+ if (!tab) goto table_loaded;
+ UsedItemsCount = tab->GetRowCount();
+ UsedItems = (UsedItemType *) malloc( sizeof(UsedItemType) * UsedItemsCount);
+ for (i=0;i<UsedItemsCount;i++) {
+ strnlwrcpy(UsedItems[i].itemname, tab->GetRowName(i),8 );
+ strnlwrcpy(UsedItems[i].username, tab->QueryField(i,0),32 );
+ if (UsedItems[i].username[0]=='*') {
+ UsedItems[i].username[0] = 0;
+ }
+ //this is an strref
+ UsedItems[i].value = atoi(tab->QueryField(i,1) );
+ //1 - named actor cannot remove it
+ //2 - anyone else cannot equip it
+ //4 - can only swap it for something else
+ UsedItems[i].flags = atoi(tab->QueryField(i,2) );
+ }
+table_loaded:
+ gamedata->DelTable(table);
+ }
+}
+
+static void ReadSpecialItems()
+{
+ int i;
+
+ SpecialItemsCount = 0;
+ int table = gamedata->LoadTable("itemspec");
+ if (table>=0) {
+ Holder<TableMgr> tab = gamedata->GetTable(table);
+ if (!tab) goto table_loaded;
+ SpecialItemsCount = tab->GetRowCount();
+ SpecialItems = (SpellDescType *) malloc( sizeof(SpellDescType) * SpecialItemsCount);
+ for (i=0;i<SpecialItemsCount;i++) {
+ strnlwrcpy(SpecialItems[i].resref, tab->GetRowName(i),8 );
+ //if there are more flags, compose this value into a bitfield
+ SpecialItems[i].value = atoi(tab->QueryField(i,0) );
+ }
+table_loaded:
+ gamedata->DelTable(table);
+ }
+}
+
+static ieStrRef GetSpellDesc(ieResRef CureResRef)
+{
+ int i;
+
+ if (StoreSpellsCount==-1) {
+ StoreSpellsCount = 0;
+ int table = gamedata->LoadTable("speldesc");
+ if (table>=0) {
+ Holder<TableMgr> tab = gamedata->GetTable(table);
+ if (!tab) goto table_loaded;
+ StoreSpellsCount = tab->GetRowCount();
+ StoreSpells = (SpellDescType *) malloc( sizeof(SpellDescType) * StoreSpellsCount);
+ for (i=0;i<StoreSpellsCount;i++) {
+ strnlwrcpy(StoreSpells[i].resref, tab->GetRowName(i),8 );
+ StoreSpells[i].value = atoi(tab->QueryField(i,0) );
+ }
+table_loaded:
+ gamedata->DelTable(table);
+ }
+ }
+ if (StoreSpellsCount==0) {
+ Spell *spell = gamedata->GetSpell(CureResRef);
+ if (!spell) {
+ return 0;
+ }
+ int ret = spell->SpellDescIdentified;
+ gamedata->FreeSpell(spell, CureResRef, false);
+ return ret;
+ }
+ for (i=0;i<StoreSpellsCount;i++) {
+ if (!strnicmp(StoreSpells[i].resref, CureResRef, 8) ) {
+ return StoreSpells[i].value;
+ }
+ }
+ return 0;
+}
+
+PyDoc_STRVAR( GemRB_GetStoreCure__doc,
+"GetStoreCure(idx) => dictionary\n\n"
+"Returns the cure structure indexed. Returns None if the index is wrong." );
+
+static PyObject* GemRB_GetStoreCure(PyObject * /*self*/, PyObject* args)
+{
+ int index;
+
+ if (!PyArg_ParseTuple( args, "i", &index )) {
+ return AttributeError( GemRB_GetStoreCure__doc );
+ }
+ Store *store = core->GetCurrentStore();
+ if (!store) {
+ return RuntimeError("No current store!");
+ }
+ if (index>=(int) store->CuresCount) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ PyObject* dict = PyDict_New();
+ STOCure *cure=store->GetCure(index);
+ PyDict_SetItemString(dict, "CureResRef", PyString_FromResRef( cure->CureResRef ));
+ PyDict_SetItemString(dict, "Price", PyInt_FromLong( cure->Price ));
+ PyDict_SetItemString(dict, "Description", PyInt_FromLong( (signed) GetSpellDesc(cure->CureResRef) ) );
+ return dict;
+}
+
+PyDoc_STRVAR( GemRB_ExecuteString__doc,
+"ExecuteString(String[,PC])\n\n"
+"Executes an In-Game Script Action in the current Area Script Context. "
+"If a number was given, it will execute the action in the numbered PC's context." );
+
+static PyObject* GemRB_ExecuteString(PyObject * /*self*/, PyObject* args)
+{
+ char* String;
+ int actornum=0;
+
+ if (!PyArg_ParseTuple( args, "s|i", &String, &actornum )) {
+ return AttributeError( GemRB_ExecuteString__doc );
+ }
+ GET_GAME();
+
+ if (actornum) {
+ Actor *pc = game->FindPC(actornum);
+ if (pc) {
+ GameScript::ExecuteString( pc, String );
+ } else {
+ printMessage("GUIScript","Player not found!\n", YELLOW);
+ }
+ } else {
+ GameScript::ExecuteString( game->GetCurrentArea( ), String );
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_EvaluateString__doc,
+"EvaluateString(String)\n\n"
+"Evaluate an In-Game Script Trigger in the current Area Script Context." );
+
+static PyObject* GemRB_EvaluateString(PyObject * /*self*/, PyObject* args)
+{
+ char* String;
+
+ if (!PyArg_ParseTuple( args, "s", &String )) {
+ return AttributeError( GemRB_EvaluateString__doc );
+ }
+ GET_GAME();
+
+ if (GameScript::EvaluateString( game->GetCurrentArea( ), String )) {
+ printf( "%s returned True\n", String );
+ } else {
+ printf( "%s returned False\n", String );
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_UpdateMusicVolume__doc,
+"UpdateMusicVolume()\n\n"
+"Update music volume on-the-fly." );
+
+static PyObject* GemRB_UpdateMusicVolume(PyObject * /*self*/, PyObject* /*args*/)
+{
+ core->GetAudioDrv()->UpdateVolume( GEM_SND_VOL_MUSIC );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_UpdateAmbientsVolume__doc,
+"UpdateAmbientsVolume()\n\n"
+"Update ambients volume on-the-fly." );
+
+static PyObject* GemRB_UpdateAmbientsVolume(PyObject * /*self*/, PyObject* /*args*/)
+{
+ core->GetAudioDrv()->UpdateVolume( GEM_SND_VOL_AMBIENTS );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetCurrentArea__doc,
+"GetCurrentArea()=>resref\n\n"
+"Returns current area's ResRef." );
+
+static PyObject* GemRB_GetCurrentArea(PyObject * /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ return PyString_FromString( game->CurrentArea );
+}
+
+PyDoc_STRVAR( GemRB_MoveToArea__doc,
+"MoveToArea(resref)\n\n"
+"Moves the selected characters to the area." );
+
+static PyObject* GemRB_MoveToArea(PyObject * /*self*/, PyObject* args)
+{
+ const char *String;
+
+ if (!PyArg_ParseTuple( args, "s", &String )) {
+ return AttributeError( GemRB_MoveToArea__doc );
+ }
+ GET_GAME();
+
+ Map* map2 = game->GetMap(String, true);
+ if (!map2) {
+ return RuntimeError( "Map not found!" );
+ }
+ int i = game->GetPartySize(false);
+ while (i--) {
+ Actor* actor = game->GetPC(i, false);
+ if (!actor->Selected) {
+ continue;
+ }
+ Map* map1 = actor->GetCurrentArea();
+ if (map1) {
+ map1->RemoveActor( actor );
+ }
+ map2->AddActor( actor );
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetMemorizableSpellsCount__doc,
+"GetMemorizableSpellsCount(PartyID, SpellType, Level [,Bonus])=>int\n\n"
+"Returns number of memorizable spells of given type and level in PC's spellbook." );
+
+static PyObject* GemRB_GetMemorizableSpellsCount(PyObject* /*self*/, PyObject* args)
+{
+ int PartyID, SpellType, Level, Bonus=1;
+
+ if (!PyArg_ParseTuple( args, "iii|i", &PartyID, &SpellType, &Level, &Bonus )) {
+ return AttributeError( GemRB_GetMemorizableSpellsCount__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ //this isn't in the actor's spellbook, handles Wisdom
+ return PyInt_FromLong(actor->spellbook.GetMemorizableSpellsCount( (ieSpellType) SpellType, Level, (bool) Bonus ) );
+}
+
+PyDoc_STRVAR( GemRB_SetMemorizableSpellsCount__doc,
+"SetMemorizableSpellsCount(PartyID, Value, SpellType, Level)=>int\n\n"
+"Sets number of memorizable spells of given type and level in PC's spellbook." );
+
+static PyObject* GemRB_SetMemorizableSpellsCount(PyObject* /*self*/, PyObject* args)
+{
+ int PartyID, Value, SpellType, Level;
+
+ if (!PyArg_ParseTuple( args, "iiii", &PartyID, &Value, &SpellType, &Level)) {
+ return AttributeError( GemRB_SetMemorizableSpellsCount__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ //the bonus increased value (with wisdom too) is handled by the core
+ actor->spellbook.SetMemorizableSpellsCount( Value, (ieSpellType) SpellType, Level, 0 );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetKnownSpellsCount__doc,
+"GetKnownSpellsCount(PartyID, SpellType, Level)=>int\n\n"
+"Returns number of known spells of given type and level in PC's spellbook." );
+
+static PyObject* GemRB_GetKnownSpellsCount(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, SpellType, Level;
+
+ if (!PyArg_ParseTuple( args, "iii", &PartyID, &SpellType, &Level )) {
+ return AttributeError( GemRB_GetKnownSpellsCount__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ return PyInt_FromLong(actor->spellbook.GetKnownSpellsCount( SpellType, Level ) );
+}
+
+PyDoc_STRVAR( GemRB_GetKnownSpell__doc,
+"GetKnownSpell(PartyID, SpellType, Level, Index)=>dict\n\n"
+"Returns dict with specified known spell from PC's spellbook." );
+
+static PyObject* GemRB_GetKnownSpell(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, SpellType, Level, Index;
+
+ if (!PyArg_ParseTuple( args, "iiii", &PartyID, &SpellType, &Level, &Index )) {
+ return AttributeError( GemRB_GetKnownSpell__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ CREKnownSpell* ks = actor->spellbook.GetKnownSpell( SpellType, Level, Index );
+ if (! ks) {
+ return RuntimeError( "Spell not found!" );
+ }
+
+ PyObject* dict = PyDict_New();
+ PyDict_SetItemString(dict, "SpellResRef", PyString_FromResRef (ks->SpellResRef));
+ //PyDict_SetItemString(dict, "Flags", PyInt_FromLong (ms->Flags));
+
+ return dict;
+}
+
+
+PyDoc_STRVAR( GemRB_GetMemorizedSpellsCount__doc,
+"GetMemorizedSpellsCount(PartyID, SpellType[, Level, global])=>int\n\n"
+"Returns number of spells of given type and level in PartyID's memory. "
+"If level is omitted then it returns the number of distinct spells memorised.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_GetMemorizedSpellsCount(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, SpellType, Level = -1;
+ int global = 0;
+
+ if (!PyArg_ParseTuple( args, "ii|ii", &PartyID, &SpellType, &Level, &global )) {
+ return AttributeError( GemRB_GetMemorizedSpellsCount__doc );
+ }
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( PartyID );
+ } else {
+ actor = game->FindPC( PartyID );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ if (Level<0) {
+ return PyInt_FromLong( actor->spellbook.GetSpellInfoSize( SpellType ) );
+ } else {
+ return PyInt_FromLong( actor->spellbook.GetMemorizedSpellsCount( SpellType, Level ) );
+ }
+}
+
+PyDoc_STRVAR( GemRB_GetMemorizedSpell__doc,
+"GetMemorizedSpell(PartyID, SpellType, Level, Index)=>dict\n\n"
+"Returns dict with specified memorized spell from PC's spellbook." );
+
+static PyObject* GemRB_GetMemorizedSpell(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, SpellType, Level, Index;
+
+ if (!PyArg_ParseTuple( args, "iiii", &PartyID, &SpellType, &Level, &Index )) {
+ return AttributeError( GemRB_GetMemorizedSpell__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ CREMemorizedSpell* ms = actor->spellbook.GetMemorizedSpell( SpellType, Level, Index );
+ if (! ms) {
+ return RuntimeError( "Spell not found!" );
+ }
+
+ PyObject* dict = PyDict_New();
+ PyDict_SetItemString(dict, "SpellResRef", PyString_FromResRef (ms->SpellResRef));
+ PyDict_SetItemString(dict, "Flags", PyInt_FromLong (ms->Flags));
+
+ return dict;
+}
+
+
+PyDoc_STRVAR( GemRB_GetSpell__doc,
+"GetSpell(ResRef[, silent])=>dict\n\n"
+"Returns dict with specified spell. Verbose by default." );
+
+static PyObject* GemRB_GetSpell(PyObject * /*self*/, PyObject* args)
+{
+ const char* ResRef;
+ int silent = 0;
+
+ if (!PyArg_ParseTuple( args, "s|i", &ResRef, &silent)) {
+ return AttributeError( GemRB_GetSpell__doc );
+ }
+
+ if (silent && !gamedata->Exists(ResRef,IE_SPL_CLASS_ID, true)) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ Spell* spell = gamedata->GetSpell(ResRef, silent);
+ if (spell == NULL) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ PyObject* dict = PyDict_New();
+ PyDict_SetItemString(dict, "SpellName", PyInt_FromLong ((signed) spell->SpellName));
+ PyDict_SetItemString(dict, "SpellDesc", PyInt_FromLong ((signed) spell->SpellDesc));
+ PyDict_SetItemString(dict, "SpellbookIcon", PyString_FromResRef (spell->SpellbookIcon));
+ PyDict_SetItemString(dict, "SpellExclusion", PyInt_FromLong (spell->ExclusionSchool)); //this will list school exclusions and alignment
+ PyDict_SetItemString(dict, "SpellDivine", PyInt_FromLong (spell->PriestType)); //this will tell apart a priest spell from a druid spell
+ PyDict_SetItemString(dict, "SpellSchool", PyInt_FromLong (spell->PrimaryType));
+ PyDict_SetItemString(dict, "SpellType", PyInt_FromLong (spell->SecondaryType));
+ PyDict_SetItemString(dict, "SpellLevel", PyInt_FromLong (spell->SpellLevel));
+ PyDict_SetItemString(dict, "SpellTargetType", PyInt_FromLong (spell->GetExtHeader(0)->Target));
+ gamedata->FreeSpell( spell, ResRef, false );
+ return dict;
+}
+
+
+PyDoc_STRVAR( GemRB_LearnSpell__doc,
+"LearnSpell(PartyID, SpellResRef[, Flags])=>int\n\n"
+"Learns specified spell. Returns 0 on success." );
+
+static PyObject* GemRB_LearnSpell(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ const char *Spell;
+ int Flags=0;
+
+ if (!PyArg_ParseTuple( args, "is|i", &PartyID, &Spell, &Flags )) {
+ return AttributeError( GemRB_LearnSpell__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ int ret = actor->LearnSpell( Spell, Flags ); // returns 0 on success
+ if (!ret) core->SetEventFlag( EF_ACTION );
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_DispelEffect__doc,
+"DispelEffect(PartyID, EffectName, Parameter2)\n\n"
+"Removes all effects from target whose opcode and second parameter matches the arguments." );
+
+static EffectRef work_ref;
+
+static PyObject* GemRB_DispelEffect(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, Parameter2;
+ const char *EffectName;
+
+ if (!PyArg_ParseTuple( args, "isi", &PartyID, &EffectName, &Parameter2 )) {
+ return AttributeError( GemRB_DispelEffect__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ work_ref.Name=EffectName;
+ work_ref.opcode=-1;
+ actor->fxqueue.RemoveAllEffectsWithParam(work_ref, Parameter2);
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_RemoveEffects__doc,
+"RemoveEffects(PartyID, SpellResRef)\n\n"
+"Removes all effects from target whose source is SpellResRef." );
+
+static PyObject* GemRB_RemoveEffects(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ const char * SpellResRef;
+
+ if (!PyArg_ParseTuple( args, "is", &PartyID, &SpellResRef )) {
+ return AttributeError( GemRB_RemoveEffects__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ actor->fxqueue.RemoveAllEffects(SpellResRef);
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_RemoveSpell__doc,
+"RemoveSpell(PartyID, SpellType, Level, Index)=>bool\n\n"
+"Removes specified known spell. Returns 1 on success." );
+
+static PyObject* GemRB_RemoveSpell(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, SpellType, Level, Index;
+
+ if (!PyArg_ParseTuple( args, "iiii", &PartyID, &SpellType, &Level, &Index )) {
+ return AttributeError( GemRB_RemoveSpell__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ CREKnownSpell* ks = actor->spellbook.GetKnownSpell( SpellType, Level, Index );
+ if (! ks) {
+ return RuntimeError( "Spell not known!" );
+ }
+
+ return PyInt_FromLong( actor->spellbook.RemoveSpell( ks ) );
+}
+
+PyDoc_STRVAR( GemRB_RemoveItem__doc,
+"RemoveItem(PartyID, Slot[, Count])=>bool\n\n"
+"Removes (or decreases the charges) of a specified item. Returns 1 on success." );
+
+static PyObject* GemRB_RemoveItem(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, Slot;
+ int Count = 0;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &PartyID, &Slot, &Count )) {
+ return AttributeError( GemRB_RemoveItem__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ int ok;
+
+ Slot = core->QuerySlot(Slot);
+ actor->inventory.UnEquipItem( Slot, false );
+ CREItem *si = actor->inventory.RemoveItem( Slot, Count );
+ if (si) {
+ ok = true;
+ delete si;
+ } else {
+ ok = false;
+ }
+ return PyInt_FromLong( ok );
+}
+
+PyDoc_STRVAR( GemRB_MemorizeSpell__doc,
+"MemorizeSpell(PartyID, SpellType, Level, Index)=>bool\n\n"
+"Memorizes specified known spell. Returns 1 on success." );
+
+static PyObject* GemRB_MemorizeSpell(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, SpellType, Level, Index;
+
+ if (!PyArg_ParseTuple( args, "iiii", &PartyID, &SpellType, &Level, &Index )) {
+ return AttributeError( GemRB_MemorizeSpell__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ CREKnownSpell* ks = actor->spellbook.GetKnownSpell( SpellType, Level, Index );
+ if (! ks) {
+ return RuntimeError( "Spell not found!" );
+ }
+
+ bool enabled = false;
+ if (SpellType == IE_SPELL_TYPE_INNATE) enabled = true;
+
+ return PyInt_FromLong( actor->spellbook.MemorizeSpell( ks, enabled ) );
+}
+
+
+PyDoc_STRVAR( GemRB_UnmemorizeSpell__doc,
+"UnmemorizeSpell(PartyID, SpellType, Level, Index)=>bool\n\n"
+"Unmemorizes specified known spell. Returns 1 on success." );
+
+static PyObject* GemRB_UnmemorizeSpell(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, SpellType, Level, Index;
+
+ if (!PyArg_ParseTuple( args, "iiii", &PartyID, &SpellType, &Level, &Index )) {
+ return AttributeError( GemRB_UnmemorizeSpell__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ CREMemorizedSpell* ms = actor->spellbook.GetMemorizedSpell( SpellType, Level, Index );
+ if (! ms) {
+ return RuntimeError( "Spell not found!\n" );
+ }
+
+ return PyInt_FromLong( actor->spellbook.UnmemorizeSpell( ms ) );
+}
+
+PyDoc_STRVAR( GemRB_GetSlotItem__doc,
+"GetSlotItem(PartyID, slot[, global])=>dict\n\n"
+"Returns dict with specified slot item from PC's inventory or the dragged item if PartyID is 0.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_GetSlotItem(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, Slot;
+ int global = 0;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &PartyID, &Slot, &global)) {
+ return AttributeError( GemRB_GetSlotItem__doc );
+ }
+ CREItem *si;
+ int header = -1;
+
+ if (PartyID==0) {
+ si = core->GetDraggedItem();
+ } else {
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( PartyID );
+ } else {
+ actor = game->FindPC( PartyID );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ Slot = core->QuerySlot(Slot);
+ header = actor->PCStats->GetHeaderForSlot(Slot);
+
+ si = actor->inventory.GetSlotItem( Slot );
+ }
+ if (! si) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ PyObject* dict = PyDict_New();
+ PyDict_SetItemString(dict, "ItemResRef", PyString_FromResRef (si->ItemResRef));
+ PyDict_SetItemString(dict, "Usages0", PyInt_FromLong (si->Usages[0]));
+ PyDict_SetItemString(dict, "Usages1", PyInt_FromLong (si->Usages[1]));
+ PyDict_SetItemString(dict, "Usages2", PyInt_FromLong (si->Usages[2]));
+ PyDict_SetItemString(dict, "Flags", PyInt_FromLong (si->Flags));
+ PyDict_SetItemString(dict, "Header", PyInt_FromLong (header));
+
+ return dict;
+}
+
+PyDoc_STRVAR( GemRB_ChangeItemFlag__doc,
+"ChangeItemFlag(PartyID, slot, flags, op) => bool\n\n"
+"Changes an item flag of a player character in inventory slot. Returns false if failed." );
+
+static PyObject* GemRB_ChangeItemFlag(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, Slot, Flags, Mode;
+
+ if (!PyArg_ParseTuple( args, "iiii", &PartyID, &Slot, &Flags, &Mode)) {
+ return AttributeError( GemRB_ChangeItemFlag__doc );
+ }
+ GET_GAME();
+
+ Actor *actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ if (actor->inventory.ChangeItemFlag(core->QuerySlot(Slot), Flags, Mode)) {
+ return PyInt_FromLong(1);
+ }
+ return PyInt_FromLong(0);
+}
+
+
+PyDoc_STRVAR( GemRB_CanUseItemType__doc,
+"CanUseItemType( slottype, itemname[, actor, equipped])=>bool\n\n"
+"Checks the itemtype vs. slottype, and also checks the usability flags vs. Actor's stats (alignment, class, race, kit etc.)" );
+
+static PyObject* GemRB_CanUseItemType(PyObject * /*self*/, PyObject* args)
+{
+ int SlotType, PartyID, Equipped;
+ const char *ItemName;
+
+ PartyID = 0;
+ if (!PyArg_ParseTuple( args, "is|ii", &SlotType, &ItemName, &PartyID, &Equipped)) {
+ return AttributeError( GemRB_CanUseItemType__doc );
+ }
+ if (!ItemName[0]) {
+ return PyInt_FromLong(0);
+ }
+ Item *item = gamedata->GetItem(ItemName);
+ if (!item) {
+ return PyInt_FromLong(0);
+ }
+ Actor* actor = 0;
+ if (PartyID) {
+ GET_GAME();
+
+ actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ }
+
+ int ret=core->CanUseItemType(SlotType, item, actor, false, Equipped != 0);
+ gamedata->FreeItem(item, ItemName, false);
+ return PyInt_FromLong(ret);
+}
+
+
+PyDoc_STRVAR( GemRB_GetSlots__doc,
+"GetSlots(PartyID, SlotType[,flag])=>dict\n\n"
+"Returns a tuple of slots of the inventory of a PC matching the slot type criteria.\n"
+"If the flag is >0, it will ignore empty slots.\n"
+"If the flag is <0, it will ignore filled slots.\n"
+"If the flag is 0, it will return all slots.\n"
+"The default is 1." );
+
+static PyObject* GemRB_GetSlots(PyObject * /*self*/, PyObject* args)
+{
+ int SlotType, Count, MaxCount, PartyID;
+ int flag = 1;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &PartyID, &SlotType, &flag)) {
+ return AttributeError( GemRB_GetSlots__doc );
+ }
+
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ MaxCount = core->SlotTypes;
+ int i;
+ Count = 0;
+ for (i=0;i<MaxCount;i++) {
+ int id = core->QuerySlot(i);
+ if ((core->QuerySlotType( id ) & (ieDword) SlotType) != (ieDword) SlotType) {
+ continue;
+ }
+ CREItem *slot = actor->inventory.GetSlotItem( id );
+ if (flag) {
+ if(flag<0 && slot) continue;
+ if(flag>0 && !slot) continue;
+ }
+ Count++;
+ }
+
+ PyObject* tuple = PyTuple_New( Count );
+ Count = 0;
+ for (i=0;i<MaxCount;i++) {
+ int id = core->QuerySlot(i);
+ if ((core->QuerySlotType( id ) & (ieDword) SlotType) != (ieDword) SlotType) {
+ continue;
+ }
+ CREItem *slot = actor->inventory.GetSlotItem( id );
+ if (flag) {
+ if(flag<0 && slot) continue;
+ if(flag>0 && !slot) continue;
+ }
+ PyTuple_SetItem( tuple, Count++, PyInt_FromLong( i ) );
+ }
+
+ return tuple;
+}
+
+PyDoc_STRVAR( GemRB_GetItem__doc,
+"GetItem(ResRef)=>dict\n\n"
+"Returns dict with specified item." );
+
+#define CAN_DRINK 1 //potions
+#define CAN_READ 2 //scrolls
+#define CAN_STUFF 4 //containers
+#define CAN_SELECT 8 //items with more abilities
+
+static PyObject* GemRB_GetItem(PyObject * /*self*/, PyObject* args)
+{
+ char* ResRef;
+ int PartyID = 0;
+ Actor *actor = NULL;
+
+ if (!PyArg_ParseTuple( args, "s|i", &ResRef, &PartyID)) {
+ return AttributeError( GemRB_GetItem__doc );
+ }
+ //it isn't a problem if actor not found
+ Game *game = core->GetGame();
+ if (game) {
+ if (!PartyID) {
+ PartyID = game->GetSelectedPCSingle();
+ }
+ actor = game->FindPC( PartyID );
+ }
+
+ Item* item = gamedata->GetItem(ResRef);
+ if (item == NULL) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ PyObject* dict = PyDict_New();
+ PyDict_SetItemString(dict, "ItemName", PyInt_FromLong ((signed) item->GetItemName(false)));
+ PyDict_SetItemString(dict, "ItemNameIdentified", PyInt_FromLong ((signed) item->GetItemName(true)));
+ PyDict_SetItemString(dict, "ItemDesc", PyInt_FromLong ((signed) item->GetItemDesc(false)));
+ PyDict_SetItemString(dict, "ItemDescIdentified", PyInt_FromLong ((signed)item->GetItemDesc(true)));
+ PyDict_SetItemString(dict, "ItemIcon", PyString_FromResRef (item->ItemIcon));
+ PyDict_SetItemString(dict, "DescIcon", PyString_FromResRef (item->DescriptionIcon));
+ PyDict_SetItemString(dict, "BrokenItem", PyString_FromResRef (item->ReplacementItem));
+ PyDict_SetItemString(dict, "StackAmount", PyInt_FromLong (item->StackAmount));
+ PyDict_SetItemString(dict, "Dialog", PyString_FromResRef (item->Dialog));
+ PyDict_SetItemString(dict, "DialogName", PyInt_FromLong ((signed)item->DialogName));
+ PyDict_SetItemString(dict, "Price", PyInt_FromLong (item->Price));
+ PyDict_SetItemString(dict, "Type", PyInt_FromLong (item->ItemType));
+ PyDict_SetItemString(dict, "AnimationType", PyString_FromAnimID(item->AnimationType));
+ PyDict_SetItemString(dict, "Exclusion", PyInt_FromLong(item->ItemExcl));
+ PyDict_SetItemString(dict, "LoreToID", PyInt_FromLong(item->LoreToID));
+
+ int ehc = item->ExtHeaderCount;
+
+ PyObject* tooltiptuple = PyTuple_New(ehc);
+ for(int i=0;i<ehc;i++) {
+ int tip = core->GetItemTooltip(ResRef, i, 1);
+ PyTuple_SetItem(tooltiptuple, i, PyInt_FromLong(tip));
+ }
+
+ PyDict_SetItemString(dict, "Tooltips", tooltiptuple);
+
+ int function=0;
+
+ if (core->CanUseItemType(SLOT_POTION, item, actor, false) ) {
+ function|=CAN_DRINK;
+ }
+ if (core->CanUseItemType(SLOT_SCROLL, item, actor, false) ) {
+ ITMExtHeader *eh;
+ Effect *f;
+ //determining if this is a copyable scroll
+ if (ehc<2) {
+ goto not_a_scroll;
+ }
+ eh = item->ext_headers+1;
+ if (eh->FeatureCount<1) {
+ goto not_a_scroll;
+ }
+ f = eh->features; //+0
+
+ //normally the learn spell opcode is 147
+ EffectQueue::ResolveEffect(fx_learn_spell_ref);
+ if (f->Opcode!=(ieDword) fx_learn_spell_ref.opcode) {
+ goto not_a_scroll;
+ }
+ //maybe further checks for school exclusion?
+ //no, those were done by CanUseItemType
+ function|=CAN_READ;
+ PyDict_SetItemString(dict, "Spell", PyString_FromResRef (f->Resource));
+ } else if (ehc>1) {
+ function|=CAN_SELECT;
+ }
+not_a_scroll:
+ if (core->CanUseItemType(SLOT_BAG, item, NULL, false) ) {
+ //allow the open container flag only if there is
+ //a store file (this fixes pst eye items, which
+ //got the same item type as bags)
+ //while this isn't required anymore, as bag itemtypes are customisable
+ //we still better check for the existence of the store, or we
+ //get a crash somewhere.
+ if (gamedata->Exists( ResRef, IE_STO_CLASS_ID) ) {
+ function|=CAN_STUFF;
+ }
+ }
+ PyDict_SetItemString(dict, "Function", PyInt_FromLong(function));
+ gamedata->FreeItem( item, ResRef, false );
+ return dict;
+}
+
+void DragItem(CREItem *si)
+{
+ if (!si) {
+ return;
+ }
+ Item *item = gamedata->GetItem (si->ItemResRef );
+ if (!item) {
+ return;
+ }
+ core->DragItem(si, item->ItemIcon);
+ gamedata->FreeItem( item, si->ItemResRef, false );
+}
+
+int CheckRemoveItem(Actor *actor, CREItem *si, int action)
+{
+ ///check if item is undroppable because the actor likes it
+ if (UsedItemsCount==-1) {
+ ReadUsedItems();
+ }
+ unsigned int i=UsedItemsCount;
+
+ while(i--) {
+ if (UsedItems[i].itemname[0] && strnicmp(UsedItems[i].itemname, si->ItemResRef,8) ) {
+ continue;
+ }
+ //true if names don't match
+ int nomatch = (UsedItems[i].username[0] && strnicmp(UsedItems[i].username, actor->GetScriptName(), 32) );
+
+ switch(action) {
+ //the named actor cannot remove it
+ case CRI_REMOVE:
+ if (UsedItems[i].flags&1) {
+ if (nomatch) continue;
+ } else continue;
+ break;
+ //the named actor can equip it
+ case CRI_EQUIP:
+ if (UsedItems[i].flags&2) {
+ if (!nomatch) continue;
+ } else continue;
+ break;
+ //the named actor can swap it
+ case CRI_SWAP:
+ if (UsedItems[i].flags&4) {
+ if (!nomatch) continue;
+ } else continue;
+ break;
+ }
+
+ displaymsg->DisplayString(UsedItems[i].value,0xffffff, IE_STR_SOUND);
+ return 1;
+ }
+ return 0;
+}
+
+CREItem *TryToUnequip(Actor *actor, unsigned int Slot, unsigned int Count)
+{
+ //we should use getslotitem, because
+ //getitem would remove the item from the inventory!
+ CREItem *si = actor->inventory.GetSlotItem(Slot);
+ if (!si) {
+ return NULL;
+ }
+
+ //it is always possible to put these items into the inventory
+ if (!(core->QuerySlotType(Slot)&SLOT_INVENTORY)) {
+ bool isdragging = core->GetDraggedItem()!=NULL;
+ if (CheckRemoveItem(actor, si, isdragging?CRI_SWAP:CRI_REMOVE)) {
+ return NULL;
+ }
+ }
+ ///fixme: make difference between cursed/unmovable
+ if (! actor->inventory.UnEquipItem( Slot, false )) {
+ // Item is currently undroppable/cursed
+ displaymsg->DisplayConstantString(STR_CANT_DROP_ITEM,0xffffff);
+ return NULL;
+ }
+ si = actor->inventory.RemoveItem( Slot, Count );
+ return si;
+}
+
+PyDoc_STRVAR( GemRB_DragItem__doc,
+"DragItem(PartyID, Slot, ResRef, [Count=0, Type])\n\n"
+"Start dragging specified item, if Slot is negative, drag the PartyID portrait instead." );
+
+static PyObject* GemRB_DragItem(PyObject * /*self*/, PyObject* args)
+{
+ ieResRef Sound;
+ int PartyID, Slot, Count = 0, Type = 0;
+ const char *ResRef;
+
+ if (!PyArg_ParseTuple( args, "iis|ii", &PartyID, &Slot, &ResRef, &Count, &Type)) {
+ return AttributeError( GemRB_DragItem__doc );
+ }
+
+ // FIXME
+ // we should Drop the Dragged item in place of the current item
+ // but only if the current item is draggable, tough!
+ if (core->GetDraggedItem()) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ //allow -1,-1
+ if (!actor && ( PartyID || ResRef[0]) ) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ //dragging a portrait
+ if (!ResRef[0]) {
+ core->SetDraggedPortrait(PartyID, Slot);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ if ((unsigned int) Slot>core->GetInventorySize()) {
+ return AttributeError( "Invalid slot" );
+ }
+ CREItem* si;
+ if (Type) {
+ Map *map = actor->GetCurrentArea();
+ Container *cc = map->GetPile(actor->Pos);
+ if (!cc) {
+ return RuntimeError( "No current container" );
+ }
+ si = cc->RemoveItem(Slot, Count);
+ } else {
+ si = TryToUnequip( actor, core->QuerySlot(Slot), Count );
+ actor->RefreshEffects(NULL);
+ actor->ReinitQuickSlots();
+ core->SetEventFlag(EF_SELECTION);
+ }
+ if (! si) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ Item *item = gamedata->GetItem(si->ItemResRef);
+ if (item) {
+ if (core->HasFeature(GF_HAS_PICK_SOUND) && item->DescriptionIcon[0]) {
+ memcpy(Sound,item->DescriptionIcon,sizeof(ieResRef));
+ } else {
+ GetItemSound(Sound, item->ItemType, item->AnimationType, IS_GET);
+ }
+ gamedata->FreeItem(item, si->ItemResRef,0);
+ }
+ if (Sound[0]) {
+ core->GetAudioDrv()->Play(Sound);
+ }
+
+ //if res is positive, it is gold!
+ int res = core->CanMoveItem(si);
+ if (res>0) {
+ game->AddGold(res);
+ delete si;
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ core->DragItem (si, ResRef);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_DropDraggedItem__doc,
+"DropDraggedItem(PartyID, Slot)=>int\n\n"
+"Put currently dragged item to specified PC and slot. "
+"If Slot==-1, puts it to a first usable slot. "
+"If Slot==-2, puts it to a ground pile. "
+"If Slot==-3, puts it to the first empty inventory slot. "
+"Returns 0 (unsuccessful), 1 (partial success) or 2 (complete success)."
+"Can also return 3 (swapped item)\n" );
+
+static PyObject* GemRB_DropDraggedItem(PyObject * /*self*/, PyObject* args)
+{
+ ieResRef Sound;
+ int PartyID, Slot;
+
+ if (!PyArg_ParseTuple( args, "ii", &PartyID, &Slot)) {
+ return AttributeError( GemRB_DropDraggedItem__doc );
+ }
+
+ // FIXME
+ if (core->GetDraggedItem() == NULL) {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ int res;
+
+ if (Slot==-2) {
+ Map *map = actor->GetCurrentArea();
+ Container *cc = map->GetPile(actor->Pos);
+ if (!cc) {
+ return RuntimeError( "No current container" );
+ }
+ CREItem *si = core->GetDraggedItem();
+ res = cc->AddItem(si);
+ Item *item = gamedata->GetItem(si->ItemResRef);
+ if (item) {
+ if (core->HasFeature(GF_HAS_PICK_SOUND) && item->ReplacementItem[0]) {
+ memcpy(Sound,item->ReplacementItem,sizeof(ieResRef));
+ } else {
+ GetItemSound(Sound, item->ItemType, item->AnimationType, IS_DROP);
+ }
+ gamedata->FreeItem(item, si->ItemResRef,0);
+ if (Sound[0]) {
+ core->GetAudioDrv()->Play(Sound);
+ }
+ }
+ if (res == 2) {
+ // Whole amount was placed
+ core->ReleaseDraggedItem ();
+ }
+ return PyInt_FromLong( res );
+ }
+
+ int Slottype, Effect;
+ switch(Slot) {
+ case -1:
+ //anything but inventory
+ Slottype = ~SLOT_INVENTORY;
+ Effect = 1;
+ break;
+ case -3:
+ //only inventory
+ Slottype = -1;
+ Effect = 0;
+ break;
+ default:
+ Slot = core->QuerySlot(Slot);
+ Slottype = core->QuerySlotType( Slot );
+ Effect = core->QuerySlotEffects( Slot );
+ }
+ CREItem * slotitem = core->GetDraggedItem();
+ Item *item = gamedata->GetItem( slotitem->ItemResRef );
+ if (!item) {
+ return PyInt_FromLong( -1 );
+ }
+
+ // can't equip item because of similar already equipped
+ if (Effect) {
+ if (item->ItemExcl & actor->inventory.GetEquipExclusion(Slot)) {
+ displaymsg->DisplayConstantString(STR_ITEMEXCL, 0xf0f0f0);
+ //freeing the item before returning
+ gamedata->FreeItem( item, slotitem->ItemResRef, false );
+ return PyInt_FromLong( 0 );
+ }
+ }
+
+ // can't equip item because it is not identified
+ if ( (Slottype == SLOT_ITEM) && !(slotitem->Flags&IE_INV_ITEM_IDENTIFIED)) {
+ ITMExtHeader *eh = item->GetExtHeader(0);
+ if (eh && eh->IDReq) {
+ displaymsg->DisplayConstantString(STR_ITEMID, 0xf0f0f0);
+ gamedata->FreeItem( item, slotitem->ItemResRef, false );
+ return PyInt_FromLong( 0 );
+ }
+ }
+
+ //it is always possible to put these items into the inventory
+ if (!(Slottype&SLOT_INVENTORY)) {
+ if (CheckRemoveItem(actor, slotitem, CRI_EQUIP)) {
+ return PyInt_FromLong( 0 );
+ }
+ }
+
+ //CanUseItemType will check actor's class bits too
+ Slottype = core->CanUseItemType (Slottype, item, actor, true);
+ //resolve the equipping sound, it needs to be resolved before
+ //the item is freed
+ if (core->HasFeature(GF_HAS_PICK_SOUND) && item->ReplacementItem[0]) {
+ memcpy(Sound, item->ReplacementItem, 9);
+ } else {
+ GetItemSound(Sound, item->ItemType, item->AnimationType, IS_DROP);
+ }
+
+ //freeing the item before returning
+ gamedata->FreeItem( item, slotitem->ItemResRef, false );
+ if ( !Slottype) {
+ return PyInt_FromLong( 0 );
+ }
+ res = actor->inventory.AddSlotItem( slotitem, Slot, Slottype );
+ if (res) {
+ //release it only when fully placed
+ if (res==ASI_SUCCESS) {
+ core->ReleaseDraggedItem ();
+ } else {
+ // res == ASI_PARTIAL
+ // swap the items, so the existing one ends up being filled;
+ // the items are the same, so we just need to adjust the usage count
+ int usages = slotitem->Usages[0];
+ CREItem *itm = actor->inventory.GetSlotItem(Slot);
+ if (itm) {
+ slotitem->Usages[0] = itm->Usages[0];
+ itm->Usages[0] = usages;
+ }
+ }
+ //EquipItem (in AddSlotItem) already called RefreshEffects
+ actor->ReinitQuickSlots();
+ //couldn't place item there, try swapping (only if slot is explicit)
+ } else if ( Slot >= 0 ) {
+ //swapping won't cure this
+ res = actor->inventory.WhyCantEquip(Slot, slotitem->Flags&IE_INV_ITEM_TWOHANDED);
+ if (res) {
+ displaymsg->DisplayConstantString(res,0xffffff);
+ return PyInt_FromLong( 0 );
+ }
+ CREItem *tmp = TryToUnequip(actor, Slot, 0 );
+ if (tmp) {
+ //this addslotitem MUST succeed because the slot was
+ //just emptied (canuseitemtype already confirmed too)
+ actor->inventory.AddSlotItem( slotitem, Slot, Slottype );
+ core->ReleaseDraggedItem ();
+ DragItem(tmp);
+ // switched items, not returned by normal AddSlotItem
+ res = ASI_SWAPPED;
+ //EquipItem (in AddSlotItem) already called RefreshEffects
+ actor->RefreshEffects(NULL);
+ actor->ReinitQuickSlots();
+ core->SetEventFlag(EF_SELECTION);
+ } else {
+ res = ASI_FAILED;
+ }
+ }
+ if (Sound[0]) {
+ core->GetAudioDrv()->Play(Sound);
+ }
+ return PyInt_FromLong( res );
+}
+
+PyDoc_STRVAR( GemRB_IsDraggingItem__doc,
+"IsDraggingItem()=>int\n\n"
+"Returns 1 if we are dragging some item.\n"
+"Returns 2 if we are dragging a portrait." );
+
+static PyObject* GemRB_IsDraggingItem(PyObject * /*self*/, PyObject* /*args*/)
+{
+ if (core->GetDraggedPortrait()) {
+ return PyInt_FromLong(2);
+ }
+ return PyInt_FromLong( core->GetDraggedItem() != NULL );
+}
+
+PyDoc_STRVAR( GemRB_GetSystemVariable__doc,
+"GetSystemVariable(Variable)=>int\n\n"
+"Returns the named Interface attribute." );
+
+static PyObject* GemRB_GetSystemVariable(PyObject * /*self*/, PyObject* args)
+{
+ int Variable, value;
+
+ if (!PyArg_ParseTuple( args, "i", &Variable)) {
+ return AttributeError( GemRB_GetSystemVariable__doc );
+ }
+ switch(Variable) {
+ case SV_BPP: value = core->Bpp; break;
+ case SV_WIDTH: value = core->Width; break;
+ case SV_HEIGHT: value = core->Height; break;
+ default: value = -1; break;
+ }
+ return PyInt_FromLong( value );
+}
+
+PyDoc_STRVAR( GemRB_CreateItem__doc,
+"CreateItem(PartyID, ItemResRef, [SlotID, Charge0, Charge1, Charge2])\n\n"
+"Creates Item in the inventory of the player character.");
+
+static PyObject* GemRB_CreateItem(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ int SlotID=-1;
+ int Charge0=1,Charge1=0,Charge2=0;
+ const char *ItemResRef;
+
+ if (!PyArg_ParseTuple( args, "is|iiii", &PartyID, &ItemResRef, &SlotID, &Charge0, &Charge1, &Charge2)) {
+ return AttributeError( GemRB_CreateItem__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ if (SlotID==-1) {
+ //This is already a slot ID we need later
+ SlotID=actor->inventory.FindCandidateSlot(SLOT_INVENTORY,0);
+ } else {
+ //I believe we need this only here
+ SlotID = core->QuerySlot(SlotID);
+ }
+
+ if (SlotID==-1) {
+ // Create item on ground
+ Map *map = actor->GetCurrentArea();
+ if (map) {
+ CREItem *item = CreateCreItem(ItemResRef, Charge0, Charge1, Charge2 );
+ if (item) {
+ map->AddItemToLocation(actor->Pos, item);
+ }
+ }
+ } else {
+ // Note: this forcefully gets rid of any item currently
+ // in the slot without properly unequipping it
+ actor->inventory.SetSlotItemRes( ItemResRef, SlotID, Charge0, Charge1, Charge2 );
+ actor->inventory.EquipItem(SlotID);
+ //EquipItem already called RefreshEffects
+ actor->ReinitQuickSlots();
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetMapAnimation__doc,
+"SetMapAnimation(X, Y, BAMresref[, flags, cycle, height])\n\n"
+"Creates an area animation.");
+
+static PyObject* GemRB_SetMapAnimation(PyObject * /*self*/, PyObject* args)
+{
+ int x,y;
+ const char *ResRef;
+ int Cycle = 0;
+ int Flags = 0x19;
+ int Height = 0x1e;
+ //the animation is cloned by AddAnimation, so we can keep the original on
+ //the stack
+ AreaAnimation anim;
+ memset(&anim,0,sizeof(anim));
+
+ if (!PyArg_ParseTuple( args, "iis|iii", &x, &y, &ResRef, &Flags, &Cycle, &Height)) {
+ return AttributeError( GemRB_CreateItem__doc );
+ }
+
+ GET_GAME();
+
+ GET_MAP();
+
+ anim.appearance=0xffffffff; //scheduled for every hour
+ anim.Pos.x=(short) x;
+ anim.Pos.y=(short) y;
+ strnlwrcpy(anim.Name, ResRef, 8);
+ strnlwrcpy(anim.BAM, ResRef, 8);
+ anim.Flags=Flags;
+ anim.sequence=Cycle;
+ anim.height=Height;
+ if (Flags&A_ANI_ACTIVE) {
+ map->AddAnimation(&anim);
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetMapnote__doc,
+"SetMapnote(X, Y, color, Text)\n\n"
+"Adds or removes a mapnote.");
+
+static PyObject* GemRB_SetMapnote(PyObject * /*self*/, PyObject* args)
+{
+ int x,y;
+ int color=0;
+ const char *txt=NULL;
+
+ if (!PyArg_ParseTuple( args, "ii|is", &x, &y, &color, &txt)) {
+ return AttributeError( GemRB_SetMapnote__doc );
+ }
+ GET_GAME();
+
+ GET_MAP();
+
+ ieStrRef strref;
+ Point point;
+ point.x=x;
+ point.y=y;
+ if (txt && txt[0]) {
+ char* newvalue = ( char* ) malloc( strlen( txt ) + 1 ); //duplicating the string
+ strcpy( newvalue, txt );
+ MapNote *old = map->GetMapNote(point);
+ if (old) strref = old->strref;
+ else strref = 0xffffffff;
+ map->AddMapNote(point, color, newvalue, strref);
+ } else {
+ map->RemoveMapNote(point);
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetMapDoor__doc,
+"SetMapDoor(DoorName, State)\n\n"
+"Modifies a door's open state in the current area.");
+
+static PyObject* GemRB_SetMapDoor(PyObject * /*self*/, PyObject* args)
+{
+ const char *DoorName;
+ int State;
+
+ if (!PyArg_ParseTuple( args, "si", &DoorName, &State) ) {
+ return AttributeError( GemRB_SetMapDoor__doc);
+ }
+
+ GET_GAME();
+
+ GET_MAP();
+
+ Door *door = map->TMap->GetDoor(DoorName);
+ if (!door) {
+ return RuntimeError( "No such door!" );
+ }
+
+ door->SetDoorOpen(State, 0, 0);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetMapExit__doc,
+"SetMapExit(ExitName[, NewArea, NewEntrance])\n\n"
+"Modifies the target of an exit in the current area. If no destination is given, "
+"then the exit will be disabled.");
+
+static PyObject* GemRB_SetMapExit(PyObject * /*self*/, PyObject* args)
+{
+ const char *ExitName;
+ const char *NewArea = NULL;
+ const char *NewEntrance = NULL;
+
+ if (!PyArg_ParseTuple( args, "s|ss", &ExitName, &NewArea, &NewEntrance)) {
+ return AttributeError( GemRB_SetMapExit__doc );
+ }
+
+ GET_GAME();
+
+ GET_MAP();
+
+ InfoPoint *ip = map->TMap->GetInfoPoint(ExitName);
+ if (!ip || ip->Type!=ST_TRAVEL) {
+ return RuntimeError( "No such exit!" );
+ }
+
+ if (!NewArea) {
+ //disable entrance
+ ip->Flags|=TRAP_DEACTIVATED;
+ } else {
+ //activate entrance
+ ip->Flags&=~TRAP_DEACTIVATED;
+ //set destination area
+ strnuprcpy(ip->Destination, NewArea, sizeof(ieResRef)-1 );
+ //change entrance only if supplied
+ if (NewEntrance) {
+ strnuprcpy(ip->EntranceName, NewEntrance, sizeof(ieVariable)-1 );
+ }
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetMapRegion__doc,
+"SetMapRegion(TrapName[, trapscript])\n\n"
+"Enables or disables an infopoint in the current area.");
+
+static PyObject* GemRB_SetMapRegion(PyObject * /*self*/, PyObject* args)
+{
+ const char *Name;
+ const char *TrapScript = NULL;
+
+ if (!PyArg_ParseTuple( args, "s|s", &Name, &TrapScript)) {
+ return AttributeError( GemRB_SetMapRegion__doc );
+ }
+
+ GET_GAME();
+
+ GET_MAP();
+
+ InfoPoint *ip = map->TMap->GetInfoPoint(Name);
+ if (ip) {
+ if (TrapScript && TrapScript[0]) {
+ ip->Flags&=~TRAP_DEACTIVATED;
+ ip->SetScript(TrapScript,0);
+ } else {
+ ip->Flags|=TRAP_DEACTIVATED;
+ }
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_CreateCreature__doc,
+"CreateCreature(PartyID, CreResRef[, posX, posY])\n\n"
+"Creates Creature at a point. If the position parameters are unspecified "
+"then the creature will be put near the player character given by the first parameter.");
+
+static PyObject* GemRB_CreateCreature(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ const char *CreResRef;
+ int PosX = -1, PosY = -1;
+
+ if (!PyArg_ParseTuple( args, "is|ii", &PartyID, &CreResRef, &PosX, &PosY)) {
+ return AttributeError( GemRB_CreateCreature__doc );
+ }
+
+ GET_GAME();
+
+ GET_MAP();
+
+ if (PosX!=-1 && PosY!=-1) {
+ map->SpawnCreature(Point(PosX, PosY), CreResRef, 0);
+ } else {
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ map->SpawnCreature(actor->Pos, CreResRef, 10);
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_RevealArea__doc,
+"RevealArea(x, y, radius, type)\n\n"
+"Reveals part of the area.");
+
+static PyObject* GemRB_RevealArea(PyObject * /*self*/, PyObject* args)
+{
+ int x,y;
+ int radius;
+ int Value;
+
+ if (!PyArg_ParseTuple( args, "iiii", &x, &y, &radius, &Value)) {
+ return AttributeError( GemRB_RevealArea__doc );
+ }
+
+ Point p(x,y);
+ GET_GAME();
+
+ GET_MAP();
+
+ map->ExploreMapChunk( p, radius, Value );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_ExploreArea__doc,
+"ExploreArea([bitvalue=-1])\n\n"
+"Explores or unexplores whole area.");
+
+static PyObject* GemRB_ExploreArea(PyObject * /*self*/, PyObject* args)
+{
+ int Value=-1;
+
+ if (!PyArg_ParseTuple( args, "|i", &Value)) {
+ return AttributeError( GemRB_ExploreArea__doc );
+ }
+ GET_GAME();
+
+ GET_MAP();
+
+ map->Explore( Value );
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_GetRumour__doc,
+"GetRumour(percent, ResRef) => ieStrRef\n\n"
+"Returns a string to a rumour message. ResRef is a dialog resource.");
+
+static PyObject* GemRB_GetRumour(PyObject * /*self*/, PyObject* args)
+{
+ int percent;
+ const char *ResRef;
+
+ if (!PyArg_ParseTuple( args, "is", &percent, &ResRef)) {
+ return AttributeError( GemRB_GetRumour__doc );
+ }
+ if (rand()%100 >= percent) {
+ return PyInt_FromLong( -1 );
+ }
+
+ ieStrRef strref = core->GetRumour( ResRef );
+ return PyInt_FromLong( strref );
+}
+
+PyDoc_STRVAR( GemRB_GamePause__doc,
+"GamePause(Pause, Quiet)\n\n"
+"Pause or unpause the game or just toggle the pause.");
+
+static PyObject* GemRB_GamePause(PyObject * /*self*/, PyObject* args)
+{
+ int pause, quiet;
+
+ if (!PyArg_ParseTuple( args, "ii", &pause, &quiet)) {
+ return AttributeError( GemRB_GamePause__doc );
+ }
+
+ GameControl *gc = core->GetGameControl();
+ if (gc) {
+ //this will trigger when pause is not 0 or 1
+ if ((unsigned int) pause > 1) {
+ pause = ~gc->GetDialogueFlags()&DF_FREEZE_SCRIPTS;
+ }
+
+ if (pause) {
+ gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR);
+ } else {
+ gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_NAND);
+ }
+ if (!quiet) {
+ if (gc->GetDialogueFlags()&DF_FREEZE_SCRIPTS) {
+ displaymsg->DisplayConstantString(STR_PAUSED,0xff0000);
+ } else {
+ displaymsg->DisplayConstantString(STR_UNPAUSED,0xff0000);
+ }
+ }
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_CheckFeatCondition__doc,
+"CheckFeatCondition(partyslot, a_stat, a_value, b_stat, b_value, c_stat, c_value, d_stat, d_value[,a_op, b_op, c_op, d_op]) => bool\n\n"
+"Checks if actor in partyslot is eligible for a feat, the formula is: (stat[a]~a or stat[b]~b) and (stat[c]~c or stat[d]~d). Where ~ is a relational operator. If the operators are omitted, the default operator is <=.");
+
+static PyObject* GemRB_CheckFeatCondition(PyObject * /*self*/, PyObject* args)
+{
+ int i;
+ const char *callback = NULL;
+ PyObject* p[13];
+ int v[13];
+ for(i=9;i<13;i++) {
+ p[i]=NULL;
+ v[i]=GREATER_OR_EQUALS;
+ }
+
+ if (!PyArg_UnpackTuple( args, "ref", 9, 13, &p[0], &p[1], &p[2], &p[3], &p[4], &p[5], &p[6], &p[7], &p[8], &p[9], &p[10], &p[11], &p[12] )) {
+ return AttributeError( GemRB_CheckFeatCondition__doc );
+ }
+
+ if (!PyObject_TypeCheck( p[0], &PyInt_Type )) {
+ return AttributeError( GemRB_CheckFeatCondition__doc );
+ }
+ v[0]=PyInt_AsLong( p[0] ); //slot
+
+ if (PyObject_TypeCheck( p[1], &PyInt_Type )) {
+ v[1]=PyInt_AsLong( p[1] ); //a_stat
+ } else {
+ if (!PyObject_TypeCheck( p[1], &PyString_Type )) {
+ return AttributeError( GemRB_CheckFeatCondition__doc );
+ }
+ callback = PyString_AsString( p[1] ); // callback
+ if (callback == NULL) {
+ return RuntimeError("Null string received");
+ }
+ }
+ v[0]=PyInt_AsLong( p[0] );
+
+ for(i=2;i<9;i++) {
+ if (!PyObject_TypeCheck( p[i], &PyInt_Type )) {
+ return AttributeError( GemRB_CheckFeatCondition__doc );
+ }
+ v[i]=PyInt_AsLong( p[i] );
+ }
+
+ if (p[9]) {
+ for(i=9;i<13;i++) {
+ if (!PyObject_TypeCheck( p[i], &PyInt_Type )) {
+ return AttributeError( GemRB_CheckFeatCondition__doc );
+ }
+ v[i]=PyInt_AsLong( p[i] );
+ }
+ }
+
+ GET_GAME();
+
+ Actor *actor = game->FindPC(v[0]);
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ /* see if the special function exists */
+ if (callback) {
+ char fname[32];
+
+ snprintf(fname, 32, "Check_%s", callback);
+ PyObject* param = PyTuple_New( 11 );
+ PyTuple_SetItem( param, 0, PyInt_FromLong(v[0]) );
+ for (i = 3;i<13;i++) {
+ PyTuple_SetItem( param, i-2, PyInt_FromLong( v[i] ) );
+ }
+
+ PyObject *pValue = gs->CallbackFunction(fname, param);
+
+ /* we created this parameter, now we don't need it*/
+ Py_DECREF( param );
+ if (pValue) {
+ /* don't think we need any incref */
+ return pValue;
+ }
+ return RuntimeError( "Callback failed" );
+ }
+
+ bool ret = true;
+
+ if (v[1] || v[2])
+ ret = CheckStat(actor, v[1], v[2], v[9]);
+
+ if (v[3] || v[4])
+ ret |= CheckStat(actor, v[3], v[4], v[10]);
+
+ if (!ret)
+ goto endofquest;
+
+ if (v[5] || v[6])
+ // no | because the formula is (a|b) & (c|d)
+ ret = CheckStat(actor, v[5], v[6], v[11]);
+
+ if (v[7] || v[8])
+ ret |= CheckStat(actor, v[7], v[8], v[12]);
+
+endofquest:
+ if (ret) {
+ Py_INCREF( Py_True );
+ return Py_True;
+ } else {
+ Py_INCREF( Py_False );
+ return Py_False;
+ }
+}
+
+PyDoc_STRVAR( GemRB_GetAbilityBonus__doc,
+"GetAbilityBonus(stat, column, value[, ex])\n\n"
+"Returns an ability bonus value based on various .2da files.");
+
+static PyObject* GemRB_GetAbilityBonus(PyObject * /*self*/, PyObject* args)
+{
+ int stat, column, value, ex = 0;
+ int ret;
+
+ if (!PyArg_ParseTuple( args, "iii|i", &stat, &column, &value, &ex)) {
+ return AttributeError( GemRB_GetAbilityBonus__doc );
+ }
+
+ GET_GAME();
+
+ Actor *actor = game->FindPC(game->GetSelectedPCSingle());
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ switch (stat) {
+ case IE_STR:
+ ret=core->GetStrengthBonus(column, value, ex);
+ break;
+ case IE_INT:
+ ret=core->GetIntelligenceBonus(column, value);
+ break;
+ case IE_DEX:
+ ret=core->GetDexterityBonus(column, value);
+ break;
+ case IE_CON:
+ ret=core->GetConstitutionBonus(column, value);
+ break;
+ case IE_CHR:
+ ret=core->GetCharismaBonus(column, value);
+ break;
+ case IE_LORE:
+ ret=core->GetLoreBonus(column, value);
+ break;
+ case IE_REPUTATION: //both chr and reputation affect the reaction, but chr is already taken
+ ret=GetReaction(actor, NULL); // this is used only for display, so the null is fine
+ break;
+ case IE_WIS:
+ ret=core->GetWisdomBonus(column, value);
+ break;
+ default:
+ return RuntimeError( "Invalid ability!");
+ }
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_LeaveParty__doc,
+"LeaveParty(Slot [,dialog])\n\n"
+"Makes player in Slot leave party, and initiate dialog if demanded." );
+
+static PyObject* GemRB_LeaveParty(PyObject * /*self*/, PyObject* args)
+{
+ int PlayerSlot, initDialog = 0;
+
+ if (!PyArg_ParseTuple( args, "i|i", &PlayerSlot, &initDialog )) {
+ return AttributeError( GemRB_LeaveParty__doc );
+ }
+ GET_GAME();
+
+ Actor *actor = game->FindPC(PlayerSlot);
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ if (initDialog) {
+ if (initDialog == 2)
+ GameScript::SetLeavePartyDialogFile(actor, NULL);
+ if(actor->GetBase(IE_HITPOINTS) > 0) {
+ char Tmp[40];
+ actor->ClearPath();
+ actor->ClearActions();
+ strncpy(Tmp,"Dialogue([PC])",sizeof(Tmp) );
+ actor->AddAction( GenerateAction(Tmp) );
+ }
+ }
+ game->LeaveParty (actor);
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+typedef union pack {
+ ieDword data;
+ ieByte bytes[4];
+} packtype;
+
+static void ReadActionButtons()
+{
+ unsigned int i;
+
+ memset(GUIAction, -1, sizeof(GUIAction));
+ memset(GUITooltip, -1, sizeof(GUITooltip));
+ memset(GUIResRef, 0, sizeof(GUIResRef));
+ memset(GUIEvent, 0, sizeof(GUIEvent));
+ int table = gamedata->LoadTable( "guibtact" );
+ if (table<0) {
+ return;
+ }
+ Holder<TableMgr> tab = gamedata->GetTable( table );
+ for (i = 0; i < MAX_ACT_COUNT; i++) {
+ packtype row;
+
+ row.bytes[0] = (ieByte) atoi( tab->QueryField(i,0) );
+ row.bytes[1] = (ieByte) atoi( tab->QueryField(i,1) );
+ row.bytes[2] = (ieByte) atoi( tab->QueryField(i,2) );
+ row.bytes[3] = (ieByte) atoi( tab->QueryField(i,3) );
+ GUIAction[i] = row.data;
+ GUITooltip[i] = atoi( tab->QueryField(i,4) );
+ strnlwrcpy(GUIResRef[i], tab->QueryField(i,5), 8);
+ strncpy(GUIEvent[i], tab->GetRowName(i), 16);
+ }
+ gamedata->DelTable( table );
+}
+
+static void SetButtonCycle(AnimationFactory *bam, Button *btn, int cycle, unsigned char which)
+{
+ Sprite2D *tspr = bam->GetFrame( cycle, 0 );
+ btn->SetImage( which, tspr );
+}
+
+PyDoc_STRVAR( GemRB_Button_SetActionIcon__doc,
+"SetActionIcon(Window, Button, Dict, ActionIndex[, Function])\n\n"
+"Sets up an action button. The ActionIndex should be less than 34." );
+
+static PyObject* SetActionIcon(int WindowIndex, int ControlIndex, PyObject *dict, int Index, int Function)
+{
+ if (ControlIndex>99) {
+ return AttributeError( GemRB_Button_SetActionIcon__doc );
+ }
+ if (Index>=MAX_ACT_COUNT) {
+ return AttributeError( GemRB_Button_SetActionIcon__doc );
+ }
+ Button* btn = ( Button* ) GetControl(WindowIndex, ControlIndex, IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+
+ if (Index<0) {
+ btn->SetImage( IE_GUI_BUTTON_UNPRESSED, 0 );
+ btn->SetImage( IE_GUI_BUTTON_PRESSED, 0 );
+ btn->SetImage( IE_GUI_BUTTON_SELECTED, 0 );
+ btn->SetImage( IE_GUI_BUTTON_DISABLED, 0 );
+ btn->SetFlags( IE_GUI_BUTTON_NO_IMAGE, BM_SET );
+ btn->SetEvent( IE_GUI_BUTTON_ON_PRESS, NULL );
+ core->SetTooltip( (ieWord) WindowIndex, (ieWord) ControlIndex, "" );
+ //no incref
+ return Py_None;
+ }
+
+ if (GUIAction[0]==0xcccccccc) {
+ ReadActionButtons();
+ }
+
+
+ //FIXME: this is a hardcoded resource (pst has no such one)
+ AnimationFactory* bam = ( AnimationFactory* )
+ gamedata->GetFactoryResource( GUIResRef[Index],
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (!bam) {
+ char tmpstr[24];
+
+ snprintf(tmpstr,sizeof(tmpstr),"%s BAM not found", GUIResRef[Index]);
+ return RuntimeError( tmpstr );
+ }
+ packtype row;
+
+ row.data = GUIAction[Index];
+ SetButtonCycle(bam, btn, (char) row.bytes[0], IE_GUI_BUTTON_UNPRESSED);
+ SetButtonCycle(bam, btn, (char) row.bytes[1], IE_GUI_BUTTON_PRESSED);
+ SetButtonCycle(bam, btn, (char) row.bytes[2], IE_GUI_BUTTON_SELECTED);
+ SetButtonCycle(bam, btn, (char) row.bytes[3], IE_GUI_BUTTON_DISABLED);
+ btn->SetFlags( IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_PICTURE, BM_NAND );
+ PyObject *Event = PyString_FromFormat("Action%sPressed", GUIEvent[Index]);
+ PyObject *func = PyDict_GetItem(dict, Event);
+ btn->SetEvent( IE_GUI_BUTTON_ON_PRESS, new PythonCallback(func) );
+
+ PyObject *Event2 = PyString_FromFormat("Action%sRightPressed", GUIEvent[Index]);
+ PyObject *func2 = PyDict_GetItem(dict, Event2);
+ btn->SetEvent( IE_GUI_BUTTON_ON_RIGHT_PRESS, new PythonCallback(func2) );
+
+ //cannot make this const, because it will be freed
+ char *txt = core->GetString( GUITooltip[Index] );
+ //will free txt
+ SetFunctionTooltip(WindowIndex, ControlIndex, txt, Function);
+ //no incref
+ return Py_None;
+}
+
+static PyObject* GemRB_Button_SetActionIcon(PyObject * /*self*/, PyObject* args)
+{
+ int WindowIndex, ControlIndex, Index;
+ int Function = 0;
+ PyObject *dict;
+
+ if (!PyArg_ParseTuple( args, "iiOi|i", &WindowIndex, &ControlIndex, &dict, &Index, &Function )) {
+ return AttributeError( GemRB_Button_SetActionIcon__doc );
+ }
+
+ PyObject* ret = SetActionIcon(WindowIndex, ControlIndex, dict, Index, Function);
+ if (ret) {
+ Py_INCREF(ret);
+ }
+ return ret;
+}
+
+PyDoc_STRVAR( GemRB_HasResource__doc,
+"HasResource(ResRef, ResType[, silent])\n\n"
+"Returns true if resource is accessible." );
+
+static PyObject* GemRB_HasResource(PyObject * /*self*/, PyObject* args)
+{
+ const char *ResRef;
+ int ResType;
+ int silent = 0;
+
+ if (!PyArg_ParseTuple( args, "si|i", &ResRef, &ResType, &silent )) {
+ return AttributeError( GemRB_HasResource__doc );
+ }
+ if (gamedata->Exists(ResRef, ResType, silent)) {
+ Py_INCREF( Py_True );
+ return Py_True;
+ } else {
+ Py_INCREF( Py_False );
+ return Py_False;
+ }
+}
+
+PyDoc_STRVAR( GemRB_Window_SetupEquipmentIcons__doc,
+"SetupEquipmentIcons(WindowIndex, dict, slot[, Start, Offset, global])\n\n"
+"Automagically sets up the controls of the equipment list window for a PC indexed by globalID.\n"
+"Start is the beginning of the visible part of the item list.\n"
+"Offset is the ID of the first usable button.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_Window_SetupEquipmentIcons(PyObject * /*self*/, PyObject* args)
+{
+ int wi, slot;
+ int Start = 0;
+ int Offset = 0; //control offset (iwd2 has the action buttons starting at 6)
+ int global = 0;
+ PyObject *dict;
+
+ if (!PyArg_ParseTuple( args, "iOi|iii", &wi, &dict, &slot, &Start, &Offset, &global )) {
+ return AttributeError( GemRB_Window_SetupEquipmentIcons__doc );
+ }
+
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( slot );
+ } else {
+ actor = game->FindPC( slot );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ //-2 because of the left/right scroll icons
+ if (!ItemArray) {
+ ItemArray = (ItemExtHeader *) malloc((GUIBT_COUNT) * sizeof (ItemExtHeader) );
+ }
+ bool more = actor->inventory.GetEquipmentInfo(ItemArray, Start, GUIBT_COUNT-(Start?1:0));
+ int i;
+ if (Start) {
+ PyObject *ret = SetActionIcon(wi,core->GetControl(wi, Offset),dict, ACT_LEFT,0);
+ if (!ret) {
+ return RuntimeError("Cannot set action button!\n");
+ }
+ }
+ //FIXME: this is a hardcoded resource (pst has no such one)
+ AnimationFactory* bam = ( AnimationFactory* )
+ gamedata->GetFactoryResource( "guibtbut",
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (!bam) {
+ return RuntimeError("guibtbut BAM not found");
+ }
+
+ for (i=0;i<GUIBT_COUNT-(more?1:0);i++) {
+ int ci = core->GetControl(wi, i+Offset+(Start?1:0) );
+ Button* btn = (Button *) GetControl( wi, ci, IE_GUI_BUTTON );
+ PyObject *Function = PyDict_GetItemString(dict, "EquipmentPressed");
+ btn->SetEvent(IE_GUI_BUTTON_ON_PRESS, new PythonCallback(Function));
+ strcpy(btn->VarName,"Equipment");
+ btn->Value = Start+i;
+
+ ItemExtHeader *item = ItemArray+i;
+ Sprite2D *Picture = NULL;
+
+ if (item->UseIcon[0]) {
+ Picture = gamedata->GetBAMSprite(item->UseIcon, 1, 0);
+ }
+
+ if (!Picture) {
+ btn->SetState(IE_GUI_BUTTON_DISABLED);
+ btn->SetFlags(IE_GUI_BUTTON_NO_IMAGE, BM_SET);
+ btn->SetTooltip(NULL);
+ } else {
+ SetButtonCycle(bam, btn, 0, IE_GUI_BUTTON_UNPRESSED);
+ SetButtonCycle(bam, btn, 1, IE_GUI_BUTTON_PRESSED);
+ SetButtonCycle(bam, btn, 2, IE_GUI_BUTTON_SELECTED);
+ SetButtonCycle(bam, btn, 3, IE_GUI_BUTTON_DISABLED);
+ btn->SetPicture( Picture );
+ btn->SetState(IE_GUI_BUTTON_UNPRESSED);
+ btn->SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ALIGN_BOTTOM|IE_GUI_BUTTON_ALIGN_RIGHT, BM_SET);
+
+ const CREItem *item_slot = actor->inventory.GetSlotItem(item->slot);
+ int tip = core->GetItemTooltip(item->itemname, item->headerindex, item_slot->Flags&IE_INV_ITEM_IDENTIFIED);
+ if (tip>0) {
+ //cannot make this const, because it will be freed
+ char *tmp = core->GetString((ieStrRef) tip,0);
+ btn->SetTooltip(tmp);
+ core->FreeString(tmp);
+ } else {
+ btn->SetTooltip(NULL);
+ }
+ char usagestr[10];
+
+ if (item->Charges && (item->Charges!=0xffff) ) {
+ sprintf(usagestr,"%d", item->Charges);
+ btn->SetText( usagestr );
+ }
+ if (!item->Charges && (item->ChargeDepletion==CHG_NONE) ) {
+ btn->SetState(IE_GUI_BUTTON_DISABLED);
+ }
+ }
+ }
+
+ if (more) {
+ PyObject *ret = SetActionIcon(wi,core->GetControl(wi, i+Offset+1),dict,ACT_RIGHT,i+1);
+ if (!ret) {
+ return RuntimeError("Cannot set action button!\n");
+ }
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_SetupSpellIcons__doc,
+"SetupSpellIcons(WindowIndex, dict, slot, type[, Start, Offset, global])\n\n"
+"Automagically sets up the controls of the spell or innate list window for a PC indexed by slot.\n"
+"Start is the beginning of the visible part of the spell list.\n"
+"Offset is the ID of the first usable button.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_Window_SetupSpellIcons(PyObject * /*self*/, PyObject* args)
+{
+ int wi, slot, Type;
+ int Start = 0;
+ int Offset = 0;
+ int global = 0;
+ PyObject *dict;
+
+ if (!PyArg_ParseTuple( args, "iOii|iii", &wi, &dict, &slot, &Type, &Start, &Offset, &global )) {
+ return AttributeError( GemRB_Window_SetupSpellIcons__doc );
+ }
+
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( slot );
+ } else {
+ actor = game->FindPC( slot );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ //-2 because of the left/right scroll icons
+ if (!SpellArray) {
+ SpellArray = (SpellExtHeader *) malloc((GUIBT_COUNT) * sizeof (SpellExtHeader) );
+ }
+ int more = actor->spellbook.GetSpellInfo(SpellArray, Type, Start, GUIBT_COUNT-(Start?1:0));
+ int i;
+ if (Start) {
+ more |= 2;
+ }
+ if (more) {
+ int ci = core->GetControl(wi, Offset);
+ PyObject *ret = SetActionIcon(wi, ci, dict, ACT_LEFT, 0);
+ if (!ret) {
+ return RuntimeError("Cannot set action button!\n");
+ }
+ Button * btn = (Button *) GetControl(wi, ci, IE_GUI_BUTTON);
+ if (Start) {
+ btn->SetState(IE_GUI_BUTTON_UNPRESSED);
+ } else {
+ btn->SetState(IE_GUI_BUTTON_DISABLED);
+ }
+ }
+
+ //FIXME: this is a hardcoded resource (pst has no such one)
+ AnimationFactory* bam = ( AnimationFactory* )
+ gamedata->GetFactoryResource( "guibtbut",
+ IE_BAM_CLASS_ID, IE_NORMAL );
+ if (!bam) {
+ return RuntimeError("guibtbut BAM not found");
+ }
+
+ // disable all spells if fx_disable_spellcasting was run with the same type
+ // but only if there are any spells of that type to disable
+ int disabled_spellcasting = actor->GetStat(IE_CASTING);
+
+ for (i=0;i<GUIBT_COUNT-(more?2:0);i++) {
+ SpellExtHeader *spell = SpellArray+i;
+
+ int ci = core->GetControl(wi, i+Offset+(more?1:0) );
+ Button* btn = (Button *) GetControl( wi, ci, IE_GUI_BUTTON );
+ strcpy(btn->VarName,"Spell");
+ btn->Value = i+Start;
+
+ // disable spells that should be cast from the inventory
+ // Identify is misclassified and has Target 3 (Dead char)
+
+ ieDword spelltype = ResolveSpellNumber(spell->spellname)/1000;
+ if (core->CheckSpecialSpell(spell->spellname, actor) || (disabled_spellcasting&(1<<spelltype)) ) {
+ btn->SetState(IE_GUI_BUTTON_DISABLED);
+ btn->EnableBorder(1, IE_GUI_BUTTON_DISABLED);
+ PyObject *Function = PyDict_GetItemString(dict, "UpdateActionsWindow");
+ btn->SetEvent(IE_GUI_BUTTON_ON_PRESS, new PythonCallback(Function)); //noop
+ } else {
+ btn->SetState(IE_GUI_BUTTON_UNPRESSED);
+ PyObject *Function = PyDict_GetItemString(dict, "SpellPressed");
+ btn->SetEvent(IE_GUI_BUTTON_ON_PRESS, new PythonCallback(Function));
+ }
+ Sprite2D *Picture = NULL;
+
+ if (spell->MemorisedIcon[0]) {
+ Picture = gamedata->GetBAMSprite(spell->MemorisedIcon, 0, 0);
+ }
+
+ if (!Picture) {
+ btn->SetState(IE_GUI_BUTTON_DISABLED);
+ btn->SetFlags(IE_GUI_BUTTON_NO_IMAGE, BM_SET);
+ btn->SetTooltip(NULL);
+ } else {
+ SetButtonCycle(bam, btn, 0, IE_GUI_BUTTON_UNPRESSED);
+ SetButtonCycle(bam, btn, 1, IE_GUI_BUTTON_PRESSED);
+ SetButtonCycle(bam, btn, 2, IE_GUI_BUTTON_SELECTED);
+ SetButtonCycle(bam, btn, 3, IE_GUI_BUTTON_DISABLED);
+ btn->SetPicture( Picture );
+ btn->SetFlags(IE_GUI_BUTTON_PICTURE|IE_GUI_BUTTON_ALIGN_BOTTOM|IE_GUI_BUTTON_ALIGN_RIGHT, BM_SET);
+ //cannot make this const, because it will be freed
+ char *tmp = core->GetString(spell->strref,0);
+ btn->SetTooltip(tmp);
+ core->FreeString(tmp);
+ char usagestr[10];
+
+ if (spell->count>0) {
+ sprintf(usagestr,"%d", spell->count);
+ btn->SetText( usagestr );
+ btn->SetState(IE_GUI_BUTTON_UNPRESSED);
+ } else {
+ btn->SetState(IE_GUI_BUTTON_DISABLED);
+ }
+ }
+ }
+
+ if (more) {
+ int ci = core->GetControl(wi, i+Offset+1);
+ PyObject *ret = SetActionIcon(wi, ci, dict, ACT_RIGHT, i+1);
+ if (!ret) {
+ return RuntimeError("Cannot set action button!\n");
+ }
+ Button* btn = (Button *) GetControl( wi, ci, IE_GUI_BUTTON );
+ if (more&1) {
+ btn->SetState(IE_GUI_BUTTON_UNPRESSED);
+ } else {
+ btn->SetState(IE_GUI_BUTTON_DISABLED);
+ btn->SetFlags(IE_GUI_BUTTON_NO_IMAGE, BM_SET);
+ btn->SetTooltip(NULL);
+ }
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_Window_SetupControls__doc,
+"SetupControls(WindowIndex, dict, slot[, Start, global])\n\n"
+"Automagically sets up the controls of the action window for a PC indexed by slot.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_Window_SetupControls(PyObject * /*self*/, PyObject* args)
+{
+ int wi, slot;
+ int Start = 0;
+ int global = 0;
+ PyObject *dict;
+
+ if (!PyArg_ParseTuple( args, "iOi|ii", &wi, &dict, &slot, &Start, &global )) {
+ return AttributeError( GemRB_Window_SetupControls__doc );
+ }
+
+ GET_GAME();
+
+ GET_GAMECONTROL();
+
+ Actor* actor = NULL;
+
+ if (slot) {
+ if (global) {
+ actor = game->GetActorByGlobalID( slot );
+ } else {
+ actor = game->FindPC( slot );
+ }
+ } else {
+ if (game->selected.size()==1) {
+ actor = game->selected[0];
+ }
+ }
+
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ ActionButtonRow myrow;
+ actor->GetActionButtonRow(myrow);
+ bool fistdrawn = true;
+ ieDword magicweapon = actor->inventory.GetMagicSlot();
+ if (!actor->inventory.HasItemInSlot("",magicweapon) ) {
+ magicweapon = 0xffff;
+ }
+ ieDword fistweapon = actor->inventory.GetFistSlot();
+ ieDword usedslot = actor->inventory.GetEquippedSlot();
+ ieDword disabledbutton = actor->GetStat(IE_DISABLEDBUTTON);
+ int tmp;
+ for (int i=0;i<GUIBT_COUNT;i++) {
+ int ci = core->GetControl(wi, i+Start);
+ if (ci<0) {
+ printf("Couldn't find button #%d on Window #%d\n", i+Start, wi);
+ return RuntimeError ("No such control!\n");
+ }
+ int action = myrow[i];
+ if (action==100) {
+ action = -1;
+ } else {
+ action&=31;
+ }
+ Button * btn = (Button *) GetControl(wi,ci,IE_GUI_BUTTON);
+ if (!btn) {
+ return NULL;
+ }
+ btn->SetFlags(IE_GUI_BUTTON_NO_IMAGE|IE_GUI_BUTTON_ALIGN_BOTTOM|IE_GUI_BUTTON_ALIGN_RIGHT, BM_SET);
+ SetItemText(wi, ci, 0, false);
+ PyObject *ret = SetActionIcon(wi,ci,dict, action,i+1);
+
+ int state = IE_GUI_BUTTON_UNPRESSED;
+ ieDword modalstate = actor->ModalState;
+ int type;
+ switch (action) {
+ case ACT_INNATE:
+ if (actor->spellbook.IsIWDSpellBook()) {
+ type = IE_IWD2_SPELL_INNATE;
+ } else {
+ type = IE_SPELL_TYPE_INNATE;
+ }
+ if (!actor->spellbook.GetMemorizedSpellsCount(type)) {
+ state = IE_GUI_BUTTON_DISABLED;
+ }
+ break;
+ case ACT_CAST:
+ //luckily the castable spells in IWD2 are all bits below INNATE, so we can do this trick
+ if (actor->spellbook.IsIWDSpellBook()) {
+ type = (1<<IE_IWD2_SPELL_INNATE)-1;
+ } else {
+ type = (1<<IE_SPELL_TYPE_INNATE)-1;
+ }
+ //returns true if there are ANY spells to cast
+ if (!actor->spellbook.GetSpellInfoSize(type)) {
+ state = IE_GUI_BUTTON_DISABLED;
+ }
+ break;
+ case ACT_SHAPE:
+ if (actor->spellbook.IsIWDSpellBook()) {
+ type = 1<<IE_IWD2_SPELL_SHAPE;
+ } else {
+ type = 0; //no separate shapes in old spellbook
+ }
+ //returns true if there is ANY shape
+ if (!actor->spellbook.GetSpellInfoSize(type)) {
+ state = IE_GUI_BUTTON_DISABLED;
+ }
+ break;
+ case ACT_USE:
+ //returns true if there is ANY equipment
+ if (!actor->inventory.GetEquipmentInfo(NULL, 0, 0)) {
+ state = IE_GUI_BUTTON_DISABLED;
+ }
+ break;
+ case ACT_BARDSONG:
+ if (actor->spellbook.IsIWDSpellBook()) {
+ type = 1<<IE_IWD2_SPELL_SONG;
+ if (!actor->spellbook.GetSpellInfoSize(type)) {
+ state = IE_GUI_BUTTON_DISABLED;
+ }
+ } else {
+ if (modalstate==MS_BATTLESONG) {
+ state = IE_GUI_BUTTON_SELECTED;
+ }
+ }
+ break;
+ case ACT_TURN:
+ if (actor->GetStat(IE_TURNUNDEADLEVEL)<1) {
+ state = IE_GUI_BUTTON_DISABLED;
+ } else {
+ if (modalstate==MS_TURNUNDEAD) {
+ state = IE_GUI_BUTTON_SELECTED;
+ }
+ }
+ break;
+ case ACT_STEALTH:
+ if (modalstate==MS_STEALTH) {
+ state = IE_GUI_BUTTON_SELECTED;
+ }
+ break;
+ case ACT_SEARCH:
+ if (modalstate==MS_DETECTTRAPS) {
+ state = IE_GUI_BUTTON_SELECTED;
+ }
+ break;
+ case ACT_WEAPON1:
+ case ACT_WEAPON2:
+ case ACT_WEAPON3:
+ case ACT_WEAPON4:
+ {
+ SetButtonBAM(wi, ci, "stonweap",0,0,-1);
+ ieDword slot;
+ if (magicweapon!=0xffff) {
+ slot = magicweapon;
+ } else {
+ slot = actor->GetQuickSlot(action-ACT_WEAPON1);
+ }
+ if (slot!=0xffff) {
+ CREItem *item = actor->inventory.GetSlotItem(slot);
+ //no slot translation required
+ int launcherslot = actor->inventory.FindSlotRangedWeapon(slot);
+ const char* Item2ResRef = 0;
+ if (launcherslot != actor->inventory.GetFistSlot()) {
+ // launcher/projectile in this slot
+ CREItem* item2;
+ item2 = actor->inventory.GetSlotItem(launcherslot);
+ Item2ResRef = item2->ItemResRef;
+ }
+
+ if (item) {
+ int mode = 4;
+ if (slot == fistweapon) {
+ if (fistdrawn) {
+ fistdrawn = false;
+ } else {
+ //empty weapon slot, already drawn
+ break;
+ }
+ }
+ SetItemIcon(wi, ci, item->ItemResRef,mode,(item->Flags&IE_INV_ITEM_IDENTIFIED)?2:1, i+1, Item2ResRef);
+ SetItemText(wi, ci, item->Usages[actor->PCStats->QuickWeaponHeaders[action-ACT_WEAPON1]], true);
+ if (usedslot == slot) {
+ btn->EnableBorder(0, true);
+ if (gc->GetTargetMode() == TARGET_MODE_ATTACK) {
+ state = IE_GUI_BUTTON_SELECTED;
+ } else {
+ state = IE_GUI_BUTTON_THIRD;
+ }
+ } else {
+ btn->EnableBorder(0, false);
+ }
+ }
+ }
+ }
+ break;
+ case ACT_QSPELL1:
+ case ACT_QSPELL2:
+ case ACT_QSPELL3:
+ //fixme iwd2 has 9
+ {
+ SetButtonBAM(wi, ci, "stonspel",0,0,-1);
+ ieResRef *poi = &actor->PCStats->QuickSpells[action-ACT_QSPELL1];
+ if ((*poi)[0]) {
+ SetSpellIcon(wi, ci, *poi, 1, 1, i+1);
+ }
+ }
+ break;
+ case ACT_QSLOT1:
+ tmp=0;
+ goto jump_label;
+ case ACT_QSLOT2:
+ tmp=1;
+ goto jump_label;
+ case ACT_QSLOT3:
+ tmp=2;
+ goto jump_label;
+ case ACT_QSLOT4:
+ tmp=3;
+ goto jump_label;
+ case ACT_QSLOT5:
+ tmp=4;
+jump_label:
+ {
+ SetButtonBAM(wi, ci, "stonitem",0,0,-1);
+ ieDword slot = actor->PCStats->QuickItemSlots[tmp];
+ if (slot!=0xffff) {
+ //no slot translation required
+ CREItem *item = actor->inventory.GetSlotItem(slot);
+ if (item) {
+ //MISC3H (horn of blasting) is not displayed when it is out of usages
+ int header = actor->PCStats->QuickItemHeaders[tmp];
+ int usages = item->Usages[header];
+ //I don't like this feature, if the goal is full IE compatibility
+ //uncomment the next line.
+ //if (usages)
+ {
+ //SetItemIcon parameter needs header+6 to display extended header icons
+ SetItemIcon(wi, ci, item->ItemResRef,header+6,(item->Flags&IE_INV_ITEM_IDENTIFIED)?2:1, i+1, NULL);
+ SetItemText(wi, ci, usages, false);
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if (!ret) {
+ return RuntimeError("Cannot set action button!\n");
+ }
+ if (action<0 || (disabledbutton & (1<<action) )) {
+ state = IE_GUI_BUTTON_DISABLED;
+ }
+ btn->SetState(state);
+ //you have to set this overlay up
+ btn->EnableBorder(1, state==IE_GUI_BUTTON_DISABLED);
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_ClearActions__doc,
+"ClearActions(slot[, global])\n\n"
+"Stops an action for a PC indexed by slot or if global is set, by global ID." );
+
+static PyObject* GemRB_ClearActions(PyObject * /*self*/, PyObject* args)
+{
+ int slot;
+ int global = 0;
+
+ if (!PyArg_ParseTuple( args, "i|i", &slot, &global )) {
+ return AttributeError( GemRB_ClearActions__doc );
+ }
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( slot );
+ } else {
+ actor = game->FindPC( slot );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ if (actor->GetInternalFlag()&IF_NOINT) {
+ printMessage( "GuiScript","Cannot break action!\n", GREEN);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ if (!(actor->GetNextStep()) && !actor->ModalState && !actor->LastTarget) {
+ printMessage( "GuiScript","No breakable action!\n", GREEN);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ game->OutAttack(actor->GetGlobalID()); //stop attacking
+ actor->ClearPath(); //stop walking
+ actor->ClearActions(); //stop pending action involved walking
+ actor->SetModal(MS_NONE);//stop modal actions
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+
+PyDoc_STRVAR( GemRB_SetDefaultActions__doc,
+"SetDefaultActions(qslot, slot1, slot2, slot3)\n\n"
+"Sets whether qslots need an additional translation like in iwd2. "
+"Also sets up the first three default action types." );
+
+static PyObject* GemRB_SetDefaultActions(PyObject * /*self*/, PyObject* args)
+{
+ int qslot;
+ int slot1, slot2, slot3;
+
+ if (!PyArg_ParseTuple( args, "iiii", &qslot, &slot1, &slot2, &slot3 )) {
+ return AttributeError( GemRB_SetDefaultActions__doc );
+ }
+ Actor::SetDefaultActions((bool) qslot, (ieByte) slot1, (ieByte) slot2, (ieByte) slot3);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetupQuickSlot__doc,
+"SetupQuickSlot(PartyID, quickslot, inventoryslot[, headerindex, global])\n\n"
+"Set up a quick slot or weapon slot of a PC to use a weapon ability.\n\n"
+"If the inventoryslot number is -1, only the header index will be changed. "
+"If the quick slot is 0, then the inventory slot will be used to find which "
+"headerindex should be set. The default value for headerindex is 0.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_SetupQuickSlot(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, which, slot, headerindex = 0;
+ int global = 0;
+
+ if (!PyArg_ParseTuple( args, "iii|ii", &PartyID, &which, &slot, &headerindex, &global )) {
+ return AttributeError( GemRB_SetupQuickSlot__doc );
+ }
+
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( PartyID );
+ } else {
+ actor = game->FindPC( PartyID );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ slot = core->QuerySlot(slot);
+ actor->SetupQuickSlot(which, slot, headerindex);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetEquippedQuickSlot__doc,
+"SetEquippedQuickSlot(PartyID, QWeaponSlot[, ability, global])->int\n\n"
+"Sets the named weapon/item slot as equipped weapon slot, optionally sets the used ability."
+"Returns strref number of failure (0 success, -1 silent failure).\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_SetEquippedQuickSlot(PyObject * /*self*/, PyObject* args)
+{
+ int slot;
+ int PartyID;
+ int ability = -1;
+ int global = 0;
+
+ if (!PyArg_ParseTuple( args, "ii|ii", &PartyID, &slot, &ability, &global)) {
+ return AttributeError( GemRB_SetEquippedQuickSlot__doc );
+ }
+
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( PartyID );
+ } else {
+ actor = game->FindPC( PartyID );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ int ret = actor->SetEquippedQuickSlot(slot, ability);
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_GetEquippedQuickSlot__doc,
+"GetEquippedQuickSlot(PartyID[, NoTrans, global]) => Slot\n\n"
+"Returns the inventory slot (translation) or quick weapon index (no translation) of the equipped weapon.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_GetEquippedQuickSlot(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ int NoTrans = 0;
+ int global = 0;
+
+ if (!PyArg_ParseTuple( args, "i|ii", &PartyID, &NoTrans, &global)) {
+ return AttributeError( GemRB_GetEquippedQuickSlot__doc );
+ }
+
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( PartyID );
+ } else {
+ actor = game->FindPC( PartyID );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ int ret = actor->inventory.GetEquippedSlot();
+ /*int effect = core->QuerySlotEffects(ret);
+ if (effect == SLOT_EFFECT_MISSILE) {
+ ret = actor->inventory.FindRangedWeapon();
+ }*/
+ if (actor->PCStats) {
+ for(int i=0;i<4;i++) {
+ if (ret == actor->PCStats->QuickWeaponSlots[i]) {
+ if (NoTrans) {
+ return PyInt_FromLong(i);
+ }
+ ret = i+actor->inventory.GetWeaponSlot();
+ break;
+ }
+ }
+ } /*else {
+ ret-=actor->inventory.GetWeaponSlot();
+ }*/
+ return PyInt_FromLong( core->FindSlot(ret) );
+}
+
+PyDoc_STRVAR( GemRB_GetEquippedAmmunition__doc,
+"GetEquippedAmmunition(PartyID) => QSlot\n\n"
+"Returns the equipped ammunition slot, if any; -1 if none." );
+
+static PyObject* GemRB_GetEquippedAmmunition(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+
+ if (!PyArg_ParseTuple( args, "i", &PartyID)) {
+ return AttributeError( GemRB_GetEquippedQuickSlot__doc );
+ }
+
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ int ret = actor->inventory.GetEquippedSlot();
+ int effect = core->QuerySlotEffects(ret);
+ if (effect == SLOT_EFFECT_MISSILE) {
+ return PyInt_FromLong( core->FindSlot(ret) );
+ } else {
+ return PyInt_FromLong( -1 );
+ }
+}
+
+PyDoc_STRVAR( GemRB_SetModalState__doc,
+"SetModalState(slot, state[, global, spell])\n\n"
+"Sets the modal state of the actor.\n"
+"If 'spell' is not given, it will set a default spell resource associated with the state.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_SetModalState(PyObject * /*self*/, PyObject* args)
+{
+ int slot;
+ int state;
+ int global = 0;
+ const char *spell=NULL;
+
+ if (!PyArg_ParseTuple( args, "ii|is", &slot, &state, &global, &spell )) {
+ return AttributeError( GemRB_SetModalState__doc );
+ }
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( slot );
+ } else {
+ actor = game->FindPC( slot );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ actor->SetModal( (ieDword) state, 0);
+ actor->SetModalSpell(state, spell);
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SpellCast__doc,
+"SpellCast(slot, type, spell[, global])\n\n"
+"Makes the actor try to cast a spell. Type is the spell type like 3 for normal spells and 4 for innates.\n"
+"If type is -1, then the castable spell list will be deleted and no spell will be cast.\n"
+"Spell is the index of the spell in the memorised spell list.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.\n");
+
+static PyObject* GemRB_SpellCast(PyObject * /*self*/, PyObject* args)
+{
+ unsigned int slot;
+ int type;
+ unsigned int spell;
+ int global = 0;
+
+ if (!PyArg_ParseTuple( args, "iii|i", &slot, &type, &spell, &global )) {
+ return AttributeError( GemRB_SpellCast__doc );
+ }
+
+ GET_GAME();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( slot );
+ } else {
+ actor = game->FindPC( slot );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ //don't cast anything, just reinit the spell list
+ if (type==-1) {
+ actor->spellbook.ClearSpellInfo();
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ SpellExtHeader spelldata; // = SpellArray[spell];
+ actor->spellbook.GetSpellInfo(&spelldata, type, spell, 1);
+
+ printf("Cast spell: %s\n", spelldata.spellname);
+ printf("Slot: %d\n", spelldata.slot);
+ printf("Type: %d\n", spelldata.type);
+ //cannot make this const, because it will be freed
+ char *tmp = core->GetString(spelldata.strref);
+ printf("Spellname: %s\n", tmp);
+ core->FreeString(tmp);
+ printf("Target: %d\n", spelldata.Target);
+ printf("Range: %d\n", spelldata.Range);
+ if(! (1<<spelldata.type) & type) {
+ return RuntimeError( "Wrong type of spell!");
+ }
+
+ GET_GAMECONTROL();
+
+ switch (spelldata.Target) {
+ case TARGET_SELF:
+ // FIXME: GA_NO_DEAD and such are not actually used by SetupCasting
+ gc->SetupCasting(spelldata.spellname, spelldata.type, spelldata.level, spelldata.slot, actor, GA_NO_DEAD, spelldata.TargetNumber);
+ gc->TryToCast(actor, actor);
+ break;
+ case TARGET_NONE:
+ //reset the cursor
+ gc->ResetTargetMode();
+ //this is always instant casting without spending the spell
+ core->ApplySpell(spelldata.spellname, actor, actor, 0);
+ break;
+ case TARGET_AREA:
+ gc->SetupCasting(spelldata.spellname, spelldata.type, spelldata.level, spelldata.slot, actor, GA_POINT, spelldata.TargetNumber);
+ break;
+ case TARGET_CREA:
+ gc->SetupCasting(spelldata.spellname, spelldata.type, spelldata.level, spelldata.slot, actor, GA_NO_DEAD, spelldata.TargetNumber);
+ break;
+ case TARGET_DEAD:
+ gc->SetupCasting(spelldata.spellname, spelldata.type, spelldata.level, spelldata.slot, actor, 0, spelldata.TargetNumber);
+ break;
+ case TARGET_INV:
+ //bring up inventory in the end???
+ //break;
+ default:
+ printf("Unhandled target type: %d\n", spelldata.Target);
+ break;
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_ApplySpell__doc,
+"ApplySpell(actor, spellname)\n\n"
+"Applies a spell on actor.");
+
+static PyObject* GemRB_ApplySpell(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, casterID = 0;
+ const char *spell;
+
+ if (!PyArg_ParseTuple( args, "is|i", &PartyID, &spell, &casterID )) {
+ return AttributeError( GemRB_ApplySpell__doc );
+ }
+
+ GET_GAME();
+
+ Map *map = game->GetCurrentArea();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ Actor *caster = NULL;
+ if (map) caster = map->GetActorByGlobalID(casterID);
+ if (!caster) caster = game->GetActorByGlobalID(casterID);
+ if (!caster) caster = actor;
+
+ core->ApplySpell(spell, actor, caster, 0);
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_UseItem__doc,
+"UseItem(actor, slot, header[,forcetarget,global])\n\n"
+"Makes the actor try to use an item. "
+"If slot is -1, then header is the index of the item functionality in the use item list. "
+"If slot is -2, then header is the quickslot index. "
+"If slot is non-negative, then header is the header of the item in the 'slot'.\n"
+"If global is set, the actor will be looked up by its global ID instead of party slot.");
+
+static PyObject* GemRB_UseItem(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ int slot;
+ int header;
+ int forcetarget=-1; //some crappy scrolls don't target self correctly!
+ int global = 0;
+
+ if (!PyArg_ParseTuple( args, "iii|ii", &PartyID, &slot, &header, &forcetarget, &global )) {
+ return AttributeError( GemRB_UseItem__doc );
+ }
+
+ GET_GAME();
+
+ GET_GAMECONTROL();
+
+ Actor* actor;
+ if (global) {
+ actor = game->GetActorByGlobalID( PartyID );
+ } else {
+ actor = game->FindPC( PartyID );
+ }
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ ItemExtHeader itemdata;
+ int flags = 0;
+
+ switch (slot) {
+ case -1:
+ //some equipment
+ actor->inventory.GetEquipmentInfo(&itemdata, header, 1);
+ break;
+ case -2:
+ //quickslot
+ actor->GetItemSlotInfo(&itemdata, header, -1);
+ if (!itemdata.Charges) {
+ printMessage("GUIScript","QuickItem has no charges.\n", WHITE);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ break;
+ default:
+ //any normal slot
+ actor->GetItemSlotInfo(&itemdata, core->QuerySlot(slot), header);
+ flags = UI_SILENT;
+ break;
+ }
+
+ if(forcetarget==-1) {
+ forcetarget = itemdata.Target;
+ }
+
+ //is there any better check for a non existent item?
+ if (!itemdata.itemname[0]) {
+ printMessage("GUIScript","Empty slot used?\n", YELLOW);
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ /// remove this after projectile is done
+ printf("Use item: %s\n", itemdata.itemname);
+ printf("Extended header: %d\n", itemdata.headerindex);
+ printf("Attacktype: %d\n",itemdata.AttackType);
+ printf("Range: %d\n",itemdata.Range);
+ printf("Target: %d\n",forcetarget);
+ printf("Projectile: %d\n", itemdata.ProjectileAnimation);
+ //
+ switch (forcetarget) {
+ case TARGET_SELF:
+ gc->SetupItemUse(itemdata.slot, itemdata.headerindex, actor, GA_NO_DEAD, itemdata.TargetNumber);
+ gc->TryToCast(actor, actor);
+ break;
+ case TARGET_NONE:
+ gc->ResetTargetMode();
+ actor->UseItem(itemdata.slot, itemdata.headerindex, NULL, flags);
+ break;
+ case TARGET_AREA:
+ gc->SetupItemUse(itemdata.slot, itemdata.headerindex, actor, GA_POINT, itemdata.TargetNumber);
+ break;
+ case TARGET_CREA:
+ gc->SetupItemUse(itemdata.slot, itemdata.headerindex, actor, GA_NO_DEAD, itemdata.TargetNumber);
+ break;
+ case TARGET_DEAD:
+ gc->SetupItemUse(itemdata.slot, itemdata.headerindex, actor, 0, itemdata.TargetNumber);
+ break;
+ default:
+ printMessage("GUIScript", "Unhandled target type!", LIGHT_RED );
+ break;
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetGamma__doc,
+"SetGamma(brightness, contrast)\n\n"
+"Adjusts brightness and contrast.");
+
+static PyObject* GemRB_SetGamma(PyObject * /*self*/, PyObject* args)
+{
+ int brightness, contrast;
+
+ if (!PyArg_ParseTuple( args, "ii", &brightness, &contrast )) {
+ return AttributeError( GemRB_SetGamma__doc );
+ }
+ if (brightness<0 || brightness>40) {
+ return RuntimeError( "Brightness must be 0-40" );
+ }
+ if (contrast<0 || contrast>5) {
+ return RuntimeError( "Contrast must be 0-5" );
+ }
+ core->GetVideoDriver()->SetGamma(brightness, contrast);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetMouseScrollSpeed__doc,
+"SetMouseScrollSpeed(mouseSpeed)\n\n"
+"Adjusts mouse scroll speed.");
+
+static PyObject* GemRB_SetMouseScrollSpeed(PyObject * /*self*/, PyObject* args)
+{
+ int mouseSpeed;
+
+ if (!PyArg_ParseTuple( args, "i", &mouseSpeed)) {
+ return AttributeError( GemRB_SetMouseScrollSpeed__doc );
+ }
+
+ core->SetMouseScrollSpeed(mouseSpeed);
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetTooltipDelay__doc,
+"SetTooltipDelay(tooltipDelay)\n\n"
+"Adjusts tooltip appearing speed.");
+
+static PyObject* GemRB_SetTooltipDelay(PyObject * /*self*/, PyObject* args)
+{
+ int tooltipDelay;
+
+ if (!PyArg_ParseTuple( args, "i", &tooltipDelay)) {
+ return AttributeError( GemRB_SetTooltipDelay__doc );
+ }
+
+ core->TooltipDelay = tooltipDelay;
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetFullScreen__doc,
+"SetFullScreen(int)\n\n"
+"0 - windowed, 1 - fullscreen, -1 - toggle");
+
+static PyObject* GemRB_SetFullScreen(PyObject * /*self*/, PyObject* args)
+{
+ int fullscreen;
+
+ if (!PyArg_ParseTuple( args, "i", &fullscreen )) {
+ return AttributeError( GemRB_SetFullScreen__doc );
+ }
+ core->GetVideoDriver()->ToggleFullscreenMode(fullscreen);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_RestParty__doc,
+"RestParty(noareacheck, dream, hp)\n\n"
+"Executes the party rest function, used from both stores and via the main screen.");
+
+static PyObject* GemRB_RestParty(PyObject * /*self*/, PyObject* args)
+{
+ int noareacheck;
+ int dream, hp;
+
+ if (!PyArg_ParseTuple( args, "iii", &noareacheck, &dream, &hp)) {
+ return AttributeError( GemRB_RestParty__doc );
+ }
+ GET_GAME();
+
+ game->RestParty(noareacheck, dream, hp);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_HasSpecialItem__doc,
+"HasSpecialItem(pc, itemtype, useup) => bool\n\n"
+"Checks if a team member has an item, optionally uses it.");
+
+//itemtype 1 - identify
+static PyObject* GemRB_HasSpecialItem(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, itemtype, useup;
+
+ if (!PyArg_ParseTuple( args, "iii", &PartyID, &itemtype, &useup)) {
+ return AttributeError( GemRB_HasSpecialItem__doc );
+ }
+ if (SpecialItemsCount==-1) {
+ ReadSpecialItems();
+ }
+
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ int i = SpecialItemsCount;
+ int slot = -1;
+ while(i--) {
+ if (itemtype&SpecialItems[i].value) {
+ slot = actor->inventory.FindItem(SpecialItems[i].resref,0);
+ if (slot>=0) {
+ break;
+ }
+ }
+ }
+
+ if (slot<0) {
+ return PyInt_FromLong( 0 );
+ }
+
+ if (useup) {
+ //use the found item's first usage
+ useup = actor->UseItem((ieDword) slot, 0, actor, UI_SILENT);
+ } else {
+ CREItem *si = actor->inventory.GetSlotItem( slot );
+ if (si->Usages[0]) useup = 1;
+ }
+ return PyInt_FromLong( useup );
+}
+
+PyDoc_STRVAR( GemRB_HasSpecialSpell__doc,
+"HasSpecialSpell(pc, itemtype, useup) => bool\n\n"
+"Checks if a team member has a spell, optionally uses it.");
+
+//itemtype 1 - identify
+static PyObject* GemRB_HasSpecialSpell(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID, itemtype, useup;
+
+ if (!PyArg_ParseTuple( args, "iii", &PartyID, &itemtype, &useup)) {
+ return AttributeError( GemRB_HasSpecialSpell__doc );
+ }
+
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ int i = core->GetSpecialSpellsCount();
+ if (i == -1) {
+ return RuntimeError( "Game has no splspec.2da table!" );
+ }
+ SpellDescType *special_spells = core->GetSpecialSpells();
+ while(i--) {
+ if (itemtype&special_spells[i].value) {
+ if (actor->spellbook.HaveSpell(special_spells[i].resref,useup)) {
+ if (useup) {
+ //actor->SpellCast(SpecialSpells[i].resref, actor);
+ }
+ break;
+ }
+ }
+ }
+
+ if (i<0) {
+ return PyInt_FromLong( 0 );
+ }
+ return PyInt_FromLong( 1 );
+}
+
+PyDoc_STRVAR( GemRB_ApplyEffect__doc,
+"ApplyEffect(pc, effect, param1, param2[, resref, resref2, resref3, source])\n\n"
+"Creates a basic effect and applies it on the player character. "
+"This function could be used to add stats that are stored in effect blocks. "
+"The resource fields are optional.");
+
+static PyObject* GemRB_ApplyEffect(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ const char *opcodename;
+ int param1, param2;
+ const char *resref1 = NULL;
+ const char *resref2 = NULL;
+ const char *resref3 = NULL;
+ const char *source = NULL;
+
+ if (!PyArg_ParseTuple( args, "isii|ssss", &PartyID, &opcodename, ¶m1, ¶m2, &resref1, &resref2, &resref3, &source)) {
+ return AttributeError( GemRB_ApplyEffect__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ work_ref.Name=opcodename;
+ work_ref.opcode=-1;
+ Effect *fx = EffectQueue::CreateEffect(work_ref, param1, param2, FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES);
+ if (!fx) {
+ //invalid effect name didn't resolve to opcode
+ return RuntimeError( "Invalid effect name!\n" );
+ }
+ if (resref1) {
+ strnlwrcpy(fx->Resource, resref1, 8);
+ }
+ if (resref2) {
+ strnlwrcpy(fx->Resource2, resref2, 8);
+ }
+ if (resref3) {
+ strnlwrcpy(fx->Resource3, resref3, 8);
+ }
+ if (source) {
+ strnlwrcpy(fx->Source, source, 8);
+ }
+ //This is a hack...
+ fx->Parameter3=1;
+
+ //fx is not freed by this function
+ core->ApplyEffect(fx, actor, actor);
+
+ //lets kill it
+ delete fx;
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_CountEffects__doc,
+"CountEffects(pc, effect, param1, param2[,resref])\n\n"
+"Counts how many matching effects are applied on the player character. "
+"This function could be used to get HLA information in ToB. "
+"The resource field is optional.");
+
+static PyObject* GemRB_CountEffects(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ const char *opcodename;
+ int param1, param2;
+ const char *resref = NULL;
+
+ if (!PyArg_ParseTuple( args, "isii|s", &PartyID, &opcodename, ¶m1, ¶m2, &resref)) {
+ return AttributeError( GemRB_CountEffects__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ work_ref.Name=opcodename;
+ work_ref.opcode=-1;
+ ieDword ret = actor->fxqueue.CountEffects(work_ref, param1, param2, resref);
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_ModifyEffect__doc,
+"ModifyEffect(pc, effect, p1, p2)\n\n"
+"Changes/sets the target coordinates of the specified effect. "
+"This command is used for the farsight spell.");
+
+static PyObject* GemRB_ModifyEffect(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ const char *opcodename;
+ int px, py;
+
+ if (!PyArg_ParseTuple( args, "isii", &PartyID, &opcodename, &px, &py)) {
+ return AttributeError( GemRB_ModifyEffect__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ work_ref.Name=opcodename;
+ work_ref.opcode=-1;
+ actor->fxqueue.ModifyEffectPoint(work_ref, px, py);
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_StealFailed__doc,
+"StealFailed()\n\n"
+"Sends the steal failed trigger (attacked) to the owner of the current store. "
+"The owner of the current store was set to the Sender of StartStore action.");
+
+static PyObject* GemRB_StealFailed(PyObject * /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ Store *store = core->GetCurrentStore();
+ if (!store) {
+ return RuntimeError( "No store loaded!" );
+ }
+ GET_MAP();
+
+ Actor* owner = map->GetActorByGlobalID( store->GetOwnerID() );
+ if (!owner) owner = game->GetActorByGlobalID( store->GetOwnerID() );
+ if (!owner) {
+ printMessage("GUIScript", "No owner found!", YELLOW );
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ Actor* attacker = game->FindPC((int) game->GetSelectedPCSingle() );
+ if (!attacker) {
+ printMessage("GUIScript", "No thief found!", YELLOW );
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+
+ // apply the reputation penalty
+ int repmod = core->GetReputationMod(2);
+ if (repmod) {
+ game->SetReputation(game->Reputation + repmod);
+ }
+
+ //not sure if this is ok
+ //owner->LastAttacker = attacker->GetID();
+ owner->LastDisarmFailed = attacker->GetGlobalID();
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SwapPCs__doc,
+"SwapPCs(idx1, idx2)\n\n"
+"Swaps the party order for two player characters.");
+
+static PyObject* GemRB_SwapPCs(PyObject * /*self*/, PyObject* args)
+{
+ int idx1, idx2;
+
+ if (!PyArg_ParseTuple( args, "ii", &idx1, &idx2)) {
+ return AttributeError( GemRB_SwapPCs__doc );
+ }
+
+ GET_GAME();
+
+ game->SwapPCs(game->FindPlayer(idx1), game->FindPlayer(idx2));
+ //leader changed
+ if (idx1==1 || idx2==1) {
+ DisplayStringCore( game->FindPC(1), VB_LEADER, DS_CONST);
+ }
+
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetRepeatClickFlags__doc,
+"SetRepeatClickFlags(value, op)\n\n"
+"Sets the mode repeat clicks are handled.");
+
+static PyObject* GemRB_SetRepeatClickFlags(PyObject * /*self*/, PyObject* args)
+{
+ int value, op;
+ unsigned long ret;
+
+ if (!PyArg_ParseTuple( args, "ii", &value, &op)) {
+ return AttributeError( GemRB_SetRepeatClickFlags__doc );
+ }
+ ret = core->GetEventMgr()->SetRKFlags( (unsigned long) value, (unsigned long) op);
+ return PyInt_FromLong( ret );
+}
+
+PyDoc_STRVAR( GemRB_DisplayString__doc,
+"DisplayString(strref, color[,actor])\n\n"
+"Displays string on the MessageWindow using methods supplied by the engine core. "
+"The optional actor is the party ID of the character whose name will be displayed.");
+
+static PyObject* GemRB_DisplayString(PyObject * /*self*/, PyObject* args)
+{
+ int strref, color;
+ int PartyID = 0;
+
+ if (!PyArg_ParseTuple( args, "ii|i", &strref, &color, &PartyID)) {
+ return AttributeError( GemRB_DisplayString__doc );
+ }
+ if (PartyID) {
+ GET_GAME();
+
+ Actor *actor = game->FindPC(PartyID);
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+ displaymsg->DisplayStringName(strref, (unsigned int) color, actor, IE_STR_SOUND);
+ } else {
+ displaymsg->DisplayString(strref, (unsigned int) color, IE_STR_SOUND);
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetCombatDetails__doc,
+"GetCombatDetails(pc, leftorright) => dict\n\n"
+"Returns the current THAC0 and other data in relation to the equipped weapon.");
+
+static PyObject* GemRB_GetCombatDetails(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+ int leftorright;
+
+ if (!PyArg_ParseTuple( args, "ii", &PartyID, &leftorright)) {
+ return AttributeError( GemRB_GetCombatDetails__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ leftorright = leftorright&1;
+ WeaponInfo wi;
+ ITMExtHeader *header = NULL;
+ ITMExtHeader *hittingheader = NULL;
+ int tohit=20;
+ ieDword Flags=0;
+ int DamageBonus=0, CriticalBonus=0;
+ int speed, style=0;
+
+ PyObject* dict = PyDict_New();
+ if (!actor->GetCombatDetails(tohit, leftorright, wi, header, hittingheader, Flags, DamageBonus, speed, CriticalBonus, style, NULL)) {
+ //TODO: handle error, so tohit will still be set correctly?
+ }
+ PyDict_SetItemString(dict, "ToHit", PyInt_FromLong (tohit));
+ PyDict_SetItemString(dict, "Flags", PyInt_FromLong (Flags));
+ PyDict_SetItemString(dict, "DamageBonus", PyInt_FromLong (DamageBonus));
+ PyDict_SetItemString(dict, "Speed", PyInt_FromLong (speed));
+ PyDict_SetItemString(dict, "CriticalBonus", PyInt_FromLong (CriticalBonus));
+ PyDict_SetItemString(dict, "Style", PyInt_FromLong (style));
+ return dict;
+}
+
+PyDoc_STRVAR( GemRB_IsDualWielding__doc,
+"IsDualWielding(pc)\n\n"
+"1 if the pc is dual wielding; 0 otherwise.");
+
+static PyObject* GemRB_IsDualWielding(PyObject * /*self*/, PyObject* args)
+{
+ int PartyID;
+
+ if (!PyArg_ParseTuple( args, "i", &PartyID)) {
+ return AttributeError( GemRB_IsDualWielding__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ int dualwield = actor->IsDualWielding();
+ return PyInt_FromLong( dualwield );
+}
+
+PyDoc_STRVAR( GemRB_GetSelectedSize__doc,
+"GetSelectedSize() => int\n\n"
+"Returns the number of actors selected in the party.");
+
+static PyObject* GemRB_GetSelectedSize(PyObject* /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ return PyInt_FromLong(game->selected.size());
+}
+
+PyDoc_STRVAR( GemRB_GetSelectedActors__doc,
+"GetSelectedActors() => int\n\n"
+"Returns the global ids of selected actors in a tuple.");
+
+static PyObject* GemRB_GetSelectedActors(PyObject* /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ int count = game->selected.size();
+ PyObject* actor_list = PyTuple_New(count);
+ for (int i = 0; i < count; i++) {
+ PyTuple_SetItem(actor_list, i, PyInt_FromLong( game->selected[i]->GetGlobalID() ) );
+ }
+ return actor_list;
+}
+
+PyDoc_STRVAR( GemRB_GetSpellCastOn__doc,
+"GetSpellCastOn(pc) => resref\n\n"
+"Returns the last spell cast on a partymember.");
+
+static PyObject* GemRB_GetSpellCastOn(PyObject* /*self*/, PyObject* args)
+{
+ int PartyID;
+ ieResRef splname;
+
+ if (!PyArg_ParseTuple( args, "i", &PartyID )) {
+ return AttributeError( GemRB_GetSpellCastOn__doc );
+ }
+ GET_GAME();
+
+ Actor* actor = game->FindPC( PartyID );
+ if (!actor) {
+ return RuntimeError( "Actor not found!\n" );
+ }
+
+ ResolveSpellName(splname, actor->LastSpellOnMe);
+ return PyString_FromString(splname);
+}
+
+PyDoc_STRVAR( GemRB_SetTickHook__doc,
+"Set callback to be called every main loop iteration.\n\n"
+"This is useful for things like running a twisted reactor.");
+
+static PyObject* GemRB_SetTickHook(PyObject* /*self*/, PyObject* args)
+{
+ PyObject* function;
+
+ if (!PyArg_ParseTuple(args, "O", &function)) {
+ return AttributeError( GemRB_SetTickHook__doc );
+ }
+
+ EventHandler handler;
+ if (function == Py_None) {
+ handler = new Callback();
+ } else if (PyCallable_Check(function)) {
+ handler = new PythonCallback(function);
+ } else {
+ char buf[256];
+ // TODO: Print function name. (func.__name__)
+ snprintf(buf, sizeof(buf), "Can't set timed event handler!");
+ return RuntimeError(buf);
+ }
+
+ core->SetTickHook(handler);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetupMaze__doc,
+"SetupMaze(x,y)\n\n"
+"Initializes a maze of XxY size. "
+"The dimensions shouldn't exceed the maximum possible maze size (8x8).");
+
+static PyObject* GemRB_SetupMaze(PyObject* /*self*/, PyObject* args)
+{
+ int xsize, ysize;
+
+ if (!PyArg_ParseTuple( args, "ii", &xsize, &ysize )) {
+ return AttributeError( GemRB_SetupMaze__doc );
+ }
+
+ if ((unsigned) xsize>MAZE_MAX_DIM || (unsigned) ysize>MAZE_MAX_DIM) {
+ return AttributeError( GemRB_SetupMaze__doc );
+ }
+
+ GET_GAME();
+
+ maze_header *h = (maze_header *) (game->AllocateMazeData()+MAZE_ENTRY_COUNT*MAZE_ENTRY_SIZE);
+ memset(h, 0, MAZE_HEADER_SIZE);
+ h->maze_sizex = xsize;
+ h->maze_sizey = ysize;
+ for(int i=0;i<MAZE_ENTRY_COUNT;i++) {
+ maze_entry *m = (maze_entry *) (game->mazedata+i*MAZE_ENTRY_SIZE);
+ memset(m, 0, MAZE_ENTRY_SIZE);
+ bool used = (i/MAZE_MAX_DIM<ysize) && (i%MAZE_MAX_DIM<xsize);
+ m->valid = used;
+ m->accessible = used;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetMazeEntry__doc,
+"SetMazeEntry(entry, type, value)\n\n"
+"Sets a field in a maze entry. "
+"The entry index shouldn't exceed the maximum possible maze size (64). "
+"The type could be: ME_ACCESSED, ME_WALLS, ME_TRAP or ME_SPECIAL.");
+
+static PyObject* GemRB_SetMazeEntry(PyObject* /*self*/, PyObject* args)
+{
+ int entry;
+ int index;
+ int value;
+
+ if (!PyArg_ParseTuple( args, "iii", &entry, &index, &value )) {
+ return AttributeError( GemRB_SetMazeEntry__doc );
+ }
+
+ if (entry<0 || entry>63) {
+ return AttributeError( GemRB_SetMazeEntry__doc );
+ }
+
+ GET_GAME();
+
+ if (!game->mazedata) {
+ return RuntimeError( "No maze set up!" );
+ }
+
+ maze_entry *m = (maze_entry *) (game->mazedata+entry*MAZE_ENTRY_SIZE);
+ maze_entry *m2;
+ switch(index) {
+ case ME_OVERRIDE:
+ m->override = value;
+ break;
+ default:
+ case ME_VALID:
+ case ME_ACCESSIBLE:
+ return AttributeError( GemRB_SetMazeEntry__doc );
+ break;
+ case ME_TRAP: //trapped/traptype
+ if (value==-1) {
+ m->trapped = 0;
+ m->traptype = 0;
+ } else {
+ m->trapped = 1;
+ m->traptype = value;
+ }
+ break;
+ case ME_WALLS:
+ m->walls |= value;
+ if (value & WALL_SOUTH) {
+ if (entry%MAZE_MAX_DIM!=MAZE_MAX_DIM-1) {
+ m2 = (maze_entry *) (game->mazedata+(entry+1)*MAZE_ENTRY_SIZE);
+ m2->walls|=WALL_NORTH;
+ }
+ }
+
+ if (value & WALL_NORTH) {
+ if (entry%MAZE_MAX_DIM) {
+ m2 = (maze_entry *) (game->mazedata+(entry-1)*MAZE_ENTRY_SIZE);
+ m2->walls|=WALL_SOUTH;
+ }
+ }
+
+ if (value & WALL_EAST) {
+ if (entry+MAZE_MAX_DIM<MAZE_ENTRY_COUNT) {
+ m2 = (maze_entry *) (game->mazedata+(entry+MAZE_MAX_DIM)*MAZE_ENTRY_SIZE);
+ m2->walls|=WALL_WEST;
+ }
+ }
+
+ if (value & WALL_WEST) {
+ if (entry>=MAZE_MAX_DIM) {
+ m2 = (maze_entry *) (game->mazedata+(entry-MAZE_MAX_DIM)*MAZE_ENTRY_SIZE);
+ m2->walls|=WALL_EAST;
+ }
+ }
+
+ break;
+ case ME_VISITED:
+ m->visited = value;
+ break;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_SetMazeData__doc,
+"SetMazeData(type, value)\n\n"
+"Sets a field in the maze header. "
+"The type could be: ME_0, ME_WALLS, ME_TRAP or ME_16.");
+
+static PyObject* GemRB_SetMazeData(PyObject* /*self*/, PyObject* args)
+{
+ int entry;
+ int value;
+
+ if (!PyArg_ParseTuple( args, "ii", &entry, &value )) {
+ return AttributeError( GemRB_SetMazeData__doc );
+ }
+
+ GET_GAME();
+
+ if (!game->mazedata) {
+ return RuntimeError( "No maze set up!" );
+ }
+
+ maze_header *h = (maze_header *) (game->mazedata+MAZE_ENTRY_COUNT*MAZE_ENTRY_SIZE);
+ switch(entry) {
+ case MH_POS1X:
+ h->pos1x = value;
+ break;
+ case MH_POS1Y:
+ h->pos1y = value;
+ break;
+ case MH_POS2X:
+ h->pos2x = value;
+ break;
+ case MH_POS2Y:
+ h->pos2y = value;
+ break;
+ case MH_POS3X:
+ h->pos3x = value;
+ break;
+ case MH_POS3Y:
+ h->pos3y = value;
+ break;
+ case MH_POS4X:
+ h->pos4x = value;
+ break;
+ case MH_POS4Y:
+ h->pos4y = value;
+ break;
+ case MH_TRAPCOUNT:
+ h->trapcount = value;
+ break;
+ case MH_INITED:
+ h->initialized = value;
+ break;
+ case MH_UNKNOWN2C:
+ h->unknown2c = value;
+ break;
+ case MH_UNKNOWN30:
+ h->unknown30 = value;
+ break;
+ default:
+ return AttributeError( GemRB_SetMazeData__doc );
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR( GemRB_GetMazeHeader__doc,
+"GetMazeHeader()=>dict\n\n"
+"Returns the Maze header of Planescape Torment savegames." );
+
+static PyObject* GemRB_GetMazeHeader(PyObject* /*self*/, PyObject* /*args*/)
+{
+ GET_GAME();
+
+ if (!game->mazedata) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ PyObject* dict = PyDict_New();
+ maze_header *h = (maze_header *) (game->mazedata+MAZE_ENTRY_COUNT*MAZE_ENTRY_SIZE);
+ PyDict_SetItemString(dict, "MazeX", PyInt_FromLong (h->maze_sizex));
+ PyDict_SetItemString(dict, "MazeY", PyInt_FromLong (h->maze_sizey));
+ PyDict_SetItemString(dict, "Pos1X", PyInt_FromLong (h->pos1x));
+ PyDict_SetItemString(dict, "Pos1Y", PyInt_FromLong (h->pos1y));
+ PyDict_SetItemString(dict, "Pos2X", PyInt_FromLong (h->pos2x));
+ PyDict_SetItemString(dict, "Pos2Y", PyInt_FromLong (h->pos2y));
+ PyDict_SetItemString(dict, "Pos3X", PyInt_FromLong (h->pos3x));
+ PyDict_SetItemString(dict, "Pos3Y", PyInt_FromLong (h->pos3y));
+ PyDict_SetItemString(dict, "Pos4X", PyInt_FromLong (h->pos4x));
+ PyDict_SetItemString(dict, "Pos4Y", PyInt_FromLong (h->pos4y));
+ PyDict_SetItemString(dict, "TrapCount", PyInt_FromLong (h->trapcount));
+ PyDict_SetItemString(dict, "Inited", PyInt_FromLong (h->initialized));
+ return dict;
+}
+
+PyDoc_STRVAR( GemRB_GetMazeEntry__doc,
+"GetMazeEntry(entry)=>dict\n\n"
+"Returns a Maze entry from Planescape Torment savegames. Entry must be 0-63." );
+
+static PyObject* GemRB_GetMazeEntry(PyObject* /*self*/, PyObject* args)
+{
+ int entry;
+
+ if (!PyArg_ParseTuple( args, "i", &entry )) {
+ return AttributeError( GemRB_GetMazeEntry__doc );
+ }
+
+ if (entry<0 || entry>=MAZE_ENTRY_COUNT) {
+ return AttributeError( GemRB_GetMazeEntry__doc );
+ }
+
+ GET_GAME();
+
+ if (!game->mazedata) {
+ return RuntimeError( "No maze set up!" );
+ }
+
+ PyObject* dict = PyDict_New();
+ maze_entry *m = (maze_entry *) (game->mazedata+entry*MAZE_ENTRY_SIZE);
+ PyDict_SetItemString(dict, "Override", PyInt_FromLong (m->override));
+ PyDict_SetItemString(dict, "Accessible", PyInt_FromLong (m->accessible));
+ PyDict_SetItemString(dict, "Valid", PyInt_FromLong (m->valid));
+ if (m->trapped) {
+ PyDict_SetItemString(dict, "Trapped", PyInt_FromLong (m->traptype));
+ } else {
+ PyDict_SetItemString(dict, "Trapped", PyInt_FromLong (-1));
+ }
+ PyDict_SetItemString(dict, "Walls", PyInt_FromLong (m->walls));
+ PyDict_SetItemString(dict, "Visited", PyInt_FromLong (m->visited));
+ return dict;
+}
+
+char gametype_hint[100];
+int gametype_hint_weight;
+
+PyDoc_STRVAR( GemRB_AddGameTypeHint__doc,
+"AddGameTypeHint(type, weight, flags=0)\n\n"
+"Asserts that GameType should be TYPE, with confidence WEIGHT. "
+"Original games should use WEIGHT <= 100, greater values are reserved for new games. "
+"FLAGS are not used at the moment.");
+
+static PyObject* GemRB_AddGameTypeHint(PyObject* /*self*/, PyObject* args)
+{
+ char* type;
+ int weight;
+ int flags = 0;
+
+ if (!PyArg_ParseTuple( args, "si|i", &type, &weight, &flags )) {
+ return AttributeError( GemRB_AddGameTypeHint__doc );
+ }
+
+ if (weight > gametype_hint_weight) {
+ gametype_hint_weight = weight;
+ strncpy(gametype_hint, type, sizeof(gametype_hint)-1);
+ // I assume the '\0' in the end of gametype_hint
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyMethodDef GemRBMethods[] = {
+ METHOD(ActOnPC, METH_VARARGS),
+ METHOD(AddGameTypeHint, METH_VARARGS),
+ METHOD(AddNewArea, METH_VARARGS),
+ METHOD(ApplyEffect, METH_VARARGS),
+ METHOD(ApplySpell, METH_VARARGS),
+ METHOD(CanUseItemType, METH_VARARGS),
+ METHOD(ChangeContainerItem, METH_VARARGS),
+ METHOD(ChangeItemFlag, METH_VARARGS),
+ METHOD(ChangeStoreItem, METH_VARARGS),
+ METHOD(CheckFeatCondition, METH_VARARGS),
+ METHOD(CheckVar, METH_VARARGS),
+ METHOD(ClearActions, METH_VARARGS),
+ METHOD(CountEffects, METH_VARARGS),
+ METHOD(CreateCreature, METH_VARARGS),
+ METHOD(CreateItem, METH_VARARGS),
+ METHOD(CreateMovement, METH_VARARGS),
+ METHOD(CreatePlayer, METH_VARARGS),
+ METHOD(CreateString, METH_VARARGS),
+ METHOD(CreateWindow, METH_VARARGS),
+ METHOD(DeleteSaveGame, METH_VARARGS),
+ METHOD(DispelEffect, METH_VARARGS),
+ METHOD(DisplayString, METH_VARARGS),
+ METHOD(DragItem, METH_VARARGS),
+ METHOD(DrawWindows, METH_NOARGS),
+ METHOD(DropDraggedItem, METH_VARARGS),
+ METHOD(EnableCheatKeys, METH_VARARGS),
+ METHOD(EndCutSceneMode, METH_NOARGS),
+ METHOD(EnterGame, METH_NOARGS),
+ METHOD(EnterStore, METH_VARARGS),
+ METHOD(EvaluateString, METH_VARARGS),
+ METHOD(ExecuteString, METH_VARARGS),
+ METHOD(ExploreArea, METH_VARARGS),
+ METHOD(FillPlayerInfo, METH_VARARGS),
+ METHOD(FindStoreItem, METH_VARARGS),
+ METHOD(GameControlGetTargetMode, METH_NOARGS),
+ METHOD(GameControlSetLastActor, METH_VARARGS),
+ METHOD(GameControlSetScreenFlags, METH_VARARGS),
+ METHOD(GameControlSetTargetMode, METH_VARARGS),
+ METHOD(GameGetReputation, METH_NOARGS),
+ METHOD(GameSetReputation, METH_VARARGS),
+ METHOD(GameGetFirstSelectedActor, METH_NOARGS),
+ METHOD(GameGetFirstSelectedPC, METH_NOARGS),
+ METHOD(GameGetFormation, METH_VARARGS),
+ METHOD(GameGetPartyGold, METH_NOARGS),
+ METHOD(GameGetSelectedPCSingle, METH_VARARGS),
+ METHOD(GameIsBeastKnown, METH_VARARGS),
+ METHOD(GameIsPCSelected, METH_VARARGS),
+ METHOD(GamePause, METH_VARARGS),
+ METHOD(GameSelectPC, METH_VARARGS),
+ METHOD(GameSelectPCSingle, METH_VARARGS),
+ METHOD(GameSetExpansion, METH_VARARGS),
+ METHOD(GameGetExpansion, METH_NOARGS),
+ METHOD(GameSetFormation, METH_VARARGS),
+ METHOD(GameSetPartyGold, METH_VARARGS),
+ METHOD(GameSetPartySize, METH_VARARGS),
+ METHOD(GameSetProtagonistMode, METH_VARARGS),
+ METHOD(GameSetScreenFlags, METH_VARARGS),
+ METHOD(GetAbilityBonus, METH_VARARGS),
+ METHOD(GetCombatDetails, METH_VARARGS),
+ METHOD(GetContainer, METH_VARARGS),
+ METHOD(GetContainerItem, METH_VARARGS),
+ METHOD(GetCurrentArea, METH_NOARGS),
+ METHOD(GetEquippedAmmunition, METH_VARARGS),
+ METHOD(GetEquippedQuickSlot, METH_VARARGS),
+ METHOD(GetGamePortraitPreview, METH_VARARGS),
+ METHOD(GetGamePreview, METH_VARARGS),
+ METHOD(GetGameString, METH_VARARGS),
+ METHOD(GetGameTime, METH_NOARGS),
+ METHOD(GetGameVar, METH_VARARGS),
+ METHOD(GetINIBeastsKey, METH_VARARGS),
+ METHOD(GetINIPartyCount, METH_NOARGS),
+ METHOD(GetINIPartyKey, METH_VARARGS),
+ METHOD(GetINIQuestsKey, METH_VARARGS),
+ METHOD(GetItem, METH_VARARGS),
+ METHOD(GetJournalEntry, METH_VARARGS),
+ METHOD(GetJournalSize, METH_VARARGS),
+ METHOD(GetKnownSpell, METH_VARARGS),
+ METHOD(GetKnownSpellsCount, METH_VARARGS),
+ METHOD(GetMazeEntry, METH_VARARGS),
+ METHOD(GetMazeHeader, METH_NOARGS),
+ METHOD(GetMemorizableSpellsCount, METH_VARARGS),
+ METHOD(GetMemorizedSpell, METH_VARARGS),
+ METHOD(GetMemorizedSpellsCount, METH_VARARGS),
+ METHOD(GetMessageWindowSize, METH_NOARGS),
+ METHOD(GetPartySize, METH_NOARGS),
+ METHOD(GetPCStats, METH_VARARGS),
+ METHOD(GetPlayerName, METH_VARARGS),
+ METHOD(GetPlayerPortrait, METH_VARARGS),
+ METHOD(GetPlayerStat, METH_VARARGS),
+ METHOD(GetPlayerStates, METH_VARARGS),
+ METHOD(GetPlayerScript, METH_VARARGS),
+ METHOD(GetPlayerSound, METH_VARARGS),
+ METHOD(GetPlayerString, METH_VARARGS),
+ METHOD(GetRumour, METH_VARARGS),
+ METHOD(GetSaveGames, METH_VARARGS),
+ METHOD(GetSelectedSize, METH_NOARGS),
+ METHOD(GetSelectedActors, METH_NOARGS),
+ METHOD(GetString, METH_VARARGS),
+ METHOD(GetSpellCastOn, METH_VARARGS),
+ METHOD(GetSlotType, METH_VARARGS),
+ METHOD(GetStore, METH_VARARGS),
+ METHOD(GetStoreDrink, METH_VARARGS),
+ METHOD(GetStoreCure, METH_VARARGS),
+ METHOD(GetStoreItem, METH_VARARGS),
+ METHOD(GetSpell, METH_VARARGS),
+ METHOD(GetSlotItem, METH_VARARGS),
+ METHOD(GetSlots, METH_VARARGS),
+ METHOD(GetSystemVariable, METH_VARARGS),
+ METHOD(GetToken, METH_VARARGS),
+ METHOD(GetVar, METH_VARARGS),
+ METHOD(HardEndPL, METH_NOARGS),
+ METHOD(HasResource, METH_VARARGS),
+ METHOD(HasSpecialItem, METH_VARARGS),
+ METHOD(HasSpecialSpell, METH_VARARGS),
+ METHOD(HideGUI, METH_NOARGS),
+ METHOD(IncreaseReputation, METH_VARARGS),
+ METHOD(IsDraggingItem, METH_NOARGS),
+ METHOD(IsDualWielding, METH_VARARGS),
+ METHOD(IsValidStoreItem, METH_VARARGS),
+ METHOD(LearnSpell, METH_VARARGS),
+ METHOD(LeaveContainer, METH_VARARGS),
+ METHOD(LeaveParty, METH_VARARGS),
+ METHOD(LeaveStore, METH_VARARGS),
+ METHOD(LoadGame, METH_VARARGS),
+ METHOD(LoadMusicPL, METH_VARARGS),
+ METHOD(LoadSymbol, METH_VARARGS),
+ METHOD(LoadTable, METH_VARARGS),
+ METHOD(LoadWindowPack, METH_VARARGS),
+ METHOD(LoadWindow, METH_VARARGS),
+ METHOD(LoadWindowFrame, METH_VARARGS),
+ METHOD(MemorizeSpell, METH_VARARGS),
+ METHOD(ModifyEffect, METH_VARARGS),
+ METHOD(MoveToArea, METH_VARARGS),
+ METHOD(Quit, METH_NOARGS),
+ METHOD(QuitGame, METH_NOARGS),
+ METHOD(PlaySound, METH_VARARGS),
+ METHOD(PlayMovie, METH_VARARGS),
+ METHOD(RemoveItem, METH_VARARGS),
+ METHOD(RemoveSpell, METH_VARARGS),
+ METHOD(RemoveEffects, METH_VARARGS),
+ METHOD(RestParty, METH_VARARGS),
+ METHOD(RevealArea, METH_VARARGS),
+ METHOD(Roll, METH_VARARGS),
+ METHOD(SaveCharacter, METH_VARARGS),
+ METHOD(SaveGame, METH_VARARGS),
+ METHOD(SetDefaultActions, METH_VARARGS),
+ METHOD(SetEquippedQuickSlot, METH_VARARGS),
+ METHOD(SetFullScreen, METH_VARARGS),
+ METHOD(SetGamma, METH_VARARGS),
+ METHOD(SetGlobal, METH_VARARGS),
+ METHOD(SetInfoTextColor, METH_VARARGS),
+ METHOD(SetJournalEntry, METH_VARARGS),
+ METHOD(SetMapAnimation, METH_VARARGS),
+ METHOD(SetMapDoor, METH_VARARGS),
+ METHOD(SetMapExit, METH_VARARGS),
+ METHOD(SetMapnote, METH_VARARGS),
+ METHOD(SetMapRegion, METH_VARARGS),
+ METHOD(SetMasterScript, METH_VARARGS),
+ METHOD(SetMazeEntry, METH_VARARGS),
+ METHOD(SetMazeData, METH_VARARGS),
+ METHOD(SetMemorizableSpellsCount, METH_VARARGS),
+ METHOD(SetModalState, METH_VARARGS),
+ METHOD(SetMouseScrollSpeed, METH_VARARGS),
+ METHOD(SetNextScript, METH_VARARGS),
+ METHOD(SetPlayerName, METH_VARARGS),
+ METHOD(SetPlayerScript, METH_VARARGS),
+ METHOD(SetPlayerStat, METH_VARARGS),
+ METHOD(SetPlayerString, METH_VARARGS),
+ METHOD(SetPlayerSound, METH_VARARGS),
+ METHOD(SetPurchasedAmount, METH_VARARGS),
+ METHOD(SetRepeatClickFlags, METH_VARARGS),
+ METHOD(SetTickHook, METH_VARARGS),
+ METHOD(SetTimedEvent, METH_VARARGS),
+ METHOD(SetToken, METH_VARARGS),
+ METHOD(SetTooltipDelay, METH_VARARGS),
+ METHOD(SetupMaze, METH_VARARGS),
+ METHOD(SetupQuickSlot, METH_VARARGS),
+ METHOD(SetVar, METH_VARARGS),
+ METHOD(SoftEndPL, METH_NOARGS),
+ METHOD(SpellCast, METH_VARARGS),
+ METHOD(StatComment, METH_VARARGS),
+ METHOD(StealFailed, METH_NOARGS),
+ METHOD(SwapPCs, METH_VARARGS),
+ METHOD(UnhideGUI, METH_NOARGS),
+ METHOD(UnmemorizeSpell, METH_VARARGS),
+ METHOD(UpdateAmbientsVolume, METH_NOARGS),
+ METHOD(UpdateMusicVolume, METH_NOARGS),
+ METHOD(UseItem, METH_VARARGS),
+ METHOD(VerbalConstant, METH_VARARGS),
+ // terminating entry
+ {NULL, NULL, 0, NULL}
+};
+
+static PyMethodDef GemRBInternalMethods[] = {
+ METHOD(Button_CreateLabelOnButton, METH_VARARGS),
+ METHOD(Button_EnableBorder, METH_VARARGS),
+ METHOD(Button_SetActionIcon, METH_VARARGS),
+ METHOD(Button_SetBAM, METH_VARARGS),
+ METHOD(Button_SetBorder, METH_VARARGS),
+ METHOD(Button_SetFlags, METH_VARARGS),
+ METHOD(Button_SetFont, METH_VARARGS),
+ METHOD(Button_SetItemIcon, METH_VARARGS),
+ METHOD(Button_SetMOS, METH_VARARGS),
+ METHOD(Button_SetOverlay, METH_VARARGS),
+ METHOD(Button_SetPLT, METH_VARARGS),
+ METHOD(Button_SetPicture, METH_VARARGS),
+ METHOD(Button_SetPictureClipping, METH_VARARGS),
+ METHOD(Button_SetSpellIcon, METH_VARARGS),
+ METHOD(Button_SetSprite2D, METH_VARARGS),
+ METHOD(Button_SetSprites, METH_VARARGS),
+ METHOD(Button_SetState, METH_VARARGS),
+ METHOD(Button_SetTextColor, METH_VARARGS),
+ METHOD(Control_AttachScrollBar, METH_VARARGS),
+ METHOD(Control_QueryText, METH_VARARGS),
+ METHOD(Control_SetAnimation, METH_VARARGS),
+ METHOD(Control_SetAnimationPalette, METH_VARARGS),
+ METHOD(Control_SetEvent, METH_VARARGS),
+ METHOD(Control_SetPos, METH_VARARGS),
+ METHOD(Control_SetSize, METH_VARARGS),
+ METHOD(Control_SetStatus, METH_VARARGS),
+ METHOD(Control_SetText, METH_VARARGS),
+ METHOD(Control_SetTooltip, METH_VARARGS),
+ METHOD(Control_SetVarAssoc, METH_VARARGS),
+ METHOD(Control_TextArea_SetFlags, METH_VARARGS),
+ METHOD(Label_SetTextColor, METH_VARARGS),
+ METHOD(Label_SetUseRGB, METH_VARARGS),
+ METHOD(SaveGame_GetDate, METH_VARARGS),
+ METHOD(SaveGame_GetGameDate, METH_VARARGS),
+ METHOD(SaveGame_GetName, METH_VARARGS),
+ METHOD(SaveGame_GetPortrait, METH_VARARGS),
+ METHOD(SaveGame_GetPreview, METH_VARARGS),
+ METHOD(SaveGame_GetSaveID, METH_VARARGS),
+ METHOD(ScrollBar_SetDefaultScrollBar, METH_VARARGS),
+ METHOD(ScrollBar_SetSprites, METH_VARARGS),
+ METHOD(Symbol_GetValue, METH_VARARGS),
+ METHOD(Symbol_Unload, METH_VARARGS),
+ METHOD(Table_FindValue, METH_VARARGS),
+ METHOD(Table_GetColumnCount, METH_VARARGS),
+ METHOD(Table_GetColumnIndex, METH_VARARGS),
+ METHOD(Table_GetColumnName, METH_VARARGS),
+ METHOD(Table_GetRowCount, METH_VARARGS),
+ METHOD(Table_GetRowIndex, METH_VARARGS),
+ METHOD(Table_GetRowName, METH_VARARGS),
+ METHOD(Table_GetValue, METH_VARARGS),
+ METHOD(Table_Unload, METH_VARARGS),
+ METHOD(TextArea_Append, METH_VARARGS),
+ METHOD(TextArea_Clear, METH_VARARGS),
+ METHOD(TextArea_GetCharSounds, METH_VARARGS),
+ METHOD(TextArea_GetCharacters, METH_VARARGS),
+ METHOD(TextArea_GetPortraits, METH_VARARGS),
+ METHOD(TextArea_MoveText, METH_VARARGS),
+ METHOD(TextArea_Rewind, METH_VARARGS),
+ METHOD(TextArea_Scroll, METH_VARARGS),
+ METHOD(TextArea_SelectText, METH_VARARGS),
+ METHOD(TextArea_SetHistory, METH_VARARGS),
+ METHOD(TextEdit_ConvertEdit, METH_VARARGS),
+ METHOD(TextEdit_SetBufferLength, METH_VARARGS),
+ METHOD(Window_CreateButton, METH_VARARGS),
+ METHOD(Window_CreateLabel, METH_VARARGS),
+ METHOD(Window_CreateMapControl, METH_VARARGS),
+ METHOD(Window_CreateScrollBar, METH_VARARGS),
+ METHOD(Window_CreateTextEdit, METH_VARARGS),
+ METHOD(Window_CreateWorldMapControl, METH_VARARGS),
+ METHOD(Window_DeleteControl, METH_VARARGS),
+ METHOD(Window_GetControl, METH_VARARGS),
+ METHOD(Window_HasControl, METH_VARARGS),
+ METHOD(Window_Invalidate, METH_VARARGS),
+ METHOD(Window_SetFrame, METH_VARARGS),
+ METHOD(Window_SetPicture, METH_VARARGS),
+ METHOD(Window_SetPos, METH_VARARGS),
+ METHOD(Window_SetSize, METH_VARARGS),
+ METHOD(Window_SetVisible, METH_VARARGS),
+ METHOD(Window_SetupControls, METH_VARARGS),
+ METHOD(Window_SetupEquipmentIcons, METH_VARARGS),
+ METHOD(Window_SetupSpellIcons, METH_VARARGS),
+ METHOD(Window_ShowModal, METH_VARARGS),
+ METHOD(Window_Unload, METH_VARARGS),
+ METHOD(WorldMap_AdjustScrolling, METH_VARARGS),
+ METHOD(WorldMap_GetDestinationArea, METH_VARARGS),
+ METHOD(WorldMap_SetTextColor, METH_VARARGS),
+ // terminating entry
+ {NULL, NULL, 0, NULL}
+};
+
+GUIScript::GUIScript(void)
+{
+ gs = this;
+ pDict = NULL; //borrowed, but used outside a function
+ pModule = NULL; //should decref it
+ pMainDic = NULL; //borrowed, but used outside a function
+}
+
+GUIScript::~GUIScript(void)
+{
+ if (Py_IsInitialized()) {
+ if (pModule) {
+ Py_DECREF( pModule );
+ }
+ Py_Finalize();
+ }
+ if (ItemArray) {
+ free(ItemArray);
+ ItemArray=NULL;
+ }
+ if (SpellArray) {
+ free(SpellArray);
+ SpellArray=NULL;
+ }
+ if (StoreSpells) {
+ free(StoreSpells);
+ StoreSpells=NULL;
+ }
+ if (SpecialItems) {
+ free(SpecialItems);
+ SpecialItems=NULL;
+ }
+ if (UsedItems) {
+ free(UsedItems);
+ UsedItems=NULL;
+ }
+ if (ItemSounds) {
+ free(ItemSounds);
+ ItemSounds=NULL;
+ }
+
+ StoreSpellsCount = -1;
+ SpecialItemsCount = -1;
+ UsedItemsCount = -1;
+ ItemSoundsCount = -1;
+ ReputationIncrease[0]=(int) UNINIT_IEDWORD;
+ ReputationDonation[0]=(int) UNINIT_IEDWORD;
+ GUIAction[0]=UNINIT_IEDWORD;
+}
+
+/**
+ * Quote path for use in python strings.
+ * On windows also convert backslashes to forward slashes.
+ */
+char* QuotePath(char* tgt, const char* src)
+{
+ char *p = tgt;
+ char c;
+
+ do {
+ c = *src++;
+#ifdef WIN32
+ if (c == '\\') c = '/';
+#endif
+ if (c == '"' || c == '\\') *p++ = '\\';
+ } while (0 != (*p++ = c));
+ return tgt;
+}
+
+
+PyDoc_STRVAR( GemRB__doc,
+"Module exposing GemRB data and engine internals\n\n"
+"This module exposes to python GUIScripts GemRB engine data and internals."
+"It's implemented in gemrb/plugins/GUIScript/GUIScript.cpp" );
+
+PyDoc_STRVAR( GemRB_internal__doc,
+"Internal module for GemRB metaclasses.\n\n"
+"This module is only for implementing GUIClass.py."
+"It's implemented in gemrb/plugins/GUIScript/GUIScript.cpp" );
+
+/** Initialization Routine */
+
+bool GUIScript::Init(void)
+{
+ Py_Initialize();
+ if (!Py_IsInitialized()) {
+ return false;
+ }
+ PyObject* pGemRB = Py_InitModule3( "GemRB", GemRBMethods, GemRB__doc );
+ if (!pGemRB) {
+ return false;
+ }
+
+ PyObject* p_GemRB = Py_InitModule3( "_GemRB", GemRBInternalMethods, GemRB_internal__doc );
+ if (!p_GemRB) {
+ return false;
+ }
+
+ char string[256];
+
+ sprintf( string, "import sys" );
+ if (PyRun_SimpleString( string ) == -1) {
+ printMessage( "GUIScript", string, RED );
+ return false;
+ }
+
+ // 2.6+ only, so we ignore failures
+ sprintf( string, "sys.dont_write_bytecode = True");
+ PyRun_SimpleString( string );
+
+ char path[_MAX_PATH];
+ char path2[_MAX_PATH];
+ char quoted[_MAX_PATH];
+
+ PathJoin(path, core->GUIScriptsPath, "GUIScripts", NULL);
+
+ // Add generic script path early, so GameType detection works
+ sprintf( string, "sys.path.append(\"%s\")", QuotePath( quoted, path ));
+ if (PyRun_SimpleString( string ) == -1) {
+ printMessage( "GUIScript", string, RED );
+ return false;
+ }
+
+ sprintf( string, "import GemRB\n");
+ if (PyRun_SimpleString( "import GemRB" ) == -1) {
+ printMessage( "GUIScript", string, RED );
+ return false;
+ }
+
+ // FIXME: better would be to add GemRB.GetGamePath() or some such
+ sprintf( string, "GemRB.GamePath = \"%s\"", QuotePath( quoted, core->GamePath ));
+ if (PyRun_SimpleString( string ) == -1) {
+ printMessage( "GUIScript", string, RED );
+ return false;
+ }
+
+ // Detect GameType if it was set to auto
+ if (stricmp( core->GameType, "auto" ) == 0) {
+ Autodetect();
+ }
+
+ // use the iwd guiscripts for how, but leave its override
+ if (stricmp( core->GameType, "how" ) == 0) {
+ PathJoin(path2, path, "iwd", NULL);
+ } else {
+ PathJoin(path2, path, core->GameType, NULL);
+ }
+
+ // GameType-specific import path must have a higher priority than
+ // the generic one, so insert it before it
+ sprintf( string, "sys.path.insert(-1, \"%s\")", QuotePath( quoted, path2 ));
+ if (PyRun_SimpleString( string ) == -1) {
+ printMessage( "GUIScript", string, RED );
+ return false;
+ }
+ sprintf( string, "GemRB.GameType = \"%s\"", core->GameType);
+ if (PyRun_SimpleString( string ) == -1) {
+ printMessage( "GUIScript", string, RED );
+ return false;
+ }
+
+#if PY_MAJOR_VERSION == 2
+#if PY_MINOR_VERSION > 5
+ // warn about python stuff that will go away in 3.0
+ Py_Py3kWarningFlag = true;
+#endif
+#endif
+
+ if (PyRun_SimpleString( "from GUIDefines import *" ) == -1) {
+ printMessage( "GUIScript", " ", RED );
+ printf("Check if %s/GUIDefines.py exists! ", path);
+ return false;
+ }
+
+ if (PyRun_SimpleString( "from GUIClasses import *" ) == -1) {
+ printMessage( "GUIScript", " ", RED );
+ printf("Check if %s/GUIClasses.py exists! ", path);
+ return false;
+ }
+
+ if (PyRun_SimpleString( "from GemRB import *" ) == -1) {
+ printMessage( "GUIScript", " ", RED );
+ printf("builtin GemRB module failed to load!!! ");
+ return false;
+ }
+
+ // TODO: Put this file somewhere user editable
+ // TODO: Search multiple places for this file
+ char include[_MAX_PATH];
+ PathJoin(include, core->GUIScriptsPath, "GUIScripts/include.py", NULL);
+ ExecFile(include);
+
+ PyObject *pMainMod = PyImport_AddModule( "__main__" );
+ /* pMainMod is a borrowed reference */
+ pMainDic = PyModule_GetDict( pMainMod );
+ /* pMainDic is a borrowed reference */
+
+ PyObject *pClassesMod = PyImport_AddModule( "GUIClasses" );
+ /* pClassesMod is a borrowed reference */
+ pGUIClasses = PyModule_GetDict( pClassesMod );
+ /* pGUIClasses is a borrowed reference */
+
+ return true;
+}
+
+bool GUIScript::Autodetect(void)
+{
+ printMessage( "GUIScript", "Detecting GameType: ", WHITE);
+
+ char path[_MAX_PATH];
+ PathJoin( path, core->GUIScriptsPath, "GUIScripts", NULL );
+ DirectoryIterator iter( path );
+ if (!iter)
+ return false;
+
+ gametype_hint[0] = '\0';
+ gametype_hint_weight = 0;
+
+ do {
+ const char *dirent = iter.GetName();
+ char module[_MAX_PATH];
+
+ //printf("DE: %s\n", dirent);
+ if (iter.IsDirectory() && dirent[0] != '.') {
+ // NOTE: these methods subtly differ in sys.path content, need for __init__.py files ...
+ // Method1:
+ PathJoin(module, core->GUIScriptsPath, "GUIScripts", dirent, "Autodetect.py", NULL);
+ ExecFile(module);
+ // Method2:
+ //strcpy( module, dirent );
+ //strcat( module, ".Autodetect");
+ //LoadScript(module);
+ }
+ } while (++iter);
+
+ if (gametype_hint[0]) {
+ printStatus(gametype_hint, GREEN);
+ strcpy(core->GameType, gametype_hint);
+ return true;
+ }
+ else {
+ printStatus("ERROR", LIGHT_RED);
+ return false;
+ }
+}
+
+bool GUIScript::LoadScript(const char* filename)
+{
+ if (!Py_IsInitialized()) {
+ return false;
+ }
+ printMessage( "GUIScript", "Loading Script ", WHITE );
+ printf( "%s...", filename );
+
+ char path[_MAX_PATH];
+ strcpy( path, filename );
+
+ PyObject *pName = PyString_FromString( filename );
+ /* Error checking of pName left out */
+ if (pName == NULL) {
+ printStatus( "ERROR", LIGHT_RED );
+ return false;
+ }
+
+ if (pModule) {
+ Py_DECREF( pModule );
+ }
+
+ pModule = PyImport_Import( pName );
+ Py_DECREF( pName );
+
+ if (pModule != NULL) {
+ pDict = PyModule_GetDict( pModule );
+ if (PyDict_Merge( pDict, pMainDic, false ) == -1)
+ return false;
+ /* pDict is a borrowed reference */
+ } else {
+ PyErr_Print();
+ printStatus( "ERROR", LIGHT_RED );
+ return false;
+ }
+ printStatus( "OK", LIGHT_GREEN );
+ return true;
+}
+
+/* Similar to RunFunction, but with parameters, and doesn't necessarily fails */
+PyObject *GUIScript::CallbackFunction(const char* fname, PyObject* pArgs)
+{
+ if (!Py_IsInitialized()) {
+ return NULL;
+ }
+ if (pDict == NULL) {
+ return NULL;
+ }
+ PyObject *pFunc = PyDict_GetItemString( pDict, (char *) fname );
+ /* pFunc: Borrowed reference */
+ if (( !pFunc ) || ( !PyCallable_Check( pFunc ) )) {
+ printMessage("GUIScript", " ", LIGHT_RED);
+ printf("%s is not callable!\n", fname);
+ return NULL;
+ }
+ PyObject *pValue = PyObject_CallObject( pFunc, pArgs );
+ if (pValue == NULL) {
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ }
+ }
+ return pValue;
+}
+
+bool GUIScript::RunFunction(const char *ModuleName, const char* FunctionName, bool error, int intparam)
+{
+ if (!Py_IsInitialized()) {
+ return false;
+ }
+
+ PyObject *module;
+ if (ModuleName) {
+ module = PyImport_ImportModule(const_cast<char*> (ModuleName) );
+ } else {
+ module = pModule;
+ Py_XINCREF(module);
+ }
+ if (module == NULL) {
+ PyErr_Print();
+ return false;
+ }
+ PyObject *dict = PyModule_GetDict(module);
+
+ PyObject *pFunc = PyDict_GetItemString( dict, const_cast<char*>(FunctionName) );
+ /* pFunc: Borrowed reference */
+ if (( !pFunc ) || ( !PyCallable_Check( pFunc ) )) {
+ if (error) {
+ printMessage( "GUIScript", "Missing function:", LIGHT_RED );
+ printf("%s\n", FunctionName);
+ }
+ Py_DECREF(module);
+ return false;
+ }
+ PyObject *pArgs;
+ if (intparam == -1) {
+ pArgs = NULL;
+ } else {
+ pArgs = Py_BuildValue("(i)", intparam);
+ }
+ PyObject *pValue = PyObject_CallObject( pFunc, pArgs );
+ Py_XDECREF( pArgs );
+ if (pValue == NULL) {
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ }
+ Py_DECREF(module);
+ return false;
+ }
+ Py_DECREF( pValue );
+ Py_DECREF(module);
+ return true;
+}
+
+void GUIScript::ExecFile(const char* file)
+{
+ FileStream fs;
+ if (!fs.Open(file, true))
+ return;
+
+ int len = fs.Remains();
+ if (len <= 0)
+ return;
+
+ char* buffer = (char *) malloc(len+1);
+ if (!buffer)
+ return;
+
+ if (fs.Read(buffer, len) == GEM_ERROR) {
+ free(buffer);
+ return;
+ }
+ buffer[len] = 0;
+
+ ExecString(buffer);
+ free(buffer);
+}
+
+/** Exec a single String */
+void GUIScript::ExecString(const char* string)
+{
+ if (PyRun_SimpleString((char*) string) == -1) {
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ }
+ }
+}
+
+PyObject* GUIScript::ConstructObject(const char* type, int arg)
+{
+ PyObject* tuple = PyTuple_New(1);
+ PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(arg));
+ PyObject* ret = gs->ConstructObject(type, tuple);
+ Py_DECREF(tuple);
+ return ret;
+}
+
+PyObject* GUIScript::ConstructObject(const char* type, PyObject* pArgs)
+{
+ char classname[_MAX_PATH] = "G";
+ strncat(classname, type, _MAX_PATH - 2);
+ if (!pGUIClasses) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "Tried to use an object (%s) before script compiled!", classname);
+ return RuntimeError(buf);
+ }
+
+ PyObject* cobj = PyDict_GetItemString( pGUIClasses, classname );
+ if (!cobj) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "Failed to lookup name '%s'", classname);
+ return RuntimeError(buf);
+ }
+ PyObject* ret = PyObject_Call(cobj, pArgs, NULL);
+ if (!ret) {
+ return RuntimeError("Failed to call constructor");
+ }
+ return ret;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x1B01BE6B, "GUI Script Engine (Python)")
+PLUGIN_CLASS(IE_GUI_SCRIPT_CLASS_ID, GUIScript)
+END_PLUGIN()
diff --git a/gemrb/plugins/GUIScript/GUIScript.h b/gemrb/plugins/GUIScript/GUIScript.h
new file mode 100644
index 0000000..05d8bfd
--- /dev/null
+++ b/gemrb/plugins/GUIScript/GUIScript.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 GUISCRIPT_H
+#define GUISCRIPT_H
+
+// NOTE: Python.h has to be included first.
+
+#if defined(WIN32) && defined(_DEBUG)
+#undef _DEBUG
+#include <Python.h>
+#define _DEBUG
+#else
+#include <Python.h>
+#endif
+
+#include "ScriptEngine.h"
+
+#define SV_BPP 0
+#define SV_WIDTH 1
+#define SV_HEIGHT 2
+
+class GUIScript : public ScriptEngine {
+public:
+ PyObject* pModule, * pDict;
+ PyObject* pMainDic;
+ PyObject* pGUIClasses;
+public:
+ GUIScript(void);
+ ~GUIScript(void);
+ /** Initialization Routine */
+ bool Init(void);
+ /** Autodetect GameType */
+ bool Autodetect(void);
+ /** Load Script */
+ bool LoadScript(const char* filename);
+ /** Run Function */
+ bool RunFunction(const char *module, const char* fname, bool error=true, int intparam=-1);
+ /** Exec a single File */
+ void ExecFile(const char* file);
+ /** Exec a single String */
+ void ExecString(const char* string);
+ /** lets hope this one can be here without screwing up the general interface */
+ PyObject *CallbackFunction(const char* fname, PyObject* pArgs);
+ PyObject* ConstructObject(const char* classname, int arg);
+ PyObject* ConstructObject(const char* classname, PyObject* pArgs);
+};
+
+extern GUIScript *gs;
+
+#endif
diff --git a/gemrb/plugins/GUIScript/Makefile.am b/gemrb/plugins/GUIScript/Makefile.am
new file mode 100644
index 0000000..c23f9ce
--- /dev/null
+++ b/gemrb/plugins/GUIScript/Makefile.am
@@ -0,0 +1,5 @@
+plugin_LTLIBRARIES = GUIScript.la
+INCLUDES = $(PYTHON_INCLUDES)
+GUIScript_la_LDFLAGS = -module -avoid-version -shared
+GUIScript_la_LIBADD = @PYTHON_LIBS@
+GUIScript_la_SOURCES = GUIScript.cpp GUIScript.h PythonHelpers.cpp PythonHelpers.h
diff --git a/gemrb/plugins/GUIScript/PythonHelpers.cpp b/gemrb/plugins/GUIScript/PythonHelpers.cpp
new file mode 100644
index 0000000..f1c27a3
--- /dev/null
+++ b/gemrb/plugins/GUIScript/PythonHelpers.cpp
@@ -0,0 +1,80 @@
+/* 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 "PythonHelpers.h"
+
+static bool CallPython(PyObject *Function, PyObject *args = NULL)
+{
+ if (!Function) {
+ return false;
+ }
+ PyObject *ret = PyObject_CallObject(Function, args);
+ Py_XDECREF( args );
+ if (ret == NULL) {
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ }
+ return false;
+ }
+ Py_DECREF(ret);
+ return true;
+}
+
+static bool CallPython(PyObject *Function, int arg)
+{
+ if (!Function) {
+ return false;
+ }
+ PyObject *args = Py_BuildValue("(i)", arg);
+ return CallPython(Function, args);
+}
+
+PythonCallback::PythonCallback(PyObject *Function)
+ : Function(Function)
+{
+ if (Function && PyCallable_Check(Function)) {
+ Py_INCREF(Function);
+ } else {
+ Function = NULL;
+ }
+}
+
+PythonCallback::~PythonCallback()
+{
+ if (Py_IsInitialized()) {
+ Py_XDECREF(Function);
+ }
+}
+
+bool PythonCallback::call ()
+{
+ if (!Function || !Py_IsInitialized()) {
+ return false;
+ }
+ return CallPython(Function);
+}
+
+bool PythonCallback::call (int arg)
+{
+ if (!Function || !Py_IsInitialized()) {
+ return false;
+ }
+ return CallPython(Function, arg);
+}
diff --git a/gemrb/plugins/GUIScript/PythonHelpers.h b/gemrb/plugins/GUIScript/PythonHelpers.h
new file mode 100644
index 0000000..85fbe21
--- /dev/null
+++ b/gemrb/plugins/GUIScript/PythonHelpers.h
@@ -0,0 +1,112 @@
+/* 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 PYTHON_HELPERS_H
+#define PYTHON_HELPERS_H
+
+// Python.h needs to be included first.
+#include "GUIScript.h"
+
+#include "win32def.h" // For Logging
+
+#include "Callback.h"
+#include "Holder.h"
+#include "Interface.h"
+
+struct PythonCallback : public Callback {
+public:
+ PythonCallback(PyObject *Function);
+ ~PythonCallback();
+ bool call();
+ bool call(int);
+private:
+ PyObject *Function;
+};
+
+
+template <typename T>
+class CObject : public Holder<T> {
+private:
+public:
+ operator PyObject* () const
+ {
+ if (Holder<T>::ptr) {
+ Holder<T>::ptr->acquire();
+ GUIScript *gs = (GUIScript *) core->GetGUIScriptEngine();
+ PyObject *obj = PyCObject_FromVoidPtrAndDesc(Holder<T>::ptr,const_cast<TypeID*>(&T::ID),PyRelease);
+ PyObject *tuple = PyTuple_New(1);
+ PyTuple_SET_ITEM(tuple, 0, obj);
+ PyObject *ret = gs->ConstructObject(T::ID.description, tuple);
+ Py_DECREF(tuple);
+ return ret;
+ } else {
+ Py_INCREF( Py_None );
+ return Py_None;
+ }
+ }
+ CObject(PyObject *obj)
+ {
+ if (obj == Py_None)
+ return;
+ PyObject *id = PyObject_GetAttrString(obj, "ID");
+ if (id)
+ obj = id;
+ else
+ PyErr_Clear();
+ if (!PyCObject_Check(obj) || PyCObject_GetDesc(obj) != const_cast<TypeID*>(&T::ID)) {
+ printMessage("GUIScript","Bad CObject extracted.\n",LIGHT_RED);
+ Py_XDECREF(id);
+ return;
+ }
+ Holder<T>::ptr = static_cast<T*>(PyCObject_AsVoidPtr(obj));
+ Holder<T>::ptr->acquire();
+ Py_XDECREF(id);
+ }
+ CObject(const Holder<T>& ptr)
+ : Holder<T>(ptr)
+ {
+ }
+ // This is here because of lookup order issues.
+ operator bool () const
+ {
+ return Holder<T>::ptr;
+ }
+private:
+ static void PyRelease(void *obj, void *desc)
+ {
+ if (desc != const_cast<TypeID*>(&T::ID)) {
+ printMessage("GUIScript","Bad CObject deleted.\n",LIGHT_RED);
+ return;
+ }
+ static_cast<T*>(obj)->release();
+ }
+};
+
+template <typename T, class Container>
+PyObject* MakePyList(const Container &source)
+{
+ size_t size = source.size();
+ PyObject *list = PyList_New(size);
+ for (size_t i = 0; i < size; ++i) {
+ // SET_ITEM might be preferable to SetItem here, but MSVC6 doesn't like it.
+ PyList_SetItem(list, i, CObject<T>(source[i]));
+ }
+ return list;
+}
+
+#endif
diff --git a/gemrb/plugins/IDSImporter/CMakeLists.txt b/gemrb/plugins/IDSImporter/CMakeLists.txt
new file mode 100644
index 0000000..6615c51
--- /dev/null
+++ b/gemrb/plugins/IDSImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (IDSImporter IDSImporter.cpp)
diff --git a/gemrb/plugins/IDSImporter/IDSImporter.cpp b/gemrb/plugins/IDSImporter/IDSImporter.cpp
new file mode 100644
index 0000000..254582b
--- /dev/null
+++ b/gemrb/plugins/IDSImporter/IDSImporter.cpp
@@ -0,0 +1,158 @@
+/* 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 "IDSImporter.h"
+
+#include "IDSImporterDefs.h"
+
+#include "globals.h"
+#include "win32def.h"
+
+#include <cstring>
+
+IDSImporter::IDSImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+IDSImporter::~IDSImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+
+ for (unsigned int i = 0; i < ptrs.size(); i++) {
+ free( ptrs[i] );
+ }
+}
+
+bool IDSImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str) {
+ return false;
+ }
+ str = stream;
+ this->autoFree = autoFree;
+
+ str->CheckEncrypted();
+ char tmp[11];
+ str->ReadLine( tmp, 10 );
+ tmp[10] = 0;
+ if (tmp[0] != 'I') {
+ str->Rewind();
+ }
+ while (true) {
+ char* line = ( char* ) malloc( 256 );
+ int len = str->ReadLine( line, 256 );
+ strlwr( line );
+ if (len == -1) {
+ free( line );
+ break;
+ }
+ if (len == 0) {
+ free( line );
+ continue;
+ }
+ if (len < 256)
+ line = ( char * ) realloc( line, len + 1 );
+ char* str = strtok( line, " " );
+ Pair p;
+ p.val = strtoul( str, NULL, 0 );
+ str = strtok( NULL, " " );
+ p.str = str;
+ if (str != NULL) {
+ ptrs.push_back( line );
+ pairs.push_back( p );
+ } else {
+ free( line );
+ }
+ }
+
+ return true;
+}
+
+int IDSImporter::GetValue(const char* txt) const
+{
+ for (unsigned int i = 0; i < pairs.size(); i++) {
+ if (stricmp( pairs[i].str, txt ) == 0) {
+ return pairs[i].val;
+ }
+ }
+ return -1;
+}
+
+char* IDSImporter::GetValue(int val) const
+{
+ for (unsigned int i = 0; i < pairs.size(); i++) {
+ if (pairs[i].val == val) {
+ return pairs[i].str;
+ }
+ }
+ return NULL;
+}
+
+char* IDSImporter::GetStringIndex(unsigned int Index) const
+{
+ if (Index >= pairs.size()) {
+ return NULL;
+ }
+ return pairs[Index].str;
+}
+
+int IDSImporter::GetValueIndex(unsigned int Index) const
+{
+ if (Index >= pairs.size()) {
+ return 0;
+ }
+ return pairs[Index].val;
+}
+
+int IDSImporter::FindString(char *str, int len) const
+{
+ int i=pairs.size();
+ while(i--) {
+ if (strnicmp(pairs[i].str, str, len) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int IDSImporter::FindValue(int val) const
+{
+ int i=pairs.size();
+ while(i--) {
+ if(pairs[i].val==val) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x1F41B94C, "IDS File Importer")
+PLUGIN_CLASS(IE_IDS_CLASS_ID, IDSImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/IDSImporter/IDSImporter.h b/gemrb/plugins/IDSImporter/IDSImporter.h
new file mode 100644
index 0000000..d91daf2
--- /dev/null
+++ b/gemrb/plugins/IDSImporter/IDSImporter.h
@@ -0,0 +1,53 @@
+/* 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 IDSIMPORTER_H
+#define IDSIMPORTER_H
+
+#include "SymbolMgr.h"
+
+struct Pair {
+ int val;
+ char* str;
+};
+
+class IDSImporter : public SymbolMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+
+ std::vector< Pair> pairs;
+ std::vector< char*> ptrs;
+
+public:
+ IDSImporter(void);
+ ~IDSImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ int GetValue(const char* txt) const;
+ char* GetValue(int val) const;
+ char* GetStringIndex(unsigned int Index) const;
+ int GetValueIndex(unsigned int Index) const;
+ int FindString(char *str, int len) const;
+ int FindValue(int val) const;
+ int GetSize() const { return pairs.size(); }
+};
+
+#endif
+
diff --git a/gemrb/plugins/IDSImporter/IDSImporterDefs.h b/gemrb/plugins/IDSImporter/IDSImporterDefs.h
new file mode 100644
index 0000000..68e4af8
--- /dev/null
+++ b/gemrb/plugins/IDSImporter/IDSImporterDefs.h
@@ -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.
+ *
+ *
+ */
+
+#define MAX_LINES 400
+#define MAX_VALUE_LENGTH 20
+#define MAX_TEXT_LENGTH 60 // maximum text length in case IDS file doesn't specify
+#define MAX_LINE_LENGTH MAX_VALUE_LENGTH + MAX_TEXT_LENGTH
+#define MAX_HEADER_LENGTH 20
+
+#define HEADER_IDS 1
+#define HEADER_LENGTH 2
+#define HEADER_BLANK 3
+#define HEADER_RECORD 4
+#define HEADER_ERROR -1
+
+// need to transfer this to a header file in the includes dir:
+//#define IDS_VALUE_NOT_LOCATED -65535 // GetValue returns this if text is not found in arrays ... this needs to be a unique number that does not exist in the value[] array
+
+// need to transfer this to a header file in the includes dir too:
+//#define GEM_ENCRYPTION_KEY "\x88\xa8\x8f\xba\x8a\xd3\xb9\xf5\xed\xb1\xcf\xea\xaa\xe4\xb5\xfb\xeb\x82\xf9\x90\xca\xc9\xb5\xe7\xdc\x8e\xb7\xac\xee\xf7\xe0\xca\x8e\xea\xca\x80\xce\xc5\xad\xb7\xc4\xd0\x84\x93\xd5\xf0\xeb\xc8\xb4\x9d\xcc\xaf\xa5\x95\xba\x99\x87\xd2\x9d\xe3\x91\xba\x90\xca"
diff --git a/gemrb/plugins/IDSImporter/Makefile.am b/gemrb/plugins/IDSImporter/Makefile.am
new file mode 100644
index 0000000..98b9975
--- /dev/null
+++ b/gemrb/plugins/IDSImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = IDSImporter.la
+IDSImporter_la_LDFLAGS = -module -avoid-version -shared
+IDSImporter_la_SOURCES = IDSImporter.cpp IDSImporter.h IDSImporterDefs.h
diff --git a/gemrb/plugins/INIImporter/CMakeLists.txt b/gemrb/plugins/INIImporter/CMakeLists.txt
new file mode 100644
index 0000000..e848597
--- /dev/null
+++ b/gemrb/plugins/INIImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (INIImporter INIImporter.cpp)
diff --git a/gemrb/plugins/INIImporter/INIImporter.cpp b/gemrb/plugins/INIImporter/INIImporter.cpp
new file mode 100644
index 0000000..e2b252a
--- /dev/null
+++ b/gemrb/plugins/INIImporter/INIImporter.cpp
@@ -0,0 +1,165 @@
+/* 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 "INIImporter.h"
+
+#include "win32def.h"
+
+#include "Interface.h"
+
+INIImporter::INIImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+INIImporter::~INIImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+ for (unsigned int i = 0; i < tags.size(); i++)
+ delete( tags[i] );
+}
+
+bool INIImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ int cnt = 0;
+ char* strbuf = ( char* ) malloc( 4097 );
+ INITag* lastTag = NULL;
+ do {
+ cnt = str->ReadLine( strbuf, 4096 );
+ if (cnt == -1)
+ break;
+ if (cnt == 0)
+ continue;
+ if (strbuf[0] == ';') //Rem
+ continue;
+ if (strbuf[0] == '[') {
+ // this is a tag
+ char* sbptr = strbuf + 1;
+ char* TagName = sbptr;
+ while (*sbptr != '\0') {
+ if (*sbptr == ']') {
+ *sbptr = 0;
+ break;
+ }
+ sbptr++;
+ }
+ INITag* it = new INITag( TagName );
+ tags.push_back( it );
+ lastTag = it;
+ continue;
+ }
+ if (lastTag == NULL)
+ continue;
+ if (lastTag->AddLine( strbuf )) {
+ printMessage("INIImporter","", LIGHT_RED);
+ printf("Bad Line in file: %s, Section: [%s], Entry: '%s'\n", stream->filename, lastTag->GetTagName(), strbuf);
+ }
+
+ } while (true);
+ free( strbuf );
+ return true;
+}
+
+int INIImporter::GetKeysCount(const char* Tag) const
+{
+ for (unsigned int i = 0; i < tags.size(); i++) {
+ const char* TagName = tags[i]->GetTagName();
+ if (stricmp( TagName, Tag ) == 0) {
+ return tags[i]->GetKeyCount();
+ }
+ }
+ return 0;
+}
+
+const char* INIImporter::GetKeyNameByIndex(const char* Tag, int index) const
+{
+ for (unsigned int i = 0; i < tags.size(); i++) {
+ const char* TagName = tags[i]->GetTagName();
+ if (stricmp( TagName, Tag ) == 0) {
+ return tags[i]->GetKeyNameByIndex(index);
+ }
+ }
+ return NULL;
+}
+
+const char* INIImporter::GetKeyAsString(const char* Tag, const char* Key,
+ const char* Default) const
+{
+ for (unsigned int i = 0; i < tags.size(); i++) {
+ const char* TagName = tags[i]->GetTagName();
+ if (stricmp( TagName, Tag ) == 0) {
+ return tags[i]->GetKeyAsString( Key, Default );
+ }
+ }
+ return Default;
+}
+
+int INIImporter::GetKeyAsInt(const char* Tag, const char* Key,
+ const int Default) const
+{
+ for (unsigned int i = 0; i < tags.size(); i++) {
+ const char* TagName = tags[i]->GetTagName();
+ if (stricmp( TagName, Tag ) == 0) {
+ return tags[i]->GetKeyAsInt( Key, Default );
+ }
+ }
+ return Default;
+}
+
+float INIImporter::GetKeyAsFloat(const char* Tag, const char* Key,
+ const float Default) const
+{
+ for (unsigned int i = 0; i < tags.size(); i++) {
+ const char* TagName = tags[i]->GetTagName();
+ if (stricmp( TagName, Tag ) == 0) {
+ return tags[i]->GetKeyAsFloat( Key, Default );
+ }
+ }
+ return Default;
+}
+
+bool INIImporter::GetKeyAsBool(const char* Tag, const char* Key,
+ const bool Default) const
+{
+ for (unsigned int i = 0; i < tags.size(); i++) {
+ const char* TagName = tags[i]->GetTagName();
+ if (stricmp( TagName, Tag ) == 0) {
+ return tags[i]->GetKeyAsBool( Key, Default );
+ }
+ }
+ return Default;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xB62F6D7, "INI File Importer")
+PLUGIN_CLASS(IE_INI_CLASS_ID, INIImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/INIImporter/INIImporter.h b/gemrb/plugins/INIImporter/INIImporter.h
new file mode 100644
index 0000000..46c0320
--- /dev/null
+++ b/gemrb/plugins/INIImporter/INIImporter.h
@@ -0,0 +1,212 @@
+/* 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 INIIMPORTER_H
+#define INIIMPORTER_H
+
+#include "DataFileMgr.h"
+
+#include "globals.h"
+
+#include <cstring>
+
+struct INIPair {
+ char* Name, * Value;
+};
+
+class INITag {
+private:
+ std::vector< INIPair> pairs;
+ char* TagName;
+public:
+ INITag(const char* Name)
+ {
+ int len = ( int ) strlen( Name ) + 1;
+ TagName = ( char * ) malloc( len );
+ memcpy( TagName, Name, len );
+ }
+
+ ~INITag()
+ {
+ free( TagName );
+ for (unsigned int i = 0; i < pairs.size(); i++) {
+ free( pairs[i].Name );
+ free( pairs[i].Value );
+ }
+ }
+
+ const char* GetTagName() const
+ {
+ return TagName;
+ }
+
+ int GetKeyCount() const
+ {
+ return (int) pairs.size();
+ }
+
+ const char* GetKeyNameByIndex(int index) const
+ {
+ return pairs[index].Name;
+ }
+
+ bool AddLine(char* Line)
+ {
+ INIPair p;
+ char* equal = strchr( Line, '=' );
+ if(!equal) {
+ return true;
+ }
+ *equal = 0;
+ char* NameKey = Line;
+ char* ValueKey = equal + 1;
+ //Left Trimming
+ while (*NameKey != '\0') {
+ if (*NameKey != ' ')
+ break;
+ NameKey++;
+ }
+ while (*ValueKey != '\0') {
+ if (*ValueKey != ' ')
+ break;
+ ValueKey++;
+ }
+ //Right Trimming
+ int NameKeyLen = ( int ) strlen( NameKey );
+ int ValueKeyLen = ( int ) strlen( ValueKey );
+ char* endNameKey = NameKey + NameKeyLen - 1;
+ char* endValueKey = ValueKey + ValueKeyLen - 1;
+ while (endNameKey != NameKey) {
+ if (*endNameKey != ' ')
+ break;
+ *endNameKey-- = 0;
+ NameKeyLen--;
+ }
+ while (endValueKey != ValueKey) {
+ if (*endValueKey != ' ')
+ break;
+ *endValueKey-- = 0;
+ ValueKeyLen--;
+ }
+ //Allocating Buffers
+ p.Name = ( char * ) malloc( NameKeyLen + 1 );
+ p.Value = ( char * ) malloc( ValueKeyLen + 1 );
+ //Copying buffers
+ memcpy( p.Name, NameKey, NameKeyLen + 1 );
+ memcpy( p.Value, ValueKey, ValueKeyLen + 1 );
+ //Adding to Tag Pairs
+ pairs.push_back( p );
+ return false;
+ }
+
+ const char* GetKeyAsString(const char* Key, const char* Default) const
+ {
+ for (unsigned int i = 0; i < pairs.size(); i++) {
+ if (stricmp( Key, pairs[i].Name ) == 0) {
+ return pairs[i].Value;
+ }
+ }
+ return Default;
+ }
+
+ int GetKeyAsInt(const char* Key, const int Default) const
+ {
+ const char* ret = NULL;
+ for (unsigned int i = 0; i < pairs.size(); i++) {
+ if (stricmp( Key, pairs[i].Name ) == 0) {
+ ret = pairs[i].Value;
+ break;
+ }
+ }
+ if (!ret) {
+ return Default;
+ }
+ return atoi( ret );
+ }
+
+ float GetKeyAsFloat(const char* Key, const float Default) const
+ {
+ const char* ret = NULL;
+ for (unsigned int i = 0; i < pairs.size(); i++) {
+ if (stricmp( Key, pairs[i].Name ) == 0) {
+ ret = pairs[i].Value;
+ break;
+ }
+ }
+ if (!ret) {
+ return Default;
+ }
+ return atof( ret );
+ }
+
+ bool GetKeyAsBool(const char* Key, const bool Default) const
+ {
+ const char* ret = NULL;
+ for (unsigned int i = 0; i < pairs.size(); i++) {
+ if (stricmp( Key, pairs[i].Name ) == 0) {
+ ret = pairs[i].Value;
+ break;
+ }
+ }
+ if (!ret) {
+ return Default;
+ }
+ if (!stricmp( ret, "true") ) {
+ return true;
+ }
+ if (!stricmp( ret, "false") ) {
+ return false;
+ }
+ return (atoi( ret ) )!=0;
+ }
+};
+
+class INIImporter : public DataFileMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ std::vector< INITag*> tags;
+
+public:
+ INIImporter(void);
+ ~INIImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ int GetTagsCount() const
+ {
+ return ( int ) tags.size();
+ }
+ const char* GetTagNameByIndex(int index) const
+ {
+ return tags[index]->GetTagName();
+ }
+
+ int GetKeysCount(const char* Tag) const;
+ const char* GetKeyNameByIndex(const char* Tag, int index) const;
+ const char* GetKeyAsString(const char* Tag, const char* Key,
+ const char* Default) const;
+ int GetKeyAsInt(const char* Tag, const char* Key,
+ const int Default) const;
+ float GetKeyAsFloat(const char* Tag, const char* Key,
+ const float Default) const;
+ bool GetKeyAsBool(const char* Tag, const char* Key,
+ const bool Default) const;
+};
+
+#endif
diff --git a/gemrb/plugins/INIImporter/Makefile.am b/gemrb/plugins/INIImporter/Makefile.am
new file mode 100644
index 0000000..838d1ad
--- /dev/null
+++ b/gemrb/plugins/INIImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = INIImporter.la
+INIImporter_la_LDFLAGS = -module -avoid-version -shared
+INIImporter_la_SOURCES = INIImporter.cpp INIImporter.h
diff --git a/gemrb/plugins/ITMImporter/CMakeLists.txt b/gemrb/plugins/ITMImporter/CMakeLists.txt
new file mode 100644
index 0000000..e3fbda6
--- /dev/null
+++ b/gemrb/plugins/ITMImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (ITMImporter ITMImporter.cpp)
diff --git a/gemrb/plugins/ITMImporter/ITMImporter.cpp b/gemrb/plugins/ITMImporter/ITMImporter.cpp
new file mode 100644
index 0000000..742528a
--- /dev/null
+++ b/gemrb/plugins/ITMImporter/ITMImporter.cpp
@@ -0,0 +1,255 @@
+/* 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 "ITMImporter.h"
+
+#include "win32def.h"
+
+#include "EffectMgr.h"
+#include "Interface.h"
+
+ITMImporter::ITMImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+ITMImporter::~ITMImporter(void)
+{
+ if (autoFree) {
+ delete str;
+ }
+ str = NULL;
+}
+
+bool ITMImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (this->autoFree) {
+ delete str;
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "ITM V1 ", 8 ) == 0) {
+ version = 10;
+ } else if (strncmp( Signature, "ITM V1.1", 8 ) == 0) {
+ version = 11;
+ } else if (strncmp( Signature, "ITM V2.0", 8 ) == 0) {
+ version = 20;
+ } else {
+ printf( "[ITMImporter]: This file is not a valid ITM File\n" );
+ return false;
+ }
+
+ return true;
+}
+
+Item* ITMImporter::GetItem(Item *s)
+{
+ unsigned int i;
+ ieByte k1,k2,k3,k4;
+
+ if( !s) {
+ return NULL;
+ }
+ str->ReadDword( &s->ItemName );
+ str->ReadDword( &s->ItemNameIdentified );
+ str->ReadResRef( s->ReplacementItem );
+ str->ReadDword( &s->Flags );
+ str->ReadWord( &s->ItemType );
+ str->ReadDword( &s->UsabilityBitmask );
+ str->Read( s->AnimationType,2 ); //intentionally not reading word!
+ for (i=0;i<2;i++) {
+ if (s->AnimationType[i]==' ') {
+ s->AnimationType[i]=0;
+ }
+ }
+ str->Read( &s->MinLevel, 1 );
+ str->Read( &s->unknown1, 1 );
+ str->Read( &s->MinStrength,1 );
+ str->Read( &s->unknown2, 1 );
+ str->Read( &s->MinStrengthBonus, 1 );
+ str->Read( &k1,1 );
+ str->Read( &s->MinIntelligence, 1 );
+ str->Read( &k2,1 );
+ str->Read( &s->MinDexterity, 1 );
+ str->Read( &k3,1 );
+ str->Read( &s->MinWisdom, 1 );
+ str->Read( &k4,1 );
+ s->KitUsability=(k1<<24) | (k2<<16) | (k3<<8) | k4; //bg2/iwd2 specific
+ str->Read( &s->MinConstitution, 1 );
+ str->Read( &s->WeaProf, 1 ); //bg2 specific
+ str->Read( &s->MinCharisma, 1 );
+ str->Read( &s->unknown3, 1 );
+ str->ReadDword( &s->Price );
+ str->ReadWord( &s->StackAmount );
+ str->ReadResRef( s->ItemIcon );
+ str->ReadWord( &s->LoreToID );
+ str->ReadResRef( s->GroundIcon );
+ str->ReadDword( &s->Weight );
+ str->ReadDword( &s->ItemDesc );
+ str->ReadDword( &s->ItemDescIdentified );
+ str->ReadResRef( s->DescriptionIcon );
+ str->ReadDword( &s->Enchantment );
+ str->ReadDword( &s->ExtHeaderOffset );
+ str->ReadWord( &s->ExtHeaderCount );
+ str->ReadDword( &s->FeatureBlockOffset );
+ str->ReadWord( &s->EquippingFeatureOffset );
+ str->ReadWord( &s->EquippingFeatureCount );
+
+ s->Dialog[0] = 0;
+ s->DialogName = 0;
+ s->WieldColor = 0xffff;
+ memset( s->unknown, 0, 26 );
+
+ //skipping header data for iwd2
+ if (version == ITM_VER_IWD2) {
+ str->Read( s->unknown, 16 );
+ }
+ //pst data
+ if (version == ITM_VER_PST) {
+ str->ReadResRef( s->Dialog );
+ str->ReadDword( &s->DialogName );
+ ieWord WieldColor;
+ str->ReadWord( &WieldColor );
+ if (s->AnimationType[0]) {
+ s->WieldColor = WieldColor;
+ }
+ str->Read( s->unknown, 26 );
+ } else {
+ //all non pst
+ s->DialogName = core->GetItemDialStr(s->Name);
+ core->GetItemDialRes(s->Name, s->Dialog);
+ }
+ s->ItemExcl=core->GetItemExcl(s->Name);
+
+ s->ext_headers = core->GetITMExt( s->ExtHeaderCount );
+
+ for (i = 0; i < s->ExtHeaderCount; i++) {
+ str->Seek( s->ExtHeaderOffset + i * 56, GEM_STREAM_START );
+ GetExtHeader( s, s->ext_headers + i );
+ }
+
+ //48 is the size of the feature block
+ s->equipping_features = core->GetFeatures( s->EquippingFeatureCount);
+
+ str->Seek( s->FeatureBlockOffset + 48*s->EquippingFeatureOffset,
+ GEM_STREAM_START );
+ for (i = 0; i < s->EquippingFeatureCount; i++) {
+ GetFeature(s->equipping_features+i);
+ }
+
+
+ if (!core->IsAvailable( IE_BAM_CLASS_ID )) {
+ printf( "[ITMImporter]: No BAM Importer Available.\n" );
+ return NULL;
+ }
+ return s;
+}
+
+void ITMImporter::GetExtHeader(Item *s, ITMExtHeader* eh)
+{
+ ieByte tmpByte;
+ ieWord ProjectileType;
+
+ str->Read( &eh->AttackType,1 );
+ str->Read( &eh->IDReq,1 );
+ str->Read( &eh->Location,1 );
+ str->Read( &eh->unknown1,1 );
+ str->ReadResRef( eh->UseIcon );
+ str->Read( &eh->Target,1 );
+ str->Read( &tmpByte,1 );
+ if (!tmpByte) {
+ tmpByte = 1;
+ }
+ eh->TargetNumber = tmpByte;
+ str->ReadWord( &eh->Range );
+ str->ReadWord( &ProjectileType );
+ str->ReadWord( &eh->Speed );
+ str->ReadWord( &eh->THAC0Bonus );
+ str->ReadWord( &eh->DiceSides );
+ str->ReadWord( &eh->DiceThrown );
+ //if your compiler doesn't like this, then we need a ReadWordSigned
+ str->ReadWord( (ieWord *) &eh->DamageBonus );
+ str->ReadWord( &eh->DamageType );
+ str->ReadWord( &eh->FeatureCount );
+ str->ReadWord( &eh->FeatureOffset );
+ str->ReadWord( &eh->Charges );
+ str->ReadWord( &eh->ChargeDepletion );
+ str->ReadDword( &eh->RechargeFlags );
+ str->ReadWord( &eh->ProjectileAnimation );
+ //for some odd reasons 0 and 1 are the same
+ if (eh->ProjectileAnimation) {
+ eh->ProjectileAnimation--;
+ }
+
+ unsigned int i; //msvc6.0 can't cope with index variable scope
+
+ for (i = 0; i < 3; i++) {
+ str->ReadWord( &eh->MeleeAnimation[i] );
+ }
+ ieWord tmp;
+
+ i = 0;
+ str->ReadWord( &tmp ); //arrow
+ if (tmp) i|=PROJ_ARROW;
+ str->ReadWord( &tmp ); //xbow
+ if (tmp) i|=PROJ_BOLT;
+ str->ReadWord( &tmp ); //bullet
+ if (tmp) i|=PROJ_BULLET;
+ //this hack is required for Nordom's crossbow in PST
+ if (!i && (eh->AttackType==ITEM_AT_BOW) ) {
+ i|=PROJ_BOLT;
+ }
+
+ //this hack is required for the practicing arrows in BG1
+ if (!i && (eh->AttackType==ITEM_AT_PROJECTILE) ) {
+ //0->0
+ //1->1
+ //2->2
+ //3->4
+ i|=(1<<ProjectileType)>>1;
+ }
+ eh->ProjectileQualifier=i;
+
+ //48 is the size of the feature block
+ eh->features = core->GetFeatures(eh->FeatureCount);
+ str->Seek( s->FeatureBlockOffset + 48*eh->FeatureOffset, GEM_STREAM_START );
+ for (i = 0; i < eh->FeatureCount; i++) {
+ GetFeature(eh->features+i);
+ }
+}
+
+void ITMImporter::GetFeature(Effect *fx)
+{
+ PluginHolder<EffectMgr> eM(IE_EFF_CLASS_ID);
+ eM->Open( str, false );
+ eM->GetEffect( fx );
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xD913A54, "ITM File Importer")
+PLUGIN_CLASS(IE_ITM_CLASS_ID, ITMImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/ITMImporter/ITMImporter.h b/gemrb/plugins/ITMImporter/ITMImporter.h
new file mode 100644
index 0000000..c188a5c
--- /dev/null
+++ b/gemrb/plugins/ITMImporter/ITMImporter.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 ITMIMPORTER_H
+#define ITMIMPORTER_H
+
+#include "ItemMgr.h"
+
+#include "ie_types.h"
+
+#include "Item.h"
+
+#define ITM_VER_BG 10
+#define ITM_VER_PST 11
+#define ITM_VER_IWD2 20
+
+class ITMImporter : public ItemMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ int version;
+
+public:
+ ITMImporter(void);
+ ~ITMImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ Item* GetItem(Item *s);
+private:
+ void GetExtHeader(Item *s, ITMExtHeader* eh);
+ void GetFeature(Effect *f);
+};
+
+
+#endif
diff --git a/gemrb/plugins/ITMImporter/Makefile.am b/gemrb/plugins/ITMImporter/Makefile.am
new file mode 100644
index 0000000..d6db57f
--- /dev/null
+++ b/gemrb/plugins/ITMImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = ITMImporter.la
+ITMImporter_la_LDFLAGS = -module -avoid-version -shared
+ITMImporter_la_SOURCES = ITMImporter.cpp ITMImporter.h
diff --git a/gemrb/plugins/IWDOpcodes/CMakeLists.txt b/gemrb/plugins/IWDOpcodes/CMakeLists.txt
new file mode 100644
index 0000000..f544dd4
--- /dev/null
+++ b/gemrb/plugins/IWDOpcodes/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (IWDOpcodes IWDOpcodes.cpp)
diff --git a/gemrb/plugins/IWDOpcodes/IWDOpcodes.cpp b/gemrb/plugins/IWDOpcodes/IWDOpcodes.cpp
new file mode 100644
index 0000000..e55f636
--- /dev/null
+++ b/gemrb/plugins/IWDOpcodes/IWDOpcodes.cpp
@@ -0,0 +1,3392 @@
+/* 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 "ie_feats.h" //cannot avoid declaring these
+#include "opcode_params.h"
+#include "overlays.h"
+#include "win32def.h"
+
+#include "Audio.h" //needs for a playsound call
+#include "DisplayMessage.h"
+#include "EffectQueue.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "ProjectileServer.h" //needs for alter_animation
+#include "Spell.h"
+#include "damages.h"
+#include "Scriptable/Actor.h"
+#include "GameScript/GSUtils.h" //needs for displaystringcore
+
+static const ieResRef SevenEyes[7]={"spin126","spin127","spin128","spin129","spin130","spin131","spin132"};
+
+static bool enhanced_effects = false;
+//a scripting object for enemy (used for enemy in line of sight check)
+static Trigger *Enemy = NULL;
+
+#define PI_PROTFROMEVIL 9
+#define PI_FREEACTION 19
+#define PI_BARKSKIN 20
+#define PI_BANE 35
+#define PI_PANIC 36
+#define PI_NAUSEA 43
+#define PI_HOPELESSNESS 44
+#define PI_STONESKIN 46
+#define PI_RIGHTEOUS 67
+#define PI_ELEMENTS 76
+#define PI_FAITHARMOR 84
+#define PI_BLEEDING 85
+#define PI_HOLYPOWER 86
+#define PI_DEATHWARD 87
+#define PI_UNCONSCIOUS 88
+#define PI_IRONSKIN 89
+#define PI_ENFEEBLEMENT 90
+#define PI_ELEMPROT 93
+#define PI_MINORGLOBE 96
+#define PI_MAJORGLOBE 97
+#define PI_SHROUD 98
+#define PI_ANTIMAGIC 99
+#define PI_RESILIENT 100
+#define PI_MINDFLAYER 101
+#define PI_CLOAKOFFEAR 102
+#define PI_ENTROPY 103
+#define PI_INSECT 104
+#define PI_STORMSHELL 105
+//#define PI_LOWERRESIST 106 //this is different in iwd2 and bg2
+
+#define PI_AEGIS 119
+#define PI_EXECUTIONER 120
+#define PI_FIRESHIELD 121
+#define PI_ICESHIELD 122
+
+#define PI_TORTOISE 125
+
+#define PI_BLINK 130
+#define PI_EMPTYBODY 145
+
+static int fx_ac_vs_damage_type_modifier_iwd2 (Scriptable* Owner, Actor* target, Effect* fx);//0
+static int fx_damage_bonus_modifier (Scriptable* Owner, Actor* target, Effect* fx);//49
+static int fx_draw_upon_holy_might (Scriptable* Owner, Actor* target, Effect* fx);//84 (iwd2)
+static int fx_ironskins (Scriptable* Owner, Actor* target, Effect* fx);//da (iwd2)
+static int fx_fade_rgb (Scriptable* Owner, Actor* target, Effect* fx);//e8
+static int fx_iwd_visual_spell_hit (Scriptable* Owner, Actor* target, Effect* fx);//e9
+static int fx_cold_damage (Scriptable* Owner, Actor* target, Effect* fx);//ea
+//int fx_iwd_casting_glow (Scriptable* Owner, Actor* target, Effect* fx);//eb
+static int fx_chill_touch (Scriptable* Owner, Actor* target, Effect* fx);//ec (how)
+static int fx_chill_touch_panic (Scriptable* Owner, Actor* target, Effect* fx);//ec (iwd2)
+static int fx_crushing_damage (Scriptable* Owner, Actor* target, Effect* fx);//ed
+static int fx_save_bonus (Scriptable* Owner, Actor* target, Effect* fx); //ee
+static int fx_slow_poison (Scriptable* Owner, Actor* target, Effect* fx); //ef
+static int fx_iwd_monster_summoning (Scriptable* Owner, Actor* target, Effect* fx); //f0
+static int fx_vampiric_touch (Scriptable* Owner, Actor* target, Effect* fx); //f1
+// int fx_overlay f2 (same as PST)
+static int fx_animate_dead (Scriptable* Owner, Actor* target, Effect* fx);//f3
+static int fx_prayer (Scriptable* Owner, Actor* target, Effect* fx); //f4
+static int fx_curse (Scriptable* Owner, Actor* target, Effect* fx); //f5
+static int fx_summon_monster2 (Scriptable* Owner, Actor* target, Effect* fx); //f6
+static int fx_burning_blood (Scriptable* Owner, Actor* target, Effect* fx); //f7 iwd
+static int fx_burning_blood2 (Scriptable* Owner, Actor* target, Effect* fx); //f7 how, iwd2
+static int fx_summon_shadow_monster (Scriptable* Owner, Actor* target, Effect* fx); //f8
+static int fx_recitation (Scriptable* Owner, Actor* target, Effect* fx); //f9
+static int fx_recitation_bad (Scriptable* Owner, Actor* target, Effect* fx); //fa
+static int fx_lich_touch (Scriptable* Owner, Actor* target, Effect* fx); //fb
+static int fx_blinding_orb (Scriptable* Owner, Actor* target, Effect* fx); //fc
+// ac vs damage //fd
+static int fx_remove_effects (Scriptable* Owner, Actor* target, Effect* fx); //fe
+static int fx_salamander_aura (Scriptable* Owner, Actor* target, Effect* fx); //ff
+static int fx_umberhulk_gaze (Scriptable* Owner, Actor* target, Effect* fx); //100
+static int fx_zombielord_aura (Scriptable* Owner, Actor* target, Effect* fx); //101, duff in iwd2
+static int fx_resist_spell (Scriptable* Owner, Actor* target, Effect* fx); //102
+static int fx_summon_creature2 (Scriptable* Owner, Actor* target, Effect* fx); //103
+static int fx_avatar_removal (Scriptable* Owner, Actor* target, Effect* fx); //104
+//int fx_immunity_effect2 (Scriptable* Owner, Actor* target, Effect* fx); //105
+static int fx_summon_pomab (Scriptable* Owner, Actor* target, Effect* fx); //106
+static int fx_control_undead (Scriptable* Owner, Actor* target, Effect* fx); //107
+static int fx_static_charge (Scriptable* Owner, Actor* target, Effect* fx); //108
+static int fx_cloak_of_fear (Scriptable* Owner, Actor* target, Effect* fx); //109
+//int fx_movement_modifier (Scriptable* Owner, Actor* target, Effect* fx); //10a
+//int fx_remove_confusion (Scriptable* Owner, Actor* target, Effect* fx);//10b
+static int fx_eye_of_the_mind (Scriptable* Owner, Actor* target, Effect* fx);//10c
+static int fx_eye_of_the_sword (Scriptable* Owner, Actor* target, Effect* fx);//10d
+static int fx_eye_of_the_mage (Scriptable* Owner, Actor* target, Effect* fx);//10e
+static int fx_eye_of_venom (Scriptable* Owner, Actor* target, Effect* fx);//10f
+static int fx_eye_of_the_spirit (Scriptable* Owner, Actor* target, Effect* fx);//110
+static int fx_eye_of_fortitude (Scriptable* Owner, Actor* target, Effect* fx);//111
+static int fx_eye_of_stone (Scriptable* Owner, Actor* target, Effect* fx);//112
+static int fx_remove_seven_eyes (Scriptable* Owner, Actor* target, Effect* fx);//113
+static int fx_remove_effect (Scriptable* Owner, Actor* target, Effect* fx);//114
+static int fx_soul_eater (Scriptable* Owner, Actor* target, Effect* fx);//115
+static int fx_shroud_of_flame (Scriptable* Owner, Actor* target, Effect* fx);//116
+static int fx_shroud_of_flame2 (Scriptable* Owner, Actor* target, Effect* fx);//116
+static int fx_animal_rage (Scriptable* Owner, Actor* target, Effect* fx);//117
+static int fx_turn_undead (Scriptable* Owner, Actor* target, Effect* fx);//118
+static int fx_turn_undead2 (Scriptable* Owner, Actor* target, Effect* fx);//118
+static int fx_vitriolic_sphere (Scriptable* Owner, Actor* target, Effect* fx);//119
+static int fx_suppress_hp (Scriptable* Owner, Actor* target, Effect* fx);//11a
+static int fx_floattext (Scriptable* Owner, Actor* target, Effect* fx);//11b
+static int fx_mace_of_disruption (Scriptable* Owner, Actor* target, Effect* fx);//11c
+//0x11d Sleep2 ??? power word sleep?
+//0x11e Reveal:Tracks (same as bg2)
+//0x11f Protection:Backstab (same as bg2)
+static int fx_set_state (Scriptable* Owner, Actor* target, Effect* fx); //120
+static int fx_cutscene (Scriptable* Owner, Actor* target, Effect* fx);//121
+static int fx_resist_spell (Scriptable* Owner, Actor* target, Effect *fx);//ce
+static int fx_resist_spell_and_message (Scriptable* Owner, Actor* target, Effect *fx);//122
+static int fx_rod_of_smithing (Scriptable* Owner, Actor* target, Effect* fx); //123
+//0x124 MagicalRest (same as bg2)
+static int fx_beholder_dispel_magic (Scriptable* Owner, Actor* target, Effect* fx); //125
+static int fx_harpy_wail (Scriptable* Owner, Actor* target, Effect* fx); //126
+static int fx_jackalwere_gaze (Scriptable* Owner, Actor* target, Effect* fx); //127
+//0x128 ModifyGlobalVariable (same as bg2)
+//0x129 HideInShadows (same as bg2)
+static int fx_use_magic_device_modifier (Scriptable* Owner, Actor* target, Effect* fx);//12a
+
+//iwd related, gemrb specific effects (IE. unhardcoded hacks)
+static int fx_alter_animation (Scriptable* Owner, Actor *target, Effect* fx); //399
+
+//iwd2 specific effects
+static int fx_hopelessness (Scriptable* Owner, Actor* target, Effect* fx);//400
+static int fx_protection_from_evil (Scriptable* Owner, Actor* target, Effect* fx);//401
+static int fx_add_effects_list (Scriptable* Owner, Actor* target, Effect* fx);//402
+static int fx_armor_of_faith (Scriptable* Owner, Actor* target, Effect* fx);//403
+static int fx_nausea (Scriptable* Owner, Actor* target, Effect* fx); //404
+static int fx_enfeeblement (Scriptable* Owner, Actor* target, Effect* fx); //405
+static int fx_fireshield (Scriptable* Owner, Actor* target, Effect* fx); //406
+static int fx_death_ward (Scriptable* Owner, Actor* target, Effect* fx); //407
+static int fx_holy_power (Scriptable* Owner, Actor* target, Effect* fx); //408
+static int fx_righteous_wrath (Scriptable* Owner, Actor* target, Effect* fx); //409
+static int fx_summon_ally (Scriptable* Owner, Actor* target, Effect* fx); //410
+static int fx_summon_enemy (Scriptable* Owner, Actor* target, Effect* fx); //411
+static int fx_control (Scriptable* Owner, Actor* target, Effect* fx); //412
+static int fx_visual_effect_iwd2 (Scriptable* Owner, Actor* target, Effect* fx); //413
+static int fx_resilient_sphere (Scriptable* Owner, Actor* target, Effect* fx); //414
+static int fx_barkskin (Scriptable* Owner, Actor* target, Effect* fx); //415
+static int fx_bleeding_wounds (Scriptable* Owner, Actor* target, Effect* fx); //416
+static int fx_area_effect (Scriptable* Owner, Actor* target, Effect* fx); //417
+static int fx_free_action_iwd2 (Scriptable* Owner, Actor* target, Effect* fx); //418
+static int fx_unconsciousness (Scriptable* Owner, Actor* target, Effect* fx); //419
+//420 Death2 (same as 0xd)
+static int fx_entropy_shield (Scriptable* Owner, Actor* target, Effect* fx); //421
+static int fx_storm_shell (Scriptable* Owner, Actor* target, Effect* fx); //422
+static int fx_protection_from_elements (Scriptable* Owner, Actor* target, Effect* fx); //423
+//424 HoldUndead (same as 0x6d)
+//425 ControlUndead
+static int fx_aegis (Scriptable* Owner, Actor* target, Effect* fx); //426
+static int fx_executioner_eyes (Scriptable* Owner, Actor* target, Effect* fx); //427
+//428 DeathMagic (same as 0xd)
+//429
+static int fx_projectile_use_effect_list (Scriptable* Owner, Actor* target, Effect* fx); //430
+static int fx_energy_drain (Scriptable* Owner, Actor* target, Effect* fx); //431
+static int fx_tortoise_shell (Scriptable* Owner, Actor* target, Effect* fx); //432
+static int fx_blink (Scriptable* Owner, Actor* target, Effect* fx); //433
+static int fx_persistent_use_effect_list (Scriptable* Owner, Actor* target, Effect* fx); //434
+static int fx_day_blindness (Scriptable* Owner, Actor* target, Effect* fx); //435
+static int fx_damage_reduction (Scriptable* Owner, Actor* target, Effect* fx); //436
+static int fx_disguise (Scriptable* Owner, Actor* target, Effect* fx); //437
+static int fx_heroic_inspiration (Scriptable* Owner, Actor* target, Effect* fx); //438
+//static int fx_prevent_ai_slowdown (Scriptable* Owner, Actor* target, Effect* fx); //439
+static int fx_barbarian_rage (Scriptable* Owner, Actor* target, Effect* fx); //440
+//441 MovementRateModifier4
+//442 unknown
+static int fx_missile_damage_reduction (Scriptable* Owner, Actor* target, Effect* fx); //443
+static int fx_tenser_transformation (Scriptable* Owner, Actor* target, Effect* fx); //444
+//static int fx_445 (Scriptable* Owner, Actor* target, Effect* fx); //445 unused
+static int fx_smite_evil (Scriptable* Owner, Actor* target, Effect* fx); //446
+static int fx_restoration (Scriptable* Owner, Actor* target, Effect* fx); //447
+static int fx_alicorn_lance (Scriptable* Owner, Actor* target, Effect* fx); //448
+static int fx_call_lightning (Scriptable* Owner, Actor* target, Effect* fx); //449
+static int fx_globe_invulnerability (Scriptable* Owner, Actor* target, Effect* fx); //450
+static int fx_lower_resistance (Scriptable* Owner, Actor* target, Effect* fx); //451
+static int fx_bane (Scriptable* Owner, Actor* target, Effect* fx); //452
+static int fx_power_attack (Scriptable* Owner, Actor* target, Effect* fx); //453
+static int fx_expertise (Scriptable* Owner, Actor* target, Effect* fx); //454
+static int fx_arterial_strike (Scriptable* Owner, Actor* target, Effect* fx); //455
+static int fx_hamstring (Scriptable* Owner, Actor* target, Effect* fx); //456
+static int fx_rapid_shot (Scriptable* Owner, Actor* target, Effect* fx); //457
+
+//No need to make these ordered, they will be ordered by EffectQueue
+static EffectDesc effectnames[] = {
+ { "ACVsDamageTypeModifierIWD2", fx_ac_vs_damage_type_modifier_iwd2, 0, -1 }, //0
+ { "DamageBonusModifier", fx_damage_bonus_modifier, 0, -1 }, //49
+ { "DrawUponHolyMight", fx_draw_upon_holy_might, 0, -1 },//84 (iwd2)
+ { "RetreatFrom", fx_turn_undead, 0, -1 },//6e
+ { "IronSkins", fx_ironskins, 0, -1 }, //da (iwd2)
+ { "Color:FadeRGB", fx_fade_rgb, 0, -1 }, //e8
+ { "IWDVisualSpellHit", fx_iwd_visual_spell_hit, 0, -1 }, //e9
+ { "ColdDamage", fx_cold_damage, EFFECT_DICED, -1 }, //ea
+ { "ChillTouch", fx_chill_touch, 0, -1 }, //ec (how)
+ { "ChillTouchPanic", fx_chill_touch_panic, 0, -1 }, //ec (iwd2)
+ { "CrushingDamage", fx_crushing_damage, EFFECT_DICED, -1 }, //ed
+ { "SaveBonus", fx_save_bonus, 0, -1 }, //ee
+ { "SlowPoison", fx_slow_poison, 0, -1 }, //ef
+ { "IWDMonsterSummoning", fx_iwd_monster_summoning, EFFECT_NO_ACTOR, -1 }, //f0
+ { "VampiricTouch", fx_vampiric_touch, EFFECT_DICED, -1 }, //f1
+ { "AnimateDead", fx_animate_dead, 0, -1 }, //f3
+ { "Prayer2", fx_prayer, 0, -1 }, //f4
+ { "Curse2", fx_curse, 0, -1 }, //f5
+ { "SummonMonster2", fx_summon_monster2, EFFECT_NO_ACTOR, -1 }, //f6
+ { "BurningBlood", fx_burning_blood, EFFECT_DICED, -1 }, //f7
+ { "BurningBlood2", fx_burning_blood2, EFFECT_NO_LEVEL_CHECK, -1 }, //f7
+ { "SummonShadowMonster", fx_summon_shadow_monster, EFFECT_NO_ACTOR, -1 }, //f8
+ { "Recitation", fx_recitation, 0, -1 }, //f9
+ { "RecitationBad", fx_recitation_bad, 0, -1 },//fa
+ { "LichTouch", fx_lich_touch, EFFECT_NO_LEVEL_CHECK, -1 },//fb
+ { "BlindingOrb", fx_blinding_orb, 0, -1 }, //fc
+ { "RemoveEffects", fx_remove_effects, 0, -1 }, //fe
+ { "SalamanderAura", fx_salamander_aura, 0, -1 }, //ff
+ { "UmberHulkGaze", fx_umberhulk_gaze, 0, -1 }, //100
+ { "ZombieLordAura", fx_zombielord_aura, 0, -1 },//101, duff in iwd2
+ { "SummonCreature2", fx_summon_creature2, 0, -1 }, //103
+ { "AvatarRemoval", fx_avatar_removal, 0, -1 }, //104
+ { "SummonPomab", fx_summon_pomab, 0, -1 }, //106
+ { "ControlUndead", fx_control_undead, 0, -1 }, //107
+ { "StaticCharge", fx_static_charge, EFFECT_NO_LEVEL_CHECK, -1 }, //108
+ { "CloakOfFear", fx_cloak_of_fear, 0, -1 }, //109 how/iwd2
+ { "EyeOfTheMind", fx_eye_of_the_mind, 0, -1 }, //10c
+ { "EyeOfTheSword", fx_eye_of_the_sword, 0, -1 }, //10d
+ { "EyeOfTheMage", fx_eye_of_the_mage, 0, -1 }, //10e
+ { "EyeOfVenom", fx_eye_of_venom, 0, -1 }, //10f
+ { "EyeOfTheSpirit", fx_eye_of_the_spirit, 0, -1 }, //110
+ { "EyeOfFortitude", fx_eye_of_fortitude, 0, -1 }, //111
+ { "EyeOfStone", fx_eye_of_stone, 0, -1 }, //112
+ { "RemoveSevenEyes", fx_remove_seven_eyes, 0, -1 }, //113
+ { "RemoveEffect", fx_remove_effect, 0, -1 }, //114
+ { "SoulEater", fx_soul_eater, EFFECT_NO_LEVEL_CHECK, -1 }, //115
+ { "ShroudOfFlame", fx_shroud_of_flame, 0, -1 },//116
+ { "ShroudOfFlame2", fx_shroud_of_flame2, 0, -1 },//116
+ { "AnimalRage", fx_animal_rage, 0, -1 }, //117 - berserk?
+ { "TurnUndead", fx_turn_undead, 0, -1 }, //118 how
+ { "TurnUndead2", fx_turn_undead2, 0, -1 }, //118 iwd2
+ { "VitriolicSphere", fx_vitriolic_sphere, EFFECT_DICED, -1 }, //119
+ { "SuppressHP", fx_suppress_hp, 0, -1 }, //11a -- some stat???
+ { "FloatText", fx_floattext, 0, -1 }, //11b
+ { "MaceOfDisruption", fx_mace_of_disruption, 0, -1 }, //11c
+ { "State:Set", fx_set_state, 0, -1 }, //120
+ { "CutScene", fx_cutscene, EFFECT_NO_ACTOR, -1 }, //121
+ { "Protection:Spell2", fx_resist_spell, 0, -1 }, //ce
+ { "Protection:Spell3", fx_resist_spell_and_message, 0, -1 }, //122
+ { "RodOfSmithing", fx_rod_of_smithing, 0, -1 }, //123
+ { "BeholderDispelMagic", fx_beholder_dispel_magic, 0, -1 },//125
+ { "HarpyWail", fx_harpy_wail, 0, -1 }, //126
+ { "JackalWereGaze", fx_jackalwere_gaze, 0, -1 }, //127
+ { "UseMagicDeviceModifier", fx_use_magic_device_modifier, 0, -1 }, //12a
+ //unhardcoded hacks for IWD
+ { "AlterAnimation", fx_alter_animation, EFFECT_NO_ACTOR, -1 }, //399
+ //iwd2 effects
+ { "Hopelessness", fx_hopelessness, 0, -1 }, //400
+ { "ProtectionFromEvil", fx_protection_from_evil, 0, -1 }, //401
+ { "AddEffectsList", fx_add_effects_list, 0, -1 }, //402
+ { "ArmorOfFaith", fx_armor_of_faith, 0, -1 }, //403
+ { "Nausea", fx_nausea, 0, -1 }, //404
+ { "Enfeeblement", fx_enfeeblement, 0, -1 }, //405
+ { "FireShield", fx_fireshield, 0, -1 }, //406
+ { "DeathWard", fx_death_ward, 0, -1 }, //407
+ { "HolyPower", fx_holy_power, 0, -1 }, //408
+ { "RighteousWrath", fx_righteous_wrath, 0, -1 }, //409
+ { "SummonAlly", fx_summon_ally, EFFECT_NO_ACTOR, -1 }, //410
+ { "SummonEnemy", fx_summon_enemy, EFFECT_NO_ACTOR, -1 }, //411
+ { "Control2", fx_control, 0, -1 }, //412
+ { "VisualEffectIWD2", fx_visual_effect_iwd2, 0, -1 }, //413
+ { "ResilientSphere", fx_resilient_sphere, 0, -1 }, //414
+ { "BarkSkin", fx_barkskin, 0, -1 }, //415
+ { "BleedingWounds", fx_bleeding_wounds, 0, -1 },//416
+ { "AreaEffect", fx_area_effect, EFFECT_NO_ACTOR, -1 }, //417
+ { "FreeAction2", fx_free_action_iwd2, 0, -1 }, //418
+ { "Unconsciousness", fx_unconsciousness, 0, -1 }, //419
+ { "EntropyShield", fx_entropy_shield, 0, -1 }, //421
+ { "StormShell", fx_storm_shell, 0, -1 }, //422
+ { "ProtectionFromElements", fx_protection_from_elements, 0, -1 }, //423
+ { "ControlUndead2", fx_control_undead, 0, -1 }, //425
+ { "Aegis", fx_aegis, 0, -1 }, //426
+ { "ExecutionerEyes", fx_executioner_eyes, 0, -1 }, //427
+ { "ProjectileUseEffectList", fx_projectile_use_effect_list, 0, -1 }, //430
+ { "EnergyDrain", fx_energy_drain, 0, -1 }, //431
+ { "TortoiseShell", fx_tortoise_shell, 0, -1 }, //432
+ { "Blink", fx_blink, 0, -1 },//433
+ { "PersistentUseEffectList", fx_persistent_use_effect_list, 0, -1 }, //434
+ { "DayBlindness", fx_day_blindness, 0, -1 }, //435
+ { "DamageReduction", fx_damage_reduction, 0, -1 }, //436
+ { "Disguise", fx_disguise, 0, -1 }, //437
+ { "HeroicInspiration", fx_heroic_inspiration, 0, -1 },//438
+ //{ "PreventAISlowDown", fx_prevent_ai_slowdown, 0, -1 }, //439 same as bg2
+ { "BarbarianRage", fx_barbarian_rage, 0, -1 }, //440
+ { "MissileDamageReduction", fx_missile_damage_reduction, 0, -1 }, //443
+ { "TensersTransformation", fx_tenser_transformation, 0, -1 }, //444
+ { "SmiteEvil", fx_smite_evil, 0, -1 }, //446
+ { "Restoration", fx_restoration, 0, -1 }, //447
+ { "AlicornLance", fx_alicorn_lance, 0, -1 }, //448
+ { "CallLightning", fx_call_lightning, 0, -1 }, //449
+ { "GlobeInvulnerability", fx_globe_invulnerability, 0, -1 }, //450
+ { "LowerResistance", fx_lower_resistance, 0, -1 }, //451
+ { "Bane", fx_bane, 0, -1 }, //452
+ { "PowerAttack", fx_power_attack, 0, -1 }, //453
+ { "Expertise", fx_expertise, 0, -1 }, //454
+ { "ArterialStrike", fx_arterial_strike, 0, -1 }, //455
+ { "HamString", fx_hamstring, 0, -1 }, //456
+ { "RapidShot", fx_rapid_shot, 0, -1 }, //457
+ { NULL, NULL, 0, 0 },
+};
+
+struct IWDIDSEntry {
+ ieDword value;
+ ieWord stat;
+ ieWord relation;
+};
+
+static int spellrescnt = -1;
+static IWDIDSEntry *spellres = NULL;
+static Variables tables;
+
+static void Cleanup()
+{
+ if (Enemy) {
+ delete Enemy;
+ }
+ Enemy=NULL;
+
+ if (spellres) {
+ free (spellres);
+ }
+}
+
+void RegisterIWDOpcodes()
+{
+ core->RegisterOpcodes( sizeof( effectnames ) / sizeof( EffectDesc ) - 1, effectnames );
+ enhanced_effects=!!core->HasFeature(GF_ENHANCED_EFFECTS);
+ //create enemy trigger object for enemy in line of sight check
+ if (!Enemy) {
+ Enemy = new Trigger;
+ Object *o = new Object;
+ Enemy->objectParameter = o;
+ o->objectFields[0]=EA_ENEMY;
+ }
+}
+
+//iwd got a weird targeting system
+//the opcode parameters are:
+//param1 - optional value used only rarely
+//param2 - a specific condition mostly based on target's stats
+//this is partly superior, partly inferior to the bioware
+//ids targeting.
+//superior because it can handle other stats and conditions
+//inferior because it is not so readily moddable
+//The hardcoded conditions are simulated via the IWDIDSEntry
+//structure.
+//stat is usually a stat, but for special conditions it is a
+//function code (>=0x100).
+//If value is -1, then GemRB will use Param1, otherwise it is
+//compared to the target's stat using the relation function.
+//The relation function is exactly the same as the extended
+//diffmode for gemrb. (Thus scripts can use the very same relation
+//functions).
+
+static void ReadSpellProtTable(const ieResRef tablename)
+{
+ if (spellres) {
+ free(spellres);
+ }
+ spellres = NULL;
+ spellrescnt = 0;
+ AutoTable tab(tablename);
+ if (!tab) {
+ return;
+ }
+ spellrescnt=tab->GetRowCount();
+ spellres = (IWDIDSEntry *) malloc(sizeof(IWDIDSEntry) * spellrescnt);
+ if (!spellres) {
+ return;
+ }
+ for( int i=0;i<spellrescnt;i++) {
+ ieDword tmp = core->TranslateStat(tab->QueryField(i,0) );
+ spellres[i].stat = (ieWord) tmp;
+
+ spellres[i].value = (ieDword) strtol(tab->QueryField(i,1),NULL,0 );
+ spellres[i].relation = (ieWord) strtol(tab->QueryField(i,2),NULL,0 );
+ }
+}
+
+//unusual types which need hacking (fake stats)
+#define STI_SOURCE_TARGET 0x100
+#define STI_SOURCE_NOT_TARGET 0x101
+#define STI_CIRCLESIZE 0x102
+#define STI_TWO_ROWS 0x103
+#define STI_NOT_TWO_ROWS 0x104
+#define STI_MORAL_ALIGNMENT 0x105
+#define STI_AREATYPE 0x106
+#define STI_DAYTIME 0x107
+#define STI_EA 0x108
+#define STI_EVASION 0x109
+#define STI_INVALID 0xffff
+
+//returns true if iwd ids targeting resists the spell
+//FIXME: the constant return values do not match the rest
+static int check_iwd_targeting(Scriptable* Owner, Actor* target, ieDword value, ieDword type)
+{
+ if (spellrescnt==-1) {
+ ReadSpellProtTable("splprot");
+ }
+ if (type>=(ieDword) spellrescnt) {
+ return 0; //not resisted
+ }
+
+ ieDword idx = spellres[type].stat;
+ ieDword val = spellres[type].value;
+ //if IDS value is 'anything' then the supplied value is in Parameter1
+ if (val==0xffffffff) {
+ val = value;
+ }
+ switch (idx) {
+ case STI_INVALID:
+ return 0;
+ case STI_EA:
+ return DiffCore(EARelation(Owner, target), val, spellres[type].relation);
+ case STI_DAYTIME:
+ return (core->GetGame()->GameTime%7200/3600) != val;
+ case STI_AREATYPE:
+ return DiffCore((ieDword) target->GetCurrentArea()->AreaType, val, spellres[type].relation);
+ case STI_MORAL_ALIGNMENT:
+ if(Owner && Owner->Type==ST_ACTOR) {
+ return DiffCore( ((Actor *) Owner)->GetStat(IE_ALIGNMENT)&AL_GE_MASK,STAT_GET(IE_ALIGNMENT)&AL_GE_MASK, spellres[type].relation);
+ } else {
+ return DiffCore(AL_TRUE_NEUTRAL,STAT_GET(IE_ALIGNMENT)&AL_GE_MASK, spellres[type].relation);
+ }
+ case STI_TWO_ROWS:
+ //used in checks where any of two matches are ok (golem or undead etc)
+ if (check_iwd_targeting(Owner, target, value, idx)) return 1;
+ if (check_iwd_targeting(Owner, target, value, val)) return 1;
+ return 0;
+ case STI_NOT_TWO_ROWS:
+ //this should be the opposite as above
+ if (check_iwd_targeting(Owner, target, value, idx)) return 0;
+ if (check_iwd_targeting(Owner, target, value, val)) return 0;
+ return 1;
+ case STI_SOURCE_TARGET:
+ if (Owner==target) {
+ return 0;
+ }
+ return 1;
+ case STI_SOURCE_NOT_TARGET:
+ if (Owner!=target) {
+ return 0;
+ }
+ return 1;
+ case STI_CIRCLESIZE:
+ return DiffCore((ieDword) target->GetAnims()->GetCircleSize(), val, spellres[type].relation);
+ case STI_EVASION:
+ if (target->GetThiefLevel() < 7 ) {
+ return 0;
+ }
+ val = target->GetSavingThrow(1,0); //breath
+ if (val) {
+ return 1;
+ }
+ return 0;
+ default:
+ return DiffCore(STAT_GET(idx), val, spellres[type].relation);
+ }
+}
+
+//iwd got a hardcoded 'fireshield' system
+//this effect applies damage on ALL nearby actors, except the center
+static EffectRef fx_damage_opcode_ref = { "Damage", -1 };
+
+static void ApplyDamageNearby(Scriptable* Owner, Actor* target, Effect *fx, ieDword damagetype)
+{
+ Effect *newfx = EffectQueue::CreateEffect(fx_damage_opcode_ref, fx->Parameter1, damagetype,FX_DURATION_INSTANT_PERMANENT);
+ newfx->Power = fx->Power;
+ newfx->DiceThrown = fx->DiceThrown;
+ newfx->DiceSides = fx->DiceSides;
+ memcpy(newfx->Resource, fx->Resource,sizeof(newfx->Resource) );
+ //applyeffectcopy on everyone near us
+ Map *area = target->GetCurrentArea();
+ int i = area->GetActorCount(true);
+ while(i--) {
+ Actor *victim = area->GetActor(i,true);
+ if (target==victim) continue;
+ if (PersonalDistance(target, victim)<20) {
+ //this function deletes newfx (not anymore)
+ core->ApplyEffect(newfx, victim, Owner);
+ }
+ }
+ //finally remove the master copy
+ delete newfx;
+}
+
+//this function implements AC bonus handling
+//ReverseToHit is the 2nd ed way of AC (lower is better)
+static inline void HandleBonus(Actor *target, int stat, int mod, int mode)
+{
+ if (mode==FX_DURATION_INSTANT_PERMANENT) {
+ if (target->IsReverseToHit()) {
+ BASE_SUB( stat, mod );
+ } else {
+ BASE_ADD( stat, mod );
+ }
+ return;
+ }
+ if (target->IsReverseToHit()) {
+ STAT_SUB( stat, mod );
+ } else {
+ STAT_ADD( stat, mod );
+ }
+}
+
+// fx_ac_vs_damage_type_modifier_iwd2
+int fx_ac_vs_damage_type_modifier_iwd2 (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_ac_vs_damage_type_modifier_iwd2 (%2d): AC Modif: %d ; Type: %d ; MinLevel: %d ; MaxLevel: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2, (int) fx->DiceSides, (int) fx->DiceThrown );
+ //check level was pulled outside as a common functionality
+ //CHECK_LEVEL();
+
+ // it is a bitmask
+ int type = fx->Parameter2;
+ //the original engine did work with the combination of these bits
+ //but since it crashed, we are not bound to the same rules
+ switch(type)
+ {
+ case 0: //generic
+ HandleBonus(target, IE_ARMORCLASS, fx->Parameter1, fx->TimingMode);
+ break;
+ case 1: //armor
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ if (BASE_GET( IE_ARMORCLASS) > fx->Parameter1) {
+ BASE_SET( IE_ARMORCLASS, fx->Parameter1 );
+ }
+ } else {
+ if (STAT_GET( IE_ARMORCLASS) > fx->Parameter1) {
+ STAT_SET( IE_ARMORCLASS, fx->Parameter1 );
+ }
+ }
+ return FX_INSERT;
+ case 2: //deflection
+ break;
+ case 3: //shield
+ break;
+ case 4:
+ HandleBonus(target, IE_ACCRUSHINGMOD, fx->Parameter1, fx->TimingMode);
+ break;
+ case 5:
+ HandleBonus(target, IE_ACPIERCINGMOD, fx->Parameter1, fx->TimingMode);
+ break;
+ case 6:
+ HandleBonus(target, IE_ACSLASHINGMOD, fx->Parameter1, fx->TimingMode);
+ break;
+ case 7:
+ HandleBonus(target, IE_ACMISSILEMOD, fx->Parameter1, fx->TimingMode);
+ break;
+ }
+
+ return FX_PERMANENT;
+}
+
+// 0x49 DamageBonusModifier
+// iwd/iwd2 supports different damage types, but only flat and percentage boni
+// only the special type of 0 means a flat bonus
+int fx_damage_bonus_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_damage_bonus_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ switch (fx->Parameter2) {
+ case 0:
+ STAT_MOD(IE_DAMAGEBONUS);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ // no stat to save to, so we handle it when dealing damage
+ break;
+ default:
+ return FX_NOT_APPLIED;
+ }
+ return FX_APPLIED;
+}
+
+// 0x84 DrawUponHolyMight
+// this effect differs from bg2 because it doesn't use the actor state field
+// it uses the spell state field
+// in bg2 the effect is called: HolyNonCumulative
+int fx_draw_upon_holy_might (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_draw_upon_holy_might (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (target->SetSpellState( SS_HOLYMIGHT)) return FX_NOT_APPLIED;
+ STAT_ADD( IE_STR, fx->Parameter1);
+ STAT_ADD( IE_CON, fx->Parameter1);
+ STAT_ADD( IE_DEX, fx->Parameter1);
+ return FX_APPLIED;
+}
+
+//0xda IronSkins (iwd2)
+//This is about damage reduction, not full stoneskin like in bg2
+int fx_ironskins (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ ieDword tmp;
+
+ if (fx->Parameter2) {
+ //ironskins
+ tmp = STAT_GET(IE_STONESKINS);
+ if (fx->Parameter1>tmp) {
+ STAT_SET(IE_STONESKINS, fx->Parameter1);
+ }
+ target->SetSpellState( SS_IRONSKIN);
+ target->AddPortraitIcon(PI_IRONSKIN);
+ return FX_APPLIED;
+ }
+
+ //stoneskins (iwd2)
+ if (fx->FirstApply) {
+ tmp=fx->CasterLevel*10;
+ if (tmp>150) tmp=150;
+ fx->Parameter3=tmp;
+ }
+ if (!fx->Parameter3) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (target->SetSpellState( SS_STONESKIN)) return FX_NOT_APPLIED;
+ target->SetGradient(14);
+ target->AddPortraitIcon(PI_STONESKIN);
+ return FX_APPLIED;
+}
+
+//0xe8 Colour:FadeRGB
+int fx_fade_rgb (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_fade_rgb (%2d): RGB:%x\n", fx->Opcode, fx->Parameter1 );
+
+ int speed = (fx->Parameter2 >> 16) & 0xFF;
+ target->SetColorMod(0xff, RGBModifier::ADD, speed,
+ fx->Parameter1 >> 8, fx->Parameter1 >> 16,
+ fx->Parameter1 >> 24, speed);
+
+ return FX_NOT_APPLIED;
+}
+
+//0xe9 IWDVisualSpellHit
+int fx_iwd_visual_spell_hit (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_iwd_visual_spell_hit (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ if (!Owner) {
+ return FX_NOT_APPLIED;
+ }
+ //remove effect if there is no current area
+ Map *map = Owner->GetCurrentArea();
+ if (!map) {
+ return FX_NOT_APPLIED;
+ }
+ Point pos(fx->PosX,fx->PosY);
+ Projectile *pro = core->GetProjectileServer()->GetProjectileByIndex(0x1001+fx->Parameter2);
+ pro->SetCaster(fx->CasterID, fx->CasterLevel);
+ if (target) {
+ //i believe the spell hit projectiles don't follow anyone
+ map->AddProjectile( pro, pos, target->GetGlobalID(), true);
+ } else {
+ map->AddProjectile( pro, pos, pos);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xea ColdDamage (how)
+int fx_cold_damage (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cold_damage (%2d): Damage %d\n", fx->Opcode, fx->Parameter1 );
+ target->Damage(fx->Parameter1, DAMAGE_COLD, Owner);
+ return FX_NOT_APPLIED;
+}
+
+//0xeb CastingGlow2 will be same as original casting glow (iwd2 does the same)
+
+//0xec ChillTouch (how)
+//this effect is to simulate the composite effects of chill touch
+//it is the usual iwd/how style hack
+int fx_chill_touch (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_chill_touch (%2d)\n", fx->Opcode);
+ target->Damage(fx->Parameter1, DAMAGE_COLD, Owner);
+ if (STAT_GET(IE_GENERAL)==GEN_UNDEAD) {
+ target->Panic(Owner, PANIC_RUNAWAY);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xec ChillTouchPanic (iwd2)
+//the undead check is made by IDS targeting as it should be
+int fx_chill_touch_panic (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_chill_touch_panic (%2d)\n", fx->Opcode);
+ ieDword state;
+
+ if (fx->Parameter2) {
+ state = STATE_HELPLESS|STATE_STUNNED;
+ }
+ else {
+ state = STATE_PANIC;
+ }
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_STATE_SET(state);
+ } else {
+ STATE_SET(state);
+ }
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_PANIC);
+ }
+ return FX_PERMANENT;
+}
+
+//0xed CrushingDamage (how)
+int fx_crushing_damage (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_crushing_damage (%2d): Damage %d\n", fx->Opcode, fx->Parameter1 );
+ target->Damage(fx->Parameter1, DAMAGE_CRUSHING, Owner);
+ return FX_NOT_APPLIED;
+}
+
+//0xee SaveBonus
+int fx_save_bonus (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_save_bonus (%2d): Bonus %d Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_SAVEVSDEATH );
+ STAT_MOD( IE_SAVEVSWANDS );
+ STAT_MOD( IE_SAVEVSPOLY );
+ STAT_MOD( IE_SAVEVSBREATH );
+ STAT_MOD( IE_SAVEVSSPELL );
+ return FX_APPLIED;
+}
+
+//0xef SlowPoison
+//gemrb extension: can slow bleeding wounds (like bandage)
+static EffectRef fx_poison_ref = { "Poison", -1 };
+static EffectRef fx_wound_ref = { "BleedingWounds", -1 };
+
+int fx_slow_poison (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ ieDword my_opcode;
+ if (fx->Parameter2) my_opcode = EffectQueue::ResolveEffect(fx_wound_ref);
+ else my_opcode = EffectQueue::ResolveEffect(fx_poison_ref);
+ if (0) printf( "fx_slow_poison (%2d): Damage %d\n", fx->Opcode, fx->Parameter1 );
+ std::list< Effect* >::const_iterator f=target->fxqueue.GetFirstEffect();
+ Effect *poison;
+ //this is intentionally an assignment
+ while( (poison = target->fxqueue.GetNextEffect(f)) ) {
+ if (poison->Opcode!=my_opcode) continue;
+ switch (poison->Parameter2) {
+ case RPD_SECONDS:
+ poison->Parameter2=RPD_ROUNDS;
+ break;
+ case RPD_POINTS:
+ //i'm not sure if this is ok
+ //the hardcoded formula is supposed to be this:
+ //duration = (duration - gametime)*7+gametime;
+ //duration = duration*7 - gametime*6;
+ //but in fact it is like this.
+ //it is (duration - gametime)*8+gametime;
+ //like the damage is spread for a longer time than
+ //it should
+ poison->Duration=poison->Duration*8-core->GetGame()->GameTime*7;
+ poison->Parameter1*=7;
+ break;
+ case RPD_ROUNDS:
+ poison->Parameter2=RPD_TURNS;
+ break;
+ }
+ }
+ return FX_NOT_APPLIED;
+}
+
+#define IWD_MSC 13
+
+//this requires the FXOpcode package
+ieResRef iwd_monster_2da[IWD_MSC]={"MSUMMO1","MSUMMO2","MSUMMO3","MSUMMO4",
+ "MSUMMO5","MSUMMO6","MSUMMO7","ASUMMO1","ASUMMO2","ASUMMO3","CDOOM","GINSECT",
+ "MSUMMOM"};
+
+//0xf0 IWDMonsterSummoning
+int fx_iwd_monster_summoning (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_iwd_monster_summoning (%2d): ResRef:%s Anim:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Resource2, fx->Parameter2 );
+
+ //check the summoning limit?
+
+ ieResRef monster;
+ ieResRef hit;
+ ieResRef areahit;
+
+ if (fx->Parameter2>=IWD_MSC) {
+ fx->Parameter2=0;
+ }
+ core->GetResRefFrom2DA(iwd_monster_2da[fx->Parameter2], monster, hit, areahit);
+
+ //the monster should appear near the effect position
+ Point p(fx->PosX, fx->PosY);
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ core->SummonCreature(monster, areahit, Owner, target, p, EAM_SOURCEALLY, fx->Parameter1, newfx);
+ delete newfx;
+ return FX_NOT_APPLIED;
+}
+
+//0xf1 VampiricTouch
+int fx_vampiric_touch (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_vampiric_touch (%2d): ResRef:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ if (Owner->Type!=ST_ACTOR) {
+ return FX_NOT_APPLIED;
+ }
+
+ Actor *owner = (Actor *) Owner;
+
+ if (owner==target) {
+ return FX_NOT_APPLIED;
+ }
+
+ Actor *receiver;
+ Actor *donor;
+
+ switch(fx->Parameter2) {
+ case 0: receiver = target; donor = owner; break;
+ case 1: receiver = owner; donor = target; break;
+ default:
+ return FX_NOT_APPLIED;
+ }
+ int damage = donor->Damage(fx->Parameter1, fx->Parameter2, owner);
+ receiver->SetBase( IE_HITPOINTS, BASE_GET( IE_HITPOINTS ) + ( damage ) );
+ return FX_NOT_APPLIED;
+}
+
+#define IWD_AD 2
+ieResRef animate_dead_2da[IWD_AD]={"ADEAD","ADEADL"};
+
+//0xf3 AnimateDead
+int fx_animate_dead (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_animate_dead (%2d): ResRef:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ //check the summoning limit?
+ if (!target) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (!target->GetCurrentArea()) {
+ return FX_APPLIED;
+ }
+
+ ieResRef monster;
+ ieResRef hit;
+ ieResRef areahit;
+
+ if (fx->Parameter2>=IWD_AD) {
+ fx->Parameter2=0;
+ }
+ core->GetResRefFrom2DA(animate_dead_2da[fx->Parameter2], monster, hit, areahit);
+
+ //the monster should appear near the effect position
+ Point p(fx->PosX, fx->PosY);
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ core->SummonCreature(monster, areahit, Owner, target, p, EAM_SOURCEALLY, fx->Parameter1, newfx);
+ delete newfx;
+ return FX_NOT_APPLIED;
+}
+//f4 Prayer
+int fx_prayer (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_prayer (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ ieDword value;
+
+ if (fx->Parameter2)
+ {
+ if (target->SetSpellState(SS_BADPRAYER)) return FX_NOT_APPLIED;
+ EXTSTATE_SET(EXTSTATE_PRAYER_BAD);
+ value = (ieDword) -1;
+ }
+ else
+ {
+ if (target->SetSpellState(SS_GOODPRAYER)) return FX_NOT_APPLIED;
+ EXTSTATE_SET(EXTSTATE_PRAYER);
+ value = 1;
+ }
+
+ STAT_ADD( IE_TOHIT, value);
+ STAT_ADD( IE_SAVEFORTITUDE, value);
+ STAT_ADD( IE_SAVEREFLEX, value);
+ STAT_ADD( IE_SAVEWILL, value);
+ //make it compatible with 2nd edition
+ STAT_ADD( IE_SAVEVSBREATH, value);
+ STAT_ADD( IE_SAVEVSSPELL, value);
+ return FX_APPLIED;
+}
+//0xf5
+int fx_curse (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_curse (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ if (target->SetSpellState(SS_BADPRAYER)) return FX_NOT_APPLIED;
+ EXTSTATE_SET(EXTSTATE_PRAYER_BAD);
+ STAT_ADD( IE_TOHIT, -1);
+ STAT_ADD( IE_SAVEFORTITUDE, -1);
+ STAT_ADD( IE_SAVEREFLEX, -1);
+ STAT_ADD( IE_SAVEWILL, -1);
+ //make it compatible with 2nd edition
+ STAT_ADD( IE_SAVEVSBREATH, -1);
+ STAT_ADD( IE_SAVEVSSPELL, -1);
+ return FX_APPLIED;
+}
+
+//0xf6 SummonMonster2
+#define IWD_SM2 11
+ieResRef summon_monster_2da[IWD_SM2]={"SLIZARD","STROLLS","SSHADOW","ISTALKE",
+ "CFELEMW","CEELEMW","CWELEMW","CFELEMP","CEELEMP","CWELEMP","CEELEMM"};
+
+int fx_summon_monster2 (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_summon_monster2 (%2d): ResRef:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+
+ ieResRef monster;
+ ieResRef hit;
+ ieResRef areahit;
+
+ if (fx->Parameter2>=IWD_SM2) {
+ fx->Parameter2=0;
+ }
+ core->GetResRefFrom2DA(summon_monster_2da[fx->Parameter2], monster, hit, areahit);
+
+ //the monster should appear near the effect position
+ Point p(fx->PosX, fx->PosY);
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ core->SummonCreature(monster, areahit, Owner, target, p, EAM_SOURCEALLY, fx->Parameter1, newfx);
+ delete newfx;
+ return FX_NOT_APPLIED;
+}
+
+//0xf7 BurningBlood (iwd)
+int fx_burning_blood (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_burning_blood (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //inflicts damage calculated by dice values+parameter1
+ //creates damage opcode on everyone around. fx->Parameter2 - 0 fire, 1 - ice
+ ieDword damage = DAMAGE_FIRE;
+
+ if (fx->Parameter2==1) {
+ damage = DAMAGE_COLD;
+ }
+
+ target->Damage(fx->Parameter1, damage, Owner);
+ STAT_SET(IE_CHECKFORBERSERK,1);
+ return FX_NOT_APPLIED;
+}
+//0xf7 BurningBlood2 (how, iwd2)
+int fx_burning_blood2 (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_burning_blood2 (%2d): Count: %d Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //timing
+ if (core->GetGame()->GameTime%6) {
+ return FX_APPLIED;
+ }
+
+ if (!fx->Parameter1) {
+ return FX_NOT_APPLIED;
+ }
+ fx->Parameter1--;
+
+ ieDword damage = DAMAGE_FIRE;
+
+ if (fx->Parameter2==1) {
+ damage = DAMAGE_COLD;
+ }
+
+ //this effect doesn't use Parameter1 to modify damage, it is a counter instead
+ target->Damage(DICE_ROLL(0), damage, Owner);
+ STAT_SET(IE_CHECKFORBERSERK,1);
+ return FX_APPLIED;
+}
+
+//0xf8 SummonShadowMonster
+
+#define IWD_SSM 3
+ieResRef summon_shadow_monster_2da[IWD_SM2]={"SMONSTE","DSMONST","SHADES" };
+
+int fx_summon_shadow_monster (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_summon_shadow_monster (%2d): ResRef:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+
+ ieResRef monster;
+ ieResRef hit;
+ ieResRef areahit;
+
+ if (fx->Parameter2>=IWD_SSM) {
+ fx->Parameter2=0;
+ }
+ core->GetResRefFrom2DA(summon_shadow_monster_2da[fx->Parameter2], monster, hit, areahit);
+
+ //the monster should appear near the effect position
+ Point p(fx->PosX, fx->PosY);
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ core->SummonCreature(monster, areahit, Owner, target, p, EAM_SOURCEALLY, fx->Parameter1, newfx);
+ delete newfx;
+ return FX_NOT_APPLIED;
+}
+//0xf9 Recitation
+int fx_recitation (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_recitation (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ ieDword value;
+
+ if (fx->Parameter2)
+ {
+ if (target->SetSpellState(SS_BADRECIT)) return FX_NOT_APPLIED;
+ EXTSTATE_SET(EXTSTATE_REC_BAD);
+ value = (ieDword) -2;
+ }
+ else
+ {
+ if (target->SetSpellState(SS_GOODRECIT)) return FX_NOT_APPLIED;
+ EXTSTATE_SET(EXTSTATE_RECITATION);
+ value = 2;
+ }
+
+ STAT_ADD( IE_TOHIT, value);
+ STAT_ADD( IE_SAVEFORTITUDE, value);
+ STAT_ADD( IE_SAVEREFLEX, value);
+ STAT_ADD( IE_SAVEWILL, value);
+ //make it compatible with 2nd edition
+ STAT_ADD( IE_SAVEVSBREATH, value);
+ STAT_ADD( IE_SAVEVSSPELL, value);
+ return FX_APPLIED;
+}
+//0xfa RecitationBad
+int fx_recitation_bad (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_recitation (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ if (target->SetSpellState(SS_BADRECIT)) return FX_NOT_APPLIED;
+ EXTSTATE_SET(EXTSTATE_REC_BAD);
+ STAT_ADD( IE_TOHIT, -2);
+ STAT_ADD( IE_SAVEFORTITUDE, -2);
+ STAT_ADD( IE_SAVEREFLEX, -2);
+ STAT_ADD( IE_SAVEWILL, -2);
+ //make it compatible with 2nd edition
+ STAT_ADD( IE_SAVEVSBREATH, -2);
+ STAT_ADD( IE_SAVEVSSPELL, -2);
+ return FX_APPLIED;
+}
+//0xfb LichTouch (how)
+//0xfb State:Hold4 (iwd2)
+static EffectRef fx_hold_creature_ref = { "State:Hold", -1 };
+
+int fx_lich_touch (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_lich_touch (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ if (STAT_GET(IE_GENERAL)==GEN_UNDEAD) {
+ return FX_NOT_APPLIED;
+ }
+ target->Damage(DICE_ROLL(0), DAMAGE_COLD, Owner);
+ ///convert to hold creature
+ ///shall we check for immunity vs. #175?
+ ///if yes, then probably it is easier to apply the hold effect instead of converting to it
+ fx->Opcode = EffectQueue::ResolveEffect(fx_hold_creature_ref);
+ fx->Duration = fx->Parameter1;
+ fx->TimingMode = FX_DURATION_INSTANT_LIMITED;
+ ieDword GameTime = core->GetGame()->GameTime;
+ PrepareDuration(fx);
+ return FX_APPLIED;
+}
+
+//0xfc BlindingOrb (how)
+static EffectRef fx_state_blind_ref = { "State:Blind", -1 };
+
+int fx_blinding_orb (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ ieDword damage = fx->Parameter1;
+
+ //original code checks race: 0x6c, 0x73, 0xa7
+ if (STAT_GET(IE_GENERAL)==GEN_UNDEAD) {
+ damage *= 2;
+ }
+ //check saving throw
+ bool st = target->GetSavingThrow(0,0); //spell
+ if (st) {
+ target->Damage(damage/2, DAMAGE_FIRE, Owner);
+ return FX_NOT_APPLIED;
+ }
+ target->Damage(damage, DAMAGE_FIRE, Owner);
+
+ //convert effect to a blind effect.
+ fx->Opcode = EffectQueue::ResolveEffect(fx_state_blind_ref);
+ fx->Duration = core->Roll(1,6,0);
+ fx->TimingMode = FX_DURATION_INSTANT_LIMITED;
+ ieDword GameTime = core->GetGame()->GameTime;
+ PrepareDuration(fx);
+ return FX_APPLIED;
+}
+
+//0xfe RemoveEffects
+int fx_remove_effects (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_effects (%2d): ResRef:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+
+ switch(fx->Parameter2) {
+ case 1:
+ target->fxqueue.RemoveAllEffects(fx->Resource, FX_DURATION_INSTANT_WHILE_EQUIPPED);
+ break;
+ case 2:
+ target->fxqueue.RemoveAllEffects(fx->Resource, FX_DURATION_INSTANT_LIMITED);
+ break;
+ default:
+ target->fxqueue.RemoveAllEffects(fx->Resource);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xff SalamanderAura
+int fx_salamander_aura (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_salamander_aura (%2d): ResRef:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Parameter2 );
+ //inflicts damage calculated by dice values+parameter1
+ //creates damage opcode on everyone around. fx->Parameter2 - 0 fire, 1 - ice
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //timing
+ ieDword time = core->GetGame()->GameTime;
+ if ((fx->Parameter4==time) || (time%6) ) {
+ return FX_APPLIED;
+ }
+ fx->Parameter4=time;
+
+ ieDword damage = DAMAGE_FIRE;
+
+ if (fx->Parameter2==1) {
+ damage = DAMAGE_COLD;
+ }
+
+ ApplyDamageNearby(Owner, target, fx, damage);
+ return FX_APPLIED;
+}
+
+//0x100 UmberHulkGaze (causes confusion)
+//it is a specially hacked effect to ignore certain races
+//from the confusion effect
+static EffectRef fx_confusion_ref = { "State:Confused", -1 };
+static EffectRef fx_immunity_resource_ref = { "Protection:Spell", -1 };
+
+int fx_umberhulk_gaze (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_umberhulk_gaze (%2d): Duration: %d\n", fx->Opcode, fx->Parameter1);
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+ fx->TimingMode=FX_DURATION_AFTER_EXPIRES;
+ fx->Duration=core->GetGame()->GameTime+7*15;
+
+ //build effects to apply
+ Effect * newfx1, *newfx2;
+
+ newfx1 = EffectQueue::CreateEffectCopy(fx, fx_confusion_ref, 0, 0);
+ newfx1->TimingMode = FX_DURATION_INSTANT_LIMITED;
+ newfx1->Duration = fx->Parameter1;
+
+ newfx2 = EffectQueue::CreateEffectCopy(fx, fx_immunity_resource_ref, 0, 0);
+ newfx2->TimingMode = FX_DURATION_INSTANT_LIMITED;
+ newfx2->Duration = fx->Parameter1;
+ memcpy(newfx2->Resource, fx->Source, sizeof(newfx2->Resource) );
+
+ //collect targets and apply effect on targets
+ Map *area = target->GetCurrentArea();
+ int i = area->GetActorCount(true);
+ while(i--) {
+ Actor *victim = area->GetActor(i,true);
+ if (target==victim) continue;
+ if (PersonalDistance(target, victim)>300) continue;
+
+ //check if target is golem/umber hulk/minotaur, the effect is not working
+ if (check_iwd_targeting(Owner, victim, 0, 17)) { //umber hulk
+ continue;
+ }
+ if (check_iwd_targeting(Owner, victim, 0, 27)) { //golem
+ continue;
+ }
+ if (check_iwd_targeting(Owner, victim, 0, 29)) { //minotaur
+ continue;
+ }
+ if (check_iwd_targeting(Owner, victim, 0, 23)) { //blind
+ continue;
+ }
+
+ //apply a confusion opcode on target (0x80)
+ core->ApplyEffect(newfx1, victim, Owner);
+
+ //apply a resource resistance against this spell to block flood
+ core->ApplyEffect(newfx2, victim, Owner);
+ }
+ delete newfx1;
+ delete newfx2;
+
+ return FX_APPLIED;
+}
+
+//0x101 ZombieLordAura (causes Panic) unused in all games
+static EffectRef fx_fear_ref = { "State:Panic", -1 };
+
+int fx_zombielord_aura (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_zombie_lord_aura (%2d): Duration: %d\n", fx->Opcode, fx->Parameter1);
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+ fx->TimingMode=FX_DURATION_AFTER_EXPIRES;
+ fx->Duration=core->GetGame()->GameTime+7*15;
+
+ //build effects to apply
+ Effect * newfx1, *newfx2;
+
+ newfx1 = EffectQueue::CreateEffectCopy(fx, fx_fear_ref, 0, 0);
+ newfx1->TimingMode = FX_DURATION_INSTANT_LIMITED;
+ newfx1->Duration = fx->Parameter1;
+
+ newfx2 = EffectQueue::CreateEffectCopy(fx, fx_immunity_resource_ref, 0, 0);
+ newfx2->TimingMode = FX_DURATION_INSTANT_LIMITED;
+ newfx2->Duration = fx->Parameter1;
+ memcpy(newfx2->Resource, fx->Source, sizeof(newfx2->Resource) );
+
+ //collect targets and apply effect on targets
+ Map *area = target->GetCurrentArea();
+ int i = area->GetActorCount(true);
+ while(i--) {
+ Actor *victim = area->GetActor(i,true);
+ if (target==victim) continue;
+ if (PersonalDistance(target, victim)>20) continue;
+
+ //check if target is golem/umber hulk/minotaur, the effect is not working
+ if (check_iwd_targeting(Owner, victim, 0, 27)) { //golem
+ continue;
+ }
+ if (check_iwd_targeting(Owner, victim, 0, 1)) { //undead
+ continue;
+ }
+
+ //apply a panic opcode on target (0x18)
+ core->ApplyEffect(newfx1, victim, Owner);
+
+ //apply a resource resistance against this spell to block flood
+ core->ApplyEffect(newfx2, victim, Owner);
+ }
+ delete newfx1;
+ delete newfx2;
+
+ return FX_APPLIED;
+}
+//0x102 Protection:Spell (this is the same as in bg2?)
+
+//0x103 SummonCreature2
+
+static int eamods[]={EAM_DEFAULT,EAM_SOURCEALLY,EAM_SOURCEENEMY};
+
+int fx_summon_creature2 (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_summon_creature2 (%2d): ResRef:%s Anim:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Resource2, fx->Parameter2 );
+
+ if (!target) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (!target->GetCurrentArea()) {
+ return FX_APPLIED;
+ }
+ //summon creature (resource), play vvc (resource2)
+ //creature's lastsummoner is Owner
+ //creature's target is target
+ //position of appearance is target's pos (not sure!!!)
+ int eamod = EAM_DEFAULT;
+ if (fx->Parameter2<3){
+ eamod = eamods[fx->Parameter2];
+ }
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ if (fx->Parameter2 == 3) { // summon at source
+ core->SummonCreature(fx->Resource, fx->Resource2, Owner, target, Owner->Pos, eamod, 0, newfx);
+ } else {
+ core->SummonCreature(fx->Resource, fx->Resource2, Owner, target, target->Pos, eamod, 0, newfx);
+ }
+ delete newfx;
+ return FX_NOT_APPLIED;
+}
+
+//0x104 AvatarRemoval
+int fx_avatar_removal (Scriptable* /*Owner*/, Actor* target, Effect* /*fx*/)
+{
+ BASE_SET(IE_AVATARREMOVAL, 1);
+ return FX_NOT_APPLIED;
+}
+
+//0x105 immunity to effect (same as bg2?)
+//0x106 SummonPomab
+
+int fx_summon_pomab (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_summon_pomab (%2d): ResRef:%s Anim:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Resource2, fx->Parameter2 );
+
+ if (!target) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (!target->GetCurrentArea()) {
+ return FX_APPLIED;
+ }
+
+ ieResRef tableResRef;
+
+ if (fx->Resource[0]) {
+ strnlwrcpy(tableResRef, fx->Resource, 8);
+ } else {
+ memcpy(tableResRef,"pomab",6);
+ }
+
+ AutoTable tab(tableResRef);
+ if (!tab) {
+ return FX_NOT_APPLIED;
+ }
+
+ int cnt = tab->GetRowCount()-1;
+ if (cnt<2) {
+ return FX_NOT_APPLIED;
+ }
+
+ int real = core->Roll(1,cnt,-1);
+ const char *resrefs[2]={tab->QueryField((unsigned int) 0,0), tab->QueryField((int) 0,1) };
+
+ for (int i=0;i<cnt;i++) {
+ Point p(strtol(tab->QueryField(i+1,0),NULL,0), strtol(tab->QueryField(i+1,1), NULL, 0));
+ core->SummonCreature(resrefs[real!=i], fx->Resource2, Owner,
+ target, p, EAM_DEFAULT, 0, NULL, 0);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0x107 ControlUndead (like charm?)
+//425 ControlUndead2
+int fx_control_undead (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_control_undead (%2d): General: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ if (fx->Parameter1 && (STAT_GET(IE_GENERAL)!=fx->Parameter1)) {
+ return FX_NOT_APPLIED;
+ }
+ bool enemyally = true;
+ Scriptable *caster = target->GetCurrentArea()->GetActorByGlobalID(fx->CasterID);
+ if (caster && caster->Type==ST_ACTOR) {
+ enemyally = ((Actor *) caster)->GetStat(IE_EA) > EA_GOODCUTOFF; //or evilcutoff?
+ }
+
+ //do this only on first use
+ if (fx->FirstApply) {
+ if (Owner->Type == ST_ACTOR) {
+ fx->CasterID = Owner->GetGlobalID();
+ enemyally = ((Actor *) Owner)->GetStat(IE_EA) > EA_GOODCUTOFF; //or evilcutoff?
+ }
+ switch (fx->Parameter2) {
+ case 0: //charmed (target neutral after charm)
+ displaymsg->DisplayConstantStringName(STR_CHARMED, 0xf0f0f0, target);
+ break;
+ case 1: //charmed (target hostile after charm)
+ displaymsg->DisplayConstantStringName(STR_CHARMED, 0xf0f0f0, target);
+ target->SetBase(IE_EA, EA_ENEMY);
+ break;
+ case 2: //controlled by cleric
+ displaymsg->DisplayConstantStringName(STR_CONTROLLED, 0xf0f0f0, target);
+ break;
+ case 3: //controlled by cleric (hostile after charm)
+ displaymsg->DisplayConstantStringName(STR_CONTROLLED, 0xf0f0f0, target);
+ target->SetBase(IE_EA, EA_ENEMY);
+ break;
+ case 4: //turn undead
+ displaymsg->DisplayConstantStringName(STR_CONTROLLED, 0xf0f0f0, target);
+ target->SetBase(IE_EA, EA_ENEMY);
+ target->SetStat(IE_MORALE, 0, 0);
+ break;
+ }
+ }
+
+ STATE_SET( STATE_CHARMED );
+ STAT_SET_PCF( IE_EA, enemyally?EA_ENEMY:EA_CHARMED );
+ //don't stick if permanent
+ return FX_PERMANENT;
+}
+
+//0x108 StaticCharge
+int fx_static_charge(Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_static_charge (%2d): Count: %d \n", fx->Opcode, fx->Parameter1 );
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ int ret = FX_APPLIED;
+
+ if (fx->Parameter1<=1) {
+ ret = FX_NOT_APPLIED;
+ }
+
+ //timing
+ fx->TimingMode=FX_DURATION_DELAY_PERMANENT;
+ fx->Duration=core->GetGame()->GameTime+70*15;
+ fx->Parameter1--;
+
+ //iwd2 style
+ if (fx->Resource[0]) {
+ core->ApplySpell(fx->Resource, target, Owner, fx->Power);
+ return ret;
+ }
+
+ //how style
+ target->Damage(DICE_ROLL(0), DAMAGE_ELECTRICITY, Owner);
+ return ret;
+}
+
+//0x109 CloakOfFear (HoW/IWD2)
+//if the resource is not specified, it will work like in HoW
+
+static EffectRef fx_umberhulk_gaze_ref = { "UmberHulkGaze", -1 };
+
+int fx_cloak_of_fear(Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_cloak_of_fear (%2d): Count: %d \n", fx->Opcode, fx->Parameter1 );
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (!fx->Parameter1) {
+ return FX_NOT_APPLIED;
+ }
+
+ //timing (set up next fire)
+ fx->TimingMode=FX_DURATION_DELAY_PERMANENT;
+ fx->Duration=core->GetGame()->GameTime+3*15;
+ fx->Parameter1--;
+
+ //iwd2 style
+ if (fx->Resource[0]) {
+ core->ApplySpell(fx->Resource, target, Owner, fx->Power);
+ return FX_APPLIED;
+ }
+
+ //how style (probably better would be to provide effcof.spl)
+ Effect *newfx = EffectQueue::CreateEffect(fx_umberhulk_gaze_ref, 0,
+ 8, FX_DURATION_INSTANT_PERMANENT);
+ newfx->Power = fx->Power;
+
+ //collect targets and apply effect on targets
+ Map *area = target->GetCurrentArea();
+ int i = area->GetActorCount(true);
+ while(i--) {
+ Actor *victim = area->GetActor(i,true);
+ if (target==victim) continue;
+ if (PersonalDistance(target, victim)<20) {
+ core->ApplyEffect(newfx, target, Owner);
+ }
+ }
+ delete newfx;
+
+ return FX_APPLIED;
+}
+
+//0x10a MovementRateModifier3 (Like bg2)
+//0x10b Cure:Confusion (Like bg2)
+
+//0x10c EyeOfTheMind
+int fx_eye_of_the_mind (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_eye_of_the_mind (%2d)\n", fx->Opcode );
+ if (target->SetSpellState( SS_EYEMIND)) return FX_APPLIED;
+ EXTSTATE_SET(EXTSTATE_EYE_MIND);
+
+ if (fx->FirstApply) {
+ target->LearnSpell(SevenEyes[EYE_MIND], LS_MEMO);
+ }
+ return FX_APPLIED;
+}
+//0x10d EyeOfTheSword
+int fx_eye_of_the_sword (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_eye_of_the_sword (%2d)\n", fx->Opcode );
+ if (target->SetSpellState( SS_EYESWORD)) return FX_APPLIED;
+ EXTSTATE_SET(EXTSTATE_EYE_SWORD);
+
+ if (fx->FirstApply) {
+ target->LearnSpell(SevenEyes[EYE_SWORD], LS_MEMO);
+ }
+ return FX_APPLIED;
+}
+
+//0x10e EyeOfTheMage
+int fx_eye_of_the_mage (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_eye_of_the_mage (%2d)\n", fx->Opcode );
+ if (target->SetSpellState( SS_EYEMAGE)) return FX_APPLIED;
+ EXTSTATE_SET(EXTSTATE_EYE_MAGE);
+
+ if (fx->FirstApply) {
+ target->LearnSpell(SevenEyes[EYE_MAGE], LS_MEMO);
+ }
+ return FX_APPLIED;
+}
+
+//0x10f EyeOfVenom
+int fx_eye_of_venom (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_eye_of_venom (%2d)\n", fx->Opcode );
+ if (target->SetSpellState( SS_EYEVENOM)) return FX_APPLIED;
+ EXTSTATE_SET(EXTSTATE_EYE_VENOM);
+
+ if (fx->FirstApply) {
+ target->LearnSpell(SevenEyes[EYE_VENOM], LS_MEMO);
+ }
+ return FX_APPLIED;
+}
+
+//0x110 EyeOfTheSpirit
+int fx_eye_of_the_spirit (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_eye_of_the_spirit (%2d)\n", fx->Opcode );
+ if (target->SetSpellState( SS_EYESPIRIT)) return FX_APPLIED;
+ EXTSTATE_SET(EXTSTATE_EYE_SPIRIT);
+
+ if (fx->FirstApply) {
+ target->LearnSpell(SevenEyes[EYE_SPIRIT], LS_MEMO);
+ }
+ return FX_APPLIED;
+}
+
+//0x111 EyeOfFortitude
+int fx_eye_of_fortitude (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_eye_of_fortitude (%2d)\n", fx->Opcode );
+ if (target->SetSpellState( SS_EYEFORTITUDE)) return FX_APPLIED;
+ EXTSTATE_SET(EXTSTATE_EYE_FORT);
+
+ if (fx->FirstApply) {
+ target->LearnSpell(SevenEyes[EYE_FORT], LS_MEMO);
+ }
+ return FX_APPLIED;
+}
+
+//0x112 EyeOfStone
+int fx_eye_of_stone (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_eye_of_stone (%2d)\n", fx->Opcode );
+ if (target->SetSpellState( SS_EYESTONE)) return FX_APPLIED;
+ EXTSTATE_SET(EXTSTATE_EYE_STONE);
+
+ if (fx->FirstApply) {
+ target->LearnSpell(SevenEyes[EYE_STONE], LS_MEMO);
+ }
+ return FX_APPLIED;
+}
+
+//0x113 RemoveSevenEyes
+
+int fx_remove_seven_eyes (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_seven_eyes (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ target->spellbook.RemoveSpell(SevenEyes[EYE_MIND]);
+ target->spellbook.RemoveSpell(SevenEyes[EYE_SWORD]);
+ target->spellbook.RemoveSpell(SevenEyes[EYE_MAGE]);
+ target->spellbook.RemoveSpell(SevenEyes[EYE_VENOM]);
+ target->spellbook.RemoveSpell(SevenEyes[EYE_SPIRIT]);
+ target->spellbook.RemoveSpell(SevenEyes[EYE_FORT]);
+ target->spellbook.RemoveSpell(SevenEyes[EYE_STONE]);
+ return FX_NOT_APPLIED;
+}
+
+//0x114 RemoveEffect
+int fx_remove_effect (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_remove_effect (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ if (fx->Resource[0])
+ {
+ target->fxqueue.RemoveAllEffectsWithResource(fx->Parameter2, fx->Resource);
+ }
+ else
+ {
+ target->fxqueue.RemoveAllEffects(fx->Parameter2);
+ }
+ return FX_NOT_APPLIED;
+}
+
+static EffectRef fx_str_ref = { "StrengthModifier", -1 };
+static EffectRef fx_con_ref = { "ConstitutionModifier", -1 };
+static EffectRef fx_dex_ref = { "DexterityModifier", -1 };
+
+//0x115 SoulEater
+int fx_soul_eater (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_soul_eater (%2d): Damage %d\n", fx->Opcode, fx->Parameter1 );
+ // Soul Eater has no effect on undead, constructs, and elemental creatures,
+ // but this is handled in the spells via fx_resist_spell_and_message
+ int damage = fx->Parameter1;
+ // the how spell has it set to 0, so use the damage from the description
+ if (!damage) {
+ damage = core->Roll(3, 8, 0);
+ }
+
+ target->Damage(damage, DAMAGE_SOULEATER, Owner);
+ //the state is not set soon enough!
+ //if (STATE_GET(STATE_DEAD) ) {
+ if (target->GetInternalFlag() & IF_REALLYDIED) {
+ ieResRef monster;
+ ieResRef hit;
+ ieResRef areahit;
+
+ core->GetResRefFrom2DA("souleatr", monster, hit, areahit);
+ //the monster should appear near the effect position
+ Point p(fx->PosX, fx->PosY);
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ core->SummonCreature(monster, areahit, Owner, target, p, EAM_SOURCEALLY, fx->Parameter1, newfx);
+
+ // for each kill the caster receives a +1 bonus to Str, Dex and Con for 1 turn
+ if (Owner->Type == ST_ACTOR) {
+ newfx = EffectQueue::CreateEffect(fx_str_ref, 1, MOD_ADDITIVE, FX_DURATION_INSTANT_LIMITED);
+ newfx->Duration = core->Time.turn_sec;
+ core->ApplyEffect(newfx, (Actor *)Owner, Owner);
+ newfx = EffectQueue::CreateEffect(fx_dex_ref, 1, MOD_ADDITIVE, FX_DURATION_INSTANT_LIMITED);
+ newfx->Duration = core->Time.turn_sec;
+ core->ApplyEffect(newfx, (Actor *)Owner, Owner);
+ newfx = EffectQueue::CreateEffect(fx_con_ref, 1, MOD_ADDITIVE, FX_DURATION_INSTANT_LIMITED);
+ newfx->Duration = core->Time.turn_sec;
+ core->ApplyEffect(newfx, (Actor *)Owner, Owner);
+ }
+ delete newfx;
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0x116 ShroudOfFlame (how)
+//FIXME: maybe it is cheaper to port effsof1/2 to how than having an alternate effect
+int fx_shroud_of_flame (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_shroud_of_flame (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //don't apply it twice in a round
+ if (EXTSTATE_GET(EXTSTATE_SHROUD)) {
+ return FX_APPLIED;
+ }
+ EXTSTATE_SET(EXTSTATE_SHROUD);
+ //directly modifying the color of the target
+ if (fx->Parameter2==1) {
+ target->SetColorMod(0xff, RGBModifier::ADD, -1, 0, 0, 0x96);
+ }
+ else {
+ target->SetColorMod(0xff, RGBModifier::ADD, -1, 0x96, 0, 0);
+ }
+
+ //timing
+ ieDword time = core->GetGame()->GameTime;
+ if ((fx->Parameter4==time) || (time%6) ) {
+ return FX_APPLIED;
+ }
+ fx->Parameter4=time;
+
+ //inflicts damage calculated by dice values+parameter1
+ //creates damage opcode on everyone around. fx->Parameter2 - 0 fire, 1 - ice
+ ieDword damage = DAMAGE_FIRE;
+
+ if (fx->Parameter2==1) {
+ damage = DAMAGE_COLD;
+ }
+
+ target->Damage(fx->Parameter1, damage, Owner);
+ ApplyDamageNearby(Owner, target, fx, damage);
+ return FX_APPLIED;
+}
+
+static ieResRef resref_sof1={"effsof1"};
+static ieResRef resref_sof2={"effsof2"};
+
+//0x116 ShroudOfFlame (iwd2)
+int fx_shroud_of_flame2 (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_shroud_of_flame2 (%2d)\n", fx->Opcode );
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (target->SetSpellState( SS_FLAMESHROUD)) return FX_APPLIED;
+ EXTSTATE_SET(EXTSTATE_SHROUD); //just for compatibility
+
+ if(enhanced_effects) {
+ target->SetColorMod(0xff, RGBModifier::ADD, 1, 0xa0, 0, 0);
+ }
+
+ //apply resource on hitter
+ //actually, this should be a list of triggers
+ if (fx->Resource[0]) {
+ memcpy(target->applyWhenBeingHit,fx->Resource,sizeof(ieResRef));
+ } else {
+ memcpy(target->applyWhenBeingHit,resref_sof1,sizeof(ieResRef));
+ }
+
+ //timing
+ ieDword time = core->GetGame()->GameTime;
+ if ((fx->Parameter4==time) || (time%6) ) {
+ return FX_APPLIED;
+ }
+ fx->Parameter4=time;
+
+ if (fx->Resource2[0]) {
+ core->ApplySpell(fx->Resource2, target, Owner, fx->Power);
+ } else {
+ core->ApplySpell(resref_sof2, target, Owner, fx->Power);
+ }
+ return FX_APPLIED;
+}
+
+//0x117 AnimalRage
+int fx_animal_rage (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_animal_rage (%2d): Mode: %d\n", fx->Opcode, fx->Parameter2 );
+
+ //param2==1 sets only the spell state
+ if (fx->Parameter2) {
+ target->SetSpellState( SS_ANIMALRAGE);
+ return FX_APPLIED;
+ }
+
+ //the state check is done after the parameter differentiation
+ //it might be a bug but i cannot decide (going with the original)
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //param2==0 doesn't set the spell state
+
+ //don't do anything if already berserking
+ //FIXME: is it berserkstage2 or 1?
+ if (STAT_GET(IE_BERSERKSTAGE1)) {
+ return FX_APPLIED;
+ }
+
+ //it has 5% of going berserk
+ //FIXME: how much is the original bg berserking chance
+ //if it is different, use checkforberserk as a percentile chance
+ STAT_SET( IE_CHECKFORBERSERK, 1 );
+
+ //and attacks the first enemy in sight
+ //timing
+ if (core->GetGame()->GameTime%6) {
+ return FX_APPLIED;
+ }
+ //if enemy is in sight
+ //attack them
+ //FIXME: would the circle color change?
+ if (!target->LastTarget) {
+ //depends on whom it considers enemy
+ if (STAT_GET(IE_EA)<EA_EVILCUTOFF) {
+ Enemy->objectParameter->objectFilters[0]=EA_ENEMY;
+ } else {
+ Enemy->objectParameter->objectFilters[0]=EA_ALLY;
+ }
+ //see the nearest enemy
+ if (SeeCore(target, Enemy, false)) {
+ target->SetTarget(target->GetCurrentArea()->GetActorByGlobalID(target->LastSeen));
+ //this is highly unsure
+ //fx->Parameter1=1;
+ }
+ }
+ return FX_APPLIED;
+}
+
+//0x118 TurnUndead how
+int fx_turn_undead (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_turn_undead (%2d): Level %d\n", fx->Opcode, fx->Parameter1 );
+ if (fx->Parameter1) {
+ target->Turn(Owner, fx->Parameter1);
+ } else {
+ if (Owner->Type!=ST_ACTOR) {
+ return FX_NOT_APPLIED;
+ }
+ target->Turn(Owner, ((Actor *) Owner)->GetStat(IE_TURNUNDEADLEVEL));
+ }
+ return FX_APPLIED;
+}
+
+//0x118 TurnUndead2 iwd2
+int fx_turn_undead2 (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_turn_undead2 (%2d): Level: %d Type %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ switch (fx->Parameter2)
+ {
+ case 0: //command
+ target->LastTurner = Owner->GetGlobalID();
+ target->Panic(Owner, PANIC_RUNAWAY);
+ break;
+ case 1://rebuke
+ if (target->SetSpellState(SS_REBUKED)) {
+ //display string rebuked
+ }
+ STAT_SUB(IE_ARMORCLASS,4);
+ target->LastTurner = Owner->GetGlobalID();
+ break;
+ case 2://destroy
+ target->LastTurner = Owner->GetGlobalID();
+ target->Die(Owner);
+ break;
+ case 3://panic
+ target->LastTurner = Owner->GetGlobalID();
+ target->Panic(Owner, PANIC_RUNAWAY);
+ break;
+ default://depends on caster
+ if (fx->Parameter1) {
+ target->Turn(Owner, fx->Parameter1);
+ } else {
+ if (Owner->Type!=ST_ACTOR) {
+ return FX_NOT_APPLIED;
+ }
+ target->Turn(Owner, ((Actor *) Owner)->GetStat(IE_TURNUNDEADLEVEL));
+ }
+ break;
+ }
+ return FX_APPLIED;
+}
+
+//0x119 VitriolicSphere
+int fx_vitriolic_sphere (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_vitriolic_sphere (%2d): Damage %d\n", fx->Opcode, fx->Parameter1 );
+ //timing
+ if (core->GetGame()->GameTime%6) {
+ return FX_APPLIED;
+ }
+ target->Damage(fx->Parameter1, DAMAGE_ACID, Owner);
+ fx->DiceThrown-=2;
+ if ((signed) fx->DiceThrown<1) {
+ return FX_NOT_APPLIED;
+ }
+ //also damage people nearby?
+// ApplyDamageNearby(Owner, target, fx, DAMAGE_ACID);
+ return FX_APPLIED;
+}
+
+//0x11a SuppressHP
+int fx_suppress_hp (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_suppress_hp (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_NOHPINFO)) return FX_APPLIED;
+ EXTSTATE_SET(EXTSTATE_NO_HP);
+ return FX_APPLIED;
+}
+
+//0x11b FloatText
+int fx_floattext (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_floattext (%2d): StrRef:%d Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ switch (fx->Parameter2)
+ {
+ case 1:
+ //in the original game this signified that a specific weapon is equipped
+ if (EXTSTATE_GET(EXTSTATE_FLOATTEXTS))
+ return FX_APPLIED;
+
+ EXTSTATE_SET(EXTSTATE_FLOATTEXTS);
+ if (!fx->Resource[0]) {
+ strnuprcpy(fx->Resource,"cynicism",sizeof(ieResRef)-1);
+ }
+ if (fx->Parameter1) {
+ fx->Parameter1--;
+ return FX_APPLIED;
+ } else {
+ fx->Parameter1=core->Roll(1,500,500);
+ }
+ case 2:
+ if (EXTSTATE_GET(EXTSTATE_FLOATTEXTS)) {
+ ieDword *CynicismList = core->GetListFrom2DA(fx->Resource);
+ ieDword i = CynicismList[0];
+ if (i) {
+ DisplayStringCore(target, CynicismList[core->Roll(1,i,0)], DS_HEAD);
+ }
+ }
+ return FX_APPLIED;
+ default:
+ DisplayStringCore(target, fx->Parameter1, DS_HEAD);
+ break;
+ case 3: //gemrb extension, displays verbalconstant
+ DisplayStringCore(target, fx->Parameter1, DS_CONST|DS_HEAD);
+ break;
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0x11c MaceOfDisruption
+//death with chance based on race and level
+static EffectRef fx_death_ref = { "Death", -1 };
+static EffectRef fx_iwd_visual_spell_hit_ref = { "IWDVisualSpellHit", -1 };
+
+int fx_mace_of_disruption (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_mace_of_disruption (%2d): ResRef:%s Anim:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Resource2, fx->Parameter2 );
+ ieDword race = STAT_GET(IE_RACE);
+ //golem / outer planar gets hit
+ int chance = 0;
+ switch (race) {
+ case 156: // outsider
+ chance = 5;
+ break;
+ case 108: case 115: case 167: //ghoul, skeleton, undead
+ switch (STAT_GET(IE_LEVEL)) {
+ case 1: case 2: case 3: case 4:
+ chance = 100;
+ break;
+ case 5:
+ chance = 95;
+ break;
+ case 6:
+ chance = 80;
+ break;
+ case 7:
+ chance = 65;
+ break;
+ case 8: case 9:
+ chance = 50;
+ break;
+ case 10:
+ chance = 35;
+ break;
+ default:
+ chance = 20;
+ break;
+ }
+ break;
+ default:;
+ }
+ if (chance < core->Roll(1,100,0)) {
+ return FX_NOT_APPLIED;
+ }
+
+ Effect *newfx = EffectQueue::CreateEffect(fx_iwd_visual_spell_hit_ref, 0,
+ 8, FX_DURATION_INSTANT_PERMANENT);
+ newfx->Target=FX_TARGET_PRESET;
+ newfx->Power=fx->Power;
+ core->ApplyEffect(newfx, target, Owner);
+
+ newfx = EffectQueue::CreateEffect(fx_death_ref, 0,
+ 8, FX_DURATION_INSTANT_PERMANENT);
+ newfx->Target=FX_TARGET_PRESET;
+ newfx->Power=fx->Power;
+ core->ApplyEffect(newfx, target, Owner);
+
+ delete newfx;
+
+ return FX_NOT_APPLIED;
+}
+//0x11d Sleep2 ??? power word sleep?
+//0x11e Reveal:Tracks (same as bg2)
+//0x11f Protection:Backstab (same as bg2)
+
+//0x120 State:Set
+int fx_set_state (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_state (%2d): Type: %d\n", fx->Opcode, fx->Parameter2 );
+ //in IWD2 we have 176 states (original had 256)
+ target->SetSpellState(fx->Parameter2);
+ //in HoW this sets only the 10 last bits of extstate (until it runs out of bits)
+ if (fx->Parameter2<11) {
+ EXTSTATE_SET(0x40000<<fx->Parameter2);
+ }
+ return FX_APPLIED;
+}
+
+//0x121 Cutscene (this is a very ugly hack in iwd)
+//It doesn't really start a cutscene, just sets a variable
+//The script system itself will detect that variable and activate the cutscene
+//ToB has an effect which actually runs a hardcoded cutscene
+int fx_cutscene (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_cutscene (%2d)\n", fx->Opcode );
+ Game *game = core->GetGame();
+ game->locals->SetAt("GEM_ACTIVE", 1);
+ return FX_NOT_APPLIED;
+}
+
+//0xce (same place as in bg2, but different)
+int fx_resist_spell (Scriptable* Owner, Actor* target, Effect *fx)
+{
+ //check iwd ids targeting
+ //changed this to the opposite (cure light wounds resisted by undead)
+ if (!check_iwd_targeting(Owner, target, fx->Parameter1, fx->Parameter2) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (strnicmp(fx->Resource,fx->Source,sizeof(fx->Resource)) ) {
+ return FX_APPLIED;
+ }
+ //this has effect only on first apply, it will stop applying the spell
+ return FX_ABORT;
+}
+
+static EffectRef fx_resist_spell_ref = { "Protection:Spell2", -1 };
+
+//0x122 Protection:Spell3 ??? IWD ids targeting
+// this is a variant of resist spell, used in iwd2
+int fx_resist_spell_and_message (Scriptable* Owner, Actor* target, Effect *fx)
+{
+ //check iwd ids targeting
+ //changed this to the opposite (cure light wounds resisted by undead)
+ if (!check_iwd_targeting(Owner, target, fx->Parameter1, fx->Parameter2) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //convert effect to the normal resist spell effect (without text)
+ //in case it lingers
+ fx->Opcode=EffectQueue::ResolveEffect(fx_resist_spell_ref);
+
+ if (strnicmp(fx->Resource,fx->Source,sizeof(fx->Resource)) ) {
+ return FX_APPLIED;
+ }
+ //display message too
+ int spellname = -1;
+
+ if(gamedata->Exists(fx->Resource, IE_ITM_CLASS_ID) ) {
+ spellname = gamedata->GetItem(fx->Resource)->ItemName;
+ } else if (gamedata->Exists(fx->Resource, IE_SPL_CLASS_ID) ) {
+ spellname = gamedata->GetSpell(fx->Resource, true)->SpellName;
+ }
+
+ if (spellname>=0) {
+ char *tmpstr = core->GetString(spellname, 0);
+ core->GetTokenDictionary()->SetAtCopy("RESOURCE", tmpstr);
+ core->FreeString(tmpstr);
+ displaymsg->DisplayConstantStringName(STR_RES_RESISTED, 0xf0f0f0, target);
+ }
+ //this has effect only on first apply, it will stop applying the spell
+ return FX_ABORT;
+}
+
+//0x123 RodOfSmithing
+//if golem: 5% death or 1d8+3 damage
+//if outsider: 5% 8d3 damage or nothing
+//otherwise: nothing
+int fx_rod_of_smithing (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_rod_of_smithing (%2d): ResRef:%s Anim:%s Type: %d\n", fx->Opcode, fx->Resource, fx->Resource2, fx->Parameter2 );
+ int damage = 0;
+ int five_percent = core->Roll(1,100,0)<5;
+
+ if (check_iwd_targeting(Owner, target, 0, 27)) { //golem
+ if(five_percent) {
+ //instant death
+ damage = -1;
+ } else {
+ damage = core->Roll(1,8,3);
+ }
+ } else if (check_iwd_targeting(Owner, target, 0, 92)) { //outsider
+ if (five_percent) {
+ damage = core->Roll(8,3,0);
+ }
+ }
+ if (damage) {
+ Effect *newfx;
+ if (damage<0) {
+ //create death effect (chunked death)
+ newfx = EffectQueue::CreateEffect(fx_death_ref, 0, 8, FX_DURATION_INSTANT_PERMANENT);
+ } else {
+ //create damage effect (blunt)
+ newfx = EffectQueue::CreateEffect(fx_damage_opcode_ref, (ieDword) damage,
+ 0, FX_DURATION_INSTANT_PERMANENT);
+ }
+ core->ApplyEffect(newfx, target, Owner);
+ delete newfx;
+ }
+
+ return FX_NOT_APPLIED;
+}
+
+//0x124 MagicalRest (same as bg2)
+
+//0x125 BeholderDispelMagic (applies resource on nearby actors)
+//TODO: range, affected actors
+int fx_beholder_dispel_magic (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_beholder_dispel_magic (%2d): Spell: %s\n", fx->Opcode, fx->Resource );
+ if (!fx->Resource[0]) {
+ strcpy(fx->Resource,"SPIN164");
+ }
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ Map *area = target->GetCurrentArea();
+ int i = area->GetActorCount(true);
+ while(i--) {
+ Actor *victim = area->GetActor(i,true);
+ if (target==victim) continue;
+ if (PersonalDistance(target, victim)<300) {
+ //this function deletes tmp
+ core->ApplySpell(fx->Resource, victim, Owner, fx->Power);
+ }
+ }
+
+ return FX_NOT_APPLIED;
+}
+
+//0x126 HarpyWail (applies resource on nearby actors)
+//TODO: range, affected actors, sound effect
+int fx_harpy_wail (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_harpy_wail (%2d): Spell: %s\n", fx->Opcode, fx->Resource );
+ if (!fx->Resource[0]) {
+ strcpy(fx->Resource,"SPIN166");
+ }
+ if (!fx->Resource2[0]) {
+ strcpy(fx->Resource2,"EFF_P111");
+ }
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+ core->GetAudioDrv()->Play(fx->Resource2, target->Pos.x, target->Pos.y);
+
+ Map *area = target->GetCurrentArea();
+ int i = area->GetActorCount(true);
+ while(i--) {
+ Actor *victim = area->GetActor(i,true);
+ if (target==victim) continue;
+ if (PersonalDistance(target, victim)<300) {
+ //this function deletes tmp
+ core->ApplySpell(fx->Resource, victim, Owner, fx->Power);
+ }
+ }
+
+ return FX_NOT_APPLIED;
+}
+
+//0x127 JackalWereGaze (applies resource on nearby actors)
+//TODO: range, affected actors
+int fx_jackalwere_gaze (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_jackalwere_gaze (%2d): Spell: %s\n", fx->Opcode, fx->Resource );
+ if (!fx->Resource[0]) {
+ strcpy(fx->Resource,"SPIN179");
+ }
+
+ //if the target is dead, this effect ceases to exist
+ if (STATE_GET(STATE_DEAD|STATE_PETRIFIED|STATE_FROZEN) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ Map *area = target->GetCurrentArea();
+ int i = area->GetActorCount(true);
+ while(i--) {
+ Actor *victim = area->GetActor(i,true);
+ if (target==victim) continue;
+ if (PersonalDistance(target, victim)<300) {
+ //this function deletes tmp
+ core->ApplySpell(fx->Resource, victim, Owner, fx->Power);
+ }
+ }
+
+ return FX_APPLIED;
+}
+//0x128 ModifyGlobalVariable (same as bg2)
+//0x129 HideInShadows (same as bg2)
+
+//0x12a UseMagicDevice
+int fx_use_magic_device_modifier (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_use_magic_device_modifier (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ STAT_MOD( IE_MAGICDEVICE );
+ return FX_APPLIED;
+}
+
+//GemRB specific IWD related effects
+
+//This code is needed for the ending cutscene in IWD (found in a projectile)
+//The effect will alter the target animation's cycle by a xor value
+//Parameter1: the value to binary xor on the initial cycle numbers of the animation(s)
+//Parameter2: an optional projectile (IWD spell hit projectiles start at 0x1001)
+//Resource: the animation's name
+
+//Useful applications other than the HoW cutscene:
+//A fireball could affect environment by applying the effect on certain animations
+//all you need to do:
+//Name the area animation as 'burnable'.
+//Create alternate cycles for the altered area object
+//Create a spell hit animation (optionally)
+//Create the effect which will contain the spell hit projectile and the cycle change command
+int fx_alter_animation (Scriptable* Owner, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_alter_animation (%2d)\n", fx->Opcode);
+ Map *map = Owner->GetCurrentArea();
+ if (!map) {
+ return FX_NOT_APPLIED;
+ }
+
+ aniIterator iter = map->GetFirstAnimation();
+ while(AreaAnimation *an = map->GetNextAnimation(iter) ) {
+ //Only animations with 8 letters could be used, no problem, iwd uses 8 letters
+ if (!strnicmp (an->Name, fx->Resource, 8) ) {
+ //play spell hit animation
+ Projectile *pro=core->GetProjectileServer()->GetProjectileByIndex(fx->Parameter2);
+ pro->SetCaster(fx->CasterID, fx->CasterLevel);
+ map->AddProjectile(pro, an->Pos, an->Pos);
+ //alter animation, we need only this for the original, but in the
+ //spirit of unhardcoding, i provided the standard modifier codeset
+ //0->4, 1->5, 2->6, 3->7
+ //4->0, 5->1, 6->2, 7->3
+ ieWord value = fx->Parameter1>>16;
+ switch(fx->Parameter1&0xffff) {
+ case BM_SET:
+ an->sequence=value;
+ break;
+ case BM_OR:
+ an->sequence|=value;
+ break;
+ case BM_NAND:
+ an->sequence&=~value;
+ break;
+ case BM_XOR:
+ an->sequence^=value;
+ break;
+ }
+ an->frame=0;
+ an->InitAnimation();
+ }
+ }
+ return FX_NOT_APPLIED;
+}
+
+//IWD2 effects
+
+//400 Hopelessness
+int fx_hopelessness (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_hopelessness (%2d)\n", fx->Opcode);
+
+ if (target->HasSpellState(SS_BLOODRAGE)) {
+ return FX_NOT_APPLIED;
+ }
+
+ if (target->SetSpellState( SS_HOPELESSNESS)) return FX_NOT_APPLIED;
+ target->AddPortraitIcon(PI_HOPELESSNESS);
+ STATE_SET(STATE_HELPLESS);
+ return FX_APPLIED;
+}
+
+//401 ProtectionFromEvil
+int fx_protection_from_evil (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_from_evil (%2d)\n", fx->Opcode);
+ //
+ if (target->SetSpellState( SS_PROTFROMEVIL)) return FX_APPLIED;
+ target->AddPortraitIcon(PI_PROTFROMEVIL);
+ //+2 to all saving throws
+ STAT_ADD( IE_SAVEFORTITUDE, 2);
+ STAT_ADD( IE_SAVEREFLEX, 2);
+ STAT_ADD( IE_SAVEWILL, 2);
+ //make it compatible with 2nd edition
+ STAT_ADD( IE_SAVEVSBREATH, 2);
+ STAT_ADD( IE_SAVEVSSPELL, 2);
+ //immune to control
+ return FX_APPLIED;
+}
+
+//402 AddEffectsList
+int fx_add_effects_list (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ //after iwd2 style ids targeting, apply the spell named in the resource field
+ if (!check_iwd_targeting(Owner, target, fx->Parameter1, fx->Parameter2) ) {
+ return FX_NOT_APPLIED;
+ }
+ core->ApplySpell(fx->Resource, target, Owner, fx->Power);
+ return FX_NOT_APPLIED;
+}
+
+//403 ArmorOfFaith
+static int fx_armor_of_faith (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_armor_of_faith (%2d) Amount: %d\n", fx->Opcode, fx->Parameter1);
+ if (target->SetSpellState( SS_ARMOROFFAITH)) return FX_APPLIED;
+ if (!fx->Parameter1) {
+ fx->Parameter1=1;
+ }
+ //TODO: damage reduction (all types)
+ STAT_ADD(IE_RESISTFIRE,fx->Parameter1 );
+ STAT_ADD(IE_RESISTCOLD,fx->Parameter1 );
+ STAT_ADD(IE_RESISTELECTRICITY,fx->Parameter1 );
+ STAT_ADD(IE_RESISTACID,fx->Parameter1 );
+ STAT_ADD(IE_RESISTMAGIC,fx->Parameter1 );
+ STAT_ADD(IE_RESISTSLASHING,fx->Parameter1 );
+ STAT_ADD(IE_RESISTCRUSHING,fx->Parameter1 );
+ STAT_ADD(IE_RESISTPIERCING,fx->Parameter1 );
+ STAT_ADD(IE_RESISTMISSILE,fx->Parameter1 );
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_FAITHARMOR);
+ }
+ return FX_APPLIED;
+}
+
+//404 Nausea
+static EffectRef fx_unconscious_state_ref = { "State:Helpless", -1 };
+
+int fx_nausea (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_nausea (%2d)\n", fx->Opcode);
+ //FIXME: i'm not sure if this part is there
+ //create the sleep effect only once?
+ if (!fx->Parameter3 && Owner) {
+ Effect *newfx = EffectQueue::CreateEffect(fx_unconscious_state_ref,
+ fx->Parameter1, 1, fx->TimingMode);
+ newfx->Power = fx->Power;
+ core->ApplyEffect(newfx, target, Owner);
+
+ delete newfx;
+ fx->Parameter3=1;
+ }
+ //end of unsure part
+ if (target->SetSpellState( SS_NAUSEA)) return FX_APPLIED;
+ target->AddPortraitIcon(PI_NAUSEA);
+ STATE_SET(STATE_HELPLESS|STATE_SLEEP);
+ return FX_APPLIED;
+}
+
+//405 Enfeeblement
+//minimum stats in 3rd ed are 1, so this effect won't kill the target
+int fx_enfeeblement (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_enfeeblement (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_ENFEEBLED)) return FX_APPLIED;
+ target->AddPortraitIcon(PI_ENFEEBLEMENT);
+ STAT_ADD(IE_STR, -15);
+ return FX_APPLIED;
+}
+
+//406 FireShield
+int fx_fireshield (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_fireshield (%2d) Type: %d\n", fx->Opcode, fx->Parameter2);
+ if (fx->Parameter2) {
+ if (target->SetSpellState( SS_ICESHIELD)) return FX_APPLIED;
+ target->AddPortraitIcon(PI_ICESHIELD);
+ } else {
+ if (target->SetSpellState( SS_FIRESHIELD)) return FX_APPLIED;
+ target->AddPortraitIcon(PI_FIRESHIELD);
+ }
+ memcpy(target->applyWhenBeingHit,fx->Resource,sizeof(ieResRef));
+ return FX_APPLIED;
+}
+
+//407 DeathWard
+int fx_death_ward (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_death_ward (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_DEATHWARD)) return FX_APPLIED;
+ target->AddPortraitIcon(PI_DEATHWARD); // is it ok?
+
+ return FX_APPLIED;
+}
+
+//408 HolyPower
+int fx_holy_power (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_holy_power (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_HOLYPOWER)) return FX_APPLIED;
+
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_HOLYPOWER);
+ target->SetColorMod(0xff, RGBModifier::ADD, 20, 0x80, 0x80, 0x80);
+ }
+ return FX_APPLIED;
+}
+
+//409 RighteousWrath
+int fx_righteous_wrath (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_righteous_wrath (%2d) Type: %d\n", fx->Opcode, fx->Parameter2);
+ if (fx->Parameter2)
+ {
+ if (target->SetSpellState( SS_RIGHTEOUS2)) return FX_APPLIED;
+ //
+ }
+ else
+ {
+ if (target->SetSpellState( SS_RIGHTEOUS)) return FX_APPLIED;
+ //
+ }
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_RIGHTEOUS);
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0xd7, 0xb6, 0 );
+ }
+ return FX_APPLIED;
+}
+
+//410 SummonAllyIWD2
+int fx_summon_ally (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ Point p(fx->PosX, fx->PosY);
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ core->SummonCreature(fx->Resource, fx->Resource2, Owner, target, p, EAM_ALLY, 0, newfx);
+ delete newfx;
+ return FX_NOT_APPLIED;
+}
+
+//411 SummonEnemyIWD2
+int fx_summon_enemy (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ Point p(fx->PosX, fx->PosY);
+ Effect *newfx = EffectQueue::CreateUnsummonEffect(fx);
+ core->SummonCreature(fx->Resource, fx->Resource2, Owner, target, p, EAM_ENEMY, 0, newfx);
+ delete newfx;
+ return FX_NOT_APPLIED;
+}
+
+//412 Control2
+
+static EffectRef fx_protection_from_evil_ref = { "ProtectionFromEvil", -1 };
+
+int fx_control (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ //prot from evil deflects it
+ if (target->fxqueue.HasEffect(fx_protection_from_evil_ref)) return FX_NOT_APPLIED;
+
+ if (0) printf( "fx_control (%2d)\n", fx->Opcode);
+ bool enemyally = true;
+ if (Owner->Type==ST_ACTOR) {
+ enemyally = ((Actor *) Owner)->GetStat(IE_EA)>EA_GOODCUTOFF; //or evilcutoff?
+ }
+
+ switch(fx->Parameter2)
+ {
+ case 0:
+ displaymsg->DisplayConstantStringName(STR_CHARMED, 0xf0f0f0, target);
+ break;
+ case 1:
+ displaymsg->DisplayConstantStringName(STR_DIRECHARMED, 0xf0f0f0, target)
+;
+ break;
+ default:
+ displaymsg->DisplayConstantStringName(STR_CONTROLLED, 0xf0f0f0, target);
+
+ break;
+ }
+ STATE_SET( STATE_CHARMED );
+ STAT_SET( IE_EA, enemyally?EA_ENEMY:EA_CHARMED );
+ return FX_APPLIED;
+}
+
+//413 VisualEffectIWD2
+//there are 32 bits, so they will fit on IE_SANCTUARY stat
+//i put them there because the first bit is sanctuary
+int fx_visual_effect_iwd2 (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ //
+ if (0) printf( "fx_visual_effect_iwd2 (%2d) Type: %d\n", fx->Opcode, fx->Parameter2);
+ unsigned int type = fx->Parameter2;
+ if (type<32) {
+ switch(type) {
+ case OV_ENTANGLE:
+ STAT_BIT_OR(IE_ENTANGLE, 1);
+ break;
+ case OV_SHIELDGLOBE:
+ STAT_BIT_OR(IE_SHIELDGLOBE, 1);
+ break;
+ case OV_GREASE:
+ STAT_BIT_OR(IE_GREASE, 1);
+ break;
+ case OV_WEB:
+ STAT_BIT_OR(IE_WEB, 1);
+ break;
+ case OV_MINORGLOBE: case OV_GLOBE:
+ STAT_BIT_OR(IE_MINORGLOBE, 1);
+ break;
+ case OV_SEVENEYES:
+ target->SetOverlay(OV_SEVENEYES2);
+ // some more
+ }
+ target->SetOverlay(type);
+ //STAT_BIT_OR(IE_SANCTUARY, 1<<type);
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+//414 ResilientSphere
+int fx_resilient_sphere (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_resilient_sphere (%2d)\n", fx->Opcode);
+ target->SetSpellState(SS_HELD|SS_RESILIENT);
+ STATE_SET(STATE_HELPLESS);
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_RESILIENT);
+ target->SetOverlay(OV_RESILIENT);
+ }
+ return FX_APPLIED;
+}
+
+//415 Barkskin
+int fx_barkskin (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_barkskin (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_BARKSKIN)) return FX_APPLIED;
+
+ int bonus;
+ if (fx->CasterLevel>6) {
+ if (fx->CasterLevel>12) {
+ bonus=5;
+ } else {
+ bonus=4;
+ }
+ } else {
+ bonus=3;
+ }
+ STAT_ADD(IE_ARMORCLASS,bonus);
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_BARKSKIN);
+ target->SetGradient(2);
+ }
+ return FX_APPLIED;
+}
+
+//416 BleedingWounds
+int fx_bleeding_wounds (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bleeding_wounds (%2d): Damage: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ //also this effect is executed every update
+ ieDword damage;
+ int tmp = fx->Parameter1;
+
+ switch(fx->Parameter2) {
+ case RPD_PERCENT:
+ damage = STAT_GET(IE_MAXHITPOINTS) * fx->Parameter1 / 100;
+ break;
+ case RPD_ROUNDS:
+ tmp *= 6;
+ goto seconds;
+ case RPD_TURNS:
+ tmp *= 30;
+ case RPD_SECONDS:
+seconds:
+ damage = 1;
+ if (tmp && (core->GetGame()->GameTime%tmp)) {
+ return FX_APPLIED;
+ }
+ break;
+ case RPD_POINTS:
+ damage = fx->Parameter1;
+ break;
+ default:
+ damage = 1;
+ break;
+ }
+ //percent
+ target->Damage(damage, DAMAGE_POISON, Owner);
+ target->AddPortraitIcon(PI_BLEEDING);
+ return FX_APPLIED;
+}
+
+//417 AreaEffect
+//move these flags to a header file if used elsewhere
+#define AE_REPEAT 1
+#define AE_TARGETEXCL 2
+
+int fx_area_effect (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_area_effect (%2d) Radius: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2);
+
+ //this effect ceases to affect dead targets (probably on frozen and stoned too)
+ Game *game = core->GetGame();
+ Map *map = NULL;
+
+ if (target) {
+ if (STATE_GET(STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+ map = target->GetCurrentArea();
+ } else {
+ map = game->GetCurrentArea();
+ }
+
+ if (fx->FirstApply) {
+ if (!fx->Parameter3) {
+ fx->Parameter3=AI_UPDATE_TIME;
+ } else {
+ fx->Parameter3*=AI_UPDATE_TIME;
+ }
+ fx->Parameter4 = 0;
+ }
+
+ if (fx->Parameter4>=game->GameTime) {
+ return FX_APPLIED;
+ }
+
+ fx->Parameter4 = game->GameTime+fx->Parameter3;
+ Point pos(fx->PosX, fx->PosY);
+
+ Spell *spell = gamedata->GetSpell(fx->Resource);
+ if (!spell) {
+ return FX_NOT_APPLIED;
+ }
+
+ EffectQueue *fxqueue = spell->GetEffectBlock(Owner, pos, 0, fx->CasterLevel);
+ fxqueue->SetOwner(Owner);
+ //bit 2 original target is excluded or not excluded
+ fxqueue->AffectAllInRange(map, pos, 0, 0,fx->Parameter1, fx->Parameter2&AE_TARGETEXCL?target:NULL);
+ delete fxqueue;
+
+ //bit 1 repeat or only once
+ if (fx->Parameter2&AE_REPEAT) {
+ return FX_APPLIED;
+ }
+ return FX_NOT_APPLIED;
+}
+
+//418 FreeAction2
+int fx_free_action_iwd2 (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_free_action_iwd2 (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_FREEACTION)) return FX_APPLIED;
+
+ // immunity to the following effects, coded in the effects:
+ // 0x9a Overlay:Entangle, ok
+ // 0x9d Overlay:Web ok
+ // 0x9e Overlay:Grease ok
+ // 0x6d State:Hold3 ok
+ // 0x28 State:Slowed ok
+ // 0xb0 MovementRateModifier2 ok
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_FREEACTION);
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0x80, 0x60, 0x60);
+ }
+ return FX_APPLIED;
+}
+
+//419 Unconsciousness
+//same as the sleep effect, but different icon
+int fx_unconsciousness (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_unconsciousness (%2d): Type: %d\n", fx->Opcode, fx->Parameter2);
+ STATE_SET(STATE_HELPLESS|STATE_SLEEP);
+ if (fx->Parameter2) {
+ target->SetSpellState(SS_NOAWAKE);
+ }
+ //
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_UNCONSCIOUS);
+ }
+ return FX_APPLIED;
+}
+
+//420 Death2 (see in core effects)
+
+//421 EntropyShield
+int fx_entropy_shield (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_entropy_shield (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_ENTROPY)) return FX_APPLIED;
+ if (!fx->Resource[0]) {
+ strnuprcpy(fx->Resource, "entropy", sizeof(ieResRef)-1);
+ }
+ //immunity to certain projectiles
+ ieDword *EntropyProjectileList = core->GetListFrom2DA(fx->Resource);
+ ieDword i = EntropyProjectileList[0];
+ //the index is handled differently because
+ //the list's first element is the element count
+ while(i) {
+ target->AddProjectileImmunity(EntropyProjectileList[i--]);
+ }
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_ENTROPY);
+ //entropy shield overlay
+ target->SetOverlay(OV_ENTROPY);
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0x40, 0xc0, 0x40);
+ }
+ return FX_APPLIED;
+}
+
+//422 StormShell
+int fx_storm_shell (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_storm_shell (%2d)\n", fx->Opcode);
+ if (target->SetSpellState(SS_STORMSHELL)) return FX_APPLIED;
+ STAT_ADD(IE_RESISTFIRE, 15);
+ STAT_ADD(IE_RESISTCOLD, 15);
+ STAT_ADD(IE_RESISTELECTRICITY, 15);
+
+ if (enhanced_effects) {
+ target->SetOverlay(OV_STORMSHELL);
+ }
+ return FX_APPLIED;
+}
+
+//423 ProtectionFromElements
+int fx_protection_from_elements (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_protection_from_elements (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_ELEMPROT)) return FX_APPLIED;
+ target->AddPortraitIcon(PI_ELEMPROT);
+ STAT_ADD(IE_RESISTFIRE, 15);
+ STAT_ADD(IE_RESISTCOLD, 15);
+ STAT_ADD(IE_RESISTACID, 15);
+ STAT_ADD(IE_RESISTELECTRICITY, 15);
+ //compatible with 2nd ed
+ STAT_ADD(IE_RESISTMAGICFIRE, 15);
+ STAT_ADD(IE_RESISTMAGICCOLD, 15);
+
+ if (enhanced_effects) {
+ target->SetColorMod(0xff, RGBModifier::ADD, 0x4f, 0, 0, 0xc0);
+ }
+ return FX_APPLIED;
+}
+
+//424 HoldUndead (see in core effects, 0x6d)
+//425 ControlUndead2 (see above)
+//426 Aegis
+int fx_aegis (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ ieDword tmp;
+
+ if (0) printf( "fx_aegis (%2d)\n", fx->Opcode);
+ //gives immunity against:
+ //0xda stoneskin
+ //0x9a entangle
+ //0x9e grease
+ //0x9d web
+ //0x6d hold
+ //0x28 slow
+
+ if (target->SetSpellState( SS_AEGIS)) return FX_APPLIED;
+ //deflection AC bonus
+ //
+ //physical damage reduction
+ STAT_ADD(IE_RESISTSLASHING, 10);
+ STAT_ADD(IE_RESISTCRUSHING, 10);
+ STAT_ADD(IE_RESISTPIERCING, 10);
+
+ //elemental damage reduction
+ STAT_ADD(IE_RESISTFIRE, 15);
+ STAT_ADD(IE_RESISTCOLD, 15);
+ STAT_ADD(IE_RESISTELECTRICITY, 15);
+ STAT_ADD(IE_RESISTACID, 15);
+
+ //magic resistance
+ STAT_ADD(IE_RESISTMAGIC, 3);
+
+ //saving throws
+ STAT_ADD(IE_SAVEFORTITUDE, 2);
+ STAT_ADD(IE_SAVEWILL, 2);
+ STAT_ADD(IE_SAVEREFLEX, 2);
+
+ if (fx->FirstApply) {
+ fx->Parameter1=8;
+ }
+ tmp = STAT_GET(IE_STONESKINS);
+ if (fx->Parameter1>tmp) {
+ STAT_SET(IE_STONESKINS, fx->Parameter1);
+ }
+
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_AEGIS);
+ target->SetColorMod(0xff, RGBModifier::ADD, 30, 0x80, 0x60, 0x60);
+ target->SetGradient(14);
+ }
+
+ return FX_APPLIED;
+}
+
+//427 ExecutionerEyes
+int fx_executioner_eyes (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_executioner_eyes (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_EXECUTIONER)) return FX_APPLIED;
+
+ STAT_ADD(IE_CRITICALHITBONUS, 4);
+ STAT_ADD(IE_TOHIT, 4);
+
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_EXECUTIONER);
+ target->SetGradient(8);
+ }
+ return FX_APPLIED;
+}
+
+//428 Death3 (also a death effect)
+
+//429 WhenStruckUseEffectList
+
+//430 ProjectileUseEffectList
+int fx_projectile_use_effect_list (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_projectile_use_effect_list (%2d) Type: %d Spell:%s\n", fx->Opcode, fx->Parameter2, fx->Resource);
+
+ if (!Owner) {
+ return FX_NOT_APPLIED;
+ }
+ Map *map = Owner->GetCurrentArea();
+ if (!map) {
+ return FX_NOT_APPLIED;
+ }
+ Spell* spl = gamedata->GetSpell( fx->Resource );
+ //create projectile from known spellheader
+ //cannot get the projectile from the spell
+ Projectile *pro = core->GetProjectileServer()->GetProjectileByIndex(fx->Parameter2);
+
+ if (pro) {
+ Point p(fx->PosX, fx->PosY);
+
+ pro->SetEffects(spl->GetEffectBlock(Owner, p, 0, fx->CasterLevel, fx->Parameter2));
+ Point origin(fx->PosX, fx->PosY);
+ pro->SetCaster(fx->CasterID, fx->CasterLevel);
+ if (target) {
+ map->AddProjectile( pro, origin, target->GetGlobalID(), false);
+ } else {
+ map->AddProjectile( pro, origin, origin);
+ }
+ }
+ return FX_NOT_APPLIED;
+}
+
+//431 EnergyDrain
+
+int fx_energy_drain (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_energy_drain (%2d) Type: %d\n", fx->Opcode, fx->Parameter1);
+ if (!fx->Parameter1) {
+ return FX_NOT_APPLIED;
+ }
+ if (fx->FirstApply) {
+ //display string?
+
+ //hitpoints don't have base/modified, only current
+ BASE_SUB(IE_HITPOINTS, fx->Parameter1*5);
+ }
+ //if there is another energy drain effect (level drain), add them up
+ STAT_ADD(IE_LEVELDRAIN, fx->Parameter1);
+ STAT_SUB(IE_SAVEFORTITUDE, fx->Parameter1);
+ STAT_SUB(IE_SAVEREFLEX, fx->Parameter1);
+ STAT_SUB(IE_SAVEWILL, fx->Parameter1);
+ //these saving throws don't exist in 2nd edition, but to make it portable, we should affect ALL saving throws
+ STAT_SUB(IE_SAVEVSBREATH, fx->Parameter1);
+ STAT_SUB(IE_SAVEVSSPELL, fx->Parameter1);
+ STAT_SUB(IE_MAXHITPOINTS, fx->Parameter1*5);
+ return FX_APPLIED;
+}
+
+//432 TortoiseShell
+int fx_tortoise_shell (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_tortoise_shell (%2d) Type: %d\n", fx->Opcode, fx->Parameter2);
+ if (target->SetSpellState( SS_TORTOISE)) return FX_NOT_APPLIED;
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_TORTOISE);
+ target->SetOverlay(OV_TORTOISE);
+ }
+ return FX_APPLIED;
+}
+
+//433 Blink
+int fx_blink (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_blink (%2d) Type: %d\n", fx->Opcode, fx->Parameter2);
+
+ if (target->SetSpellState( SS_BLINK)) return FX_APPLIED;
+
+ //pulsating translucence (like with invisibility)
+ ieDword Trans = fx->Parameter4;
+ if (fx->Parameter3) {
+ if (Trans>=240) {
+ fx->Parameter3=0;
+ } else {
+ Trans+=16;
+ }
+ } else {
+ if (Trans<=32) {
+ fx->Parameter3=1;
+ } else {
+ Trans-=16;
+ }
+ }
+ fx->Parameter4=Trans;
+ STAT_SET( IE_TRANSLUCENT, Trans);
+
+ if(fx->Parameter2) {
+ target->AddPortraitIcon(PI_EMPTYBODY);
+ return FX_APPLIED;
+ }
+
+ target->AddPortraitIcon(PI_BLINK);
+ return FX_APPLIED;
+}
+
+//434 PersistentUseEffectList
+int fx_persistent_use_effect_list (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_persistent_use_effect_list (%2d) Interval: %d Resource: %.8s\n", fx->Opcode, fx->Parameter1, fx->Resource);
+ if (fx->Parameter3) {
+ fx->Parameter3--;
+ } else {
+ core->ApplySpell(fx->Resource, target, Owner, fx->Power);
+ fx->Parameter3=fx->Parameter1;
+ }
+ return FX_APPLIED;
+}
+
+//435 DayBlindness
+int fx_day_blindness (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_day_blindness (%2d) Amount: %d\n", fx->Opcode, fx->Parameter2);
+ if (target->SetSpellState(SS_DAYBLINDNESS)) return FX_NOT_APPLIED;
+ // medium hack (better than original)
+ // the original used explicit race/subrace values
+ ieDword penalty;
+
+ if (check_iwd_targeting(Owner, target, 0, 82)) penalty = 2; //dark elf
+ else if (check_iwd_targeting(Owner, target, 0, 84)) penalty = 2; //duergar
+ else penalty = 0;
+
+ STAT_ADD(IE_SAVEFORTITUDE, penalty);
+ STAT_ADD(IE_SAVEREFLEX, penalty);
+ STAT_ADD(IE_SAVEWILL, penalty);
+ //for compatibility reasons
+ STAT_ADD(IE_SAVEVSBREATH, penalty);
+ STAT_ADD(IE_SAVEVSSPELL, penalty);
+
+ //TODO: implement the rest
+ return FX_APPLIED;
+}
+
+//436 DamageReduction
+int fx_damage_reduction (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_damage_reduction (%2d) Amount: %d\n", fx->Opcode, fx->Parameter2);
+ STAT_SET(IE_RESISTSLASHING, fx->Parameter2*5);
+ STAT_SET(IE_RESISTCRUSHING, fx->Parameter2*5);
+ STAT_SET(IE_RESISTPIERCING, fx->Parameter2*5);
+ return FX_APPLIED;
+}
+
+//437 Disguise
+//modifies character animations to look like clerics of same gender/race
+int fx_disguise (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_disguise (%2d) Amount: %d\n", fx->Opcode, fx->Parameter2);
+ ieDword anim = BASE_GET(IE_ANIMATION_ID);
+ if (anim>=0x6000 || anim<=0x6fff) {
+ STAT_SET(IE_ANIMATION_ID, anim&0x600f);
+ return FX_APPLIED;
+ }
+
+ if (anim>=0x5000 || anim<=0x5fff) {
+ STAT_SET(IE_ANIMATION_ID, anim&0x500f);
+ return FX_APPLIED;
+ }
+ //set avatar anim?
+ return FX_NOT_APPLIED;
+}
+
+//438 HeroicInspiration
+int fx_heroic_inspiration (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_heroic_inspiration (%2d)\n", fx->Opcode);
+
+ //+1 to all saves
+ STAT_ADD( IE_SAVEFORTITUDE, 1);
+ STAT_ADD( IE_SAVEREFLEX, 1);
+ STAT_ADD( IE_SAVEWILL, 1);
+ //make it compatible with 2nd edition
+ STAT_ADD( IE_SAVEVSBREATH, 1);
+ STAT_ADD( IE_SAVEVSSPELL, 1);
+
+ return FX_APPLIED;
+}
+//439 PreventAISlowDown
+//same as BG2 OffscreenAIModifier
+
+//440 BarbarianRage
+int fx_barbarian_rage (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_barbarian_rage (%2d) Amount:%d\n", fx->Opcode, fx->Parameter1);
+ //TODO: implement this
+ return FX_APPLIED;
+}
+
+//441 MovementRateModifier4 (same as others)
+
+//442 Unknown (needs research)
+
+//443 MissileDamageReduction
+int fx_missile_damage_reduction (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_missile_damage_reduction (%2d) Amount:%d\n", fx->Opcode, fx->Parameter1);
+ STAT_SET(IE_RESISTMISSILE, fx->Parameter1);
+ //TODO: didn't set the pluses
+ return FX_APPLIED;
+}
+
+//444 TensersTransformation
+int fx_tenser_transformation (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_tenser_transformation (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_TENSER)) return FX_APPLIED;
+ STAT_SUB(IE_ARMORCLASS, 2);
+
+ if (enhanced_effects) {
+ target->SetGradient(0x3e);
+ }
+ return FX_APPLIED;
+}
+
+//445 Unknown (empty function in iwd2)
+
+//446 SmiteEvil
+int fx_smite_evil (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_smite_evil (%2d)\n", fx->Opcode);
+ target->SetSpellState(SS_SMITEEVIL);
+ return FX_NOT_APPLIED;
+}
+
+//447 Restoration
+
+static EffectRef fx_disease_ref = { "Disease", -1 };
+static EffectRef fx_int_ref = { "IntelligenceModifier", -1 };
+static EffectRef fx_wis_ref = { "WisdomModifier", -1 };
+static EffectRef fx_cha_ref = { "CharismaModifier", -1 };
+
+int fx_restoration (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_restoration (%2d)\n", fx->Opcode);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_disease_ref, 4);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_disease_ref, 5);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_disease_ref, 6);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_disease_ref, 7);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_disease_ref, 8);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_disease_ref, 9);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_disease_ref, 13);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_disease_ref, 14);
+ target->fxqueue.RemoveAllEffectsWithParam(fx_disease_ref, 15);
+
+ target->fxqueue.RemoveAllDetrimentalEffects(fx_str_ref, BASE_GET(IE_STR));
+ target->fxqueue.RemoveAllDetrimentalEffects(fx_int_ref, BASE_GET(IE_INT));
+ target->fxqueue.RemoveAllDetrimentalEffects(fx_wis_ref, BASE_GET(IE_WIS));
+ target->fxqueue.RemoveAllDetrimentalEffects(fx_con_ref, BASE_GET(IE_CON));
+ target->fxqueue.RemoveAllDetrimentalEffects(fx_dex_ref, BASE_GET(IE_DEX));
+ target->fxqueue.RemoveAllDetrimentalEffects(fx_cha_ref, BASE_GET(IE_CHR));
+ return FX_NOT_APPLIED;
+}
+
+//448 AlicornLance
+int fx_alicorn_lance (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_alicorn_lance (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_ALICORNLANCE)) return FX_APPLIED;
+ ////target->AddPortraitIcon(PI_ALICORN); //no portrait icon
+ STAT_SUB(IE_ARMORCLASS, 2);
+ //color glow
+ if (enhanced_effects) {
+ target->SetColorMod(0xff, RGBModifier::ADD, 1, 0xb9, 0xb9, 0xb9);
+ }
+ return FX_APPLIED;
+}
+
+//449 CallLightning
+Actor *GetRandomEnemySeen(Map *map, Actor *origin)
+{
+ int type = GetGroup(origin);
+
+ if (type==2) {
+ return NULL; //no enemies
+ }
+ int i = map->GetActorCount(true);
+ //see a random enemy
+ int pos = core->Roll(1,i,-1);
+ i -= pos;
+ while(i--) {
+ Actor *ac = map->GetActor(i,true);
+ if (!CanSee(origin, ac, true, GA_NO_DEAD|GA_NO_HIDDEN)) continue;
+ if (type) { //origin is PC
+ if (ac->GetStat(IE_EA) >= EA_EVILCUTOFF) {
+ return ac;
+ }
+ }
+ else {
+ if (ac->GetStat(IE_EA) <= EA_GOODCUTOFF) {
+ return ac;
+ }
+ }
+ }
+
+ i=map->GetActorCount(true);
+ while(i--!=pos) {
+ Actor *ac = map->GetActor(i,true);
+ if (!CanSee(origin, ac, true, GA_NO_DEAD|GA_NO_HIDDEN)) continue;
+ if (type) { //origin is PC
+ if (ac->GetStat(IE_EA) >= EA_EVILCUTOFF) {
+ return ac;
+ }
+ }
+ else {
+ if (ac->GetStat(IE_EA) <= EA_GOODCUTOFF) {
+ return ac;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int fx_call_lightning (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_call_lightning (%2d)\n", fx->Opcode);
+
+ //this effect ceases to affect dead targets (probably on frozen and stoned too)
+ if (STATE_GET(STATE_DEAD)) {
+ return FX_NOT_APPLIED;
+ }
+ int ret = FX_APPLIED;
+
+ Map *map = target->GetCurrentArea();
+ if (!map) return ret;
+
+ if (fx->Parameter1<=1) {
+ ret = FX_NOT_APPLIED;
+ }
+
+ //timing
+ fx->TimingMode=FX_DURATION_DELAY_PERMANENT;
+ fx->Duration=core->GetGame()->GameTime+70*15;
+ fx->Parameter1--;
+
+ //calculate victim (an opponent of target)
+ Actor *victim = GetRandomEnemySeen(map, target);
+ if (!victim) {
+ displaymsg->DisplayConstantStringName(STR_LIGHTNING_DISS, 0xf0f0f0, target);
+ return ret;
+ }
+
+ //iwd2 style
+ if (fx->Resource[0]) {
+ core->ApplySpell(fx->Resource, victim, target, fx->Power);
+ return ret;
+ }
+
+ //how style
+ victim->Damage(DICE_ROLL(0), DAMAGE_ELECTRICITY, target);
+ return ret;
+}
+
+//450 GlobeInvulnerability
+int fx_globe_invulnerability (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_globe_invulnerability (%2d)\n", fx->Opcode);
+ int state;
+ int icon;
+ int value;
+ int overlay;
+
+ if (fx->Parameter2) {
+ state = SS_MAJORGLOBE;
+ icon = PI_MAJORGLOBE;
+ value = 30; //if globe is needed, use 31
+ overlay = OV_GLOBE;
+ } else {
+ state = SS_MINORGLOBE;
+ icon = PI_MINORGLOBE;
+ value = 14; //if globe is needed use 15
+ overlay = OV_MINORGLOBE;
+ }
+ if (target->SetSpellState( state)) return FX_APPLIED;
+
+ STAT_BIT_OR(IE_MINORGLOBE, value);
+ if (enhanced_effects) {
+ target->AddPortraitIcon(icon);
+ target->SetOverlay(overlay);
+ }
+ return FX_APPLIED;
+}
+
+//451 LowerResistance
+int fx_lower_resistance (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_lower_resistance (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_LOWERRESIST)) return FX_APPLIED;
+
+ STAT_SUB(IE_RESISTMAGIC, 15);
+ return FX_APPLIED;
+}
+
+//452 Bane
+static EffectRef fx_bless_ref = { "Bless", -1 };
+
+int fx_bane (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_bane (%2d)\n", fx->Opcode);
+
+ if (target->SetSpellState( SS_BANE)) return FX_NOT_APPLIED;
+ //do this once
+ if (fx->FirstApply)
+ target->fxqueue.RemoveAllEffects(fx_bless_ref);
+ if (enhanced_effects) {
+ target->AddPortraitIcon(PI_BANE);
+ target->SetColorMod(0xff, RGBModifier::ADD, 20, 0, 0, 0x80);
+ }
+ STAT_ADD( IE_TOHIT, -fx->Parameter1);
+ STAT_ADD( IE_MORALEBREAK, -fx->Parameter1);
+ return FX_APPLIED;
+}
+
+//453 PowerAttack
+static EffectRef fx_expertise_ref = { "Expertise", -1 };
+
+int fx_power_attack (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_power_attack (%2d)\n", fx->Opcode);
+
+ if (fx->FirstApply) {
+ if (target->HasSpellState(SS_EXPERTISE)) {
+ //stopped using feat: Expertise
+ target->fxqueue.RemoveAllEffects(fx_expertise_ref);
+ }
+ }
+ unsigned int x;
+ x = target->GetFeat(FEAT_POWER_ATTACK);
+ if (x>5) x=5;
+ for(unsigned int i = 0;i<5;i++) {
+ if (i<x) {
+ target->SetSpellState( SS_POWERATTACK);
+ } else {
+ //FIXME: doubt we need UnSetSpellState
+ //target->UnSetSpellState(SS_POWERATTACK+x);
+ }
+ }
+ return FX_APPLIED;
+}
+
+//454 Expertise
+static EffectRef fx_powerattack_ref = { "PowerAttack", -1 };
+
+int fx_expertise (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+//expertise feat:
+//convert positive base attack bonus into AC (dodge bonus)
+//up to feat_expertise count (player's choice)
+ if (0) printf( "fx_expertise (%2d)\n", fx->Opcode);
+ if (fx->FirstApply) {
+ if (target->HasSpellState(SS_POWERATTACK)) {
+ target->fxqueue.RemoveAllEffects(fx_powerattack_ref);
+ }
+ }
+ unsigned int x;
+
+ x = target->GetFeat(FEAT_EXPERTISE);
+ if (x>5) x=5;
+ for(unsigned int i = 0;i<5;i++) {
+ if (i<x) {
+ target->SetSpellState(SS_EXPERTISE+x);
+ } else {
+ //FIXME: doubt we need UnSetSpellState
+ //target->UnSetSpellState(SS_EXPERTISE+x);
+ }
+ }
+ return FX_APPLIED;
+}
+
+//455 ArterialStrike
+static EffectRef fx_hamstring_ref = { "HamString", -1 };
+
+int fx_arterial_strike (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_arterial_strike (%2d)\n", fx->Opcode);
+ if (fx->FirstApply) {
+ if (target->HasSpellState(SS_HAMSTRING)) {
+ target->fxqueue.RemoveAllEffects(fx_hamstring_ref);
+ }
+ }
+ if (target->SetSpellState( SS_ARTERIAL)) {
+ //TODO:display using feat arterial strike
+ return FX_APPLIED;
+ }
+ return FX_APPLIED;
+}
+
+//456 HamString
+static EffectRef fx_arterialstrike_ref = { "ArterialStrike", -1 };
+
+int fx_hamstring (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_hamstring (%2d)\n", fx->Opcode);
+ if (fx->FirstApply) {
+ if (target->HasSpellState(SS_ARTERIAL)) {
+ target->fxqueue.RemoveAllEffects(fx_arterialstrike_ref);
+ }
+ }
+ if (target->SetSpellState( SS_HAMSTRING)) {
+ //TODO:display using feat hamstring
+ return FX_APPLIED;
+ }
+ return FX_APPLIED;
+}
+
+//457 RapidShot
+int fx_rapid_shot (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_rapid_shot (%2d)\n", fx->Opcode);
+ if (target->SetSpellState( SS_RAPIDSHOT)) return FX_APPLIED;
+ //TODO:
+ return FX_APPLIED;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x4F172B2, "Effect opcodes for the icewind branch of the games")
+PLUGIN_INITIALIZER(RegisterIWDOpcodes)
+PLUGIN_CLEANUP(Cleanup)
+END_PLUGIN()
+
diff --git a/gemrb/plugins/IWDOpcodes/Makefile.am b/gemrb/plugins/IWDOpcodes/Makefile.am
new file mode 100644
index 0000000..b7bc1f8
--- /dev/null
+++ b/gemrb/plugins/IWDOpcodes/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = IWDOpcodes.la
+IWDOpcodes_la_LDFLAGS = -module -avoid-version -shared
+IWDOpcodes_la_SOURCES = IWDOpcodes.cpp
diff --git a/gemrb/plugins/KEYImporter/CMakeLists.txt b/gemrb/plugins/KEYImporter/CMakeLists.txt
new file mode 100644
index 0000000..de14082
--- /dev/null
+++ b/gemrb/plugins/KEYImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (KEYImporter KEYImporter.cpp Dictionary.cpp)
diff --git a/gemrb/plugins/KEYImporter/Dictionary.cpp b/gemrb/plugins/KEYImporter/Dictionary.cpp
new file mode 100644
index 0000000..73be141
--- /dev/null
+++ b/gemrb/plugins/KEYImporter/Dictionary.cpp
@@ -0,0 +1,226 @@
+/* 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 "Dictionary.h"
+
+#include "globals.h"
+#include "win32def.h"
+
+#include <ctype.h>
+
+/////////////////////////////////////////////////////////////////////////////
+// inlines
+
+inline unsigned int Dictionary::MyHashKey(const char* key, unsigned int type) const
+{
+ unsigned int nHash = type;
+ for (int i = 0; i < KEYSIZE && key[i]; i++) {
+ nHash = ( nHash << 5 ) + nHash + tolower( key[i] );
+ }
+ return nHash;
+}
+inline int Dictionary::GetCount() const
+{
+ return m_nCount;
+}
+inline bool Dictionary::IsEmpty() const
+{
+ return m_nCount == 0;
+}
+/////////////////////////////////////////////////////////////////////////////
+// out of lines
+Dictionary::Dictionary(int nBlockSize, int nHashTableSize)
+{
+ assert( nBlockSize > 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 Dictionary::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 = (Dictionary::MyAssoc **) malloc( sizeof( MyAssoc * ) * nHashSize );
+ memset( m_pHashTable, 0, sizeof( MyAssoc * ) * nHashSize );
+ }
+ m_nHashTableSize = nHashSize;
+}
+
+void Dictionary::RemoveAll()
+{
+ //removed the part about freeing values/keys
+ //because the complete value/key pair is stored in the
+ //node which is freed in the memblocks
+
+ // 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;
+}
+
+Dictionary::~Dictionary()
+{
+ RemoveAll();
+}
+
+Dictionary::MyAssoc* Dictionary::NewAssoc()
+{
+ if (m_pFreeList == NULL) {
+ // add another block
+ Dictionary::MemBlock* newBlock = ( Dictionary::MemBlock* ) malloc(m_nBlockSize * sizeof( Dictionary::MyAssoc ) + sizeof( Dictionary::MemBlock ));
+ assert( newBlock != NULL ); // we must have something
+
+ newBlock->pNext = m_pBlocks;
+ m_pBlocks = newBlock;
+
+ // chain them into free list
+ Dictionary::MyAssoc* pAssoc = ( Dictionary::MyAssoc* )
+ ( newBlock + 1 );
+ for (int i = 0; i < m_nBlockSize; i++) {
+ pAssoc->pNext = m_pFreeList;
+ m_pFreeList = pAssoc++;
+ }
+ }
+
+ Dictionary::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->value = 0xcccccccc;
+#endif
+ return pAssoc;
+}
+
+void Dictionary::FreeAssoc(Dictionary::MyAssoc* pAssoc)
+{
+ 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();
+ }
+}
+
+Dictionary::MyAssoc* Dictionary::GetAssocAt(const ieResRef key,
+ unsigned int type, unsigned int& nHash) const
+ // find association (or return NULL)
+{
+ nHash = MyHashKey( key, type ) % m_nHashTableSize;
+
+ if (m_pHashTable == NULL) {
+ return NULL;
+ }
+
+ // see if it exists
+ MyAssoc* pAssoc;
+ for (pAssoc = m_pHashTable[nHash];
+ pAssoc != NULL;
+ pAssoc = pAssoc->pNext) {
+ if (type == pAssoc->type) {
+ if (!strnicmp( pAssoc->key, key, KEYSIZE )) {
+ return pAssoc;
+ }
+ }
+ }
+ return NULL;
+}
+
+bool Dictionary::Lookup(const ieResRef key, unsigned int type,
+ unsigned int& rValue) const
+{
+ unsigned int nHash;
+
+ MyAssoc* pAssoc = GetAssocAt( key, type, nHash );
+ if (pAssoc == NULL) {
+ return false;
+ } // not in map
+
+ rValue = pAssoc->value;
+ return true;
+}
+
+void Dictionary::RemoveAt(const ieResRef key, unsigned int type)
+{
+ unsigned int nHash;
+ MyAssoc* pAssoc = GetAssocAt( key, type, nHash );
+
+ if (pAssoc != NULL) {
+ FreeAssoc(pAssoc);
+ }
+}
+
+void Dictionary::SetAt(const ieResRef key, unsigned int type, unsigned int value)
+{
+ int i;
+ unsigned int nHash;
+ MyAssoc* pAssoc=GetAssocAt( key, type, nHash );
+
+ if (pAssoc == NULL) {
+ if (m_pHashTable == NULL)
+ InitHashTable( m_nHashTableSize );
+
+ // it doesn't exist, add a new Association
+ pAssoc = NewAssoc();
+ // put into hash table
+ pAssoc->pNext = m_pHashTable[nHash];
+ m_pHashTable[nHash] = pAssoc;
+ }
+ for(i=0;i<KEYSIZE && key[i];i++) {
+ pAssoc->key[i]=tolower(key[i]);
+ }
+ for(;i<KEYSIZE;i++) {
+ pAssoc->key[i]=0;
+ }
+ pAssoc->type = type;
+ pAssoc->value = value;
+}
diff --git a/gemrb/plugins/KEYImporter/Dictionary.h b/gemrb/plugins/KEYImporter/Dictionary.h
new file mode 100644
index 0000000..ce8aee3
--- /dev/null
+++ b/gemrb/plugins/KEYImporter/Dictionary.h
@@ -0,0 +1,84 @@
+/* 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.
+ *
+ *
+ */
+
+#if !defined(DICTIONARY_H)
+#define DICTIONARY_H
+
+#include "SClassID.h"
+#include "win32def.h"
+
+#include <cstring>
+
+/////////////////////////////////////////////////////////////////////////////
+// Dictionary<unsigned long, VALUE>
+
+#define KEYSIZE 8
+
+class Dictionary //: public CObject
+{
+protected:
+ // Association
+ struct MyAssoc {
+ MyAssoc* pNext;
+ char key[KEYSIZE]; //not ieResRef!
+ SClass_ID type;
+ unsigned int value;
+ };
+ struct MemBlock {
+ MemBlock* pNext;
+ };
+
+public:
+ // Construction
+ Dictionary(int nBlockSize = 10, int nHashTableSize = 32769);
+
+ // Attributes
+ // number of elements
+ int GetCount() const;
+ bool IsEmpty() const;
+
+ // Lookup
+ bool Lookup(const ieResRef key, unsigned int type, unsigned int& rValue) const;
+
+ // Operations
+ void SetAt(const ieResRef key, unsigned int type, unsigned int newValue);
+ void RemoveAt(const ieResRef key, unsigned int type);
+ void RemoveAll();
+ 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;
+
+ MyAssoc* NewAssoc();
+ void FreeAssoc(MyAssoc*);
+ MyAssoc* GetAssocAt(const ieResRef, unsigned int, unsigned int&) const;
+ unsigned int MyHashKey(const ieResRef, unsigned int) const;
+
+public:
+ ~Dictionary();
+};
+
+#endif // !defined(DICTIONARY_H)
diff --git a/gemrb/plugins/KEYImporter/KEYImporter.cpp b/gemrb/plugins/KEYImporter/KEYImporter.cpp
new file mode 100644
index 0000000..a444a49
--- /dev/null
+++ b/gemrb/plugins/KEYImporter/KEYImporter.cpp
@@ -0,0 +1,289 @@
+/* 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 "KEYImporter.h"
+
+#include "win32def.h"
+#include "globals.h"
+
+#include "ArchiveImporter.h"
+#include "Interface.h"
+#include "ResourceDesc.h"
+#include "System/FileStream.h"
+
+KEYImporter::KEYImporter(void)
+{
+ description = NULL;
+}
+
+KEYImporter::~KEYImporter(void)
+{
+ free(description);
+ for (unsigned int i = 0; i < biffiles.size(); i++) {
+ free( biffiles[i].name );
+ }
+}
+
+static char* AddCBF(char *file)
+{
+ // This is safe in single-threaded, since the
+ // return value is passed straight to PathJoin.
+ static char cbf[_MAX_PATH];
+ strcpy(cbf,file);
+ char *dot = strrchr(cbf, '.');
+ if (dot)
+ strcpy(dot, ".cbf");
+ else
+ strcat(cbf, ".cbf");
+ return cbf;
+}
+
+static bool PathExists(BIFEntry *entry, const char *path)
+{
+ PathJoin(entry->path, path, entry->name, NULL);
+ if (file_exists(entry->path)) {
+ return true;
+ }
+ PathJoin(entry->path, path, AddCBF(entry->name), NULL);
+ if (file_exists(entry->path)) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool PathExists(BIFEntry *entry, const std::vector<std::string> &pathlist)
+{
+ size_t i;
+
+ for(i=0;i<pathlist.size();i++) {
+ if (PathExists(entry, pathlist[i].c_str() )) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void FindBIF(BIFEntry *entry)
+{
+ entry->cd = 0;
+ entry->found = PathExists(entry, core->GamePath);
+ if (entry->found) {
+ return;
+ }
+
+ if (core->GameOnCD) {
+ int mask = 1<<2;
+ for(int cd = 1; cd<=MAX_CD; cd++) {
+ if ((entry->BIFLocator & mask) != 0) {
+ entry->cd = cd;
+ break;
+ }
+ mask += mask;
+ }
+
+ if (!entry->cd) {
+ printStatus( "ERROR", LIGHT_RED );
+ printf( "Cannot find %s... Resource unavailable.\n",
+ entry->name );
+ entry->found = false;
+ return;
+ }
+ entry->found = PathExists(entry, core->CD[entry->cd-1]);
+ return;
+ }
+
+ for (int i = 0; i < MAX_CD; i++) {
+ if (PathExists(entry, core->CD[i]) ) {
+ entry->found = true;
+ return;
+ }
+ }
+
+ printMessage( "KEYImporter", " ", WHITE );
+ printf( "Cannot find %s...", entry->name );
+ printStatus( "ERROR", LIGHT_RED );
+}
+
+bool KEYImporter::Open(const char *resfile, const char *desc)
+{
+ free(description);
+ description = strdup(desc);
+ if (!core->IsAvailable( IE_BIF_CLASS_ID )) {
+ printf( "[ERROR]\nAn Archive Plug-in is not Available\n" );
+ return false;
+ }
+ unsigned int i;
+ // NOTE: Interface::Init has already resolved resfile.
+ printMessage( "KEYImporter", "Opening ", WHITE );
+ printf( "%s...", resfile );
+ FileStream* f = new FileStream();
+ if (!f->Open( resfile )) {
+ // Check for backslashes (false escape characters)
+ // this check probably belongs elsewhere (e.g. ResolveFilePath)
+ if (strstr( resfile, "\\ " )) {
+ printf("%s", "\nEscaped space(s) detected in path!. Do not escape spaces in your GamePath! " );
+ }
+ printStatus( "ERROR", LIGHT_RED );
+ printMessage( "KEYImporter", "Cannot open Chitin.key\n", LIGHT_RED );
+ textcolor( WHITE );
+ delete( f );
+ return false;
+ }
+ printStatus( "OK", LIGHT_GREEN );
+ printMessage( "KEYImporter", "Checking file type...", WHITE );
+ char Signature[8];
+ f->Read( Signature, 8 );
+ if (strncmp( Signature, "KEY V1 ", 8 ) != 0) {
+ printStatus( "ERROR", LIGHT_RED );
+ printMessage( "KEYImporter", "File has an Invalid Signature.\n",
+ LIGHT_RED );
+ textcolor( WHITE );
+ delete( f );
+ return false;
+ }
+ printStatus( "OK", LIGHT_GREEN );
+ printMessage( "KEYImporter", "Reading Resources...\n", WHITE );
+ ieDword BifCount, ResCount, BifOffset, ResOffset;
+ f->ReadDword( &BifCount );
+ f->ReadDword( &ResCount );
+ f->ReadDword( &BifOffset );
+ f->ReadDword( &ResOffset );
+ printMessage( "KEYImporter", " ", WHITE );
+ printf( "BIF Files Count: %d (Starting at %d Bytes)\n", BifCount,
+ BifOffset );
+ printMessage( "KEYImporter", " ", WHITE );
+ printf( "RES Count: %d (Starting at %d Bytes)\n", ResCount, ResOffset );
+ f->Seek( BifOffset, GEM_STREAM_START );
+ ieDword BifLen, ASCIIZOffset;
+ ieWord ASCIIZLen;
+ for (i = 0; i < BifCount; i++) {
+ BIFEntry be;
+ f->Seek( BifOffset + ( 12 * i ), GEM_STREAM_START );
+ f->ReadDword( &BifLen );
+ f->ReadDword( &ASCIIZOffset );
+ f->ReadWord( &ASCIIZLen );
+ f->ReadWord( &be.BIFLocator );
+ be.name = ( char * ) malloc( ASCIIZLen );
+ f->Seek( ASCIIZOffset, GEM_STREAM_START );
+ f->Read( be.name, ASCIIZLen );
+ for (int p = 0; p < ASCIIZLen; p++) {
+ //some MAC versions use : as delimiter
+ if (be.name[p] == '\\' || be.name[p] == ':')
+ be.name[p] = PathDelimiter;
+ }
+ if (be.name[0] == PathDelimiter) {
+ // totl has '\data\zcMHar.bif' in the key file, and the CaseSensitive
+ // code breaks with that extra slash, so simple fix: remove it
+ ASCIIZLen--;
+ for (int p = 0; p < ASCIIZLen; p++)
+ be.name[p] = be.name[p + 1];
+ // (if you change this, try moving to ar9700 for testing)
+ }
+ FindBIF(&be);
+ biffiles.push_back( be );
+ }
+ f->Seek( ResOffset, GEM_STREAM_START );
+ resources.InitHashTable( ResCount < 17 ? 17 : ResCount );
+ for (i = 0; i < ResCount; i++) {
+ RESEntry re;
+ f->ReadResRef( re.ResRef );
+ f->ReadWord( &re.Type );
+ f->ReadDword( &re.ResLocator );
+ resources.SetAt( re.ResRef, re.Type, re.ResLocator );
+ }
+ printMessage( "KEYImporter", "Resources Loaded...", WHITE );
+ printStatus( "OK", LIGHT_GREEN );
+ delete( f );
+ return true;
+}
+
+bool KEYImporter::HasResource(const char* resname, SClass_ID type)
+{
+ unsigned int ResLocator;
+ return resources.Lookup( resname, type, ResLocator );
+}
+
+bool KEYImporter::HasResource(const char* resname, const ResourceDesc &type)
+{
+ return HasResource(resname, type.GetKeyType());
+}
+
+static void FindBIFOnCD(BIFEntry *entry)
+{
+ if (file_exists(entry->path)) {
+ entry->found = true;
+ return;
+ }
+
+ core->WaitForDisc(entry->cd, entry->name);
+
+ entry->found = PathExists(entry, core->CD[entry->cd-1]);
+}
+
+DataStream* KEYImporter::GetStream(const char *resname, ieWord type)
+{
+ unsigned int ResLocator;
+
+ if (type == 0)
+ return NULL;
+ if (resources.Lookup( resname, type, ResLocator )) {
+ int bifnum = ( ResLocator & 0xFFF00000 ) >> 20;
+
+ if (core->GameOnCD && (biffiles[bifnum].cd != 0))
+ FindBIFOnCD(&biffiles[bifnum]);
+ if (!biffiles[bifnum].found) {
+ printf( "Cannot find %s... Resource unavailable.\n",
+ biffiles[bifnum].name );
+ return NULL;
+ }
+
+ PluginHolder<ArchiveImporter> ai(IE_BIF_CLASS_ID);
+ if (ai->OpenArchive( biffiles[bifnum].path ) == GEM_ERROR) {
+ printf("Cannot open archive %s\n", biffiles[bifnum].path );
+ return NULL;
+ }
+ DataStream* ret = ai->GetStream( ResLocator, type );
+ if (ret) {
+ strnlwrcpy( ret->filename, resname, 8 );
+ strcat( ret->filename, core->TypeExt( type ) );
+ return ret;
+ }
+ }
+ return NULL;
+}
+
+DataStream* KEYImporter::GetResource(const char* resname, SClass_ID type)
+{
+ //the word masking is a hack for synonyms, currently used for bcs==bs
+ return GetStream(resname, type&0xFFFF);
+}
+
+DataStream* KEYImporter::GetResource(const char* resname, const ResourceDesc &type)
+{
+ return GetStream(resname, type.GetKeyType());
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x1DFDEF80, "KEY File Importer")
+PLUGIN_CLASS(PLUGIN_RESOURCE_KEY, KEYImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/KEYImporter/KEYImporter.h b/gemrb/plugins/KEYImporter/KEYImporter.h
new file mode 100644
index 0000000..4ea85f9
--- /dev/null
+++ b/gemrb/plugins/KEYImporter/KEYImporter.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 KEYIMP_H
+#define KEYIMP_H
+
+#include "ResourceSource.h"
+
+#include "Dictionary.h"
+
+#include <vector>
+
+class DataStream;
+class Resource;
+class ResourceDesc;
+
+struct RESEntry {
+ ieResRef ResRef;
+ ieWord Type;
+ ieDword ResLocator;
+};
+
+struct BIFEntry {
+ char* name;
+ ieWord BIFLocator;
+ char path[_MAX_PATH];
+ int cd;
+ bool found;
+};
+
+
+class KEYImporter : public ResourceSource {
+private:
+ std::vector< BIFEntry> biffiles;
+ Dictionary resources;
+
+ /** Gets the stream assoicated to a RESKey */
+ DataStream *GetStream(const char *resname, ieWord type);
+public:
+ KEYImporter(void);
+ ~KEYImporter(void);
+ bool Open(const char *file, const char *desc);
+ /* predicts the availability of a resource */
+ bool HasResource(const char* resname, SClass_ID type);
+ bool HasResource(const char* resname, const ResourceDesc &type);
+ /* returns resource */
+ DataStream* GetResource(const char* resname, SClass_ID type);
+ DataStream* GetResource(const char* resname, const ResourceDesc &type);
+};
+
+#endif
diff --git a/gemrb/plugins/KEYImporter/Makefile.am b/gemrb/plugins/KEYImporter/Makefile.am
new file mode 100644
index 0000000..28a935b
--- /dev/null
+++ b/gemrb/plugins/KEYImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = KEYImporter.la
+KEYImporter_la_LDFLAGS = -module -avoid-version -shared
+KEYImporter_la_SOURCES = Dictionary.cpp Dictionary.h KEYImporter.cpp KEYImporter.h
diff --git a/gemrb/plugins/MOSImporter/CMakeLists.txt b/gemrb/plugins/MOSImporter/CMakeLists.txt
new file mode 100644
index 0000000..d3ae969
--- /dev/null
+++ b/gemrb/plugins/MOSImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (MOSImporter MOSImporter.cpp)
diff --git a/gemrb/plugins/MOSImporter/MOSImporter.cpp b/gemrb/plugins/MOSImporter/MOSImporter.cpp
new file mode 100644
index 0000000..660e1f9
--- /dev/null
+++ b/gemrb/plugins/MOSImporter/MOSImporter.cpp
@@ -0,0 +1,155 @@
+/* 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 "MOSImporter.h"
+
+#include "RGBAColor.h"
+#include "win32def.h"
+
+#include "Compressor.h"
+#include "Interface.h"
+#include "Video.h"
+#include "System/CachedFileStream.h"
+#include "System/FileStream.h"
+
+static ieDword red_mask = 0xff000000;
+static ieDword green_mask = 0x00ff0000;
+static ieDword blue_mask = 0x0000ff00;
+
+MOSImporter::MOSImporter(void)
+{
+ if (DataStream::IsEndianSwitch()) {
+ red_mask = 0x000000ff;
+ green_mask = 0x0000ff00;
+ blue_mask = 0x00ff0000;
+ }
+}
+
+MOSImporter::~MOSImporter(void)
+{
+}
+
+bool MOSImporter::Open(DataStream* stream)
+{
+ str = stream;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "MOSCV1 ", 8 ) == 0) {
+ char cpath[_MAX_PATH];
+ strcpy( cpath, core->CachePath );
+ strcat( cpath, stream->filename );
+ FILE* exist_in_cache = fopen( cpath, "rb" );
+ if (exist_in_cache) {
+ //File was previously cached, using local copy
+ delete( str );
+ fclose( exist_in_cache );
+ FileStream* s = new FileStream();
+ s->Open( cpath );
+ str = s;
+ str->Read( Signature, 8 );
+ } else {
+ //No file found in Cache, Decompressing and storing for further use
+ str->Seek( 4, GEM_CURRENT_POS );
+
+ if (!core->IsAvailable( PLUGIN_COMPRESSION_ZLIB )) {
+ printf( "No Compression Manager Available.\nCannot Load Compressed Mos File.\n" );
+ return false;
+ }
+ FILE* newfile = fopen( cpath, "wb" );
+ PluginHolder<Compressor> comp(PLUGIN_COMPRESSION_ZLIB);
+ comp->Decompress( newfile, str );
+ fclose( newfile );
+ delete( str );
+ FileStream* s = new FileStream();
+ s->Open( cpath );
+ str = s;
+ str->Read( Signature, 8 );
+ }
+ }
+ if (strncmp( Signature, "MOS V1 ", 8 ) != 0) {
+ return false;
+ }
+ str->ReadWord( &Width );
+ str->ReadWord( &Height );
+ str->ReadWord( &Cols );
+ str->ReadWord( &Rows );
+ str->ReadDword( &BlockSize );
+ str->ReadDword( &PalOffset );
+ return true;
+}
+
+Sprite2D* MOSImporter::GetSprite2D()
+{
+ RevColor RevCol[256];
+ unsigned char * pixels = ( unsigned char * ) malloc( Width * Height * 4 );
+ unsigned char * blockpixels = ( unsigned char * )
+ malloc( BlockSize * BlockSize );
+ ieDword blockoffset;
+ for (int y = 0; y < Rows; y++) {
+ int bh = ( y == Rows - 1 ) ?
+ ( ( Height % 64 ) == 0 ? 64 : Height % 64 ) :
+ 64;
+ for (int x = 0; x < Cols; x++) {
+ int bw = ( x == Cols - 1 ) ?
+ ( ( Width % 64 ) == 0 ? 64 : Width % 64 ) :
+ 64;
+ str->Seek( PalOffset + ( y * Cols * 1024 ) +
+ ( x * 1024 ), GEM_STREAM_START );
+ str->Read( &RevCol[0], 1024 );
+ str->Seek( PalOffset + ( Rows * Cols * 1024 ) +
+ ( y * Cols * 4 ) + ( x * 4 ),
+ GEM_STREAM_START );
+ str->ReadDword( &blockoffset );
+ str->Seek( PalOffset + ( Rows * Cols * 1024 ) +
+ ( Rows * Cols * 4 ) + blockoffset,
+ GEM_STREAM_START );
+ str->Read( blockpixels, bw * bh );
+ unsigned char * bp = blockpixels;
+ unsigned char * startpixel = pixels +
+ ( ( Width * 4 * y ) * 64 ) +
+ ( 4 * x * 64 );
+ for (int h = 0; h < bh; h++) {
+ for (int w = 0; w < bw; w++) {
+ *startpixel = RevCol[*bp].a;
+ startpixel++;
+ *startpixel = RevCol[*bp].b;
+ startpixel++;
+ *startpixel = RevCol[*bp].g;
+ startpixel++;
+ *startpixel = RevCol[*bp].r;
+ startpixel++;
+ bp++;
+ }
+ startpixel = startpixel + ( Width * 4 ) - ( 4 * bw );
+ }
+ }
+ }
+ free( blockpixels );
+ Sprite2D* ret = core->GetVideoDriver()->CreateSprite( Width, Height, 32,
+ red_mask, green_mask, blue_mask, 0,
+ pixels, true, green_mask );
+ return ret;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x167B73E, "MOS File Importer")
+PLUGIN_IE_RESOURCE(MOSImporter, ".mos", (ieWord)IE_MOS_CLASS_ID)
+END_PLUGIN()
diff --git a/gemrb/plugins/MOSImporter/MOSImporter.h b/gemrb/plugins/MOSImporter/MOSImporter.h
new file mode 100644
index 0000000..b38e0ad
--- /dev/null
+++ b/gemrb/plugins/MOSImporter/MOSImporter.h
@@ -0,0 +1,39 @@
+/* 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 MOSIMPORTER_H
+#define MOSIMPORTER_H
+
+#include "ImageMgr.h"
+
+class MOSImporter : public ImageMgr {
+private:
+ ieWord Width, Height, Cols, Rows;
+ ieDword BlockSize, PalOffset;
+public:
+ MOSImporter(void);
+ ~MOSImporter(void);
+ bool Open(DataStream* stream);
+ Sprite2D* GetSprite2D();
+ int GetWidth() { return (int) Width; }
+ int GetHeight() { return (int) Height; }
+};
+
+#endif
diff --git a/gemrb/plugins/MOSImporter/Makefile.am b/gemrb/plugins/MOSImporter/Makefile.am
new file mode 100644
index 0000000..dd4d294
--- /dev/null
+++ b/gemrb/plugins/MOSImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = MOSImporter.la
+MOSImporter_la_LDFLAGS = -module -avoid-version -shared
+MOSImporter_la_SOURCES = MOSImporter.cpp MOSImporter.h
diff --git a/gemrb/plugins/MUSImporter/CMakeLists.txt b/gemrb/plugins/MUSImporter/CMakeLists.txt
new file mode 100644
index 0000000..46f6126
--- /dev/null
+++ b/gemrb/plugins/MUSImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (MUSImporter MUSImporter.cpp)
diff --git a/gemrb/plugins/MUSImporter/MUSImporter.cpp b/gemrb/plugins/MUSImporter/MUSImporter.cpp
new file mode 100644
index 0000000..1bb6b1c
--- /dev/null
+++ b/gemrb/plugins/MUSImporter/MUSImporter.cpp
@@ -0,0 +1,335 @@
+/* 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 "MUSImporter.h"
+
+#include "win32def.h"
+
+#include "Audio.h"
+#include "GameData.h" // For ResourceHolder
+#include "Interface.h"
+#include "SoundMgr.h"
+
+static char musicsubfolder[6] = "music";
+
+MUSImporter::MUSImporter()
+{
+ Initialized = false;
+ Playing = false;
+ str = new FileStream();
+ PLpos = 0;
+ PLName[0] = '\0';
+ PLNameNew[0] = '\0';
+ lastSound = 0xffffffff;
+ char path[_MAX_PATH];
+ PathJoin(path, core->GamePath, musicsubfolder, NULL);
+ manager.AddSource(path, "Music", PLUGIN_RESOURCE_DIRECTORY);
+}
+
+MUSImporter::~MUSImporter()
+{
+ if (str) {
+ delete( str );
+ }
+}
+/** Initializes the PlayList Manager */
+bool MUSImporter::Init()
+{
+ Initialized = true;
+ return true;
+}
+/** Loads a PlayList for playing */
+bool MUSImporter::OpenPlaylist(const char* name)
+{
+ if (PLName[0] != '\0') {
+ int len = ( int ) strlen( PLName );
+ if (strnicmp( name, PLName, len ) == 0)
+ return true;
+ }
+ if (Playing) {
+ return false;
+ }
+ core->GetAudioDrv()->ResetMusics();
+ playlist.clear();
+ PLpos = 0;
+ if (name[0] == '*') {
+ return false;
+ }
+ char path[_MAX_PATH];
+ PathJoin(path, core->GamePath, musicsubfolder, name, NULL);
+ printMessage("MUSImporter", "", WHITE);
+ printf( "Loading %s...", path );
+ if (!str->Open( path, true )) {
+ printStatus("NOT FOUND", LIGHT_RED );
+ return false;
+ }
+ printStatus("FOUND", LIGHT_GREEN );
+ int c = str->ReadLine( PLName, 32 );
+ while (c > 0) {
+ if (( PLName[c - 1] == ' ' ) || ( PLName[c - 1] == '\t' ))
+ PLName[c - 1] = 0;
+ else
+ break;
+ c--;
+ }
+ char counts[5];
+ str->ReadLine( counts, 5 );
+ int count = atoi( counts );
+ while (count != 0) {
+ char line[64];
+ int len = str->ReadLine( line, 64 );
+ int i = 0;
+ int p = 0;
+ PLString pls;
+ while (i < len) {
+ if (( line[i] != ' ' ) && ( line[i] != '\t' ))
+ pls.PLFile[p++] = line[i++];
+ else {
+ while (i < len) {
+ if (( line[i] == ' ' ) || ( line[i] == '\t' ))
+ i++;
+ else
+ break;
+ }
+ break;
+ }
+ }
+ pls.PLFile[p] = 0;
+ p = 0;
+ if (line[i] != '@' && ( i < len )) {
+ while (i < len) {
+ if (( line[i] != ' ' ) && ( line[i] != '\t' ))
+ pls.PLTag[p++] = line[i++];
+ else
+ break;
+ }
+ pls.PLTag[p] = 0;
+ p = 0;
+ while (i < len) {
+ if (( line[i] == ' ' ) || ( line[i] == '\t' ))
+ i++;
+ else {
+ break;
+ }
+ }
+ if (line[i] == '@')
+ strcpy( pls.PLLoop, pls.PLTag );
+ else {
+ while (i < len) {
+ if (( line[i] != ' ' ) && ( line[i] != '\t' ))
+ pls.PLLoop[p++] = line[i++];
+ else
+ break;
+ }
+ pls.PLLoop[p] = 0;
+ }
+ while (i < len) {
+ if (( line[i] == ' ' ) || ( line[i] == '\t' ))
+ i++;
+ else
+ break;
+ }
+ p = 0;
+ } else {
+ pls.PLTag[0] = 0;
+ pls.PLLoop[0] = 0;
+ }
+ while (i < len) {
+ if (( line[i] != ' ' ) && ( line[i] != '\t' ))
+ i++;
+ else {
+ while (i < len) {
+ if (( line[i] == ' ' ) || ( line[i] == '\t' ))
+ i++;
+ else
+ break;
+ }
+ break;
+ }
+ }
+ while (i < len) {
+ if (( line[i] != ' ' ) && ( line[i] != '\t' ))
+ pls.PLEnd[p++] = line[i++];
+ else
+ break;
+ }
+ pls.PLEnd[p] = 0;
+ playlist.push_back( pls );
+ count--;
+ }
+ return true;
+}
+/** Start the PlayList Music Execution */
+void MUSImporter::Start()
+{
+ if (!Playing) {
+ PLpos = 0;
+ if (playlist.size() == 0)
+ return;
+ if (playlist[PLpos].PLLoop[0] != 0) {
+ for (unsigned int i = 0; i < playlist.size(); i++) {
+ if (stricmp( playlist[i].PLFile, playlist[PLpos].PLLoop ) == 0) {
+ PLnext = i;
+ break;
+ }
+ }
+ } else {
+ PLnext = PLpos + 1;
+ if ((unsigned int) PLnext >= playlist.size())
+ PLnext = -1;
+ }
+ PlayMusic( PLpos );
+ core->GetAudioDrv()->Play();
+ lastSound = playlist[PLpos].soundID;
+ Playing = true;
+ }
+}
+/** Ends the Current PlayList Execution */
+void MUSImporter::End()
+{
+ if (Playing) {
+ if (playlist.size() == 0)
+ return;
+ if (playlist[PLpos].PLEnd[0] != 0) {
+ if (stricmp( playlist[PLpos].PLEnd, "end" ) != 0)
+ PlayMusic( playlist[PLpos].PLEnd );
+ }
+ PLnext = -1;
+ }
+}
+
+void MUSImporter::HardEnd()
+{
+ core->GetAudioDrv()->Stop();
+ Playing = false;
+ PLpos = 0;
+}
+
+/** Switches the current PlayList while playing the current one, return nonzero on error */
+int MUSImporter::SwitchPlayList(const char* name, bool Hard)
+{
+ if (Playing) {
+ //don't do anything if the requested song is already playing
+ //this fixed PST's infinite song start
+ int len = ( int ) strlen( PLName );
+ if (strnicmp( name, PLName, len ) == 0)
+ return 0;
+ if (Hard) {
+ HardEnd();
+ } else {
+ End();
+ }
+ //if still playing, then don't insist on trying to open it now
+ //either HardEnd stopped it for us, or End marked it for early ending
+ if (Playing) {
+ strncpy(PLNameNew, name, sizeof(PLNameNew) );
+ return 0;
+ }
+ }
+
+ if (OpenPlaylist( name )) {
+ Start();
+ return 0;
+ }
+
+ return -1;
+}
+/** Plays the Next Entry */
+void MUSImporter::PlayNext()
+{
+ if (!Playing) {
+ return;
+ }
+ if (PLnext != -1) {
+ PlayMusic( PLnext );
+ PLpos = PLnext;
+ if (playlist[PLpos].PLLoop[0] != 0) {
+ for (unsigned int i = 0; i < playlist.size(); i++) {
+ if (stricmp( playlist[i].PLFile, playlist[PLpos].PLLoop ) == 0) {
+ PLnext = i;
+ break;
+ }
+ }
+ } else {
+ if (stricmp( playlist[PLnext].PLEnd, "end" ) == 0)
+ PLnext = -1;
+ else
+ PLnext = PLpos + 1;
+ if ((unsigned int) PLnext >= playlist.size() ) {
+ PLnext = 0;
+ }
+ }
+ } else {
+ Playing = false;
+ core->GetAudioDrv()->Stop();
+ //start new music after the old faded out
+ if (PLNameNew[0]) {
+ if (OpenPlaylist(PLNameNew)) {
+ Start();
+ }
+ PLNameNew[0]='\0';
+ }
+ }
+}
+
+void MUSImporter::PlayMusic(int pos)
+{
+ PlayMusic( playlist[pos].PLFile );
+}
+
+void MUSImporter::PlayMusic(char* name)
+{
+ char FName[_MAX_PATH];
+ if (strnicmp( name, "mx9000", 6 ) == 0) { //iwd2
+ PathJoin(FName, "mx9000", name, NULL);
+ } else if (strnicmp( name, "mx0000", 6 ) == 0) { //iwd
+ PathJoin(FName, "mx0000", name, NULL);
+ } else if (strnicmp( name, "SPC", 3 ) != 0) { //bg2
+ char File[_MAX_PATH];
+ snprintf(File, _MAX_PATH, "%s%s", PLName, name);
+ PathJoin(FName, PLName, File, NULL);
+ } else {
+ strncpy(FName, name, _MAX_PATH);
+ }
+
+ ResourceHolder<SoundMgr> sound(FName, manager);
+ if (sound) {
+ int soundID = core->GetAudioDrv()->CreateStream( sound );
+ if (soundID == -1) {
+ core->GetAudioDrv()->Stop();
+ }
+ } else {
+ core->GetAudioDrv()->Stop();
+ }
+ printf( "Playing: %s\n", FName );
+}
+
+bool MUSImporter::CurrentPlayList(const char* name) {
+ int len = ( int ) strlen( PLName );
+ if (strnicmp( name, PLName, len ) == 0) return true;
+ return false;
+}
+
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x2DCB9E8, "MUS File Importer")
+PLUGIN_CLASS(IE_MUS_CLASS_ID, MUSImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/MUSImporter/MUSImporter.h b/gemrb/plugins/MUSImporter/MUSImporter.h
new file mode 100644
index 0000000..1448b46
--- /dev/null
+++ b/gemrb/plugins/MUSImporter/MUSImporter.h
@@ -0,0 +1,79 @@
+/* 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 MUSIMPORTER_H
+#define MUSIMPORTER_H
+
+#include "MusicMgr.h"
+
+#include "ResourceManager.h"
+#include "System/FileStream.h"
+
+#include <cstdio>
+
+/**MUS PlayList Importer
+ *@author GemRB Development Team
+ */
+
+struct PLString {
+ char PLFile[10];
+ char PLLoop[10];
+ char PLTag[10];
+ char PLEnd[10];
+ unsigned int soundID;
+};
+
+class MUSImporter : public MusicMgr {
+private:
+ bool Initialized;
+ bool Playing;
+ char PLName[32];
+ char PLNameNew[32];
+ int PLpos, PLnext;
+ FileStream* str;
+ std::vector< PLString> playlist;
+ unsigned int lastSound;
+ ResourceManager manager;
+private:
+ void PlayMusic(int pos);
+ void PlayMusic(char* name);
+public:
+ MUSImporter();
+ ~MUSImporter();
+ /** Loads a PlayList for playing */
+ bool OpenPlaylist(const char* name);
+ /** Initializes the PlayList Manager */
+ bool Init();
+ /** Switches the current PlayList while playing the current one */
+ int SwitchPlayList(const char* name, bool Hard);
+ /** Ends the Current PlayList Execution */
+ void End();
+ void HardEnd();
+ /** Start the PlayList Music Execution */
+ void Start();
+ /** Plays the Next Entry */
+ void PlayNext();
+ /** Returns whether music is currently playing */
+ bool IsPlaying() { return Playing; }
+ /** Returns whether given playlist is currently loaded */
+ bool CurrentPlayList(const char* name);
+};
+
+#endif
diff --git a/gemrb/plugins/MUSImporter/Makefile.am b/gemrb/plugins/MUSImporter/Makefile.am
new file mode 100644
index 0000000..ae80e41
--- /dev/null
+++ b/gemrb/plugins/MUSImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = MUSImporter.la
+MUSImporter_la_LDFLAGS = -module -avoid-version -shared
+MUSImporter_la_SOURCES = MUSImporter.cpp MUSImporter.h
diff --git a/gemrb/plugins/MVEPlayer/CMakeLists.txt b/gemrb/plugins/MVEPlayer/CMakeLists.txt
new file mode 100644
index 0000000..524f956
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN ( MVEPlayer mvevideodec8.cpp mvevideodec16.cpp mveaudiodec.cpp mve_player.cpp MVEPlayer.cpp )
diff --git a/gemrb/plugins/MVEPlayer/MVEPlayer.cpp b/gemrb/plugins/MVEPlayer/MVEPlayer.cpp
new file mode 100644
index 0000000..6ff83ca
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/MVEPlayer.cpp
@@ -0,0 +1,187 @@
+/* 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 "MVEPlayer.h"
+
+#include "mve_player.h"
+
+#include "ie_types.h"
+
+#include "Audio.h"
+#include "Interface.h"
+#include "Variables.h"
+#include "Video.h"
+
+#include <cassert>
+#include <cstdio>
+
+static const char MVESignature[] = "Interplay MVE File\x1A";
+static const int MVE_SIGNATURE_LEN = 19;
+
+static unsigned char g_palette[768];
+static int g_truecolor;
+static ieDword maxRow = 0;
+static ieDword rowCount = 0;
+static ieDword frameCount = 0;
+static ieDword *cbAtFrame = NULL;
+static ieDword *strRef = NULL;
+
+MVEPlay::MVEPlay(void)
+{
+ video = core->GetVideoDriver();
+}
+
+MVEPlay::~MVEPlay(void)
+{
+}
+
+bool MVEPlay::Open(DataStream* stream)
+{
+ str = stream;
+ validVideo = false;
+
+ char Signature[MVE_SIGNATURE_LEN];
+ str->Read( Signature, MVE_SIGNATURE_LEN );
+ if (memcmp( Signature, MVESignature, MVE_SIGNATURE_LEN ) != 0) {
+ return false;
+ }
+
+ str->Seek( 0, GEM_STREAM_START );
+ validVideo = true;
+ return true;
+}
+
+void MVEPlay::CallBackAtFrames(ieDword cnt, ieDword *arg, ieDword *arg2 )
+{
+ maxRow = cnt;
+ frameCount = 0;
+ rowCount = 0;
+ cbAtFrame = arg;
+ strRef = arg2;
+}
+
+int MVEPlay::Play()
+{
+ if (!validVideo) {
+ return 0;
+ }
+ //Start Movie Playback
+ frameCount = 0;
+ return doPlay( );
+}
+
+int MVEPlay::doPlay()
+{
+ int done = 0;
+ MVEPlayer player(this);
+
+ memset( g_palette, 0, 768 );
+
+ //ieDword volume;
+ //core->GetDictionary()->Lookup( "Volume Movie", volume );
+ player.sound_init( core->GetAudioDrv()->CanPlay() );
+
+ int w,h;
+
+ video->InitMovieScreen(w,h);
+ player.video_init(w, h);
+
+ if (!player.start_playback()) {
+ printf("Failed to decode movie!\n");
+ return 1;
+ }
+
+ g_truecolor = player.is_truecolour();
+
+ while (!done && player.next_frame()) {
+ done = video->PollMovieEvents();
+ }
+
+ video->DrawMovieSubtitle(0);
+ return 0;
+}
+
+unsigned int MVEPlay::fileRead(void* buf, unsigned int count)
+{
+ unsigned numread;
+
+ numread = str->Read( buf, count );
+ return ( numread == count );
+}
+
+void MVEPlay::showFrame(unsigned char* buf, unsigned int bufw,
+ unsigned int bufh, unsigned int sx, unsigned int sy, unsigned int w,
+ unsigned int h, unsigned int dstx, unsigned int dsty)
+{
+ ieDword titleref = 0;
+
+ if (cbAtFrame && strRef) {
+ frameCount ++;
+ if ((rowCount<maxRow) && (frameCount >= cbAtFrame[rowCount]) ) {
+ rowCount++;
+ }
+ //draw subtitle here
+ if (rowCount) {
+ titleref = strRef[rowCount-1];
+ }
+ }
+ video->showFrame(buf,bufw,bufh,sx,sy,w,h,dstx,dsty, g_truecolor, g_palette, titleref);
+}
+
+void MVEPlay::setPalette(unsigned char* p, unsigned start, unsigned count)
+{
+ //Set color 0 to be black
+ g_palette[0] = g_palette[1] = g_palette[2] = 0;
+
+ //Set color 255 to be our subtitle color
+ g_palette[765] = g_palette[766] = g_palette[767] = 50;
+
+ //movie libs palette into our array
+ memcpy( g_palette + start * 3, p + start * 3, count * 3 );
+}
+
+int MVEPlay::setAudioStream()
+{
+ ieDword volume ;
+ core->GetDictionary()->Lookup( "Volume Movie", volume) ;
+ int source = core->GetAudioDrv()->SetupNewStream(0, 0, 0, volume, false, false) ;
+ return source;
+}
+
+void MVEPlay::freeAudioStream(int stream)
+{
+ if (stream > -1)
+ core->GetAudioDrv()->ReleaseStream(stream, true);
+}
+
+void MVEPlay::queueBuffer(int stream, unsigned short bits,
+ int channels, short* memory,
+ int size, int samplerate)
+{
+ if (stream > -1)
+ core->GetAudioDrv()->QueueBuffer(stream, bits, channels, memory, size, samplerate) ;
+}
+
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x218963DC, "MVE Video Player")
+PLUGIN_IE_RESOURCE(MVEPlay, ".mve", (ieWord)IE_MVE_CLASS_ID)
+END_PLUGIN()
diff --git a/gemrb/plugins/MVEPlayer/MVEPlayer.h b/gemrb/plugins/MVEPlayer/MVEPlayer.h
new file mode 100644
index 0000000..1a56438
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/MVEPlayer.h
@@ -0,0 +1,57 @@
+/* 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 MVEPLAY_H
+#define MVEPLAY_H
+
+#include "MoviePlayer.h"
+
+#include "globals.h"
+#include "win32def.h"
+
+#include "Interface.h"
+
+class MVEPlay : public MoviePlayer {
+ friend class MVEPlayer;
+private:
+ Video *video;
+ bool validVideo;
+ int doPlay();
+ unsigned int fileRead(void* buf, unsigned int count);
+ void showFrame(unsigned char* buf, unsigned int bufw,
+ unsigned int bufh, unsigned int sx, unsigned int sy,
+ unsigned int w, unsigned int h, unsigned int dstx,
+ unsigned int dsty);
+ void setPalette(unsigned char* p, unsigned start, unsigned count);
+ int pollEvents();
+ int setAudioStream();
+ void freeAudioStream(int stream);
+ void queueBuffer(int stream, unsigned short bits,
+ int channels, short* memory,
+ int size, int samplerate);
+public:
+ MVEPlay(void);
+ ~MVEPlay(void);
+ bool Open(DataStream* stream);
+ void CallBackAtFrames(ieDword cnt, ieDword *arg, ieDword *arg2);
+ int Play();
+};
+
+#endif
diff --git a/gemrb/plugins/MVEPlayer/Makefile.am b/gemrb/plugins/MVEPlayer/Makefile.am
new file mode 100644
index 0000000..1c9912f
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/Makefile.am
@@ -0,0 +1,12 @@
+plugin_LTLIBRARIES = MVEPlayer.la
+MVEPlayer_la_LDFLAGS = -module -avoid-version -shared
+MVEPlayer_la_SOURCES = \
+ MVEPlayer.cpp \
+ MVEPlayer.h \
+ mvevideodec8.cpp \
+ mvevideodec16.cpp \
+ mveaudiodec.cpp \
+ mve_player.cpp \
+ mve_player.h \
+ gstmvedemux.h \
+ mve.h
diff --git a/gemrb/plugins/MVEPlayer/gstmvedemux.h b/gemrb/plugins/MVEPlayer/gstmvedemux.h
new file mode 100644
index 0000000..c8908c2
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/gstmvedemux.h
@@ -0,0 +1,141 @@
+/*
+ * GStreamer demultiplexer plugin for Interplay MVE movie files
+ *
+ * Copyright (C) 2006 Jens Granseuer <jensgr at gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_MVE_DEMUX_H__
+#define __GST_MVE_DEMUX_H__
+
+#include "win32def.h"
+#include "globals.h"
+
+#define G_UNLIKELY(x) (x)
+#define GST_WARNING printf
+#define GST_ERROR printf
+
+/* Define GET function for unaligned memory */
+#define _GST_GET(__data, __idx, __size, __shift) \
+ (((guint##__size) (((guint8 *) (__data))[__idx])) << __shift)
+
+/**
+ * GST_READ_UINT16_LE:
+ * @data: memory location
+ *
+ * Read a 16 bit unsigned integer value in little endian format from the memory buffer.
+ */
+#define GST_READ_UINT16_LE(data) (_GST_GET (data, 1, 16, 8) | \
+ _GST_GET (data, 0, 16, 0))
+
+/**
+ * GST_READ_UINT32_LE:
+ * @data: memory location
+ *
+ * Read a 32 bit unsigned integer value in little endian format from the memory buffer.
+ */
+#define GST_READ_UINT32_LE(data) (_GST_GET (data, 3, 32, 24) | \
+ _GST_GET (data, 2, 32, 16) | \
+ _GST_GET (data, 1, 32, 8) | \
+ _GST_GET (data, 0, 32, 0))
+
+/*#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_MVE_DEMUX \
+ (gst_mve_demux_get_type())
+#define GST_MVE_DEMUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MVE_DEMUX,GstMveDemux))
+#define GST_MVE_DEMUX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MVE_DEMUX,GstMveDemuxClass))
+#define GST_IS_MVE_DEMUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MVE_DEMUX))
+#define GST_IS_MVE_DEMUX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MVE_DEMUX))*/
+
+typedef int gint;
+typedef gint gboolean;
+typedef ieByte guint8;
+typedef ieWord guint16;
+typedef ieDword guint32;
+
+/*typedef struct _GstMveDemux GstMveDemux;
+typedef struct _GstMveDemuxClass GstMveDemuxClass;*/
+typedef struct _GstMveDemuxStream GstMveDemuxStream;
+
+/*struct _GstMveDemux
+{
+ GstElement element;
+
+ GstPad *sinkpad;
+
+ GstMveDemuxStream *video_stream;
+ GstMveDemuxStream *audio_stream;
+
+ gint state;*/
+
+ /* time per frame (1/framerate) */
+/* GstClockTime frame_duration;*/
+
+ /* push based variables */
+/* guint16 needed_bytes;
+ GstAdapter *adapter;*/
+
+ /* size of current chunk */
+/* guint32 chunk_size;*/
+ /* offset in current chunk */
+/* guint32 chunk_offset;
+};*/
+
+/*struct _GstMveDemuxClass
+{
+ GstElementClass parent_class;
+};*/
+
+struct _GstMveDemuxStream {
+ /* shared properties */
+ /*GstCaps *caps;
+ GstPad *pad;
+ GstClockTime last_ts;*/
+ /*gint64 offset;*/
+
+ /* video properties */
+ guint16 width;
+ guint16 height;
+ /*guint8 bpp;*/ /* bytes per pixel */
+ guint8 *code_map;
+ /*gboolean code_map_avail;*/
+ guint16 *back_buf1;
+ guint16 *back_buf2;
+ guint32 max_block_offset;
+ /*GstBuffer *palette;
+ GstBuffer *buffer;*/
+
+ /* audio properties */
+ /*guint16 sample_rate;
+ guint16 n_channels;
+ guint16 sample_size;
+ gboolean compression;*/
+};
+
+/*GType gst_mve_demux_get_type (void);
+
+G_END_DECLS*/
+
+#endif /* __GST_MVE_DEMUX_H__ */
diff --git a/gemrb/plugins/MVEPlayer/mve.h b/gemrb/plugins/MVEPlayer/mve.h
new file mode 100644
index 0000000..9df7c05
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/mve.h
@@ -0,0 +1,64 @@
+/*
+ * Interplay MVE movie definitions
+ *
+ * Copyright (C) 2006 Jens Granseuer <jensgr at gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MVE_H__
+#define __MVE_H__
+
+#define MVE_PREAMBLE "Interplay MVE File\032\000\032\000\000\001\063\021"
+#define MVE_PREAMBLE_SIZE 26
+
+#define MVE_PALETTE_COUNT 256
+
+#define MVE_DEFAULT_AUDIO_STREAM 0x01
+
+/* MVE chunk types */
+#define MVE_CHUNK_INIT_AUDIO 0x0000
+#define MVE_CHUNK_AUDIO_ONLY 0x0001
+#define MVE_CHUNK_INIT_VIDEO 0x0002
+#define MVE_CHUNK_VIDEO 0x0003
+#define MVE_CHUNK_SHUTDOWN 0x0004
+#define MVE_CHUNK_END 0x0005
+
+/* MVE segment opcodes */
+#define MVE_OC_END_OF_STREAM 0x00
+#define MVE_OC_END_OF_CHUNK 0x01
+#define MVE_OC_CREATE_TIMER 0x02
+#define MVE_OC_AUDIO_BUFFERS 0x03
+#define MVE_OC_PLAY_AUDIO 0x04
+#define MVE_OC_VIDEO_BUFFERS 0x05
+#define MVE_OC_PLAY_VIDEO 0x07
+#define MVE_OC_AUDIO_DATA 0x08
+#define MVE_OC_AUDIO_SILENCE 0x09
+#define MVE_OC_VIDEO_MODE 0x0A
+#define MVE_OC_PALETTE 0x0C
+#define MVE_OC_PALETTE_COMPRESSED 0x0D
+#define MVE_OC_CODE_MAP 0x0F
+#define MVE_OC_VIDEO_DATA 0x11
+
+/* audio flags */
+#define MVE_AUDIO_STEREO 0x0001
+#define MVE_AUDIO_16BIT 0x0002
+#define MVE_AUDIO_COMPRESSED 0x0004
+
+/* video flags */
+#define MVE_VIDEO_DELTA_FRAME 0x0001
+
+#endif /* __MVE_H__ */
diff --git a/gemrb/plugins/MVEPlayer/mve_player.cpp b/gemrb/plugins/MVEPlayer/mve_player.cpp
new file mode 100644
index 0000000..b8edf7d
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/mve_player.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.
+ *
+ *
+ */
+
+/*
+ * The code/specifications used as reference for this code were by:
+ *
+ * Mike Melanson <melanson at pcisys.net>
+ * Jens Granseuer <jensgr at gmx.net>
+ */
+
+#if defined(__HAIKU__)
+#include <unistd.h>
+#endif
+
+#include "mve_player.h"
+#include "MVEPlayer.h"
+#include "gstmvedemux.h"
+#include "mve.h"
+
+/* mvevideodec8.cpp */
+extern int ipvideo_decode_frame8 (const GstMveDemuxStream * s,
+ const unsigned char *data, unsigned short len);
+/* mvevideodec16.cpp */
+extern int ipvideo_decode_frame16 (const GstMveDemuxStream * s,
+ const unsigned char *data, unsigned short len);
+/* mveaudiodec.cpp */
+extern void ipaudio_uncompress (short *buffer,
+ unsigned short buf_len, const unsigned char *data, unsigned char channels);
+
+/*
+ * constructor: doesn't really do anything
+ */
+MVEPlayer::MVEPlayer(class MVEPlay *file) {
+ buffer = NULL;
+ host = file;
+ done = false;
+
+ audio_buffer = NULL;
+ frame_wait = 0;
+ timer_last_sec = 0;
+
+ video_data = NULL;
+ video_back_buf = NULL;
+
+ video_frameskip = 0;
+ video_skippedframes = 0;
+
+ audio_stream = -1;
+
+ playsound = true;
+}
+
+MVEPlayer::~MVEPlayer() {
+ if (buffer) free(buffer);
+ if (audio_buffer) free(audio_buffer);
+
+ if (video_data) {
+ if (video_data->code_map) free(video_data->code_map);
+ free(video_data);
+ }
+ if (video_back_buf) free(video_back_buf);
+
+ if (audio_stream != -1) host->freeAudioStream(audio_stream);
+
+ if (video_skippedframes)
+ printf("Warning: Had to drop %d video frame(s).\n", video_skippedframes);
+}
+
+/*
+ * high-level movie playback
+ */
+
+void MVEPlayer::video_init(unsigned int w, unsigned int h) {
+ outputwidth = w;
+ outputheight = h;
+}
+
+void MVEPlayer::sound_init(bool play) {
+ playsound = play;
+}
+
+bool MVEPlayer::start_playback() {
+ if (!verify_header()) return false;
+
+ /*
+ * The first two chunks contain audio and video initialisation, hopefully.
+ */
+ if (!process_chunk() || !process_chunk()) {
+ printf("Error: Failed to read initial movie chunks.\n");
+ return false;
+ }
+
+ /* TODO: verify we have the needed information */
+
+ return true;
+}
+
+bool MVEPlayer::next_frame() {
+ if (timer_last_sec) timer_wait();
+
+ video_rendered_frame = false;
+ while (!video_rendered_frame) {
+ if (done) return false;
+ if (!process_chunk()) return false;
+ }
+
+ if (!timer_last_sec) timer_start();
+
+ return true;
+}
+
+/*
+ * parsing/demuxing
+ */
+
+bool MVEPlayer::request_data(unsigned int len) {
+ if (!buffer) {
+ buffer = (char*)malloc(len);
+ buffersize = len;
+ } else {
+ if (len > buffersize) {
+ buffer = (char*)realloc(buffer, len);
+ buffersize = len;
+ }
+ }
+ if (!host->fileRead(buffer, len)) return false;
+ return true;
+}
+
+bool MVEPlayer::verify_header() {
+ if (!request_data(MVE_PREAMBLE_SIZE)) return false;
+ if (memcmp(buffer, MVE_PREAMBLE, MVE_PREAMBLE_SIZE) != 0) {
+ printf("Error: MVE preamble didn't match\n");
+ return false;
+ }
+ return true;
+}
+
+bool MVEPlayer::process_chunk() {
+ if (!request_data(4)) return false;
+ chunk_offset = 0;
+ chunk_size = GST_READ_UINT16_LE(buffer);
+ unsigned int chunk_type = GST_READ_UINT16_LE(buffer + 2);
+ (void)chunk_type; /* we don't care */
+
+ while (chunk_offset < chunk_size) {
+ chunk_offset += 4;
+ if (!request_data(4)) return false;
+
+ unsigned int segment_size = GST_READ_UINT16_LE(buffer);
+ unsigned char segment_type = buffer[2];
+ unsigned char segment_version = buffer[3];
+
+ chunk_offset += segment_size;
+ if (!process_segment(segment_size, segment_type, segment_version)) return false;
+ }
+
+ if (chunk_offset != chunk_size) {
+ printf("Error: Decoded past the end of an MVE chunk\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool MVEPlayer::process_segment(unsigned short len, unsigned char type, unsigned char version) {
+ if (!request_data(len)) return false;
+
+ switch (type) {
+ case MVE_OC_END_OF_CHUNK:
+ /* do nothing */
+ break;
+ case MVE_OC_CREATE_TIMER:
+ segment_create_timer();
+ break;
+ case MVE_OC_AUDIO_BUFFERS:
+ segment_audio_init(version);
+ break;
+ case MVE_OC_VIDEO_BUFFERS:
+ segment_video_init(version);
+ break;
+ case MVE_OC_AUDIO_DATA:
+ case MVE_OC_AUDIO_SILENCE:
+ segment_audio_data(type == MVE_OC_AUDIO_SILENCE);
+ break;
+ case MVE_OC_VIDEO_MODE:
+ segment_video_mode();
+ break;
+ case MVE_OC_PALETTE:
+ segment_video_palette();
+ break;
+ case MVE_OC_PALETTE_COMPRESSED:
+ segment_video_compressedpalette();
+ break;
+ case MVE_OC_CODE_MAP:
+ segment_video_codemap(len);
+ break;
+ case MVE_OC_VIDEO_DATA:
+ segment_video_data(len);
+ break;
+ case MVE_OC_END_OF_STREAM:
+ done = true;
+ break;
+ case MVE_OC_PLAY_AUDIO:
+ /* we don't care */
+ break;
+ case MVE_OC_PLAY_VIDEO:
+ segment_video_play();
+ break;
+ case 0x13: case 0x14: case 0x15:
+ /* ignore these */
+ break;
+ default:
+ printf("Warning: Skipping unknown segment type 0x%02x", type);
+ }
+
+ return true;
+}
+
+/*
+ * timer handling
+ */
+
+void get_current_time(long &sec, long &usec) {
+#ifdef _WIN32
+ DWORD time;
+ time = GetTickCount();
+
+ sec = time / 1000;
+ usec = (time % 1000) * 1000;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ sec = tv.tv_sec;
+ usec = tv.tv_usec;
+#endif
+}
+
+void MVEPlayer::timer_start() {
+ get_current_time(timer_last_sec, timer_last_usec);
+}
+
+void MVEPlayer::timer_wait() {
+ long sec, usec;
+ get_current_time(sec, usec);
+
+ while (sec > timer_last_sec) {
+ usec += 1000000;
+ timer_last_sec++;
+ }
+
+ while (usec - timer_last_usec > (long)frame_wait) {
+ usec -= frame_wait;
+ video_frameskip++;
+ }
+
+ long to_sleep = frame_wait - (usec - timer_last_usec);
+#ifdef _WIN32
+ Sleep(to_sleep / 1000);
+#else
+ usleep(to_sleep);
+#endif
+
+ timer_start();
+}
+
+void MVEPlayer::segment_create_timer() {
+ /* new frame every (timer_rate * timer_subdiv) microseconds */
+ unsigned int timer_rate = GST_READ_UINT32_LE(buffer);
+ unsigned short timer_subdiv = GST_READ_UINT16_LE(buffer + 4);
+
+ frame_wait = timer_rate * timer_subdiv;
+}
+
+/*
+ * video handling
+ */
+
+void MVEPlayer::segment_video_init(unsigned char version) {
+ unsigned short width = GST_READ_UINT16_LE(buffer) << 3;
+ unsigned short height = GST_READ_UINT16_LE(buffer + 2) << 3;
+ unsigned short count = 1;
+ if (version > 0) count = GST_READ_UINT16_LE(buffer + 4);
+ unsigned short temp = 0;
+ if (version > 1) temp = GST_READ_UINT16_LE(buffer + 6);
+ truecolour = !!temp;
+
+ // some files have multiple initialisations
+ if (video_data) {
+ if (video_data->code_map) free(video_data->code_map);
+ free(video_data);
+ }
+ if (video_back_buf) free(video_back_buf);
+
+ unsigned int size = width * height * (truecolour ? 2 : 1);
+ video_back_buf = (guint16 *)malloc(size * 2);
+ memset(video_back_buf, 0, size * 2);
+
+ video_data = (GstMveDemuxStream *)malloc(sizeof(GstMveDemuxStream));
+ video_data->code_map = NULL;
+ video_data->width = width;
+ video_data->height = height;
+ video_data->back_buf1 = video_back_buf;
+ video_data->back_buf2 = video_back_buf + size/2;
+ video_data->max_block_offset = (height - 7) * width - 8;
+}
+
+void MVEPlayer::segment_video_mode() {
+ video_width = GST_READ_UINT16_LE(buffer);
+ video_height = GST_READ_UINT16_LE(buffer + 2);
+ unsigned short flags = GST_READ_UINT16_LE(buffer + 4);
+ (void)flags; /* unknown/unused */
+}
+
+void MVEPlayer::segment_video_palette() {
+ unsigned short palette_start = GST_READ_UINT16_LE(buffer);
+ unsigned short palette_count = GST_READ_UINT16_LE(buffer + 2);
+
+ char *palette = buffer + 4;
+
+ host->setPalette((unsigned char *)palette - (3 * palette_start), palette_start, palette_count);
+}
+
+void MVEPlayer::segment_video_compressedpalette() {
+ char *data = buffer;
+
+ unsigned int i, j;
+ for (i = 0; i < 32; ++i) {
+ unsigned char mask = *data;
+ data++;
+
+ if (mask) {
+ for (j = 0; j < 8; ++j) {
+ unsigned char r, g, b;
+ r = (*data) << 2;
+ ++data;
+ g = (*data) << 2;
+ ++data;
+ b = (*data) << 2;
+ ++data;
+ /* TODO: set palette position (i * 8) + j */
+ }
+ }
+ }
+}
+
+void MVEPlayer::segment_video_codemap(unsigned short size) {
+ if (!video_data) return; /* return failure? */
+
+ /* alas, a cornucopia of memory management! */
+ if (video_data->code_map) free(video_data->code_map);
+ video_data->code_map = (guint8*)malloc(size);
+ memcpy(video_data->code_map, buffer, size);
+}
+
+void MVEPlayer::segment_video_data(unsigned short size) {
+ /* check for valid code_map? */
+
+ unsigned short cur_frame = GST_READ_UINT16_LE(buffer);
+ unsigned short last_frame = GST_READ_UINT16_LE(buffer + 2);
+ unsigned short x_offset = GST_READ_UINT16_LE(buffer + 4);
+ unsigned short y_offset = GST_READ_UINT16_LE(buffer + 6);
+ unsigned short x_size = GST_READ_UINT16_LE(buffer + 8);
+ unsigned short y_size = GST_READ_UINT16_LE(buffer + 10);
+ (void)cur_frame; (void)last_frame; (void)x_offset; (void)y_offset;
+ (void)x_size; (void)y_size; /* unused? */
+ unsigned short flags = GST_READ_UINT16_LE(buffer + 12);
+
+ char *data = buffer + 14;
+
+ if (flags & MVE_VIDEO_DELTA_FRAME) {
+ guint16 *temp = video_data->back_buf1;
+ video_data->back_buf1 = video_data->back_buf2;
+ video_data->back_buf2 = temp;
+ }
+
+ /* might want to check result code.. */
+ if (truecolour) ipvideo_decode_frame16(video_data, (const unsigned char *)data, size);
+ else ipvideo_decode_frame8(video_data, (const unsigned char *)data, size);
+}
+
+void MVEPlayer::segment_video_play() {
+ if (video_frameskip) {
+ video_frameskip--;
+ video_skippedframes++;
+ } else {
+ unsigned int dest_x = (outputwidth - video_data->width) >> 1;
+ unsigned int dest_y = (outputheight - video_data->height) >> 1;
+ host->showFrame( (guint8 *) video_data->back_buf1, video_data->width, video_data->height, 0, 0, video_data->width, video_data->height, dest_x, dest_y);
+ }
+
+ video_rendered_frame = true;
+}
+
+/*
+ * audio handling
+ */
+
+void MVEPlayer::segment_audio_init(unsigned char version) {
+ if (!playsound) return;
+
+ audio_stream = host->setAudioStream();
+ if (audio_stream == -1) {
+ printf("Error: MVE player couldn't open audio. Will play silently.\n");
+ playsound = false;
+ return;
+ }
+
+ /* first 2 bytes unknown! */
+ audio_sample_rate = GST_READ_UINT16_LE(buffer + 4);
+ /* the docs say min_buffer_len is 16-bit for version 0, all other code just assumes 32-bit.. */
+ unsigned int min_buffer_len = GST_READ_UINT32_LE(buffer + 6);
+
+ unsigned short flags = GST_READ_UINT16_LE(buffer + 2);
+ /* bit 0: 0 = mono, 1 = stereo */
+ audio_num_channels = (flags & MVE_AUDIO_STEREO) + 1;
+ /* bit 1: 0 = 8 bit, 1 = 16 bit */
+ audio_sample_size = (((flags & MVE_AUDIO_16BIT) >> 1) + 1) * 8;
+ /* bit 2: 0 = uncompressed, 1 = compressed */
+ audio_compressed = ((version > 0) && (flags & MVE_AUDIO_COMPRESSED));
+
+ min_buffer_len *= audio_num_channels;
+ if (audio_sample_size == 16) min_buffer_len *= 2;
+ if (audio_buffer) free(audio_buffer);
+ audio_buffer = (short *)malloc(min_buffer_len);
+
+/* printf("Movie audio: Sample rate %d, %d channels, %d bit, requested buffer size 0x%02x, %s\n",
+ audio_sample_rate, audio_num_channels, audio_sample_size, min_buffer_len, audio_compressed ? "compressed" : "uncompressed");*/
+}
+
+void MVEPlayer::segment_audio_data(bool silent) {
+ if (!playsound) return;
+
+ unsigned short seq_index = GST_READ_UINT16_LE(buffer);
+ (void)seq_index; /* we don't care */
+ unsigned short stream_mask = GST_READ_UINT16_LE(buffer + 2);
+ unsigned short audio_size = GST_READ_UINT16_LE(buffer + 4);
+ char *data = buffer + 6;
+
+ if (stream_mask & MVE_DEFAULT_AUDIO_STREAM) {
+ if (silent) {
+ memset(audio_buffer, 0, audio_size);
+ } else {
+ /* should we check size of audio_buffer? */
+ if (audio_compressed)
+ ipaudio_uncompress(audio_buffer, audio_size, (const unsigned char *)data, audio_num_channels);
+ else
+ memcpy(audio_buffer, data, audio_size);
+ }
+ host->queueBuffer(audio_stream, audio_sample_size, audio_num_channels, audio_buffer, audio_size, audio_sample_rate);
+ } else {
+ /* alternative audio stream, which we don't care about */
+ }
+}
+
diff --git a/gemrb/plugins/MVEPlayer/mve_player.h b/gemrb/plugins/MVEPlayer/mve_player.h
new file mode 100644
index 0000000..77e1fb7
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/mve_player.h
@@ -0,0 +1,93 @@
+/* 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 MVE_PLAYER_H
+#define MVE_PLAYER_H
+
+class MVEPlayer {
+protected:
+ class MVEPlay *host;
+ char *buffer;
+ unsigned int buffersize;
+ unsigned int chunk_size;
+ unsigned int chunk_offset;
+
+ unsigned int outputwidth;
+ unsigned int outputheight;
+
+ long timer_last_sec;
+ long timer_last_usec;
+ unsigned int frame_wait;
+
+ struct _GstMveDemuxStream *video_data;
+ unsigned short video_width;
+ unsigned short video_height;
+ unsigned short *video_back_buf;
+ bool truecolour;
+ bool video_rendered_frame;
+ unsigned int video_frameskip;
+ unsigned int video_skippedframes;
+
+ bool audio_compressed;
+ int audio_num_channels;
+ unsigned short audio_sample_rate;
+ unsigned short audio_sample_size;
+ short *audio_buffer;
+ int audio_stream;
+
+ bool playsound;
+ bool done;
+
+ bool request_data(unsigned int len);
+
+ bool verify_header();
+ bool process_chunk();
+ bool process_segment(unsigned short len,
+ unsigned char type, unsigned char version);
+
+ void segment_create_timer();
+ void timer_start();
+ void timer_wait();
+
+ void segment_video_init(unsigned char version);
+ void segment_video_mode();
+ void segment_video_palette();
+ void segment_video_compressedpalette();
+ void segment_video_codemap(unsigned short size);
+ void segment_video_data(unsigned short size);
+ void segment_video_play();
+
+ void segment_audio_init(unsigned char version);
+ void segment_audio_data(bool silent);
+
+public:
+ MVEPlayer(class MVEPlay *file);
+ ~MVEPlayer();
+
+ void sound_init(bool playsound);
+ void video_init(unsigned int width, unsigned int height);
+
+ bool start_playback();
+ bool next_frame();
+
+ bool is_truecolour() { return truecolour; }
+};
+
+#endif
diff --git a/gemrb/plugins/MVEPlayer/mveaudiodec.cpp b/gemrb/plugins/MVEPlayer/mveaudiodec.cpp
new file mode 100644
index 0000000..51d47b1
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/mveaudiodec.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2003 The ffmpeg Project, Mike Melanson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Interplay compressed audio codec by Mike Melanson (melanson at pcisys.net)
+ */
+
+#include "gstmvedemux.h"
+
+static const short delta_table[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 47, 51, 56, 61,
+ 66, 72, 79, 86, 94, 102, 112, 122, 133, 145, 158, 173, 189, 206, 225, 245,
+ 267, 292, 318, 348, 379, 414, 452, 493, 538, 587, 640, 699, 763, 832, 908,
+ 991,
+ 1081, 1180, 1288, 1405, 1534, 1673, 1826, 1993, 2175, 2373, 2590, 2826, 3084,
+ 3365, 3672, 4008,
+ 4373, 4772, 5208, 5683, 6202, 6767, 7385, 8059, 8794, 9597, 10472, 11428,
+ 12471, 13609, 14851, 16206,
+ 17685, 19298, 21060, 22981, 25078, 27367, 29864, 32589, -29973, -26728,
+ -23186, -19322, -15105, -10503, -5481, -1,
+ 1, 1, 5481, 10503, 15105, 19322, 23186, 26728, 29973, -32589, -29864, -27367,
+ -25078, -22981, -21060, -19298,
+ -17685, -16206, -14851, -13609, -12471, -11428, -10472, -9597, -8794, -8059,
+ -7385, -6767, -6202, -5683, -5208, -4772,
+ -4373, -4008, -3672, -3365, -3084, -2826, -2590, -2373, -2175, -1993, -1826,
+ -1673, -1534, -1405, -1288, -1180,
+ -1081, -991, -908, -832, -763, -699, -640, -587, -538, -493, -452, -414, -379,
+ -348, -318, -292,
+ -267, -245, -225, -206, -189, -173, -158, -145, -133, -122, -112, -102, -94,
+ -86, -79, -72,
+ -66, -61, -56, -51, -47, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34,
+ -33,
+ -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18,
+ -17,
+ -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1
+};
+
+void
+ipaudio_uncompress (short *buffer, unsigned short buf_len,
+ const unsigned char *data, unsigned char channels)
+{
+ int i, out = 0;
+ int predictor[2];
+ int channel_number = 0;
+
+ for (i = 0; i < channels; ++i) {
+ predictor[i] = GST_READ_UINT16_LE (data);
+ data += 2;
+ if (predictor[i] & 0x8000)
+ predictor[i] -= 0x10000;
+ buffer[out++] = predictor[i];
+ }
+
+ /* we count in 16-bit ints, so adjust the buffer size */
+ buf_len /= 2;
+ while (out < buf_len) {
+ predictor[channel_number] += delta_table[*data++];
+ if (predictor[channel_number] < -32768)
+ predictor[channel_number] = -32768;
+ else if (predictor[channel_number] > 32767)
+ predictor[channel_number] = 32767;
+ buffer[out++] = predictor[channel_number];
+
+ /* toggle channel */
+ channel_number ^= channels - 1;
+ }
+}
diff --git a/gemrb/plugins/MVEPlayer/mvevideodec16.cpp b/gemrb/plugins/MVEPlayer/mvevideodec16.cpp
new file mode 100644
index 0000000..66d158c
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/mvevideodec16.cpp
@@ -0,0 +1,844 @@
+/*
+ * Interplay MVE Video Decoder (16 bit)
+ * Copyright (C) 2003 the ffmpeg project, Mike Melanson
+ * (C) 2006 Jens Granseuer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * For more information about the Interplay MVE format, visit:
+ * http://www.pcisys.net/~melanson/codecs/interplay-mve.txt
+ */
+
+#include "gstmvedemux.h"
+#include <string.h>
+
+#define PIXEL(s) GST_READ_UINT16_LE (s)
+
+#define CHECK_STREAM(l, n) \
+ do { \
+ if (G_UNLIKELY (*(l) < (n))) { \
+ GST_ERROR ("wanted to read %d bytes from stream, %d available", (n), *(l)); \
+ return -1; \
+ } \
+ *(l) -= (n); \
+ } while (0)
+
+/* copy an 8x8 block from the stream to the frame buffer */
+static int
+ipvideo_copy_block (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned short *src, int offset)
+{
+ int i;
+ int frame_offset;
+
+ frame_offset = frame - s->back_buf1 + offset;
+
+ if (G_UNLIKELY (frame_offset < 0)) {
+ GST_ERROR ("frame offset < 0 (%d)", frame_offset);
+ return -1;
+ } else if (G_UNLIKELY ((guint32)frame_offset > s->max_block_offset)) {
+ GST_ERROR ("frame offset above limit (%d > %u)",
+ frame_offset, s->max_block_offset);
+ return -1;
+ }
+
+ for (i = 0; i < 8; ++i) {
+ memcpy (frame, src, 16);
+ frame += s->width;
+ src += s->width;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0x2 (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ unsigned char B;
+ int x, y;
+ int offset;
+
+ /* copy block from 2 frames ago using a motion vector */
+ CHECK_STREAM (len, 1);
+ B = *(*data)++;
+
+ if (B < 56) {
+ x = 8 + (B % 7);
+ y = B / 7;
+ } else {
+ x = -14 + ((B - 56) % 29);
+ y = 8 + ((B - 56) / 29);
+ }
+ offset = y * s->width + x;
+
+ return ipvideo_copy_block (s, frame, frame + offset, offset);
+}
+
+static int
+ipvideo_decode_0x3 (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ unsigned char B;
+ int x, y;
+ int offset;
+
+ /* copy 8x8 block from current frame from an up/left block */
+ CHECK_STREAM (len, 1);
+ B = *(*data)++;
+
+ if (B < 56) {
+ x = -(8 + (B % 7));
+ y = -(B / 7);
+ } else {
+ x = -(-14 + ((B - 56) % 29));
+ y = -(8 + ((B - 56) / 29));
+ }
+ offset = y * s->width + x;
+
+ return ipvideo_copy_block (s, frame, frame + offset, offset);
+}
+
+static int
+ipvideo_decode_0x4 (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned char B;
+ int offset;
+
+ /* copy a block from the previous frame */
+ CHECK_STREAM (len, 1);
+ B = *(*data)++;
+ x = -8 + (B & 0x0F);
+ y = -8 + (B >> 4);
+ offset = y * s->width + x;
+
+ return ipvideo_copy_block (s, frame, frame +
+ (s->back_buf2 - s->back_buf1) + offset, offset);
+}
+
+static int
+ipvideo_decode_0x5 (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ signed char x, y;
+ int offset;
+
+ /* copy a block from the previous frame using an expanded range */
+ CHECK_STREAM (len, 2);
+ x = (signed char) *(*data)++;
+ y = (signed char) *(*data)++;
+ offset = y * s->width + x;
+
+ return ipvideo_copy_block (s, frame, frame +
+ ((unsigned short *) s->back_buf2 - (unsigned short *) s->back_buf1) +
+ offset, offset);
+}
+
+static int
+ipvideo_decode_0x7 (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned short P0, P1;
+ unsigned int flags;
+ int bitmask;
+
+ /* 2-color encoding */
+ CHECK_STREAM (len, 4 + 2);
+ P0 = PIXEL (*data);
+ (*data) += 2;
+ P1 = PIXEL (*data);
+ (*data) += 2;
+
+ if (!(P0 & 0x8000)) {
+
+ /* need 8 more bytes from the stream */
+ CHECK_STREAM (len, 8 - 2);
+
+ for (y = 0; y < 8; ++y) {
+ flags = *(*data)++;
+ for (x = 0x01; x <= 0x80; x <<= 1) {
+ if (flags & x)
+ *frame++ = P1;
+ else
+ *frame++ = P0;
+ }
+ frame += s->width - 8;
+ }
+
+ } else {
+ P0 &= ~0x8000;
+
+ /* need 2 more bytes from the stream */
+
+ flags = ((*data)[1] << 8) | (*data)[0];
+ (*data) += 2;
+ bitmask = 0x0001;
+ for (y = 0; y < 8; y += 2) {
+ for (x = 0; x < 8; x += 2, bitmask <<= 1) {
+ if (flags & bitmask) {
+ *(frame + x) = P1;
+ *(frame + x + 1) = P1;
+ *(frame + s->width + x) = P1;
+ *(frame + s->width + x + 1) = P1;
+ } else {
+ *(frame + x) = P0;
+ *(frame + x + 1) = P0;
+ *(frame + s->width + x) = P0;
+ *(frame + s->width + x + 1) = P0;
+ }
+ }
+ frame += s->width * 2;
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0x8 (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned short P[8];
+ unsigned char B[8];
+ unsigned int flags = 0;
+ unsigned int bitmask = 0;
+ unsigned short P0 = 0, P1 = 0;
+ int lower_half = 0;
+
+ /* 2-color encoding for each 4x4 quadrant, or 2-color encoding on
+ * either top and bottom or left and right halves */
+ CHECK_STREAM (len, 6 + 10);
+
+ P[0] = PIXEL (*data);
+ (*data) += 2;
+ P[1] = PIXEL (*data);
+ (*data) += 2;
+ B[0] = *(*data)++;
+ B[1] = *(*data)++;
+
+ if (!(P[0] & 0x8000)) {
+
+ /* need 18 more bytes */
+ CHECK_STREAM (len, 18 - 10);
+
+ P[2] = PIXEL (*data);
+ (*data) += 2;
+ P[3] = PIXEL (*data);
+ (*data) += 2;
+ B[2] = *(*data)++;
+ B[3] = *(*data)++;
+ P[4] = PIXEL (*data);
+ (*data) += 2;
+ P[5] = PIXEL (*data);
+ (*data) += 2;
+ B[4] = *(*data)++;
+ B[5] = *(*data)++;
+ P[6] = PIXEL (*data);
+ (*data) += 2;
+ P[7] = PIXEL (*data);
+ (*data) += 2;
+ B[6] = *(*data)++;
+ B[7] = *(*data)++;
+
+ flags = ((B[0] & 0xF0) << 4) | ((B[4] & 0xF0) << 8) |
+ ((B[0] & 0x0F)) | ((B[4] & 0x0F) << 4) |
+ ((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) |
+ ((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20);
+ bitmask = 0x00000001;
+ lower_half = 0; /* still on top half */
+
+ for (y = 0; y < 8; ++y) {
+
+ /* time to reload flags? */
+ if (y == 4) {
+ flags = ((B[2] & 0xF0) << 4) | ((B[6] & 0xF0) << 8) |
+ ((B[2] & 0x0F)) | ((B[6] & 0x0F) << 4) |
+ ((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) |
+ ((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20);
+ bitmask = 0x00000001;
+ lower_half = 2;
+ }
+
+ /* get the pixel values ready for this quadrant */
+ P0 = P[lower_half + 0];
+ P1 = P[lower_half + 1];
+
+ for (x = 0; x < 8; ++x, bitmask <<= 1) {
+ if (x == 4) {
+ P0 = P[lower_half + 4];
+ P1 = P[lower_half + 5];
+ }
+
+ if (flags & bitmask)
+ *frame++ = P1;
+ else
+ *frame++ = P0;
+ }
+ frame += s->width - 8;
+ }
+
+ } else {
+ P[0] &= ~0x8000;
+
+ /* need 10 more bytes */
+ B[2] = *(*data)++;
+ B[3] = *(*data)++;
+ P[2] = PIXEL (*data);
+ (*data) += 2;
+ P[3] = PIXEL (*data);
+ (*data) += 2;
+ B[4] = *(*data)++;
+ B[5] = *(*data)++;
+ B[6] = *(*data)++;
+ B[7] = *(*data)++;
+
+ if (!(P[2] & 0x8000)) {
+ /* vertical split; left & right halves are 2-color encoded */
+
+ flags =
+ ((B[0] & 0xF0) << 4) | ((B[4] & 0xF0) << 8) |
+ ((B[0] & 0x0F)) | ((B[4] & 0x0F) << 4) |
+ ((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) |
+ ((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20);
+ bitmask = 0x00000001;
+
+ for (y = 0; y < 8; ++y) {
+
+ /* time to reload flags? */
+ if (y == 4) {
+ flags = ((B[2] & 0xF0) << 4) | ((B[6] & 0xF0) << 8) |
+ ((B[2] & 0x0F)) | ((B[6] & 0x0F) << 4) |
+ ((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) |
+ ((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20);
+ bitmask = 0x00000001;
+ }
+
+ /* get the pixel values ready for this half */
+ P0 = P[0];
+ P1 = P[1];
+
+ for (x = 0; x < 8; ++x, bitmask <<= 1) {
+ if (x == 4) {
+ P0 = P[2];
+ P1 = P[3];
+ }
+
+ if (flags & bitmask)
+ *frame++ = P1;
+ else
+ *frame++ = P0;
+ }
+ frame += s->width - 8;
+ }
+
+ } else {
+ /* horizontal split; top & bottom halves are 2-color encoded */
+
+ P0 = P[0];
+ P1 = P[1];
+
+ for (y = 0; y < 8; ++y) {
+
+ flags = B[y];
+ if (y == 4) {
+ P0 = P[2] & ~0x8000;
+ P1 = P[3];
+ }
+
+ for (bitmask = 0x01; bitmask <= 0x80; bitmask <<= 1) {
+
+ if (flags & bitmask)
+ *frame++ = P1;
+ else
+ *frame++ = P0;
+ }
+ frame += s->width - 8;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0x9 (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned short P[4];
+ unsigned char B[4];
+ unsigned int flags = 0;
+ int shifter = 0;
+ unsigned short pix;
+
+ /* 4-color encoding */
+ CHECK_STREAM (len, 8 + 4);
+
+ P[0] = PIXEL (*data);
+ (*data) += 2;
+ P[1] = PIXEL (*data);
+ (*data) += 2;
+ P[2] = PIXEL (*data);
+ (*data) += 2;
+ P[3] = PIXEL (*data);
+ (*data) += 2;
+
+ if (!(P[0] & 0x8000) && !(P[2] & 0x8000)) {
+
+ /* 1 of 4 colors for each pixel, need 16 more bytes */
+ CHECK_STREAM (len, 16 - 4);
+
+ for (y = 0; y < 8; ++y) {
+ /* get the next set of 8 2-bit flags */
+ flags = ((*data)[1] << 8) | (*data)[0];
+ (*data) += 2;
+ for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+ *frame++ = P[(flags >> shifter) & 0x03];
+ }
+ frame += s->width - 8;
+ }
+
+ } else if (!(P[0] & 0x8000) && (P[2] & 0x8000)) {
+ P[2] &= ~0x8000;
+
+ /* 1 of 4 colors for each 2x2 block, need 4 more bytes */
+
+ B[0] = *(*data)++;
+ B[1] = *(*data)++;
+ B[2] = *(*data)++;
+ B[3] = *(*data)++;
+ flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+ shifter = 0;
+
+ for (y = 0; y < 8; y += 2) {
+ for (x = 0; x < 8; x += 2, shifter += 2) {
+ pix = P[(flags >> shifter) & 0x03];
+ *(frame + x) = pix;
+ *(frame + x + 1) = pix;
+ *(frame + s->width + x) = pix;
+ *(frame + s->width + x + 1) = pix;
+ }
+ frame += s->width * 2;
+ }
+
+ } else if ((P[0] & 0x8000) && !(P[2] & 0x8000)) {
+ P[0] &= ~0x8000;
+
+ /* 1 of 4 colors for each 2x1 block, need 8 more bytes */
+
+ CHECK_STREAM (len, 8 - 4);
+ for (y = 0; y < 8; ++y) {
+ /* time to reload flags? */
+ if ((y == 0) || (y == 4)) {
+ B[0] = *(*data)++;
+ B[1] = *(*data)++;
+ B[2] = *(*data)++;
+ B[3] = *(*data)++;
+ flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+ shifter = 0;
+ }
+ for (x = 0; x < 8; x += 2, shifter += 2) {
+ pix = P[(flags >> shifter) & 0x03];
+ *(frame + x) = pix;
+ *(frame + x + 1) = pix;
+ }
+ frame += s->width;
+ }
+
+ } else {
+ P[0] &= ~0x8000;
+ P[2] &= ~0x8000;
+
+ /* 1 of 4 colors for each 1x2 block, need 8 more bytes */
+ CHECK_STREAM (len, 8 - 4);
+
+ for (y = 0; y < 8; y += 2) {
+ /* time to reload flags? */
+ if ((y == 0) || (y == 4)) {
+ B[0] = *(*data)++;
+ B[1] = *(*data)++;
+ B[2] = *(*data)++;
+ B[3] = *(*data)++;
+ flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+ shifter = 0;
+ }
+ for (x = 0; x < 8; ++x, shifter += 2) {
+ pix = P[(flags >> shifter) & 0x03];
+ *(frame + x) = pix;
+ *(frame + s->width + x) = pix;
+ }
+ frame += s->width * 2;
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xa (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned short P[16];
+ unsigned char B[16];
+ int flags = 0;
+ int shifter = 0;
+ int index;
+ int split;
+ int lower_half;
+
+ /* 4-color encoding for each 4x4 quadrant, or 4-color encoding on
+ * either top and bottom or left and right halves */
+ CHECK_STREAM (len, 8 + 24);
+
+ P[0] = PIXEL (*data);
+ (*data) += 2;
+ P[1] = PIXEL (*data);
+ (*data) += 2;
+ P[2] = PIXEL (*data);
+ (*data) += 2;
+ P[3] = PIXEL (*data);
+ (*data) += 2;
+
+ if (!(P[0] & 0x8000)) {
+
+ /* 4-color encoding for each quadrant; need 40 more bytes */
+ CHECK_STREAM (len, 40 - 24);
+
+ B[0] = *(*data)++;
+ B[1] = *(*data)++;
+ B[2] = *(*data)++;
+ B[3] = *(*data)++;
+ for (y = 4; y < 16; y += 4) {
+ for (x = y; x < y + 4; ++x) {
+ P[x] = PIXEL (*data);
+ (*data) += 2;
+ }
+ for (x = y; x < y + 4; ++x)
+ B[x] = *(*data)++;
+ }
+
+ for (y = 0; y < 8; ++y) {
+
+ lower_half = (y >= 4) ? 4 : 0;
+ flags = (B[y + 8] << 8) | B[y];
+
+ for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+ split = (x >= 4) ? 8 : 0;
+ index = split + lower_half + ((flags >> shifter) & 0x03);
+ *frame++ = P[index];
+ }
+
+ frame += s->width - 8;
+ }
+
+ } else {
+ P[0] &= ~0x8000;
+
+ /* 4-color encoding for either left and right or top and bottom
+ * halves; need 24 more bytes */
+
+ memcpy (&B[0], *data, 8);
+ (*data) += 8;
+ P[4] = PIXEL (*data);
+ (*data) += 2;
+ P[5] = PIXEL (*data);
+ (*data) += 2;
+ P[6] = PIXEL (*data);
+ (*data) += 2;
+ P[7] = PIXEL (*data);
+ (*data) += 2;
+ memcpy (&B[8], *data, 8);
+ (*data) += 8;
+
+ if (!(P[4] & 0x8000)) {
+
+ /* block is divided into left and right halves */
+ for (y = 0; y < 8; ++y) {
+
+ flags = (B[y + 8] << 8) | B[y];
+ split = 0;
+
+ for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+ if (x == 4)
+ split = 4;
+ *frame++ = P[split + ((flags >> shifter) & 0x03)];
+ }
+
+ frame += s->width - 8;
+ }
+
+ } else {
+ P[4] &= ~0x8000;
+
+ /* block is divided into top and bottom halves */
+ split = 0;
+ for (y = 0; y < 8; ++y) {
+
+ flags = (B[y * 2 + 1] << 8) | B[y * 2];
+ if (y == 4)
+ split = 4;
+
+ for (x = 0, shifter = 0; x < 8; ++x, shifter += 2)
+ *frame++ = P[split + ((flags >> shifter) & 0x03)];
+
+ frame += s->width - 8;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xb (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+
+ /* 64-color encoding (each pixel in block is a different color) */
+ CHECK_STREAM (len, 128);
+
+ for (y = 0; y < 8; ++y) {
+ for (x = 0; x < 8; ++x) {
+ *frame++ = PIXEL (*data);
+ (*data) += 2;
+ }
+ frame += s->width - 8;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xc (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned short pix;
+
+ /* 16-color block encoding: each 2x2 block is a different color */
+ CHECK_STREAM (len, 32);
+
+ for (y = 0; y < 8; y += 2) {
+ for (x = 0; x < 8; x += 2) {
+ pix = PIXEL (*data);
+ (*data) += 2;
+ *(frame + x) = pix;
+ *(frame + x + 1) = pix;
+ *(frame + s->width + x) = pix;
+ *(frame + s->width + x + 1) = pix;
+ }
+ frame += s->width * 2;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xd (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned short P[4];
+ unsigned char index = 0;
+
+ /* 4-color block encoding: each 4x4 block is a different color */
+ CHECK_STREAM (len, 8);
+
+ P[0] = PIXEL (*data);
+ (*data) += 2;
+ P[1] = PIXEL (*data);
+ (*data) += 2;
+ P[2] = PIXEL (*data);
+ (*data) += 2;
+ P[3] = PIXEL (*data);
+ (*data) += 2;
+
+ for (y = 0; y < 8; ++y) {
+ if (y < 4)
+ index = 0;
+ else
+ index = 2;
+
+ for (x = 0; x < 8; ++x) {
+ if (x == 4)
+ ++index;
+ *frame++ = P[index];
+ }
+ frame += s->width - 8;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xe (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned short pix;
+
+ /* 1-color encoding: the whole block is 1 solid color */
+ CHECK_STREAM (len, 2);
+
+ pix = PIXEL (*data);
+ (*data) += 2;
+
+ for (y = 0; y < 8; ++y) {
+ for (x = 0; x < 8; ++x) {
+ *frame++ = pix;
+ }
+ frame += s->width - 8;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xf (const GstMveDemuxStream * s, unsigned short *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned short P[2];
+
+ /* dithered encoding */
+ CHECK_STREAM (len, 4);
+
+ P[0] = PIXEL (*data);
+ (*data) += 2;
+ P[1] = PIXEL (*data);
+ (*data) += 2;
+
+ for (y = 0; y < 8; ++y) {
+ for (x = 0; x < 4; ++x) {
+ *frame++ = P[y & 1];
+ *frame++ = P[(y & 1) ^ 1];
+ }
+ frame += s->width - 8;
+ }
+
+ return 0;
+}
+
+int
+ipvideo_decode_frame16 (const GstMveDemuxStream * s, const unsigned char *data,
+ unsigned short len)
+{
+ int rc = 0;
+ int x, y, xx, yy;
+ int index = 0;
+ unsigned short offset;
+ unsigned char opcode;
+ unsigned short *frame;
+ const unsigned char *data2;
+ unsigned short len2;
+
+ CHECK_STREAM (&len, 2);
+
+ offset = (data[1] << 8) | data[0];
+ data2 = data + offset;
+ len2 = len - offset + 2;
+ data += 2;
+
+ frame = s->back_buf1;
+
+ /* decoding is done in 8x8 blocks */
+ xx = s->width >> 3;
+ yy = s->height >> 3;
+
+ for (y = 0; y < yy; ++y) {
+ for (x = 0; x < xx; ++x) {
+ /* decoding map contains 4 bits of information per 8x8 block */
+ /* bottom nibble first, then top nibble */
+ if (index & 1)
+ opcode = s->code_map[index >> 1] >> 4;
+ else
+ opcode = s->code_map[index >> 1] & 0x0F;
+ ++index;
+
+ /* GST_DEBUG ("block @ (%3d, %3d): encoding 0x%X, data ptr @ %p",
+ x, y, opcode, data); */
+
+ switch (opcode) {
+ case 0x0:
+ /* copy a block from the previous frame */
+ rc = ipvideo_copy_block (s, frame, frame +
+ (s->back_buf2 - s->back_buf1), 0);
+ break;
+ case 0x1:
+ /* copy block from 2 frames ago; since we switched the back
+ * buffers we don't actually have to do anything here */
+ break;
+ case 0x2:
+ rc = ipvideo_decode_0x2 (s, frame, &data2, &len2);
+ break;
+ case 0x3:
+ rc = ipvideo_decode_0x3 (s, frame, &data2, &len2);
+ break;
+ case 0x4:
+ rc = ipvideo_decode_0x4 (s, frame, &data2, &len2);
+ break;
+ case 0x5:
+ rc = ipvideo_decode_0x5 (s, frame, &data, &len);
+ break;
+ case 0x6:
+ /* mystery opcode? skip multiple blocks? */
+ GST_WARNING ("encountered unsupported opcode 0x6");
+ rc = -1;
+ break;
+ case 0x7:
+ rc = ipvideo_decode_0x7 (s, frame, &data, &len);
+ break;
+ case 0x8:
+ rc = ipvideo_decode_0x8 (s, frame, &data, &len);
+ break;
+ case 0x9:
+ rc = ipvideo_decode_0x9 (s, frame, &data, &len);
+ break;
+ case 0xa:
+ rc = ipvideo_decode_0xa (s, frame, &data, &len);
+ break;
+ case 0xb:
+ rc = ipvideo_decode_0xb (s, frame, &data, &len);
+ break;
+ case 0xc:
+ rc = ipvideo_decode_0xc (s, frame, &data, &len);
+ break;
+ case 0xd:
+ rc = ipvideo_decode_0xd (s, frame, &data, &len);
+ break;
+ case 0xe:
+ rc = ipvideo_decode_0xe (s, frame, &data, &len);
+ break;
+ case 0xf:
+ rc = ipvideo_decode_0xf (s, frame, &data, &len);
+ break;
+ }
+
+ if (rc != 0)
+ return rc;
+
+ frame += 8;
+ }
+ frame += 7 * s->width;
+ }
+
+ return 0;
+}
diff --git a/gemrb/plugins/MVEPlayer/mvevideodec8.cpp b/gemrb/plugins/MVEPlayer/mvevideodec8.cpp
new file mode 100644
index 0000000..5d38208
--- /dev/null
+++ b/gemrb/plugins/MVEPlayer/mvevideodec8.cpp
@@ -0,0 +1,797 @@
+/*
+ * Interplay MVE Video Decoder (8 bit)
+ * Copyright (C) 2003 the ffmpeg project, Mike Melanson
+ * (C) 2006 Jens Granseuer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * For more information about the Interplay MVE format, visit:
+ * http://www.pcisys.net/~melanson/codecs/interplay-mve.txt
+ */
+
+#include "gstmvedemux.h"
+#include <string.h>
+
+#define CHECK_STREAM(l, n) \
+ do { \
+ if (G_UNLIKELY (*(l) < (n))) { \
+ GST_ERROR ("wanted to read %d bytes from stream, %d available", (n), *(l)); \
+ return -1; \
+ } \
+ *(l) -= (n); \
+ } while (0)
+
+
+/* copy an 8x8 block from the stream to the frame buffer */
+static int
+ipvideo_copy_block (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char *src, int offset)
+{
+ int i;
+ long frame_offset;
+
+ frame_offset = frame - (guint8 *) s->back_buf1 + offset;
+
+ if (G_UNLIKELY (frame_offset < 0)) {
+ GST_ERROR ("frame offset < 0 (%ld)", frame_offset);
+ return -1;
+ } else if (G_UNLIKELY ((guint32)frame_offset > s->max_block_offset)) {
+ GST_ERROR ("frame offset above limit (%ld > %u)",
+ frame_offset, s->max_block_offset);
+ return -1;
+ }
+
+ for (i = 0; i < 8; ++i) {
+ memcpy (frame, src, 8);
+ frame += s->width;
+ src += s->width;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0x2 (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ unsigned char B;
+ int x, y;
+ int offset;
+
+ /* copy block from 2 frames ago using a motion vector */
+ CHECK_STREAM (len, 1);
+ B = *(*data)++;
+
+ if (B < 56) {
+ x = 8 + (B % 7);
+ y = B / 7;
+ } else {
+ x = -14 + ((B - 56) % 29);
+ y = 8 + ((B - 56) / 29);
+ }
+ offset = y * s->width + x;
+
+ return ipvideo_copy_block (s, frame, frame + offset, offset);
+}
+
+static int
+ipvideo_decode_0x3 (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ unsigned char B;
+ int x, y;
+ int offset;
+
+ /* copy 8x8 block from current frame from an up/left block */
+ CHECK_STREAM (len, 1);
+ B = *(*data)++;
+
+ if (B < 56) {
+ x = -(8 + (B % 7));
+ y = -(B / 7);
+ } else {
+ x = -(-14 + ((B - 56) % 29));
+ y = -(8 + ((B - 56) / 29));
+ }
+ offset = y * s->width + x;
+
+ return ipvideo_copy_block (s, frame, frame + offset, offset);
+}
+
+static int
+ipvideo_decode_0x4 (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ unsigned char B;
+ int x, y;
+ int offset;
+
+ /* copy a block from the previous frame */
+ CHECK_STREAM (len, 1);
+ B = *(*data)++;
+ x = -8 + (B & 0x0F);
+ y = -8 + (B >> 4);
+ offset = y * s->width + x;
+
+ return ipvideo_copy_block (s, frame,
+ frame + ((guint8 *) s->back_buf2 - (guint8 *) s->back_buf1) + offset, offset);
+}
+
+static int
+ipvideo_decode_0x5 (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ signed char x, y;
+ int offset;
+
+ /* copy a block from the previous frame using an expanded range */
+ CHECK_STREAM (len, 2);
+
+ x = (signed char) *(*data)++;
+ y = (signed char) *(*data)++;
+ offset = y * s->width + x;
+
+ return ipvideo_copy_block (s, frame,
+ frame + ((guint8 *) s->back_buf2 - (guint8 *) s->back_buf1) + offset, offset);
+}
+
+static int
+ipvideo_decode_0x7 (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned char P0, P1;
+ unsigned int flags;
+ int bitmask;
+
+ /* 2-color encoding */
+ CHECK_STREAM (len, 2 + 2);
+
+ P0 = *(*data)++;
+ P1 = *(*data)++;
+
+ if (P0 <= P1) {
+
+ /* need 8 more bytes from the stream */
+ CHECK_STREAM (len, 8 - 2);
+
+ for (y = 0; y < 8; ++y) {
+ flags = *(*data)++;
+ for (x = 0x01; x <= 0x80; x <<= 1) {
+ if (flags & x)
+ *frame++ = P1;
+ else
+ *frame++ = P0;
+ }
+ frame += s->width - 8;
+ }
+
+ } else {
+
+ /* need 2 more bytes from the stream */
+ flags = ((*data)[1] << 8) | (*data)[0];
+ (*data) += 2;
+ bitmask = 0x0001;
+ for (y = 0; y < 8; y += 2) {
+ for (x = 0; x < 8; x += 2, bitmask <<= 1) {
+ if (flags & bitmask) {
+ *(frame + x) = P1;
+ *(frame + x + 1) = P1;
+ *(frame + s->width + x) = P1;
+ *(frame + s->width + x + 1) = P1;
+ } else {
+ *(frame + x) = P0;
+ *(frame + x + 1) = P0;
+ *(frame + s->width + x) = P0;
+ *(frame + s->width + x + 1) = P0;
+ }
+ }
+ frame += s->width * 2;
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0x8 (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned char P[8];
+ unsigned char B[8];
+ unsigned int flags = 0;
+ unsigned int bitmask = 0;
+ unsigned char P0 = 0, P1 = 0;
+ int lower_half = 0;
+
+ /* 2-color encoding for each 4x4 quadrant, or 2-color encoding on
+ * either top and bottom or left and right halves */
+ CHECK_STREAM (len, 4 + 8);
+
+ P[0] = (*data)[0];
+ P[1] = (*data)[1];
+ B[0] = (*data)[2];
+ B[1] = (*data)[3];
+ (*data) += 4;
+
+ if (P[0] <= P[1]) {
+
+ /* need 12 more bytes */
+ CHECK_STREAM (len, 12 - 8);
+
+ P[2] = (*data)[0];
+ P[3] = (*data)[1];
+ B[2] = (*data)[2];
+ B[3] = (*data)[3];
+ P[4] = (*data)[4];
+ P[5] = (*data)[5];
+ B[4] = (*data)[6];
+ B[5] = (*data)[7];
+ P[6] = (*data)[8];
+ P[7] = (*data)[9];
+ B[6] = (*data)[10];
+ B[7] = (*data)[11];
+ (*data) += 12;
+
+ flags =
+ ((B[0] & 0xF0) << 4) | ((B[4] & 0xF0) << 8) |
+ ((B[0] & 0x0F)) | ((B[4] & 0x0F) << 4) |
+ ((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) |
+ ((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20);
+ bitmask = 0x00000001;
+ lower_half = 0; /* still on top half */
+
+ for (y = 0; y < 8; ++y) {
+
+ /* time to reload flags? */
+ if (y == 4) {
+ flags =
+ ((B[2] & 0xF0) << 4) | ((B[6] & 0xF0) << 8) |
+ ((B[2] & 0x0F)) | ((B[6] & 0x0F) << 4) |
+ ((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) |
+ ((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20);
+ bitmask = 0x00000001;
+ lower_half = 2;
+ }
+
+ /* get the pixel values ready for this quadrant */
+ P0 = P[lower_half + 0];
+ P1 = P[lower_half + 1];
+
+ for (x = 0; x < 8; ++x, bitmask <<= 1) {
+ if (x == 4) {
+ P0 = P[lower_half + 4];
+ P1 = P[lower_half + 5];
+ }
+
+ if (flags & bitmask)
+ *frame++ = P1;
+ else
+ *frame++ = P0;
+ }
+ frame += s->width - 8;
+ }
+
+ } else {
+
+ /* need 8 more bytes */
+ B[2] = (*data)[0];
+ B[3] = (*data)[1];
+ P[2] = (*data)[2];
+ P[3] = (*data)[3];
+ B[4] = (*data)[4];
+ B[5] = (*data)[5];
+ B[6] = (*data)[6];
+ B[7] = (*data)[7];
+ (*data) += 8;
+
+ if (P[2] <= P[3]) {
+
+ /* vertical split; left & right halves are 2-color encoded */
+
+ flags = ((B[0] & 0xF0) << 4) | ((B[4] & 0xF0) << 8) |
+ ((B[0] & 0x0F)) | ((B[4] & 0x0F) << 4) |
+ ((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) |
+ ((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20);
+ bitmask = 0x00000001;
+
+ for (y = 0; y < 8; ++y) {
+
+ /* time to reload flags? */
+ if (y == 4) {
+ flags = ((B[2] & 0xF0) << 4) | ((B[6] & 0xF0) << 8) |
+ ((B[2] & 0x0F)) | ((B[6] & 0x0F) << 4) |
+ ((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) |
+ ((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20);
+ bitmask = 0x00000001;
+ }
+
+ /* get the pixel values ready for this half */
+ P0 = P[0];
+ P1 = P[1];
+
+ for (x = 0; x < 8; ++x, bitmask <<= 1) {
+ if (x == 4) {
+ P0 = P[2];
+ P1 = P[3];
+ }
+
+ if (flags & bitmask)
+ *frame++ = P1;
+ else
+ *frame++ = P0;
+ }
+ frame += s->width - 8;
+ }
+
+ } else {
+
+ /* horizontal split; top & bottom halves are 2-color encoded */
+
+ P0 = P[0];
+ P1 = P[1];
+
+ for (y = 0; y < 8; ++y) {
+
+ flags = B[y];
+ if (y == 4) {
+ P0 = P[2];
+ P1 = P[3];
+ }
+
+ for (bitmask = 0x01; bitmask <= 0x80; bitmask <<= 1) {
+
+ if (flags & bitmask)
+ *frame++ = P1;
+ else
+ *frame++ = P0;
+ }
+ frame += s->width - 8;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0x9 (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned char P[4];
+ unsigned char B[4];
+ unsigned long flags = 0;
+ int shifter = 0;
+ unsigned char pix;
+
+ /* 4-color encoding */
+ CHECK_STREAM (len, 4 + 4);
+
+ P[0] = (*data)[0];
+ P[1] = (*data)[1];
+ P[2] = (*data)[2];
+ P[3] = (*data)[3];
+ (*data) += 4;
+
+ if ((P[0] <= P[1]) && (P[2] <= P[3])) {
+
+ /* 1 of 4 colors for each pixel, need 16 more bytes */
+ CHECK_STREAM (len, 16 - 4);
+
+ for (y = 0; y < 8; ++y) {
+ /* get the next set of 8 2-bit flags */
+ flags = ((*data)[1] << 8) | (*data)[0];
+ (*data) += 2;
+ for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+ *frame++ = P[(flags >> shifter) & 0x03];
+ }
+ frame += s->width - 8;
+ }
+
+ } else if ((P[0] <= P[1]) && (P[2] > P[3])) {
+
+ /* 1 of 4 colors for each 2x2 block, need 4 more bytes */
+ B[0] = (*data)[0];
+ B[1] = (*data)[1];
+ B[2] = (*data)[2];
+ B[3] = (*data)[3];
+ (*data) += 4;
+ flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+ shifter = 0;
+
+ for (y = 0; y < 8; y += 2) {
+ for (x = 0; x < 8; x += 2, shifter += 2) {
+ pix = P[(flags >> shifter) & 0x03];
+ *(frame + x) = pix;
+ *(frame + x + 1) = pix;
+ *(frame + s->width + x) = pix;
+ *(frame + s->width + x + 1) = pix;
+ }
+ frame += s->width * 2;
+ }
+
+ } else if ((P[0] > P[1]) && (P[2] <= P[3])) {
+
+ /* 1 of 4 colors for each 2x1 block, need 8 more bytes */
+ CHECK_STREAM (len, 8 - 4);
+
+ for (y = 0; y < 8; ++y) {
+ /* time to reload flags? */
+ if ((y == 0) || (y == 4)) {
+ B[0] = (*data)[0];
+ B[1] = (*data)[1];
+ B[2] = (*data)[2];
+ B[3] = (*data)[3];
+ (*data) += 4;
+ flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+ shifter = 0;
+ }
+ for (x = 0; x < 8; x += 2, shifter += 2) {
+ pix = P[(flags >> shifter) & 0x03];
+ *(frame + x) = pix;
+ *(frame + x + 1) = pix;
+ }
+ frame += s->width;
+ }
+
+ } else {
+
+ /* 1 of 4 colors for each 1x2 block, need 8 more bytes */
+ CHECK_STREAM (len, 8 - 4);
+
+ for (y = 0; y < 8; y += 2) {
+ /* time to reload flags? */
+ if ((y == 0) || (y == 4)) {
+ B[0] = (*data)[0];
+ B[1] = (*data)[1];
+ B[2] = (*data)[2];
+ B[3] = (*data)[3];
+ (*data) += 4;
+ flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0];
+ shifter = 0;
+ }
+ for (x = 0; x < 8; ++x, shifter += 2) {
+ pix = P[(flags >> shifter) & 0x03];
+ *(frame + x) = pix;
+ *(frame + s->width + x) = pix;
+ }
+ frame += s->width * 2;
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xa (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned char P[16];
+ unsigned char B[16];
+ int flags = 0;
+ int shifter = 0;
+ int index;
+ int split;
+ int lower_half;
+
+ /* 4-color encoding for each 4x4 quadrant, or 4-color encoding on
+ * either top and bottom or left and right halves */
+ CHECK_STREAM (len, 8 + 16);
+
+ P[0] = (*data)[0];
+ P[1] = (*data)[1];
+ P[2] = (*data)[2];
+ P[3] = (*data)[3];
+ B[0] = (*data)[4];
+ B[1] = (*data)[5];
+ B[2] = (*data)[6];
+ B[3] = (*data)[7];
+ (*data) += 8;
+
+ if (P[0] <= P[1]) {
+
+ /* 4-color encoding for each quadrant; need 24 more bytes */
+ CHECK_STREAM (len, 24 - 16);
+
+ for (y = 4; y < 16; y += 4) {
+ for (x = y; x < y + 4; ++x)
+ P[x] = *(*data)++;
+ for (x = y; x < y + 4; ++x)
+ B[x] = *(*data)++;
+ }
+
+ for (y = 0; y < 8; ++y) {
+
+ lower_half = (y >= 4) ? 4 : 0;
+ flags = (B[y + 8] << 8) | B[y];
+
+ for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+ split = (x >= 4) ? 8 : 0;
+ index = split + lower_half + ((flags >> shifter) & 0x03);
+ *frame++ = P[index];
+ }
+
+ frame += s->width - 8;
+ }
+
+ } else {
+
+ /* 4-color encoding for either left and right or top and bottom
+ * halves; need 16 more bytes */
+
+ B[4] = (*data)[0];
+ B[5] = (*data)[1];
+ B[6] = (*data)[2];
+ B[7] = (*data)[3];
+ P[4] = (*data)[4];
+ P[5] = (*data)[5];
+ P[6] = (*data)[6];
+ P[7] = (*data)[7];
+ (*data) += 8;
+ memcpy (&B[8], *data, 8);
+ (*data) += 8;
+
+ if (P[4] <= P[5]) {
+
+ /* block is divided into left and right halves */
+ for (y = 0; y < 8; ++y) {
+
+ flags = (B[y + 8] << 8) | B[y];
+ split = 0;
+
+ for (x = 0, shifter = 0; x < 8; ++x, shifter += 2) {
+ if (x == 4)
+ split = 4;
+ *frame++ = P[split + ((flags >> shifter) & 0x03)];
+ }
+
+ frame += s->width - 8;
+ }
+
+ } else {
+
+ /* block is divided into top and bottom halves */
+ split = 0;
+ for (y = 0; y < 8; ++y) {
+
+ flags = (B[y * 2 + 1] << 8) | B[y * 2];
+ if (y == 4)
+ split = 4;
+
+ for (x = 0, shifter = 0; x < 8; ++x, shifter += 2)
+ *frame++ = P[split + ((flags >> shifter) & 0x03)];
+
+ frame += s->width - 8;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xb (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int y;
+
+ /* 64-color encoding (each pixel in block is a different color) */
+ CHECK_STREAM (len, 64);
+
+ for (y = 0; y < 8; ++y) {
+ memcpy (frame, *data, 8);
+ frame += s->width;
+ (*data) += 8;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xc (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned char pix;
+
+ /* 16-color block encoding: each 2x2 block is a different color */
+ CHECK_STREAM (len, 16);
+
+ for (y = 0; y < 8; y += 2) {
+ for (x = 0; x < 8; x += 2) {
+ pix = *(*data)++;
+ *(frame + x) = pix;
+ *(frame + x + 1) = pix;
+ *(frame + s->width + x) = pix;
+ *(frame + s->width + x + 1) = pix;
+ }
+ frame += s->width * 2;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xd (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned char P[4];
+ unsigned char index = 0;
+
+ /* 4-color block encoding: each 4x4 block is a different color */
+ CHECK_STREAM (len, 4);
+
+ P[0] = (*data)[0];
+ P[1] = (*data)[1];
+ P[2] = (*data)[2];
+ P[3] = (*data)[3];
+ (*data) += 4;
+
+ for (y = 0; y < 8; ++y) {
+ if (y < 4)
+ index = 0;
+ else
+ index = 2;
+
+ for (x = 0; x < 8; ++x) {
+ if (x == 4)
+ ++index;
+ *frame++ = P[index];
+ }
+ frame += s->width - 8;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xe (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int y;
+ unsigned char pix;
+
+ /* 1-color encoding: the whole block is 1 solid color */
+ CHECK_STREAM (len, 1);
+ pix = *(*data)++;
+
+ for (y = 0; y < 8; ++y) {
+ memset (frame, pix, 8);
+ frame += s->width;
+ }
+
+ return 0;
+}
+
+static int
+ipvideo_decode_0xf (const GstMveDemuxStream * s, unsigned char *frame,
+ const unsigned char **data, unsigned short *len)
+{
+ int x, y;
+ unsigned char P[2];
+
+ /* dithered encoding */
+ CHECK_STREAM (len, 2);
+
+ P[0] = *(*data)++;
+ P[1] = *(*data)++;
+
+ for (y = 0; y < 8; ++y) {
+ for (x = 0; x < 4; ++x) {
+ *frame++ = P[y & 1];
+ *frame++ = P[(y & 1) ^ 1];
+ }
+ frame += s->width - 8;
+ }
+
+ return 0;
+}
+
+int
+ipvideo_decode_frame8 (const GstMveDemuxStream * s, const unsigned char *data,
+ unsigned short len)
+{
+ int rc = 0;
+ int x, y, xx, yy;
+ int index = 0;
+ unsigned char opcode;
+ unsigned char *frame;
+
+ frame = (guint8 *) s->back_buf1;
+
+ /* decoding is done in 8x8 blocks */
+ xx = s->width >> 3;
+ yy = s->height >> 3;
+
+ for (y = 0; y < yy; ++y) {
+ for (x = 0; x < xx; ++x) {
+ /* decoding map contains 4 bits of information per 8x8 block */
+ /* bottom nibble first, then top nibble */
+ if (index & 1)
+ opcode = s->code_map[index >> 1] >> 4;
+ else
+ opcode = s->code_map[index >> 1] & 0x0F;
+ ++index;
+
+ switch (opcode) {
+ case 0x00:
+ /* copy a block from the previous frame */
+ rc = ipvideo_copy_block (s, frame,
+ frame + ((guint8 *) s->back_buf2 - (guint8 *) s->back_buf1), 0);
+ break;
+ case 0x01:
+ /* copy block from 2 frames ago; since we switched the back
+ * buffers we don't actually have to do anything here */
+ break;
+ case 0x02:
+ rc = ipvideo_decode_0x2 (s, frame, &data, &len);
+ break;
+ case 0x03:
+ rc = ipvideo_decode_0x3 (s, frame, &data, &len);
+ break;
+ case 0x04:
+ rc = ipvideo_decode_0x4 (s, frame, &data, &len);
+ break;
+ case 0x05:
+ rc = ipvideo_decode_0x5 (s, frame, &data, &len);
+ break;
+ case 0x06:
+ /* mystery opcode? skip multiple blocks? */
+ GST_WARNING ("encountered unsupported opcode 0x6");
+ rc = -1;
+ break;
+ case 0x07:
+ rc = ipvideo_decode_0x7 (s, frame, &data, &len);
+ break;
+ case 0x08:
+ rc = ipvideo_decode_0x8 (s, frame, &data, &len);
+ break;
+ case 0x09:
+ rc = ipvideo_decode_0x9 (s, frame, &data, &len);
+ break;
+ case 0x0a:
+ rc = ipvideo_decode_0xa (s, frame, &data, &len);
+ break;
+ case 0x0b:
+ rc = ipvideo_decode_0xb (s, frame, &data, &len);
+ break;
+ case 0x0c:
+ rc = ipvideo_decode_0xc (s, frame, &data, &len);
+ break;
+ case 0x0d:
+ rc = ipvideo_decode_0xd (s, frame, &data, &len);
+ break;
+ case 0x0e:
+ rc = ipvideo_decode_0xe (s, frame, &data, &len);
+ break;
+ case 0x0f:
+ rc = ipvideo_decode_0xf (s, frame, &data, &len);
+ break;
+ }
+
+ if (rc != 0)
+ return rc;
+
+ frame += 8;
+ }
+ frame += 7 * s->width;
+ }
+
+ return 0;
+}
diff --git a/gemrb/plugins/Makefile.am b/gemrb/plugins/Makefile.am
new file mode 100644
index 0000000..b5a7361
--- /dev/null
+++ b/gemrb/plugins/Makefile.am
@@ -0,0 +1,44 @@
+SUBDIRS = \
+ 2DAImporter \
+ ACMReader \
+ AREImporter \
+ BAMImporter \
+ BIFImporter \
+ BIKPlayer \
+ BMPImporter \
+ BMPWriter \
+ CHUImporter \
+ CREImporter \
+ DLGImporter \
+ DirectoryImporter \
+ EFFImporter \
+ FXOpcodes \
+ GAMImporter \
+ GUIScript \
+ IDSImporter \
+ INIImporter \
+ ITMImporter \
+ IWDOpcodes \
+ KEYImporter \
+ MOSImporter \
+ MUSImporter \
+ MVEPlayer \
+ NullSound \
+ OGGReader \
+ OpenALAudio \
+ PLTImporter \
+ PNGImporter \
+ PROImporter \
+ PSTOpcodes \
+ SDLVideo \
+ SPLImporter \
+ STOImporter \
+ TISImporter \
+ TLKImporter \
+ WAVReader \
+ WEDImporter \
+ WMPImporter \
+ ZLibManager
+
+DISTCLEANFILES = *.so
+EXTRA_DIST = CMakeLists.txt */CMakeLists.txt
diff --git a/gemrb/plugins/NullSound/CMakeLists.txt b/gemrb/plugins/NullSound/CMakeLists.txt
new file mode 100644
index 0000000..93c49ba
--- /dev/null
+++ b/gemrb/plugins/NullSound/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (NullSound NullSound.cpp )
diff --git a/gemrb/plugins/NullSound/Makefile.am b/gemrb/plugins/NullSound/Makefile.am
new file mode 100644
index 0000000..524b6e4
--- /dev/null
+++ b/gemrb/plugins/NullSound/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = NullSound.la
+NullSound_la_LDFLAGS = -module -avoid-version -shared
+NullSound_la_SOURCES = NullSound.cpp NullSound.h
diff --git a/gemrb/plugins/NullSound/NullSound.cpp b/gemrb/plugins/NullSound/NullSound.cpp
new file mode 100644
index 0000000..ee700e5
--- /dev/null
+++ b/gemrb/plugins/NullSound/NullSound.cpp
@@ -0,0 +1,133 @@
+/* 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 "NullSound.h"
+
+#include "win32def.h"
+
+#include "AmbientMgr.h"
+#include "SoundMgr.h"
+
+NullSound::NullSound(void)
+{
+ XPos = 0;
+ YPos = 0;
+ ambim = new AmbientMgr();
+}
+
+NullSound::~NullSound(void)
+{
+ delete ambim;
+}
+
+bool NullSound::Init(void)
+{
+ return true;
+}
+
+Holder<SoundHandle> NullSound::Play(const char*, int, int, unsigned int, unsigned int *len)
+{
+ if (len) *len = 1000; //Returning 1 Second Length
+ return Holder<SoundHandle>();
+}
+
+int NullSound::CreateStream(Holder<SoundMgr>)
+{
+ return 0;
+}
+
+bool NullSound::Stop()
+{
+ return true;
+}
+
+bool NullSound::Play()
+{
+ return true;
+}
+
+bool NullSound::Pause()
+{
+ return true;
+}
+
+bool NullSound::Resume()
+{
+ return true;
+}
+
+void NullSound::ResetMusics()
+{
+}
+
+bool NullSound::CanPlay()
+{
+ return false;
+}
+
+bool NullSound::IsSpeaking()
+{
+ return false;
+}
+
+void NullSound::UpdateListenerPos(int x, int y)
+{
+ XPos = x;
+ YPos = y;
+}
+
+void NullSound::GetListenerPos(int& x, int& y)
+{
+ x = XPos;
+ y = YPos;
+}
+
+int NullSound::SetupNewStream(ieWord, ieWord, ieWord, ieWord, bool, bool)
+{
+ return -1;
+}
+
+int NullSound::QueueAmbient(int, const char*)
+{
+ return -1;
+}
+
+bool NullSound::ReleaseStream(int, bool)
+{
+ return true;
+}
+
+void NullSound::SetAmbientStreamVolume(int, int)
+{
+
+}
+
+void NullSound::QueueBuffer(int, unsigned short, int, short*, int, int)
+{
+
+}
+
+
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x96E414D, "Null Sound Driver")
+PLUGIN_DRIVER(NullSound, "none")
+END_PLUGIN()
diff --git a/gemrb/plugins/NullSound/NullSound.h b/gemrb/plugins/NullSound/NullSound.h
new file mode 100644
index 0000000..2261c56
--- /dev/null
+++ b/gemrb/plugins/NullSound/NullSound.h
@@ -0,0 +1,55 @@
+/* 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 NULLSOUND_H
+#define NULLSOUND_H
+
+#include "Audio.h"
+
+class NullSound : public Audio {
+public:
+ NullSound(void);
+ ~NullSound(void);
+ bool Init(void);
+ Holder<SoundHandle> Play(const char* ResRef, int XPos, int YPos, unsigned int flags = 0, unsigned int *length = 0);
+ int CreateStream(Holder<SoundMgr>);
+ bool Play();
+ bool Stop();
+ bool Pause();
+ bool Resume();
+ bool CanPlay();
+ bool IsSpeaking();
+ void ResetMusics();
+ void UpdateListenerPos(int XPos, int YPos);
+ void GetListenerPos(int& XPos, int& YPos);
+ void UpdateVolume(unsigned int) {}
+
+ int SetupNewStream(ieWord x, ieWord y, ieWord z, ieWord gain, bool point, bool Ambient);
+ int QueueAmbient(int stream, const char* sound);
+ bool ReleaseStream(int stream, bool hardstop);
+ void SetAmbientStreamVolume(int stream, int gain);
+ void QueueBuffer(int stream, unsigned short bits, int channels,
+ short* memory, int size, int samplerate);
+
+private:
+ int XPos, YPos;
+};
+
+#endif
diff --git a/gemrb/plugins/OGGReader/CMakeLists.txt b/gemrb/plugins/OGGReader/CMakeLists.txt
new file mode 100644
index 0000000..244603a
--- /dev/null
+++ b/gemrb/plugins/OGGReader/CMakeLists.txt
@@ -0,0 +1,11 @@
+IF (VORBIS_LIBRARY)
+ FILE( GLOB OGGReader_files *.cpp )
+
+ # include the second parent of vorbisfile.h
+ get_filename_component(OGG_INCLUDE ${VORBIS_FILE} PATH)
+ include_directories(${OGG_INCLUDE})
+
+ ADD_GEMRB_PLUGIN (OGGReader ${OGGReader_files})
+
+ TARGET_LINK_LIBRARIES(OGGReader ${VORBIS_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
+ENDIF (VORBIS_LIBRARY)
diff --git a/gemrb/plugins/OGGReader/Makefile.am b/gemrb/plugins/OGGReader/Makefile.am
new file mode 100644
index 0000000..7a1e7eb
--- /dev/null
+++ b/gemrb/plugins/OGGReader/Makefile.am
@@ -0,0 +1,6 @@
+if VORBIS
+plugin_LTLIBRARIES = OGGReader.la
+OGGReader_la_LDFLAGS = -module -avoid-version -shared
+OGGReader_la_LIBADD = -lvorbisfile -lvorbis
+OGGReader_la_SOURCES = OGGReader.cpp OGGReader.h
+endif
diff --git a/gemrb/plugins/OGGReader/OGGReader.cpp b/gemrb/plugins/OGGReader/OGGReader.cpp
new file mode 100644
index 0000000..762ccfe
--- /dev/null
+++ b/gemrb/plugins/OGGReader/OGGReader.cpp
@@ -0,0 +1,128 @@
+/* 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 "OGGReader.h"
+
+static size_t ovfd_read(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+ DataStream *vb = (DataStream *) datasource;
+ int bytesToRead = size * nmemb;
+
+ int remains = vb->Remains();
+ if(remains<=0) {
+ /* no more reading, we're at the end */
+ return 0;
+ }
+ if(bytesToRead > remains ) {
+ bytesToRead = remains;
+ }
+ vb->Read(ptr, bytesToRead);
+ return bytesToRead;
+}
+
+static int ovfd_seek(void *datasource, ogg_int64_t offset, int whence) {
+ DataStream *vb = (DataStream *) datasource;
+ switch(whence) {
+ case SEEK_SET:
+ if(vb->Seek(offset, GEM_STREAM_START)<0)
+ return -1;
+ break;
+ case SEEK_CUR:
+ if(vb->Seek(offset, GEM_CURRENT_POS)<0)
+ return -1;
+ break;
+ case SEEK_END:
+ if(vb->Seek(vb->Size()+offset, GEM_STREAM_START)<0)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ return vb->GetPos();
+}
+
+static int ovfd_close(void * /*datasource*/) {
+ return 0;
+}
+
+static long ovfd_tell(void *datasource) {
+ DataStream *vb = (DataStream *) datasource;
+ return (long) vb->GetPos();
+}
+
+bool OGGReader::Open(DataStream* stream)
+{
+ str = stream;
+ Close();
+
+ char Signature[4];
+ stream->Read( Signature, 4 );
+ stream->Seek( 0, GEM_STREAM_START );
+ if(strnicmp(Signature, "oggs", 4) != 0)
+ return false;
+
+ vorbis_info *info;
+ int res;
+ ov_callbacks cbstruct = {
+ ovfd_read, ovfd_seek, ovfd_close, ovfd_tell
+ };
+
+ res=ov_open_callbacks(str, &OggStream, NULL, 0, cbstruct);
+ if(res<0) {
+ printMessage("Sound","Couldn't initialize vorbis!\n", LIGHT_RED);
+ return false;
+ }
+ info = ov_info(&OggStream, -1);
+ channels = info->channels;
+ samplerate = info->rate;
+ samples_left = ( samples = ov_pcm_total(&OggStream, -1) );
+ return true;
+}
+
+int OGGReader::read_samples(short* buffer, int count)
+{
+ int whatisthis;
+
+ if(samples_left<count) {
+ count=samples_left;
+ }
+ int samples_got=0;
+ int samples_need=count;
+ while(samples_need) {
+ int rd=ov_read(&OggStream, (char *)buffer, samples_need<<1, str->IsEndianSwitch(), 2, 1, &whatisthis);
+ if(rd==OV_HOLE) {
+ continue;
+ }
+ if(rd<=0) {
+ break;
+ }
+ rd>>=1;
+ buffer+=rd;
+ samples_got+=rd;
+ samples_need-=rd;
+ }
+ samples_left-=samples_got;
+
+ return samples_got;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x18C310C3, "OGG File Importer")
+PLUGIN_RESOURCE(OGGReader, ".ogg")
+END_PLUGIN()
diff --git a/gemrb/plugins/OGGReader/OGGReader.h b/gemrb/plugins/OGGReader/OGGReader.h
new file mode 100644
index 0000000..159f8d7
--- /dev/null
+++ b/gemrb/plugins/OGGReader/OGGReader.h
@@ -0,0 +1,55 @@
+/* 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 ACMREADER_H
+#define ACMREADER_H
+
+#include "SoundMgr.h"
+#include "System/DataStream.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#ifdef __APPLE_CC__
+#define OV_EXCLUDE_STATIC_CALLBACKS
+#endif
+#include <vorbis/vorbisfile.h>
+
+class OGGReader : public SoundMgr {
+private:
+ OggVorbis_File OggStream;
+ int samples_left; // count of unread samples
+public:
+ OGGReader()
+ : samples_left( 0 )
+ {
+ memset(&OggStream, 0, sizeof(OggStream) );
+ }
+ virtual ~OGGReader()
+ {
+ Close();
+ }
+ void Close()
+ {
+ ov_clear(&OggStream);
+ }
+ bool Open(DataStream* stream);
+ int read_samples(short* buffer, int count);
+};
+
+#endif
diff --git a/gemrb/plugins/OpenALAudio/AmbientMgrAL.cpp b/gemrb/plugins/OpenALAudio/AmbientMgrAL.cpp
new file mode 100644
index 0000000..c5c6c53
--- /dev/null
+++ b/gemrb/plugins/OpenALAudio/AmbientMgrAL.cpp
@@ -0,0 +1,312 @@
+/* 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 "AmbientMgrAL.h"
+
+#include "OpenALAudio.h"
+
+#include "Ambient.h"
+#include "Game.h"
+#include "Interface.h"
+
+#include <cassert>
+#include <climits>
+#include <cmath>
+#include <cstdio>
+
+#include <SDL.h>
+
+// TODO: remove last dependencies on OpenAL, and then rename and move it?
+
+// legal nop if already reset
+void AmbientMgrAL::reset()
+{
+ if (NULL != player){
+ SDL_mutexP(mutex);
+ }
+ for (std::vector<AmbientSource *>::iterator it = ambientSources.begin(); it != ambientSources.end(); ++it) {
+ delete (*it);
+ }
+ ambientSources.clear();
+ AmbientMgr::reset();
+ if (NULL != player) {
+ SDL_CondSignal(cond);
+ SDL_mutexV(mutex);
+ SDL_WaitThread(player, NULL);
+ player = NULL;
+ }
+}
+
+void AmbientMgrAL::setAmbients(const std::vector<Ambient *> &a)
+{
+ AmbientMgr::setAmbients(a);
+ assert(NULL == player);
+
+ ambientSources.reserve(a.size());
+ for (std::vector<Ambient *>::const_iterator it = a.begin(); it != a.end(); ++it) {
+ ambientSources.push_back(new AmbientSource(*it));
+ }
+ core->GetAudioDrv()->UpdateVolume( GEM_SND_VOL_AMBIENTS );
+
+ player = SDL_CreateThread(&play, (void *) this);
+}
+
+void AmbientMgrAL::activate(const std::string &name)
+{
+ if (NULL != player)
+ SDL_mutexP(mutex);
+ AmbientMgr::activate(name);
+ if (NULL != player) {
+ SDL_CondSignal(cond);
+ SDL_mutexV(mutex);
+ }
+}
+
+void AmbientMgrAL::activate()
+{
+ if (NULL != player)
+ SDL_mutexP(mutex);
+ AmbientMgr::activate();
+ if (NULL != player) {
+ SDL_CondSignal(cond);
+ SDL_mutexV(mutex);
+ }
+}
+
+void AmbientMgrAL::deactivate(const std::string &name)
+{
+ if (NULL != player)
+ SDL_mutexP(mutex);
+ AmbientMgr::deactivate(name);
+ if (NULL != player) {
+ SDL_CondSignal(cond);
+ SDL_mutexV(mutex);
+ }
+}
+
+void AmbientMgrAL::deactivate()
+{
+ if (NULL != player)
+ SDL_mutexP(mutex);
+ AmbientMgr::deactivate();
+ hardStop();
+ if (NULL != player)
+ SDL_mutexV(mutex);
+}
+
+void AmbientMgrAL::hardStop()
+{
+ for (std::vector<AmbientSource *>::iterator it = ambientSources.begin(); it != ambientSources.end(); ++it) {
+ (*it)->hardStop();
+ }
+}
+
+int AmbientMgrAL::play(void *am)
+{
+ AmbientMgrAL * ambim = (AmbientMgrAL *) am;
+ SDL_mutexP(ambim->mutex);
+ while (0 != ambim->ambientSources.size()) {
+ if (NULL == core->GetGame()) { // we don't have any game, and we need one
+ break;
+ }
+ unsigned int delay = ambim->tick(SDL_GetTicks());
+ assert(delay > 0);
+ SDL_CondWaitTimeout(ambim->cond, ambim->mutex, delay);
+ }
+ SDL_mutexV(ambim->mutex);
+ return 0;
+}
+
+unsigned int AmbientMgrAL::tick(unsigned int ticks)
+{
+ unsigned int delay = 60000; // wait one minute if all sources are off
+
+ if (!active)
+ return delay;
+
+ int xpos, ypos;
+ core->GetAudioDrv()->GetListenerPos(xpos, ypos);
+ Point listener;
+ listener.x = (short) xpos;
+ listener.y = (short) ypos;
+
+ ieDword timeslice = 1<<(((core->GetGame()->GameTime / 60 + 30) / 60 - 1) % 24);
+
+ for (std::vector<AmbientSource *>::iterator it = ambientSources.begin(); it != ambientSources.end(); ++it) {
+ unsigned int newdelay = (*it)->tick(ticks, listener, timeslice);
+ if (newdelay < delay)
+ delay = newdelay;
+ }
+ return delay;
+}
+
+AmbientMgrAL::AmbientSource::AmbientSource(const Ambient *a)
+: stream(-1), ambient(a), lastticks(0), enqueued(0), loaded(false)
+{
+ // TODO: wait random amount of time before beginning?
+}
+
+void AmbientMgrAL::AmbientSource::ensureLoaded()
+{
+ // TODO: implement this after caching in ACMImp is done
+ if (loaded) return;
+
+ unsigned int i=ambient->sounds.size();
+ soundrefs.reserve(i);
+ while(i--) {
+ // TODO: cache this sound
+ // (and skip it if it turns out to be invalid)
+ soundrefs.push_back(ambient->sounds[i]);
+ }
+
+ loaded = true;
+}
+
+AmbientMgrAL::AmbientSource::~AmbientSource()
+{
+ if (stream >= 0) {
+ core->GetAudioDrv()->ReleaseStream(stream, true);
+ stream = -1;
+ }
+}
+
+unsigned int AmbientMgrAL::AmbientSource::tick(unsigned int ticks, Point listener, ieDword timeslice)
+{
+ /* if we are out of sounds do nothing */
+ if(!ambient->sounds.size()) {
+ return UINT_MAX;
+ }
+ if (loaded && soundrefs.empty()) return UINT_MAX;
+
+ if ((! (ambient->getFlags() & IE_AMBI_ENABLED)) || (! ambient->getAppearance()×lice)) {
+ // disabled
+
+ if (stream >= 0) {
+ // release the stream without immediately stopping it
+ core->GetAudioDrv()->ReleaseStream(stream, false);
+ }
+ return UINT_MAX;
+ }
+
+ int delay = ambient->getInterval() * 1000;
+ int left = lastticks - ticks + delay;
+ if (0 < left) // we are still waiting
+ return left;
+ if (enqueued > 0) // we have already played that much
+ enqueued += left;
+ if (enqueued < 0)
+ enqueued = 0;
+
+ lastticks = ticks;
+ if (0 == delay) // it's a non-stop ambient, so in any case wait only a sec
+ delay = 1000;
+
+ if (! (ambient->getFlags() & IE_AMBI_MAIN) && !isHeard( listener )) { // we are out of range
+ if (delay > 500) {
+ // release stream if we're inactive for a while
+ core->GetAudioDrv()->ReleaseStream(stream);
+ }
+ return delay;
+ }
+
+ ensureLoaded();
+ if (soundrefs.empty()) return UINT_MAX;
+
+ if (stream < 0) {
+ // we need to allocate a stream
+ stream = core->GetAudioDrv()->SetupNewStream(ambient->getOrigin().x, ambient->getOrigin().y, ambient->getHeight(), ambient->getGain(), (ambient->getFlags() & IE_AMBI_POINT), true);
+
+ if (stream == -1) {
+ // no streams available...
+ // Try again later
+ return delay;
+ }
+ }
+
+
+ /* it seems that the following (commented out) is not the purpose of the perset field, as
+ it leads to ambients playing non-stop and queues overfilled */
+/* int leftNum = ambient -> getPerset(); */
+ int leftNum = 1;
+ int leftMS = 0;
+ if (0 == ambient->getInterval()) {
+ leftNum = 0;
+ leftMS = 1000 - enqueued; // let's have at least 1 second worth queue
+ }
+
+
+ while (0 < leftNum || 0 < leftMS) {
+ int len = enqueue();
+ if (len < 0) break;
+ --leftNum;
+ leftMS -= len;
+ enqueued += len;
+ }
+
+ return delay;
+}
+
+/* enqueues a random sound and returns its length */
+int AmbientMgrAL::AmbientSource::enqueue()
+{
+ if (soundrefs.empty()) return -1;
+ if (stream < 0) return -1;
+ int index = rand() % soundrefs.size();
+ //printf("Playing ambient %p, %s, %d/%ld on stream %d\n", (void*)this, soundrefs[index], index, soundrefs.size(), stream);
+ return core->GetAudioDrv()->QueueAmbient(stream, soundrefs[index]);
+}
+
+bool AmbientMgrAL::AmbientSource::isHeard(const Point &listener) const
+{
+ int xdist =listener.x - ambient->getOrigin().x;
+ int ydist =listener.y - ambient->getOrigin().y;
+ int dist = (int) sqrt( (double) (xdist * xdist + ydist * ydist) );
+ return dist < ambient->getRadius();
+}
+
+void AmbientMgrAL::AmbientSource::hardStop()
+{
+ if (stream >= 0) {
+ core->GetAudioDrv()->ReleaseStream(stream, true);
+ stream = -1;
+ }
+}
+
+void AmbientMgrAL::UpdateVolume(unsigned short volume)
+{
+ SDL_mutexP( mutex );
+ for (std::vector<AmbientSource *>::iterator it = ambientSources.begin(); it != ambientSources.end(); ++it) {
+ (*it) -> SetVolume( volume );
+ }
+ SDL_mutexV( mutex );
+}
+
+/* sets the overall volume (in percent)
+ * the final volume is affected by the specific ambient gain
+ */
+void AmbientMgrAL::AmbientSource::SetVolume(unsigned short volume)
+{
+ if (stream >= 0) {
+ int v = volume;
+ v *= ambient->getGain();
+ v /= 100;
+ core->GetAudioDrv()->SetAmbientStreamVolume(stream, v);
+ }
+}
diff --git a/gemrb/plugins/OpenALAudio/AmbientMgrAL.h b/gemrb/plugins/OpenALAudio/AmbientMgrAL.h
new file mode 100644
index 0000000..e276e67
--- /dev/null
+++ b/gemrb/plugins/OpenALAudio/AmbientMgrAL.h
@@ -0,0 +1,89 @@
+/* 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 AMBIENTMGRAL_H
+#define AMBIENTMGRAL_H
+
+#include "AmbientMgr.h"
+
+#include <vector>
+#include <string>
+
+#include <SDL_thread.h>
+
+#ifndef WIN32
+#ifdef __APPLE_CC__
+#include <OpenAL/al.h>
+#include <OpenAL/alc.h>
+#else
+#include <AL/al.h>
+#include <AL/alc.h>
+#endif
+#else
+#include <al.h>
+#include <alc.h>
+#endif
+
+class Ambient;
+
+class AmbientMgrAL : public AmbientMgr {
+public:
+ AmbientMgrAL() : AmbientMgr(), mutex(SDL_CreateMutex()),
+ player(NULL), cond(SDL_CreateCond()) { }
+ ~AmbientMgrAL() { reset(); SDL_DestroyMutex(mutex); SDL_DestroyCond(cond); }
+ void reset();
+ void setAmbients(const std::vector<Ambient *> &a);
+ void activate(const std::string &name);
+ void activate();
+ void deactivate(const std::string &name);
+ void deactivate();
+ void UpdateVolume(unsigned short value);
+private:
+ class AmbientSource {
+ public:
+ AmbientSource(const Ambient *a);
+ ~AmbientSource();
+ unsigned int tick(unsigned int ticks, Point listener, ieDword timeslice);
+ void hardStop();
+ void SetVolume(unsigned short volume);
+ private:
+ int stream;
+ std::vector<const char*> soundrefs;
+ const Ambient* ambient;
+ unsigned int lastticks;
+ int enqueued;
+ bool loaded;
+
+ void ensureLoaded();
+ bool isHeard(const Point &listener) const;
+ int enqueue();
+ };
+ std::vector<AmbientSource *> ambientSources;
+
+ static int play(void *am);
+ unsigned int tick(unsigned int ticks);
+ void hardStop();
+
+ SDL_mutex *mutex;
+ SDL_Thread *player;
+ SDL_cond *cond;
+};
+
+#endif /* AMBIENTMGRAL_H */
diff --git a/gemrb/plugins/OpenALAudio/CMakeLists.txt b/gemrb/plugins/OpenALAudio/CMakeLists.txt
new file mode 100644
index 0000000..dec2426
--- /dev/null
+++ b/gemrb/plugins/OpenALAudio/CMakeLists.txt
@@ -0,0 +1,7 @@
+#Don't build if openal is not there
+IF(OPENAL_FOUND)
+INCLUDE_DIRECTORIES(${OPENAL_INCLUDE_DIR} ${SDL_INCLUDE_DIR} )
+FILE( GLOB OpenALAudio_files *.cpp )
+ADD_GEMRB_PLUGIN (OpenALAudio ${OpenALAudio_files} )
+TARGET_LINK_LIBRARIES(OpenALAudio ${OPENAL_LIBRARY} ${SDL_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
+ENDIF(OPENAL_FOUND)
diff --git a/gemrb/plugins/OpenALAudio/Makefile.am b/gemrb/plugins/OpenALAudio/Makefile.am
new file mode 100644
index 0000000..a0e0f64
--- /dev/null
+++ b/gemrb/plugins/OpenALAudio/Makefile.am
@@ -0,0 +1,4 @@
+plugin_LTLIBRARIES = OpenALAudio.la
+OpenALAudio_la_LDFLAGS = -module -avoid-version -shared
+OpenALAudio_la_LIBADD = @SDL_LIBS@ @OPENAL_LIBS@
+OpenALAudio_la_SOURCES = OpenALAudio.cpp OpenALAudio.h AmbientMgrAL.cpp AmbientMgrAL.h StackLock.cpp StackLock.h
diff --git a/gemrb/plugins/OpenALAudio/OpenALAudio.cpp b/gemrb/plugins/OpenALAudio/OpenALAudio.cpp
new file mode 100644
index 0000000..cfd28ea
--- /dev/null
+++ b/gemrb/plugins/OpenALAudio/OpenALAudio.cpp
@@ -0,0 +1,924 @@
+/* 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 "OpenALAudio.h"
+
+#include "GameData.h"
+
+#include <cassert>
+#include <cstdio>
+
+bool checkALError(const char* msg, const char* status) {
+ int error = alGetError();
+ if (error != AL_NO_ERROR) {
+ printMessage("OpenAL", msg, WHITE );
+ printf (": 0x%x ", error);
+ printStatus(status, YELLOW);
+ return true;
+ }
+ return false;
+}
+
+void showALCError(const char* msg, const char* status, ALCdevice *device) {
+ int error = alcGetError(device);
+ printMessage("OpenAL", msg, WHITE );
+ if (error != AL_NO_ERROR) {
+ printf (": 0x%x ", error);
+ }
+ printStatus(status, YELLOW);
+}
+
+void OpenALSoundHandle::SetPos(int XPos, int YPos) {
+ if (!parent) return;
+
+ ALfloat SourcePos[] = {
+ (float) XPos, (float) YPos, 0.0f
+ };
+
+ alSourcefv(parent->Source, AL_POSITION, SourcePos);
+}
+
+bool OpenALSoundHandle::Playing() {
+ if (!parent) return false;
+
+ parent->ClearIfStopped();
+ return parent != 0;
+}
+
+void OpenALSoundHandle::Stop() {
+ if (!parent) return;
+
+ parent->ForceClear();
+}
+
+void OpenALSoundHandle::StopLooping() {
+ if (!parent) return;
+
+ alSourcei(parent->Source, AL_LOOPING, 0);
+}
+
+void AudioStream::ClearProcessedBuffers()
+{
+ ALint processed = 0;
+ alGetSourcei( Source, AL_BUFFERS_PROCESSED, &processed );
+
+ checkALError("Failed to get processed buffers", "WARNING");
+
+ if (processed > 0) {
+ ALuint * b = new ALuint[processed];
+ alSourceUnqueueBuffers( Source, processed, b );
+ checkALError("Failed to unqueue buffers", "WARNING");
+
+ if (delete_buffers) {
+ alDeleteBuffers(processed, b);
+ checkALError("Failed to delete buffers", "WARNING");
+ }
+
+ delete[] b;
+ }
+
+}
+
+void AudioStream::ClearIfStopped()
+{
+ if (free || locked) return;
+
+ if (!alIsSource(Source)) return;
+
+ ALint state;
+ alGetSourcei( Source, AL_SOURCE_STATE, &state );
+ if (!checkALError("Failed to check source state", "WARNING") &&
+ state == AL_STOPPED)
+ {
+ ClearProcessedBuffers();
+ alDeleteSources( 1, &Source );
+ checkALError("Failed to delete source", "WARNING");
+ Source = 0;
+ Buffer = 0;
+ free = true;
+ if (handle) { handle->Invalidate(); handle.release(); }
+ ambient = false;
+ locked = false;
+ delete_buffers = false;
+ }
+}
+
+void AudioStream::ForceClear()
+{
+ if (!alIsSource(Source)) return;
+
+ alSourceStop(Source);
+ checkALError("Failed to stop source", "WARNING");
+ ClearProcessedBuffers();
+ ClearIfStopped();
+}
+
+OpenALAudioDriver::OpenALAudioDriver(void)
+{
+ alutContext = NULL;
+ MusicPlaying = false;
+ music_memory = (short*) malloc(ACM_BUFFERSIZE);
+ MusicSource = 0;
+ memset(MusicBuffer, 0, MUSICBUFFERS*sizeof(ALuint));
+ musicMutex = SDL_CreateMutex();
+ ambim = NULL;
+}
+
+bool OpenALAudioDriver::Init(void)
+{
+ ALCdevice *device;
+ ALCcontext *context;
+
+ device = alcOpenDevice (NULL);
+ if (device == NULL) {
+ showALCError("Failed to open device", "ERROR", device);
+ return false;
+ }
+
+ context = alcCreateContext (device, NULL);
+ if (context == NULL) {
+ showALCError("Failed to create context", "ERROR", device);
+ alcCloseDevice (device);
+ return false;
+ }
+
+ if (!alcMakeContextCurrent (context)) {
+ showALCError("Failed to select context", "ERROR", device);
+ alcDestroyContext (context);
+ alcCloseDevice (device);
+ return false;
+ }
+ alutContext = context;
+
+ //1 for speech
+ int sources = CountAvailableSources(MAX_STREAMS+1);
+ num_streams = sources - 1;
+
+ char buf[255];
+ sprintf(buf, "Allocated %d streams.%s", num_streams,
+ (num_streams < MAX_STREAMS ? " (Fewer than desired.)" : "" ) );
+
+ printMessage( "OpenAL", buf, WHITE );
+
+ stayAlive = true;
+ musicThread = SDL_CreateThread( MusicManager, this );
+
+ ambim = new AmbientMgrAL;
+ speech.free = true;
+ speech.ambient = false;
+ return true;
+}
+
+int OpenALAudioDriver::CountAvailableSources(int limit)
+{
+ ALuint* src = new ALuint[limit+2];
+ int i;
+ for (i = 0; i < limit+2; ++i) {
+ alGenSources(1, &src[i]);
+ if (alGetError() != AL_NO_ERROR)
+ break;
+ }
+ if (i > 0)
+ alDeleteSources(i, src);
+ delete[] src;
+
+ // Leave two sources free for internal OpenAL usage
+ // (Might not be strictly necessary...)
+ i -= 2;
+
+ checkALError("Error while auto-detecting number of sources", "WARNING");
+
+ // Return number of succesfully allocated sources
+ return i;
+}
+
+OpenALAudioDriver::~OpenALAudioDriver(void)
+{
+ if (!ambim) {
+ // initialisation must have failed
+ return;
+ }
+
+ for(int i =0; i<num_streams; i++) {
+ streams[i].ForceClear();
+ }
+ speech.ForceClear();
+ ResetMusics();
+ clearBufferCache(true);
+
+ ALCdevice *device;
+
+ alcMakeContextCurrent (NULL);
+
+ device = alcGetContextsDevice (alutContext);
+ alcDestroyContext (alutContext);
+ if (alcGetError (device) == ALC_NO_ERROR) {
+ alcCloseDevice (device);
+ }
+ alutContext = NULL;
+ SDL_mutexP(musicMutex);
+ SDL_KillThread(musicThread);
+ SDL_mutexV(musicMutex);
+
+ SDL_DestroyMutex(musicMutex);
+ musicMutex = NULL;
+
+ free(music_memory);
+
+ delete ambim;
+}
+
+ALuint OpenALAudioDriver::loadSound(const char *ResRef, unsigned int &time_length)
+{
+ ALuint Buffer = 0;
+
+ CacheEntry *e;
+ void* p;
+
+ if (!ResRef[0]) {
+ return 0;
+ }
+ if(buffercache.Lookup(ResRef, p))
+ {
+ e = (CacheEntry*) p;
+ time_length = e->Length;
+ return e->Buffer;
+ }
+
+ //no cache entry...
+ alGenBuffers(1, &Buffer);
+ if (checkALError("Unable to create sound buffer", "ERROR")) {
+ return 0;
+ }
+
+ ResourceHolder<SoundMgr> acm(ResRef);
+ if (!acm) {
+ alDeleteBuffers( 1, &Buffer );
+ return 0;
+ }
+ int cnt = acm->get_length();
+ int riff_chans = acm->get_channels();
+ int samplerate = acm->get_samplerate();
+ //multiply always by 2 because it is in 16 bits
+ int rawsize = cnt * 2;
+ short* memory = (short*) malloc(rawsize);
+ //multiply always with 2 because it is in 16 bits
+ int cnt1 = acm->read_samples( memory, cnt ) * 2;
+ //Sound Length in milliseconds
+ time_length = ((cnt / riff_chans) * 1000) / samplerate;
+ //it is always reading the stuff into 16 bits
+ alBufferData( Buffer, GetFormatEnum( riff_chans, 16 ), memory, cnt1, samplerate );
+ free(memory);
+
+ if (checkALError("Unable to fill buffer", "ERROR")) {
+ alDeleteBuffers( 1, &Buffer );
+ checkALError("Error deleting buffer", "WARNING");
+ return 0;
+ }
+
+ e = new CacheEntry;
+ e->Buffer = Buffer;
+ e->Length = ((cnt / riff_chans) * 1000) / samplerate;
+
+ buffercache.SetAt(ResRef, (void*)e);
+ //printf("LoadSound: added %s to cache: %d. Cache size now %d\n", ResRef, e->Buffer, buffercache.GetCount());
+
+ if (buffercache.GetCount() > BUFFER_CACHE_SIZE) {
+ evictBuffer();
+ }
+ return Buffer;
+}
+
+Holder<SoundHandle> OpenALAudioDriver::Play(const char* ResRef, int XPos, int YPos, unsigned int flags, unsigned int *length)
+{
+ ALuint Buffer;
+ unsigned int time_length;
+
+ if(ResRef == NULL) {
+ if((flags & GEM_SND_SPEECH) && alIsSource(speech.Source)) {
+ //So we want him to be quiet...
+ alSourceStop( speech.Source );
+ checkALError("Unable to stop speech", "WARNING");
+ speech.ClearProcessedBuffers();
+ }
+ return Holder<SoundHandle>();
+ }
+
+ Buffer = loadSound( ResRef, time_length );
+ if (Buffer == 0) {
+ return Holder<SoundHandle>();
+ }
+
+ if (length) {
+ *length = time_length;
+ }
+
+ ALuint Source;
+ ALfloat SourcePos[] = {
+ (float) XPos, (float) YPos, 0.0f
+ };
+ ALfloat SourceVel[] = {
+ 0.0f, 0.0f, 0.0f
+ };
+
+ ieDword volume = 100;
+
+ if (flags & GEM_SND_SPEECH) {
+ //speech has a single channel, if a new speech started
+ //we stop the previous one
+ if(!speech.free && alIsSource(speech.Source)) {
+ alSourceStop( speech.Source );
+ checkALError("Unable to stop speech", "WARNING");
+ speech.ClearProcessedBuffers();
+ }
+ if(!alIsSource(speech.Source)) {
+ alGenSources( 1, &speech.Source );
+ if (checkALError("Error creating source for speech", "ERROR")) {
+ return Holder<SoundHandle>();
+ }
+ }
+
+ alSourcef( speech.Source, AL_PITCH, 1.0f );
+ alSourcefv( speech.Source, AL_VELOCITY, SourceVel );
+ alSourcei( speech.Source, AL_LOOPING, 0 );
+ alSourcef( speech.Source, AL_REFERENCE_DISTANCE, REFERENCE_DISTANCE );
+ checkALError("Unable to set speech parameters", "WARNING");
+ speech.free = false;
+ printf("speech.free: %d source:%d\n", speech.free,speech.Source);
+
+ core->GetDictionary()->Lookup( "Volume Voices", volume );
+ alSourcef( speech.Source, AL_GAIN, 0.01f * volume );
+ alSourcei( speech.Source, AL_SOURCE_RELATIVE, flags & GEM_SND_RELATIVE );
+ alSourcefv( speech.Source, AL_POSITION, SourcePos );
+ assert(!speech.delete_buffers);
+ alSourcei( speech.Source, AL_BUFFER, Buffer );
+ checkALError("Unable to set speech parameters", "WARNING");
+ speech.Buffer = Buffer;
+ alSourcePlay( speech.Source );
+ if (checkALError("Unable to play speech", "ERROR")) {
+ return Holder<SoundHandle>();
+ }
+ speech.handle = new OpenALSoundHandle(&speech);
+ return speech.handle.get();
+ }
+
+ int stream = -1;
+ for (int i = 0; i < num_streams; i++) {
+ streams[i].ClearIfStopped();
+ if (streams[i].free) {
+ stream = i;
+ break;
+ }
+ }
+
+ if (stream == -1) {
+ // Failed to assign new sound.
+ // The buffercache will handle deleting Buffer.
+ return Holder<SoundHandle>();
+ }
+
+ // not speech
+ alGenSources( 1, &Source );
+ if (checkALError("Unable to create source", "ERROR")) {
+ return Holder<SoundHandle>();
+ }
+
+ alSourcef( Source, AL_PITCH, 1.0f );
+ alSourcefv( Source, AL_VELOCITY, SourceVel );
+ alSourcei( Source, AL_LOOPING, (flags & GEM_SND_LOOPING ? 1 : 0) );
+ alSourcef( Source, AL_REFERENCE_DISTANCE, REFERENCE_DISTANCE );
+ core->GetDictionary()->Lookup( "Volume SFX", volume );
+ alSourcef( Source, AL_GAIN, 0.01f * volume );
+ alSourcei( Source, AL_SOURCE_RELATIVE, flags & GEM_SND_RELATIVE );
+ alSourcefv( Source, AL_POSITION, SourcePos );
+ assert(!streams[stream].delete_buffers);
+ alSourcei( Source, AL_BUFFER, Buffer );
+
+ if (checkALError("Unable to set sound parameters", "ERROR")) {
+ return Holder<SoundHandle>();
+ }
+
+ streams[stream].Buffer = Buffer;
+ streams[stream].Source = Source;
+ streams[stream].free = false;
+ alSourcePlay( Source );
+
+ if (checkALError("Unable to play sound", "ERROR")) {
+ return Holder<SoundHandle>();
+ }
+
+ streams[stream].handle = new OpenALSoundHandle(&streams[stream]);
+ return streams[stream].handle.get();
+}
+
+bool OpenALAudioDriver::IsSpeaking()
+{
+ speech.ClearIfStopped();
+ return !speech.free;
+}
+
+void OpenALAudioDriver::UpdateVolume(unsigned int flags)
+{
+ ieDword volume;
+
+ if (flags & GEM_SND_VOL_MUSIC) {
+ SDL_mutexP( musicMutex );
+ core->GetDictionary()->Lookup("Volume Music", volume);
+ if (alIsSource(MusicSource))
+ alSourcef(MusicSource, AL_GAIN, volume * 0.01f);
+ SDL_mutexV(musicMutex);
+ }
+
+ if (flags & GEM_SND_VOL_AMBIENTS) {
+ core->GetDictionary()->Lookup("Volume Ambients", volume);
+ ((AmbientMgrAL*) ambim)->UpdateVolume(volume);
+ }
+}
+
+bool OpenALAudioDriver::CanPlay()
+{
+ return true;
+}
+
+void OpenALAudioDriver::ResetMusics()
+{
+ MusicPlaying = false;
+ SDL_mutexP( musicMutex );
+ if (alIsSource(MusicSource)) {
+ alSourceStop(MusicSource);
+ checkALError("Unable to stop music source", "WARNING");
+ alDeleteSources(1, &MusicSource );
+ checkALError("Unable to delete music source", "WARNING");
+ MusicSource = 0;
+ for (int i=0; i<MUSICBUFFERS; i++) {
+ if (alIsBuffer(MusicBuffer[i])) {
+ alDeleteBuffers(1, MusicBuffer+i);
+ checkALError("Unable to delete music buffer", "WARNING");
+ }
+ }
+ }
+ SDL_mutexV( musicMutex );
+}
+
+bool OpenALAudioDriver::Play()
+{
+ if (!MusicReader) return false;
+
+ SDL_mutexP( musicMutex );
+ if (!MusicPlaying)
+ MusicPlaying = true;
+ SDL_mutexV( musicMutex );
+
+ return true;
+}
+
+bool OpenALAudioDriver::Stop()
+{
+ SDL_mutexP( musicMutex );
+ if (!alIsSource( MusicSource )) {
+ SDL_mutexV( musicMutex );
+ return false;
+ }
+ alSourceStop( MusicSource );
+ checkALError("Unable to stop music source", "WARNING");
+ MusicPlaying = false;
+ alDeleteSources( 1, &MusicSource );
+ checkALError("Unable to delete music source", "WARNING");
+ MusicSource = 0;
+ SDL_mutexV( musicMutex );
+ return true;
+}
+
+bool OpenALAudioDriver::Pause()
+{
+ SDL_mutexP( musicMutex );
+ if (!alIsSource( MusicSource )) {
+ SDL_mutexV( musicMutex );
+ return false;
+ }
+ alSourcePause(MusicSource);
+ checkALError("Unable to pause music source", "WARNING");
+ MusicPlaying = false;
+ SDL_mutexV( musicMutex );
+ ((AmbientMgrAL*) ambim)->deactivate();
+#ifdef ANDROID
+ al_android_pause_playback(); //call AudioTrack.pause() from JNI
+#endif
+ return true;
+}
+
+bool OpenALAudioDriver::Resume()
+{
+#ifdef ANDROID
+ al_android_resume_playback(); //call AudioTrack.play() from JNI
+#endif
+ SDL_mutexP( musicMutex );
+ if (!alIsSource( MusicSource )) {
+ SDL_mutexV( musicMutex );
+ return false;
+ }
+ alSourcePlay(MusicSource);
+ checkALError("Unable to resume music source", "WARNING");
+ MusicPlaying = true;
+ SDL_mutexV( musicMutex );
+ ((AmbientMgrAL*) ambim)->activate();
+ return true;
+}
+
+int OpenALAudioDriver::CreateStream(Holder<SoundMgr> newMusic)
+{
+ StackLock l(musicMutex, "musicMutex in CreateStream()");
+
+ // Free old MusicReader
+ MusicReader = newMusic;
+ if (!MusicReader) {
+ MusicPlaying = false;
+ }
+
+ if (MusicBuffer[0] == 0) {
+ alGenBuffers( MUSICBUFFERS, MusicBuffer );
+ if (checkALError("Unable to create music buffers", "ERROR")) {
+ return -1;
+ }
+ }
+
+ if (MusicSource == 0) {
+ alGenSources( 1, &MusicSource );
+ if (checkALError("Unable to create music source", "ERROR")) {
+ return -1;
+ }
+
+ ALfloat SourcePos[] = {
+ 0.0f, 0.0f, 0.0f
+ };
+ ALfloat SourceVel[] = {
+ 0.0f, 0.0f, 0.0f
+ };
+
+ ieDword volume;
+ core->GetDictionary()->Lookup( "Volume Music", volume );
+ alSourcef( MusicSource, AL_PITCH, 1.0f );
+ alSourcef( MusicSource, AL_GAIN, 0.01f * volume );
+ alSourcei( MusicSource, AL_SOURCE_RELATIVE, 1 );
+ alSourcefv( MusicSource, AL_POSITION, SourcePos );
+ alSourcefv( MusicSource, AL_VELOCITY, SourceVel );
+ alSourcei( MusicSource, AL_LOOPING, 0 );
+ checkALError("Unable to set music parameters", "WARNING");
+ }
+
+ return 0;
+}
+
+void OpenALAudioDriver::UpdateListenerPos(int XPos, int YPos )
+{
+ alListener3f( AL_POSITION, (float) XPos, (float) YPos, 0.0f );
+}
+
+void OpenALAudioDriver::GetListenerPos(int &XPos, int &YPos )
+{
+ ALfloat listen[3];
+ alGetListenerfv( AL_POSITION, listen );
+ if (checkALError("Unable to get listener pos", "ERROR")) return;
+ XPos = (int) listen[0];
+ YPos = (int) listen[1];
+}
+
+bool OpenALAudioDriver::ReleaseStream(int stream, bool HardStop)
+{
+ if (streams[stream].free || !streams[stream].locked)
+ return false;
+ streams[stream].locked = false;
+ if (!HardStop) {
+ // it's now unlocked, so it will automatically be reclaimed when needed
+ return true;
+ }
+
+ ALuint Source = streams[stream].Source;
+ alSourceStop(Source);
+ checkALError("Unable to stop source", "WARNING");
+ streams[stream].ClearIfStopped();
+
+ return true;
+}
+
+//This one is used for movies and ambients.
+int OpenALAudioDriver::SetupNewStream( ieWord x, ieWord y, ieWord z,
+ ieWord gain, bool point, bool Ambient )
+{
+ // Find a free (or finished) stream for this sound
+ int stream = -1;
+ for (int i = 0; i < num_streams; i++) {
+ streams[i].ClearIfStopped();
+ if (streams[i].free) {
+ stream = i;
+ break;
+ }
+ }
+ if (stream == -1) return -1;
+
+ ALuint source;
+ alGenSources(1, &source);
+ if (checkALError("Unable to create new source", "ERROR")) {
+ return -1;
+ }
+
+ ALfloat position[] = { (float) x, (float) y, (float) z };
+ alSourcef( source, AL_PITCH, 1.0f );
+ alSourcefv( source, AL_POSITION, position );
+ alSourcef( source, AL_GAIN, 0.01f * gain );
+ alSourcei( source, AL_REFERENCE_DISTANCE, REFERENCE_DISTANCE );
+ alSourcei( source, AL_ROLLOFF_FACTOR, point ? 1 : 0 );
+ alSourcei( source, AL_LOOPING, 0 );
+ checkALError("Unable to set stream parameters", "WARNING");
+
+ streams[stream].Buffer = 0;
+ streams[stream].Source = source;
+ streams[stream].free = false;
+ streams[stream].ambient = Ambient;
+ streams[stream].locked = true;
+
+ return stream;
+}
+
+int OpenALAudioDriver::QueueAmbient(int stream, const char* sound)
+{
+ if (streams[stream].free || !streams[stream].ambient)
+ return -1;
+
+ ALuint source = streams[stream].Source;
+
+ // first dequeue any processed buffers
+ streams[stream].ClearProcessedBuffers();
+
+ if (sound == 0)
+ return 0;
+
+ unsigned int time_length;
+ ALuint Buffer = loadSound(sound, time_length);
+ if (0 == Buffer) {
+ return -1;
+ }
+
+ assert(!streams[stream].delete_buffers);
+
+ alSourceQueueBuffers(source, 1, &Buffer);
+ if (checkALError("Unable to queue ambient buffer","ERROR")) {
+ return -1;
+ }
+
+ // play
+ ALint state;
+ alGetSourcei( source, AL_SOURCE_STATE, &state );
+ if (!checkALError("Unable to query ambient source state", "ERROR") &&
+ state != AL_PLAYING)
+ { // play on playing source would rewind it
+ alSourcePlay( source );
+ if (checkALError("Unable to play ambient source", "ERROR"))
+ return -1;
+ }
+
+ return time_length;
+}
+
+void OpenALAudioDriver::SetAmbientStreamVolume(int stream, int volume)
+{
+ if (streams[stream].free || !streams[stream].ambient)
+ return;
+
+ ALuint source = streams[stream].Source;
+ alSourcef( source, AL_GAIN, 0.01f * volume );
+ checkALError("Unable to set ambient volume", "WARNING");
+}
+
+bool OpenALAudioDriver::evictBuffer()
+{
+ // Note: this function assumes the caller holds bufferMutex
+
+ // Room for optimization: this is O(n^2) in the number of buffers
+ // at the tail that are used. It can be O(n) if LRUCache supports it.
+
+ unsigned int n = 0;
+ void* p;
+ const char* k;
+ bool res;
+
+ while ((res = buffercache.getLRU(n, k, p)) == true) {
+ CacheEntry* e = (CacheEntry*)p;
+ alDeleteBuffers(1, &e->Buffer);
+ if (alGetError() == AL_NO_ERROR) {
+ // Buffer was unused. An error would have indicated
+ // the buffer was still attached to a source.
+
+ delete e;
+ buffercache.Remove(k);
+
+ //printf("Removed buffer %s from ACMImp cache\n", k);
+ break;
+ }
+ ++n;
+ }
+
+ return res;
+}
+
+void OpenALAudioDriver::clearBufferCache(bool force)
+{
+ // Room for optimization: any method of iterating over the buffers
+ // would suffice. It doesn't have to be in LRU-order.
+ void* p;
+ const char* k;
+ int n = 0;
+ while (buffercache.getLRU(n, k, p)) {
+ CacheEntry* e = (CacheEntry*)p;
+ alDeleteBuffers(1, &e->Buffer);
+ if (force || alGetError() == AL_NO_ERROR) {
+ delete e;
+ buffercache.Remove(k);
+ } else
+ ++n;
+ }
+}
+
+ALenum OpenALAudioDriver::GetFormatEnum(int channels, int bits)
+{
+ switch (channels) {
+ case 1:
+ if (bits == 8)
+ return AL_FORMAT_MONO8;
+ else
+ return AL_FORMAT_MONO16;
+ break;
+
+ case 2:
+ if (bits == 8)
+ return AL_FORMAT_STEREO8;
+ else
+ return AL_FORMAT_STEREO16;
+ break;
+ }
+ return AL_FORMAT_MONO8;
+}
+
+int OpenALAudioDriver::MusicManager(void* arg)
+{
+ OpenALAudioDriver* driver = (OpenALAudioDriver*) arg;
+ ALuint buffersreturned = 0;
+ ALboolean bFinished = AL_FALSE;
+ while (driver->stayAlive) {
+ SDL_Delay(30);
+ StackLock l(driver->musicMutex, "musicMutex in PlayListManager()");
+ if (driver->MusicPlaying) {
+ ALint state;
+ alGetSourcei( driver->MusicSource, AL_SOURCE_STATE, &state );
+ if (checkALError("Unable to query music source state", "ERROR")) {
+ driver->MusicPlaying = false;
+ return -1;
+ }
+ switch (state) {
+ default:
+ printMessage("OpenAL", "WARNING: Unhandled Music state", WHITE );
+ printStatus("ERROR", YELLOW);
+ driver->MusicPlaying = false;
+ return -1;
+ case AL_INITIAL:
+ {
+ printMessage("OPENAL", "Music in INITIAL State. AutoStarting\n", WHITE );
+ for (int i = 0; i < MUSICBUFFERS; i++) {
+ driver->MusicReader->read_samples( driver->music_memory, ACM_BUFFERSIZE >> 1 );
+ alBufferData( driver->MusicBuffer[i], AL_FORMAT_STEREO16,
+ driver->music_memory, ACM_BUFFERSIZE,
+ driver->MusicReader->get_samplerate() );
+ }
+ alSourceQueueBuffers( driver->MusicSource, MUSICBUFFERS, driver->MusicBuffer );
+ if (alIsSource( driver->MusicSource )) {
+ alSourcePlay( driver->MusicSource );
+ checkALError("Error playing music source", "ERROR");
+ }
+ bFinished = AL_FALSE;
+ }
+ break;
+ case AL_STOPPED:
+ printMessage("OpenAL", "WARNING: Buffer Underrun. AutoRestarting Stream Playback\n", WHITE );
+ if (alIsSource( driver->MusicSource )) {
+ alSourcePlay( driver->MusicSource );
+ checkALError("Error playing music source", "ERROR");
+ }
+ break;
+ case AL_PLAYING:
+ break;
+ }
+ ALint processed;
+ alGetSourcei( driver->MusicSource, AL_BUFFERS_PROCESSED, &processed );
+ if (checkALError("Unable to query music source state", "ERROR")) {
+ driver->MusicPlaying = false;
+ return -1;
+ }
+ if (processed > 0) {
+ buffersreturned += processed;
+ while (processed) {
+ ALuint BufferID;
+ alSourceUnqueueBuffers( driver->MusicSource, 1, &BufferID );
+ if (checkALError("Unable to unqueue music buffers", "ERROR")) {
+ driver->MusicPlaying = false;
+ return -1;
+ }
+ if (bFinished == AL_FALSE) {
+ int size = ACM_BUFFERSIZE;
+ int cnt = driver->MusicReader->read_samples( driver->music_memory, ACM_BUFFERSIZE >> 1 );
+ size -= ( cnt * 2 );
+ if (size != 0)
+ bFinished = AL_TRUE;
+ if (bFinished) {
+ printMessage("OpenAL", "Playing Next Music\n", WHITE );
+ core->GetMusicMgr()->PlayNext();
+ if (driver->MusicPlaying) {
+ printMessage( "OpenAL", "Queuing New Music\n", WHITE );
+ driver->MusicReader->read_samples( ( driver->music_memory + cnt ), size >> 1 );
+ bFinished = AL_FALSE;
+ } else {
+ printMessage( "OpenAL", "No Other Music to play\n", WHITE );
+ memset( driver->music_memory + cnt, 0, size );
+ driver->MusicPlaying = false;
+ break;
+ }
+ }
+ alBufferData( BufferID, AL_FORMAT_STEREO16, driver->music_memory, ACM_BUFFERSIZE, driver->MusicReader->get_samplerate() );
+ if (checkALError("Unable to buffer music data", "ERROR")) {
+ driver->MusicPlaying = false;
+ return -1;
+ }
+ alSourceQueueBuffers( driver->MusicSource, 1, &BufferID );
+ if (checkALError("Unable to queue music buffers", "ERROR")) {
+ driver->MusicPlaying = false;
+ return -1;
+ }
+ processed--;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+//This one is used for movies, might be useful for others ?
+void OpenALAudioDriver::QueueBuffer(int stream, unsigned short bits,
+ int channels, short* memory,
+ int size, int samplerate)
+{
+ ALuint Buffer;
+
+ alGenBuffers(1, &Buffer);
+ if (checkALError("Unable to create buffer", "ERROR")) {
+ return;
+ }
+
+ alBufferData(Buffer, GetFormatEnum(channels, bits), memory, size, samplerate);
+ if (checkALError("Unable to buffer data", "ERROR")) {
+ return;
+ }
+
+ streams[stream].delete_buffers = true;
+ streams[stream].ClearProcessedBuffers();
+
+ alSourceQueueBuffers(streams[stream].Source, 1, &Buffer );
+ if (checkALError("Unable to queue buffer", "ERROR")) {
+ return;
+ }
+
+ ALenum state;
+ alGetSourcei(streams[stream].Source, AL_SOURCE_STATE, &state);
+ if (checkALError("Unable to query source state", "ERROR")) {
+ return;
+ }
+
+ if (state != AL_PLAYING ) {
+ alSourcePlay(streams[stream].Source);
+ checkALError("Unable to play source", "ERROR");
+ }
+
+ return;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x27DD67E0, "OpenAL Audio Driver")
+PLUGIN_DRIVER(OpenALAudioDriver, "openal")
+END_PLUGIN()
diff --git a/gemrb/plugins/OpenALAudio/OpenALAudio.h b/gemrb/plugins/OpenALAudio/OpenALAudio.h
new file mode 100644
index 0000000..085bd0d
--- /dev/null
+++ b/gemrb/plugins/OpenALAudio/OpenALAudio.h
@@ -0,0 +1,148 @@
+/* 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 OPENALAUDIO_H_INCLUDED
+#define OPENALAUDIO_H_INCLUDED
+
+#include "Audio.h"
+
+#include "AmbientMgrAL.h"
+#include "StackLock.h"
+
+#include "ie_types.h"
+
+#include "Interface.h"
+#include "LRUCache.h"
+#include "MusicMgr.h"
+#include "SoundMgr.h"
+#include "System/FileStream.h"
+
+#include <SDL.h>
+
+#ifndef WIN32
+#ifdef __APPLE_CC__
+#include <OpenAL/al.h>
+#include <OpenAL/alc.h>
+#else
+#include <AL/al.h>
+#include <AL/alc.h>
+#endif
+#else
+#include <al.h>
+#include <alc.h>
+#endif
+
+#ifdef ANDROID
+#include <AL/android.h>
+#endif
+
+#define RETRY 5
+#define BUFFER_CACHE_SIZE 100
+#define MAX_STREAMS 30
+#define MUSICBUFFERS 10
+#define REFERENCE_DISTANCE 50
+#define ACM_BUFFERSIZE 8192
+
+class OpenALSoundHandle : public SoundHandle {
+protected:
+ struct AudioStream *parent;
+
+public:
+ OpenALSoundHandle(AudioStream *p) : parent(p) { }
+ virtual ~OpenALSoundHandle() { }
+ virtual void SetPos(int XPos, int YPos);
+ virtual bool Playing();
+ virtual void Stop();
+ virtual void StopLooping();
+ void Invalidate() { parent = 0; }
+};
+
+struct AudioStream {
+ AudioStream() : Buffer(0), Source(0), Duration(0), free(true), ambient(false), locked(false), delete_buffers(false) { }
+
+ ALuint Buffer;
+ ALuint Source;
+ int Duration;
+ bool free;
+ bool ambient;
+ bool locked;
+ bool delete_buffers;
+
+ void ClearIfStopped();
+ void ClearProcessedBuffers();
+ void ForceClear();
+
+ Holder<OpenALSoundHandle> handle;
+};
+
+struct CacheEntry {
+ ALuint Buffer;
+ unsigned int Length;
+};
+
+class OpenALAudioDriver : public Audio {
+public:
+ OpenALAudioDriver(void);
+ ~OpenALAudioDriver(void);
+ bool Init(void);
+ Holder<SoundHandle> Play(const char* ResRef, int XPos, int YPos,
+ unsigned int flags = 0, unsigned int *length = 0);
+ bool IsSpeaking();
+ void UpdateVolume(unsigned int flags);
+ bool CanPlay();
+ void ResetMusics();
+ bool Play();
+ bool Stop();
+ bool Pause();
+ bool Resume();
+ int CreateStream(Holder<SoundMgr>);
+ void UpdateListenerPos(int XPos, int YPos );
+ void GetListenerPos( int &XPos, int &YPos );
+ bool ReleaseStream(int stream, bool HardStop);
+ int SetupNewStream( ieWord x, ieWord y, ieWord z,
+ ieWord gain, bool point, bool Ambient );
+ int QueueAmbient(int stream, const char* sound);
+ void SetAmbientStreamVolume(int stream, int volume);
+ void QueueBuffer(int stream, unsigned short bits,
+ int channels, short* memory,
+ int size, int samplerate) ;
+private:
+ ALCcontext *alutContext;
+ ALuint MusicSource;
+ bool MusicPlaying;
+ SDL_mutex* musicMutex;
+ ALuint MusicBuffer[MUSICBUFFERS];
+ Holder<SoundMgr> MusicReader;
+ LRUCache buffercache;
+ AudioStream speech;
+ AudioStream streams[MAX_STREAMS];
+ ALuint loadSound(const char* ResRef, unsigned int &time_length);
+ int num_streams;
+ int CountAvailableSources(int limit);
+ bool evictBuffer();
+ void clearBufferCache(bool force);
+ ALenum GetFormatEnum(int channels, int bits);
+ static int MusicManager(void* args);
+ bool stayAlive;
+ short* music_memory;
+ SDL_Thread* musicThread;
+};
+
+#endif // OPENALAUDIO_H_INCLUDED
diff --git a/gemrb/plugins/OpenALAudio/StackLock.cpp b/gemrb/plugins/OpenALAudio/StackLock.cpp
new file mode 100644
index 0000000..b1db38f
--- /dev/null
+++ b/gemrb/plugins/OpenALAudio/StackLock.cpp
@@ -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.
+ *
+ *
+ */
+
+#include "StackLock.h"
+
+#include <cstdio>
+
+// adapted from ScummVM's mutex.cpp
+
+StackLock::StackLock(SDL_mutex* mutex, const char *mutexName)
+ : _mutex(mutex), _mutexName(mutexName) {
+ lock();
+}
+
+StackLock::~StackLock() {
+ unlock();
+}
+
+void StackLock::lock() {
+#if 0
+ if (_mutexName != NULL) {
+ fprintf(stderr, "Locking mutex %s\n", _mutexName);
+ }
+#endif
+
+ SDL_mutexP(_mutex);
+}
+
+void StackLock::unlock() {
+#if 0
+ if (_mutexName != NULL) {
+ fprintf(stderr, "Unlocking mutex %s\n", _mutexName);
+ }
+#endif
+
+ SDL_mutexV(_mutex);
+}
+
diff --git a/gemrb/plugins/OpenALAudio/StackLock.h b/gemrb/plugins/OpenALAudio/StackLock.h
new file mode 100644
index 0000000..f8a881f
--- /dev/null
+++ b/gemrb/plugins/OpenALAudio/StackLock.h
@@ -0,0 +1,38 @@
+/* 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 "SDL_mutex.h"
+
+// Copied from ScummVM, mutex.h
+
+/**
+ * Auxillary class to (un)lock a mutex on the stack.
+ */
+class StackLock {
+ SDL_mutex* _mutex;
+ const char *_mutexName;
+
+ void lock();
+ void unlock();
+public:
+ StackLock(SDL_mutex* mutex, const char *mutexName = NULL);
+ ~StackLock();
+};
+
diff --git a/gemrb/plugins/PLTImporter/CMakeLists.txt b/gemrb/plugins/PLTImporter/CMakeLists.txt
new file mode 100644
index 0000000..f89f98c
--- /dev/null
+++ b/gemrb/plugins/PLTImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (PLTImporter PLTImporter.cpp)
diff --git a/gemrb/plugins/PLTImporter/Makefile.am b/gemrb/plugins/PLTImporter/Makefile.am
new file mode 100644
index 0000000..c2f6def
--- /dev/null
+++ b/gemrb/plugins/PLTImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = PLTImporter.la
+PLTImporter_la_LDFLAGS = -module -avoid-version -shared
+PLTImporter_la_SOURCES = PLTImporter.cpp PLTImporter.h
diff --git a/gemrb/plugins/PLTImporter/PLTImporter.cpp b/gemrb/plugins/PLTImporter/PLTImporter.cpp
new file mode 100644
index 0000000..c7ec0da
--- /dev/null
+++ b/gemrb/plugins/PLTImporter/PLTImporter.cpp
@@ -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.
+ *
+ *
+ */
+
+#include "PLTImporter.h"
+
+#include "RGBAColor.h"
+#include "win32def.h"
+
+#include "Interface.h"
+#include "Video.h"
+
+static int pperm[8]={3,6,0,5,4,1,2,7};
+
+static ieDword red_mask = 0xff000000;
+static ieDword green_mask = 0x00ff0000;
+static ieDword blue_mask = 0x0000ff00;
+
+PLTImporter::PLTImporter(void)
+{
+ pixels = NULL;
+ if (DataStream::IsEndianSwitch()) {
+ red_mask = 0x000000ff;
+ green_mask = 0x0000ff00;
+ blue_mask = 0x00ff0000;
+ }
+}
+
+PLTImporter::~PLTImporter(void)
+{
+ if (pixels) {
+ free( pixels );
+ }
+}
+
+bool PLTImporter::Open(DataStream* stream)
+{
+ str = stream;
+
+ char Signature[8];
+ unsigned short unknown[4];
+
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "PLT V1 ", 8 ) != 0) {
+ printf( "[PLTImporter]: Not a valid PLT File.\n" );
+ return false;
+ }
+
+ str->Read( unknown, 8 );
+ str->ReadDword( &Width );
+ str->ReadDword( &Height );
+
+ pixels = malloc( Width * Height * 2 );
+ str->Read( pixels, Width * Height * 2 );
+
+ return true;
+}
+
+Sprite2D* PLTImporter::GetSprite2D(unsigned int type, ieDword paletteIndex[8])
+{
+ Color Palettes[8][256];
+ for (int i = 0; i < 8; i++) {
+ core->GetPalette( (paletteIndex[pperm[i]] >> (8*type)) & 0xFF, 256, Palettes[i] );
+ }
+ unsigned char * p = ( unsigned char * ) malloc( Width * Height * 4 );
+ unsigned char * dest = p;
+ unsigned char * src = ( unsigned char * ) pixels;
+ for (int y = Height - 1; y >= 0; y--) {
+ src = ( unsigned char * ) pixels + ( y * Width * 2 );
+ for (unsigned int x = 0; x < Width; x++) {
+ unsigned char intensity = *src++;
+ unsigned char palindex = *src++;
+ if (intensity == 0xff)
+ *dest++ = 0x00;
+ else
+ *dest++ = 0xff;
+ *dest++ = Palettes[palindex][intensity].b;
+ *dest++ = Palettes[palindex][intensity].g;
+ *dest++ = Palettes[palindex][intensity].r;
+ }
+ }
+ Sprite2D* spr = core->GetVideoDriver()->CreateSprite( Width, Height, 32,
+ red_mask, green_mask, blue_mask, 0, p,
+ true, green_mask );
+ spr->XPos = 0;
+ spr->YPos = 0;
+ return spr;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x8D0C64F, "PLT File Importer")
+PLUGIN_IE_RESOURCE(PLTImporter, ".plt", (ieWord)IE_PLT_CLASS_ID)
+END_PLUGIN()
diff --git a/gemrb/plugins/PLTImporter/PLTImporter.h b/gemrb/plugins/PLTImporter/PLTImporter.h
new file mode 100644
index 0000000..870baec
--- /dev/null
+++ b/gemrb/plugins/PLTImporter/PLTImporter.h
@@ -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.
+ *
+ *
+ */
+
+#ifndef PLTIMPORTER_H
+#define PLTIMPORTER_H
+
+#include "PalettedImageMgr.h"
+
+class PLTImporter : public PalettedImageMgr {
+private:
+ ieDword Width, Height;
+ void* pixels;
+public:
+ PLTImporter(void);
+ ~PLTImporter(void);
+ bool Open(DataStream* stream);
+ Sprite2D* GetSprite2D(unsigned int type, ieDword col[8]);
+};
+
+#endif
diff --git a/gemrb/plugins/PNGImporter/CMakeLists.txt b/gemrb/plugins/PNGImporter/CMakeLists.txt
new file mode 100644
index 0000000..c0ad86b
--- /dev/null
+++ b/gemrb/plugins/PNGImporter/CMakeLists.txt
@@ -0,0 +1,5 @@
+IF(PNG_FOUND)
+INCLUDE_DIRECTORIES( ${PNG_INCLUDE_DIR} )
+ADD_GEMRB_PLUGIN (PNGImporter PNGImporter.cpp)
+TARGET_LINK_LIBRARIES( PNGImporter ${PNG_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} )
+ENDIF( PNG_FOUND )
diff --git a/gemrb/plugins/PNGImporter/Makefile.am b/gemrb/plugins/PNGImporter/Makefile.am
new file mode 100644
index 0000000..695d442
--- /dev/null
+++ b/gemrb/plugins/PNGImporter/Makefile.am
@@ -0,0 +1,4 @@
+plugin_LTLIBRARIES = PNGImporter.la
+PNGImporter_la_LDFLAGS = -module -avoid-version -shared
+PNGImporter_la_LIBADD = @LIBPNG@
+PNGImporter_la_SOURCES = PNGImporter.cpp PNGImporter.h
diff --git a/gemrb/plugins/PNGImporter/PNGImporter.cpp b/gemrb/plugins/PNGImporter/PNGImporter.cpp
new file mode 100644
index 0000000..30afe74
--- /dev/null
+++ b/gemrb/plugins/PNGImporter/PNGImporter.cpp
@@ -0,0 +1,217 @@
+/* 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 "PNGImporter.h"
+
+#include "RGBAColor.h"
+#include "globals.h"
+
+#include "ImageFactory.h"
+#include "Interface.h"
+#include "Video.h"
+
+// CHECKME: how should we include png.h ? (And how should we check for it?)
+#include <png.h>
+
+static void DataStream_png_read_data(png_structp png_ptr,
+ png_bytep data, png_size_t length)
+{
+ void *read_io_ptr = png_get_io_ptr(png_ptr);
+ DataStream* str = reinterpret_cast<DataStream*>(read_io_ptr);
+ str->Read(data, length);
+}
+
+struct PNGInternal {
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_infop end_info;
+};
+
+
+static ieDword blue_mask = 0x00ff0000;
+static ieDword green_mask = 0x0000ff00;
+static ieDword red_mask = 0x000000ff;
+static ieDword alpha_mask = 0xff000000;
+
+PNGImporter::PNGImporter(void)
+{
+ inf = new PNGInternal();
+ inf->png_ptr = 0;
+ inf->info_ptr = 0;
+ inf->end_info = 0;
+}
+
+PNGImporter::~PNGImporter(void)
+{
+ Close();
+ delete inf;
+}
+
+void PNGImporter::Close()
+{
+ if (inf) {
+ if (inf->png_ptr) {
+ png_destroy_read_struct(&inf->png_ptr, &inf->info_ptr,
+ &inf->end_info);
+ }
+ inf->png_ptr = 0;
+ inf->info_ptr = 0;
+ inf->end_info = 0;
+ }
+}
+
+bool PNGImporter::Open(DataStream* stream)
+{
+ str = stream;
+ Close();
+
+ png_byte header[8];
+ if (stream->Read(header, 8) < 8) return false;
+ if (png_sig_cmp(header, 0, 8)) {
+ return false;
+ }
+ inf->png_ptr = png_create_read_struct(
+ PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!inf->png_ptr)
+ return false;
+
+ inf->info_ptr = png_create_info_struct(inf->png_ptr);
+ if (!inf->info_ptr)
+ {
+ png_destroy_read_struct(&inf->png_ptr, (png_infopp)NULL,
+ (png_infopp)NULL);
+ return false;
+ }
+
+ inf->end_info = png_create_info_struct(inf->png_ptr);
+ if (!inf->end_info)
+ {
+ png_destroy_read_struct(&inf->png_ptr, &inf->info_ptr,
+ (png_infopp)NULL);
+ return false;
+ }
+
+ if (setjmp(png_jmpbuf(inf->png_ptr))) {
+ png_destroy_read_struct(&inf->png_ptr, &inf->info_ptr, &inf->end_info);
+ return false;
+ }
+
+ png_set_read_fn(inf->png_ptr, stream, DataStream_png_read_data);
+ png_set_sig_bytes(inf->png_ptr, 8);
+
+ png_read_info(inf->png_ptr, inf->info_ptr);
+
+ png_uint_32 width, height;
+ int bit_depth, color_type;
+ int interlace_type, compression_type, filter_method;
+ png_get_IHDR(inf->png_ptr, inf->info_ptr, &width, &height,
+ &bit_depth, &color_type,
+ &interlace_type, &compression_type, &filter_method);
+
+ if (color_type != PNG_COLOR_TYPE_PALETTE &&
+ png_get_valid(inf->png_ptr, inf->info_ptr, PNG_INFO_tRNS))
+ {
+ // if not indexed, turn transparency info into alpha
+ // (if indexed, we use it directly for colorkeying)
+ png_set_tRNS_to_alpha(inf->png_ptr);
+ }
+
+ if (bit_depth == 16)
+ png_set_strip_16(inf->png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_RGB)
+ png_set_filler(inf->png_ptr, 0xFF, PNG_FILLER_AFTER);
+
+ png_read_update_info(inf->png_ptr, inf->info_ptr);
+
+
+ Width = width;
+ Height = height;
+
+ hasPalette = (color_type == PNG_COLOR_TYPE_PALETTE);
+
+ return true;
+}
+
+Sprite2D* PNGImporter::GetSprite2D()
+{
+ Sprite2D* spr = 0;
+ unsigned char* buffer = 0;
+ png_bytep* row_pointers = new png_bytep[Height];
+ buffer = (unsigned char *) malloc((hasPalette?1:4)*Width*Height);
+ for (unsigned int i = 0; i < Height; ++i)
+ row_pointers[i] = reinterpret_cast<png_bytep>(&buffer[(hasPalette?1:4)*i*Width]);
+
+ if (setjmp(png_jmpbuf(inf->png_ptr))) {
+ delete[] row_pointers;
+ free( buffer );
+ png_destroy_read_struct(&inf->png_ptr, &inf->info_ptr, &inf->end_info);
+ return NULL;
+ }
+
+ png_read_image(inf->png_ptr, row_pointers);
+
+ delete[] row_pointers;
+ row_pointers = 0;
+
+ // the end_info struct isn't used, but passing it anyway for now
+ png_read_end(inf->png_ptr, inf->end_info);
+
+ if (hasPalette) {
+ Color pal[256];
+ GetPalette(256, pal);
+ // TODO: colorkey
+ spr = core->GetVideoDriver()->CreateSprite8(Width, Height, 8,
+ buffer, pal, false, 0);
+ } else {
+ spr = core->GetVideoDriver()->CreateSprite(Width, Height, 32,
+ red_mask, green_mask,
+ blue_mask, alpha_mask,
+ buffer, false, 0);
+ }
+
+ png_destroy_read_struct(&inf->png_ptr, &inf->info_ptr, &inf->end_info);
+
+ return spr;
+}
+
+void PNGImporter::GetPalette(int colors, Color* pal)
+{
+ if (!hasPalette) {
+ ImageMgr::GetPalette(colors, pal);
+ return;
+ }
+
+ png_color* palette;
+ int num_palette;
+ png_get_PLTE(inf->png_ptr, inf->info_ptr, &palette, &num_palette);
+ for (int i = 0; i < colors; i++) {
+ pal[i].r = palette[i%num_palette].red;
+ pal[i].g = palette[i%num_palette].green;
+ pal[i].b = palette[i%num_palette].blue;
+ pal[i].a = 0xff;
+ }
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x11C3EB12, "PNG File Importer")
+PLUGIN_IE_RESOURCE(PNGImporter, ".png", (ieWord)IE_PNG_CLASS_ID)
+END_PLUGIN()
diff --git a/gemrb/plugins/PNGImporter/PNGImporter.h b/gemrb/plugins/PNGImporter/PNGImporter.h
new file mode 100644
index 0000000..4aabd66
--- /dev/null
+++ b/gemrb/plugins/PNGImporter/PNGImporter.h
@@ -0,0 +1,45 @@
+/* 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 PNGIMPORTER_H
+#define PNGIMPORTER_H
+
+#include "ImageMgr.h"
+
+struct PNGInternal;
+
+class PNGImporter : public ImageMgr {
+private:
+ PNGInternal* inf;
+
+ ieDword Width, Height;
+ bool hasPalette;
+public:
+ PNGImporter(void);
+ ~PNGImporter(void);
+ void Close();
+ bool Open(DataStream* stream);
+ Sprite2D* GetSprite2D();
+ void GetPalette(int colors, Color* pal);
+ int GetWidth() { return (int) Width; }
+ int GetHeight() { return (int) Height; }
+};
+
+#endif
diff --git a/gemrb/plugins/PROImporter/CMakeLists.txt b/gemrb/plugins/PROImporter/CMakeLists.txt
new file mode 100644
index 0000000..ca50753
--- /dev/null
+++ b/gemrb/plugins/PROImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (PROImporter PROImporter.cpp)
diff --git a/gemrb/plugins/PROImporter/Makefile.am b/gemrb/plugins/PROImporter/Makefile.am
new file mode 100644
index 0000000..6eaa782
--- /dev/null
+++ b/gemrb/plugins/PROImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = PROImporter.la
+PROImporter_la_LDFLAGS = -module -avoid-version -shared
+PROImporter_la_SOURCES = PROImporter.cpp PROImporter.h
diff --git a/gemrb/plugins/PROImporter/PROImporter.cpp b/gemrb/plugins/PROImporter/PROImporter.cpp
new file mode 100644
index 0000000..013cc75
--- /dev/null
+++ b/gemrb/plugins/PROImporter/PROImporter.cpp
@@ -0,0 +1,179 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2006 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 "PROImporter.h"
+
+#include "win32def.h"
+
+#include "EffectMgr.h"
+#include "Interface.h"
+
+PROImporter::PROImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+PROImporter::~PROImporter(void)
+{
+ if (autoFree) {
+ delete str;
+ }
+ str = NULL;
+}
+
+bool PROImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (this->autoFree) {
+ delete str;
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "PRO V1.0", 8 ) == 0) {
+ version = 10;
+ } else {
+ printf( "[PROImporter]: This file is not a valid PRO File\n" );
+ return false;
+ }
+
+ return true;
+}
+
+Projectile* PROImporter::GetProjectile(Projectile *s)
+{
+ if( !s) {
+ return NULL;
+ }
+ ieWord AreaExtension;
+
+ str->ReadWord( &AreaExtension );
+ str->ReadWord( &s->Speed );
+ str->ReadDword( &s->SFlags ); //spark, ignore center, looping sound etc
+ str->ReadResRef( s->SoundRes1 );
+ str->ReadResRef( s->SoundRes2 );
+ str->ReadResRef( s->TravelVVC ); //no original game data uses this feature
+ str->ReadDword( &s->SparkColor );//enabled by PSF_SPARK
+ str->ReadDword( &s->ExtFlags ) ; //gemrb extension flags
+ str->ReadDword( &s->StrRef ); //gemrb extension strref
+ str->ReadDword( &s->RGB ); //gemrb extension rgb pulse
+ str->ReadWord( &s->ColorSpeed ); //gemrb extension rgb speed
+ str->ReadWord( &s->Shake ); //gemrb extension screen shake
+ str->ReadWord( &s->IDSValue); //gemrb extension IDS targeting
+ str->ReadWord( &s->IDSType); //gemrb extension IDS targeting
+ str->ReadWord( &s->IDSValue2); //gemrb extension IDS targeting
+ str->ReadWord( &s->IDSType2); //gemrb extension IDS targeting
+ str->ReadResRef( s->FailSpell); //gemrb extension fail effect
+ str->ReadResRef( s->SuccSpell); //gemrb extension implicit effect
+ str->Seek(172, GEM_CURRENT_POS); //skipping unused (unknown) bytes
+ //we should stand at offset 0x100 now
+ str->ReadDword( &s->TFlags ); //other projectile flags
+ str->ReadResRef( s->BAMRes1 );
+ str->ReadResRef( s->BAMRes2 );
+ str->Read( &s->Seq1,1 );
+ str->Read( &s->Seq2,1 );
+ str->ReadWord( &s->LightZ );
+ str->ReadWord( &s->LightX );
+ str->ReadWord( &s->LightY );
+ str->ReadResRef( s->PaletteRes );
+ str->Read( s->Gradients, 7);
+ str->Read( &s->SmokeSpeed, 1);
+ str->Read( s->SmokeGrad, 7);
+ str->Read( &s->Aim, 1);
+ str->ReadWord( &s->SmokeAnimID);
+ str->ReadResRef( s->TrailBAM[0] );
+ str->ReadResRef( s->TrailBAM[1] );
+ str->ReadResRef( s->TrailBAM[2] );
+ str->ReadWord( &s->TrailSpeed[0] );
+ str->ReadWord( &s->TrailSpeed[1] );
+ str->ReadWord( &s->TrailSpeed[2] );
+ str->Seek(172, GEM_CURRENT_POS);
+ if (AreaExtension!=3) {
+ return s;
+ }
+ s->InitExtension();
+ GetAreaExtension(s->Extension);
+ return s;
+}
+
+void PROImporter::GetAreaExtension(ProjectileExtension *e)
+{
+ ieWord tmp;
+
+ str->ReadDword( &e->AFlags );
+ str->ReadWord( &e->TriggerRadius );
+ str->ReadWord( &e->ExplosionRadius );
+ str->ReadResRef( e->SoundRes ); //explosion sound
+ str->ReadWord( &e->Delay );
+ str->ReadWord( &e->FragAnimID );
+ //this projectile index shouldn't be adjusted like the others!!!
+ str->ReadWord( &e->FragProjIdx );
+ str->Read( &e->ExplosionCount,1 );
+ //the area puff type (flames, puffs, clouds) fireball.ids
+ //gemrb uses areapro.2da for this
+ //It overrides Spread, VVCRes, Secondary, SoundRes, AreaSound, APFlags
+ str->Read( &e->ExplType,1 );
+ str->ReadWord( &e->ExplColor );
+ //this index is off by one in the .pro files, consolidating it here
+ str->ReadWord( &tmp);
+ if (tmp) {
+ tmp--;
+ }
+ e->ExplProjIdx = tmp;
+
+ str->ReadResRef( e->VVCRes );
+ str->ReadWord( &tmp);
+ //limit the cone width to 359 degrees (for full compatibility)
+ if (tmp>359) {
+ tmp=359;
+ }
+ e->ConeWidth = tmp;
+ str->ReadWord( &tmp);
+
+ //These are GemRB specific, not used in the original engine
+ str->ReadResRef( e->Spread );
+ str->ReadResRef( e->Secondary );
+ str->ReadResRef( e->AreaSound );
+ str->ReadDword( &e->APFlags );
+ str->ReadWord( &e->DiceCount );
+ str->ReadWord( &e->DiceSize );
+ str->ReadWord( &e->TileX );
+ str->ReadWord( &e->TileY );
+
+ if (!e->TileX) {
+ e->TileX=64;
+ }
+ if (!e->TileY) {
+ e->TileY=64;
+ }
+
+ //we skip the rest
+ str->Seek(180, GEM_CURRENT_POS);
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xCAD2D64, "PRO File Importer")
+PLUGIN_CLASS(IE_PRO_CLASS_ID, PROImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/PROImporter/PROImporter.h b/gemrb/plugins/PROImporter/PROImporter.h
new file mode 100644
index 0000000..d6c7b91
--- /dev/null
+++ b/gemrb/plugins/PROImporter/PROImporter.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 PROIMPORTER_H
+#define PROIMPORTER_H
+
+#include "ProjectileMgr.h"
+
+#include "ie_types.h"
+
+#include "Projectile.h"
+
+
+class PROImporter : public ProjectileMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ int version;
+
+public:
+ PROImporter(void);
+ ~PROImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ Projectile* GetProjectile(Projectile *s);
+private:
+ void GetAreaExtension(ProjectileExtension *s);
+};
+
+
+#endif
diff --git a/gemrb/plugins/PSTOpcodes/CMakeLists.txt b/gemrb/plugins/PSTOpcodes/CMakeLists.txt
new file mode 100644
index 0000000..5abb577
--- /dev/null
+++ b/gemrb/plugins/PSTOpcodes/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (PSTOpcodes PSTOpcodes.cpp)
diff --git a/gemrb/plugins/PSTOpcodes/Makefile.am b/gemrb/plugins/PSTOpcodes/Makefile.am
new file mode 100644
index 0000000..19aba2d
--- /dev/null
+++ b/gemrb/plugins/PSTOpcodes/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = PSTOpcodes.la
+PSTOpcodes_la_LDFLAGS = -module -avoid-version -shared
+PSTOpcodes_la_SOURCES = PSTOpcodes.cpp
diff --git a/gemrb/plugins/PSTOpcodes/PSTOpcodes.cpp b/gemrb/plugins/PSTOpcodes/PSTOpcodes.cpp
new file mode 100644
index 0000000..477894e
--- /dev/null
+++ b/gemrb/plugins/PSTOpcodes/PSTOpcodes.cpp
@@ -0,0 +1,725 @@
+/* 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 "strrefs.h"
+#include "win32def.h"
+
+#include "EffectQueue.h"
+#include "Game.h"
+#include "GameData.h"
+#include "Interface.h"
+#include "TileMap.h"
+#include "Video.h" //for tints
+#include "Scriptable/Actor.h"
+
+int fx_retreat_from (Scriptable* Owner, Actor* target, Effect* fx);//6e
+int fx_set_status (Scriptable* Owner, Actor* target, Effect* fx);//ba
+int fx_play_bam_blended (Scriptable* Owner, Actor* target, Effect* fx);//bb
+int fx_play_bam_not_blended (Scriptable* Owner, Actor* target, Effect* fx);//bc
+int fx_transfer_hp (Scriptable* Owner, Actor* target, Effect* fx);//c0
+//int fx_shake_screen (Scriptable* Owner, Actor* target, Effect* fx);//c1 already implemented in fxopcodes
+int fx_flash_screen (Scriptable* Owner, Actor* target, Effect* fx);//c2
+int fx_tint_screen (Scriptable* Owner, Actor* target, Effect* fx);//c3
+int fx_special_effect (Scriptable* Owner, Actor* target, Effect* fx);//c4
+int fx_multiple_vvc (Scriptable* Owner, Actor* target, Effect* fx);//c5 //gemrb specific
+//int fx_modify_global ((Scriptable* Owner, Actor* target, Effect* fx);//c6 already implemented in fxopcodes
+int fx_change_background (Scriptable* Owner, Actor* target, Effect* fx);//c7 //gemrb specific
+//unknown 0xc7-c8
+int fx_overlay (Scriptable* Owner, Actor* target, Effect* fx);//c9
+//unknown 0xca
+int fx_bless (Scriptable* Owner, Actor* target, Effect* fx);//82 (this is a modified effect)
+int fx_curse (Scriptable* Owner, Actor* target, Effect* fx);//cb
+int fx_prayer (Scriptable* Owner, Actor* target, Effect* fx);//cc
+int fx_move_view (Scriptable* Owner, Actor* target, Effect* fx);//cd
+int fx_embalm (Scriptable* Owner, Actor* target, Effect* fx);//ce
+int fx_stop_all_action (Scriptable* Owner, Actor* target, Effect* fx);//cf
+int fx_iron_fist (Scriptable* Owner, Actor* target, Effect* fx);//d0
+int fx_hostile_image(Scriptable* Owner, Actor* target, Effect* fx);//d1
+int fx_detect_evil (Scriptable* Owner, Actor* target, Effect* fx);//d2
+int fx_jumble_curse (Scriptable* Owner, Actor* target, Effect* fx);//d3
+int fx_speak_with_dead (Scriptable* Owner, Actor* target, Effect* fx);//d4
+
+//the engine sorts these, feel free to use any order
+static EffectDesc effectnames[] = {
+ { "Bless", fx_bless, 0, -1 },//82
+ { "ChangeBackground", fx_change_background, EFFECT_NO_ACTOR, -1 }, //c6
+ { "Curse", fx_curse, 0, -1 },//cb
+ { "DetectEvil", fx_detect_evil, 0, -1 }, //d2
+ { "Embalm", fx_embalm, 0, -1 }, //0xce
+ { "FlashScreen", fx_flash_screen, EFFECT_NO_ACTOR, -1 }, //c2
+ { "HostileImage", fx_hostile_image, 0, -1 },//d1
+ { "IronFist", fx_iron_fist, 0, -1 }, //d0
+ { "JumbleCurse", fx_jumble_curse, 0, -1 }, //d3
+ { "MoveView", fx_move_view, EFFECT_NO_ACTOR, -1 },//cd
+ { "MultipleVVC", fx_multiple_vvc, EFFECT_NO_ACTOR, -1 }, //c5
+ { "Overlay", fx_overlay, 0, -1 }, //c9
+ { "PlayBAM1", fx_play_bam_blended, 0, -1 }, //bb
+ { "PlayBAM2", fx_play_bam_not_blended, 0, -1 },//bc
+ { "PlayBAM3", fx_play_bam_not_blended, 0, -1 }, //bd
+ { "PlayBAM4", fx_play_bam_not_blended, 0, -1 }, //be
+ { "PlayBAM5", fx_play_bam_not_blended, 0, -1 }, //bf
+ { "Prayer", fx_prayer, 0, -1 },//cc
+ { "RetreatFrom", fx_retreat_from, 0, -1 },//6e
+ { "SetStatus", fx_set_status, 0, -1 }, //ba
+ { "SpeakWithDead", fx_speak_with_dead, 0, -1 }, //d4
+ { "SpecialEffect", fx_special_effect, 0, -1 },//c4
+ { "StopAllAction", fx_stop_all_action, EFFECT_NO_ACTOR, -1 }, //cf
+ { "TintScreen", fx_tint_screen, EFFECT_NO_ACTOR, -1 }, //c3
+ { "TransferHP", fx_transfer_hp, EFFECT_DICED, -1 }, //c0
+ { NULL, NULL, 0, 0 },
+};
+
+void RegisterTormentOpcodes()
+{
+ core->RegisterOpcodes( sizeof( effectnames ) / sizeof( EffectDesc ) - 1, effectnames );
+}
+
+//retreat_from (works only in PST) - forces target to run away/walk away from Owner
+int fx_retreat_from (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_retreat_from (%2d): Mod: %d, Type: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+
+ if (!Owner) {
+ return FX_NOT_APPLIED;
+ }
+
+ //distance to run
+ if (!fx->Parameter3) {
+ fx->Parameter3=100;
+ }
+
+ if (fx->Parameter2==8) {
+ //backs away from owner
+ target->RunAwayFrom(Owner->Pos, fx->Parameter3, false);
+ //one shot
+ return FX_NOT_APPLIED;
+ }
+
+ //walks (7) or runs away (all others) from owner
+ target->RunAwayFrom(Owner->Pos, fx->Parameter3, true);
+ if (fx->Parameter2!=7) {
+ target->SetRunFlags(IF_RUNNING);
+ }
+
+ //has a duration
+ return FX_APPLIED;
+}
+
+//0xba fx_set_status
+int fx_set_status (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_set_status (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ if (fx->Parameter1) {
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_STATE_SET (fx->Parameter2);
+ } else {
+ STATE_SET (fx->Parameter2);
+ }
+ } else {
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ BASE_STATE_CURE (fx->Parameter2);
+ } else {
+ STATE_CURE (fx->Parameter2);
+ }
+ }
+ return FX_PERMANENT;
+}
+
+//bb fx_play_bam_bb (play multi-part blended sticky animation)
+// 1 repeats
+// 2 not sticky (override default)
+int fx_play_bam_blended (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ bool playonce;
+
+ if (0) printf( "fx_play_bam_blended (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ if (!Owner)
+ Owner = target;
+ if (!Owner)
+ return FX_NOT_APPLIED;
+ //delay effect
+ Map *area = Owner->GetCurrentArea();
+ if (!area)
+ return FX_APPLIED;
+
+ //play once set to true
+ //check tearring.itm (0xbb effect)
+ ScriptedAnimation *sca = gamedata->GetScriptedAnimation(fx->Resource, true);
+ if (!sca)
+ return FX_NOT_APPLIED;
+
+ sca->SetBlend();
+ //the transparency is based on the original palette
+ if (fx->Parameter1) {
+ RGBModifier rgb;
+
+ rgb.speed=-1;
+ rgb.phase=0;
+ rgb.rgb.r=fx->Parameter1;
+ rgb.rgb.g=fx->Parameter1 >> 8;
+ rgb.rgb.b=fx->Parameter1 >> 16;
+ rgb.rgb.a=0;
+ rgb.type=RGBModifier::TINT;
+ sca->AlterPalette(rgb);
+ }
+ if (fx->TimingMode==FX_DURATION_INSTANT_PERMANENT) {
+ playonce=true;
+ } else {
+ playonce=false;
+ }
+ if (playonce) {
+ sca->PlayOnce();
+ } else {
+ if (fx->Parameter2&1) {
+ //four cycles, duration is in millisecond
+ sca->SetDefaultDuration(sca->GetSequenceDuration(AI_UPDATE_TIME));
+ } else {
+ sca->SetDefaultDuration(fx->Duration-core->GetGame()->Ticks);
+ }
+ }
+ //convert it to an area VVC
+ if (!target) {
+ fx->Parameter2|=2;
+ }
+
+ if (fx->Parameter2&2) {
+ sca->XPos+=fx->PosX;
+ sca->YPos+=fx->PosY;
+ area->AddVVCell(sca);
+ } else {
+ ScriptedAnimation *twin = sca->DetachTwin();
+ if (twin) {
+ target->AddVVCell(twin);
+ }
+ target->AddVVCell(sca);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//bc-bf play_bam_not_blended (play not blended single animation)
+//random placement (if not sticky): 1
+//sticky bit: 4096
+//transparency instead of rgb tint: 0x100000, fade off is in dice size
+//blend: 0x300000
+//twin animation: 0x30000
+//background animation: 0x10000
+//foreground animation: 0x20000
+int fx_play_bam_not_blended (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ bool playonce;
+ bool doublehint;
+
+ if (0) printf( "fx_play_bam_not_blended (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ if (!Owner)
+ Owner = target;
+ if (!Owner)
+ return FX_NOT_APPLIED;
+
+ Map *area = Owner->GetCurrentArea();
+ if (!area)
+ return FX_APPLIED;
+
+ //play once set to true
+ //check tearring.itm (0xbb effect)
+ if ((fx->Parameter2&0x30000)==0x30000) {
+ doublehint = true;
+ } else {
+ doublehint = false;
+ }
+ ScriptedAnimation *sca = gamedata->GetScriptedAnimation(fx->Resource, doublehint);
+ if (!sca)
+ return FX_NOT_APPLIED;
+
+ switch (fx->Parameter2&0x300000) {
+ case 0x300000:
+ sca->SetBlend(); //per pixel transparency
+ break;
+ case 0x200000: //this is an insane combo
+ sca->SetBlend(); //per pixel transparency
+ sca->SetFade((ieByte) fx->Parameter1, fx->DiceSides); //per surface transparency
+ break;
+ case 0x100000: //per surface transparency
+ sca->SetFade((ieByte) fx->Parameter1, fx->DiceSides);
+ break;
+ default:
+ if (fx->Parameter1) {
+ RGBModifier rgb;
+
+ rgb.speed=-1;
+ rgb.phase=0;
+ rgb.rgb.r=fx->Parameter1;
+ rgb.rgb.g=fx->Parameter1 >> 8;
+ rgb.rgb.b=fx->Parameter1 >> 16;
+ rgb.rgb.a=fx->Parameter1 >> 24;
+ rgb.type=RGBModifier::TINT;
+ sca->AlterPalette(rgb);
+ }
+ }
+ if (fx->TimingMode==FX_DURATION_INSTANT_LIMITED) {
+ playonce=false;
+ } else {
+ playonce=true;
+ }
+ switch (fx->Parameter2&0x30000) {
+ case 0x20000://foreground
+ sca->ZPos+=9999;
+ sca->YPos+=9999;
+ break;
+ case 0x30000: //both
+ sca->ZPos+=9999;
+ sca->YPos+=9999;
+ if (sca->twin) {
+ sca->twin->ZPos-=9999;
+ sca->twin->YPos-=9999;
+ }
+ break;
+ default: //background
+ sca->ZPos-=9999;
+ sca->YPos-=9999;
+ break;
+ }
+ if (playonce) {
+ sca->PlayOnce();
+ } else {
+ sca->SetDefaultDuration(fx->Duration-core->GetGame()->Ticks);
+ }
+ ScriptedAnimation *twin = sca->DetachTwin();
+
+ if (target && (fx->Parameter2&4096)) {
+ if (twin) {
+ target->AddVVCell(twin);
+ }
+ target->AddVVCell(sca);
+ } else {
+ //the random placement works only when it is not sticky
+ int x = 0;
+ int y = 0;
+ if (fx->Parameter2&1) {
+ ieWord tmp =(ieWord) rand();
+ x = tmp&31;
+ y = (tmp>>5)&31;
+ }
+
+ sca->XPos+=fx->PosX-x;
+ sca->YPos+=fx->PosY+sca->ZPos-y;
+ if (twin) {
+ twin->XPos+=fx->PosX-x;
+ twin->YPos+=fx->PosY+twin->ZPos-y;
+ area->AddVVCell(twin);
+ }
+ area->AddVVCell(sca);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xc0 fx_transfer_hp
+int fx_transfer_hp (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_transfer_hp (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ if (Owner->Type!=ST_ACTOR) {
+ return FX_NOT_APPLIED;
+ }
+
+ Actor *owner = GetCasterObject();
+
+ if (owner==target) {
+ return FX_NOT_APPLIED;
+ }
+
+ Actor *receiver;
+ Actor *donor;
+ int a,b;
+
+ //handle variable level hp drain (for blood bridge)
+ if (fx->IsVariable) {
+ fx->Parameter1+=fx->CasterLevel;
+ fx->IsVariable=0;
+ }
+
+ switch(fx->Parameter2) {
+ case 3:
+ case 0: receiver = target; donor = owner; break;
+ case 4:
+ case 1: receiver = owner; donor = target; break;
+ case 2:
+ a = owner->GetBase(IE_HITPOINTS);
+ b = target->GetBase(IE_HITPOINTS);
+ owner->SetBase(IE_HITPOINTS, a);
+ target->SetBase(IE_HITPOINTS, b);
+ //fallthrough
+ default:
+ return FX_NOT_APPLIED;
+ }
+ int damage = receiver->GetStat(IE_MAXHITPOINTS)-receiver->GetStat(IE_HITPOINTS);
+ if (damage>(signed) fx->Parameter1) {
+ damage=(signed) fx->Parameter1;
+ }
+ if (damage) {
+ damage = donor->Damage(damage, fx->Parameter2, owner);
+ receiver->NewBase( IE_HITPOINTS, damage, MOD_ADDITIVE );
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xc1 fx_shake_screen this is already implemented in BG2
+
+//0xc2 fx_flash_screen
+int fx_flash_screen (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_flash_screen (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ core->GetVideoDriver()->SetFadeColor(((unsigned char *) &fx->Parameter1)[0],((unsigned char *) &fx->Parameter1)[1],((unsigned char *) &fx->Parameter1)[2]);
+ //this needs to be at least 2 for any effect
+ core->timer->SetFadeFromColor(2);
+ return FX_NOT_APPLIED;
+}
+
+//0xc3 fx_tint_screen
+//FIXME: implement bit4 which would mean duration
+int fx_tint_screen (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_tint_screen (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ int fromTime = fx->DiceSides;
+ int toTime = fx->DiceSides;
+ switch(fx->Parameter2&6) {
+ case 0: toTime = 0; break;
+ case 2: fromTime = 0; break;
+ }
+ core->timer->SetFadeFromColor(fromTime);
+ core->timer->SetFadeToColor(toTime);
+ return FX_NOT_APPLIED;
+}
+
+//0xc4 fx_special_effect
+//it is a mystery, why they needed to make this effect
+int fx_special_effect (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_special_effect (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ //param2 determines the effect's behaviour
+ //0 - adder's kiss projectile (0xcd)
+ // adds play bam and damage opcodes to the projectile
+ //1 - ball lightning projectile (0xe0)
+ // adds a play bam opcode to the projectile
+ //2 - raise dead projectile - the projectile itself has the effect (why is it so complicated)
+
+ //TODO: create the spells
+ switch(fx->Parameter2) {
+ case 0:
+ strnuprcpy(fx->Resource,"adder",8);
+ break;
+ case 1:
+ strnuprcpy(fx->Resource,"ball",8);
+ break;
+ case 2:
+ strnuprcpy(fx->Resource,"rdead",8);
+ break;
+ }
+ Owner->CastSpell(fx->Resource, target, false);
+ Owner->CastSpellEnd(fx->CasterLevel);
+ return FX_NOT_APPLIED;
+}
+//0xc5 fx_multiple_vvc
+//this is a gemrb specific opcode to support the rune of torment projectile
+//it plays multiple vvc's with a given delay and duration
+int fx_multiple_vvc (Scriptable* Owner, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_multiple_vvc (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+
+ Map *area = Owner->GetCurrentArea();
+ if (!area)
+ return FX_NOT_APPLIED;
+
+ AutoTable tab(fx->Resource);
+ if (!tab)
+ return FX_NOT_APPLIED;
+
+ int rows = tab->GetRowCount();
+ while(rows--) {
+ Point offset;
+ int delay, duration;
+
+ offset.x=atoi(tab->QueryField(rows,0));
+ offset.y=atoi(tab->QueryField(rows,1));
+ delay = atoi(tab->QueryField(rows,3));
+ duration = atoi(tab->QueryField(rows,4));
+ ScriptedAnimation *sca = gamedata->GetScriptedAnimation(tab->QueryField(rows,2), true);
+ if (!sca) continue;
+ sca->SetBlend();
+ sca->SetDelay(AI_UPDATE_TIME*delay);
+ sca->SetDefaultDuration(AI_UPDATE_TIME*duration);
+ sca->XPos+=fx->PosX+offset.x;
+ sca->YPos+=fx->PosY+offset.y;
+ area->AddVVCell(sca);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xc6 ChangeBackground
+//GemRB specific, to support BMP area background changes (desert hell projectile)
+int fx_change_background (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_change_background (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ Map *map = core->GetGame()->GetCurrentArea();
+ if (map) {
+ map->SetBackground(fx->Resource, fx->Duration);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xc7-c8 fx_unknown
+//0xc9 fx_overlay
+int fx_overlay (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_overlay (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ target->AddAnimation(fx->Resource,-1,0,true);
+ //special effects based on fx_param2
+ return FX_NOT_APPLIED;
+}
+//0xca fx_unknown
+
+//0x82 fx_bless
+//static EffectRef fx_glow_ref = { "Color:PulseRGBGlobal", -1 };
+//pst bless effect spawns a color glow automatically
+//but i would rather use the IWD2 method
+int fx_bless (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_curse (%2d): Par1: %d\n", fx->Opcode, fx->Parameter1 );
+ //this bit is the same as the invisibility bit in other games
+ //it should be considered what if we replace the pst invis bit
+ //with this one (losing binary compatibility, gaining easier
+ //invis checks at core level)
+ if (STATE_GET (STATE_BLESS) ) //curse is non-cumulative
+ return FX_NOT_APPLIED;
+
+ target->SetColorMod(255, RGBModifier::ADD, 0x18, 0xc8, 0xc8, 0xc8);
+
+ STATE_SET( STATE_BLESS );
+ STAT_SUB( IE_TOHIT, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSDEATH, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSWANDS, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSPOLY, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSBREATH, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSSPELL, fx->Parameter1);
+ return FX_APPLIED;
+}
+
+//0xcb fx_curse
+int fx_curse (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_curse (%2d): Par1: %d\n", fx->Opcode, fx->Parameter1 );
+ //this bit is the same as the invisibility bit in other games
+ //it should be considered what if we replace the pst invis bit
+ //with this one (losing binary compatibility, gaining easier
+ //invis checks at core level)
+ if (STATE_GET (STATE_PST_CURSE) ) //curse is non cumulative
+ return FX_NOT_APPLIED;
+ STATE_SET( STATE_PST_CURSE );
+ STAT_SUB( IE_TOHIT, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSDEATH, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSWANDS, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSPOLY, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSBREATH, fx->Parameter1);
+ STAT_SUB( IE_SAVEVSSPELL, fx->Parameter1);
+ return FX_APPLIED;
+}
+
+//0xcc fx_prayer
+static EffectRef fx_curse_ref = { "Curse", -1 };
+static EffectRef fx_bless_ref = { "Bless", -1 };
+
+int fx_prayer (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_prayer (%2d): Par1: %d\n", fx->Opcode, fx->Parameter1 );
+ int ea = target->GetStat(IE_EA);
+ int type;
+ if (ea>EA_EVILCUTOFF) type = 1;
+ else if (ea<EA_GOODCUTOFF) type = 0;
+ else return FX_NOT_APPLIED; //what happens if the target goes neutral during the effect? if the effect remains, make this FX_APPLIED
+
+ Map *map = target->GetCurrentArea();
+ int i = map->GetActorCount(true);
+ Effect *newfx = EffectQueue::CreateEffect(type?fx_curse_ref:fx_bless_ref, fx->Parameter1, fx->Parameter2, FX_DURATION_INSTANT_LIMITED);
+ memcpy(newfx, fx->Source,sizeof(ieResRef));
+ newfx->Duration=60;
+ while(i--) {
+ Actor *tar=map->GetActor(i,true);
+ ea = tar->GetStat(IE_EA);
+ if (ea>EA_EVILCUTOFF) type^=1;
+ else if (ea>EA_GOODCUTOFF) continue;
+ //this isn't a real perma effect, just applying the effect now
+ //no idea how this should work with spell resistances, etc
+ //lets assume it is never resisted
+ //the effect will be destructed by ApplyEffect (not anymore)
+ //the effect is copied to a new memory area
+ core->ApplyEffect(newfx, tar, Owner);
+ }
+ delete newfx;
+ return FX_APPLIED;
+}
+
+//0xcd fx_move_view
+int fx_move_view (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_move_view (%2d): Speed: %d\n", fx->Opcode, fx->Parameter1 );
+ Map *map = core->GetGame()->GetCurrentArea();
+ if (map) {
+ core->timer->SetMoveViewPort( fx->PosX, fx->PosY, fx->Parameter1, true);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xce fx_embalm
+int fx_embalm (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_embalm (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ if (STATE_GET (STATE_EMBALM) ) //embalm is non cumulative
+ return FX_NOT_APPLIED;
+ STATE_SET( STATE_EMBALM );
+ if (!fx->Parameter1) {
+ if (fx->Parameter2) {
+ fx->Parameter1=fx->CasterLevel*2;
+ } else {
+ fx->Parameter1=core->Roll(1,6,1);
+ }
+ BASE_ADD( IE_HITPOINTS, fx->Parameter1 );
+ }
+ STAT_ADD( IE_MAXHITPOINTS, fx->Parameter1);
+ if (fx->Parameter2) {
+ STAT_ADD( IE_ARMORCLASS,2 );
+ } else {
+ STAT_ADD( IE_ARMORCLASS,1 );
+ }
+ return FX_APPLIED;
+}
+//0xcf fx_stop_all_action
+int fx_stop_all_action (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_stop_all_action (%2d): Par2: %d\n", fx->Opcode, fx->Parameter2 );
+ if (fx->Parameter2) {
+ core->GetGame()->TimeStop(NULL, 0xffffffff);
+ } else {
+ core->GetGame()->TimeStop(NULL, 0);
+ }
+ return FX_NOT_APPLIED;
+}
+
+//0xd0 fx_iron_fist
+//GemRB extension: lets you specify not hardcoded values
+int fx_iron_fist (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ ieDword p1,p2;
+
+ if (0) printf( "fx_iron_fist (%2d): Par1: %d Par2: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ switch (fx->Parameter2)
+ {
+ case 0: p1 = 3; p2 = 6; break;
+ default:
+ p1 = ieWord (fx->Parameter1&0xffff);
+ p2 = ieWord (fx->Parameter1>>16);
+ }
+ STAT_ADD(IE_FISTHIT, p1);
+ STAT_ADD(IE_FISTDAMAGE, p2);
+ return FX_APPLIED;
+}
+
+//0xd1 fx_hostile_image
+int fx_hostile_image (Scriptable* /*Owner*/, Actor* /*target*/, Effect* fx)
+{
+ if (0) printf( "fx_hostile_image (%2d): Par1: %d Par2: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ return FX_NOT_APPLIED;
+}
+
+//0xd2 fx_detect_evil
+static EffectRef fx_single_color_pulse_ref = { "Color:BriefRGB", -1 };
+
+int fx_detect_evil (Scriptable* Owner, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_detect_evil (%2d): Par1: %d Par2: %d\n", fx->Opcode, fx->Parameter1, fx->Parameter2 );
+ ieDword type = fx->Parameter2;
+ //default is alignment/evil/speed 30/range 10
+ if (!type) type = 0x08031e0a;
+ int speed = (type&0xff00)>>8;
+ if (!speed) speed=30;
+ if (!(core->GetGame()->GameTime%speed)) {
+ ieDword color = fx->Parameter1;
+ //default is magenta (rgba)
+ if (!color) color = 0xff00ff00;
+ Effect *newfx = EffectQueue::CreateEffect(fx_single_color_pulse_ref, color, speed<<16, FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES);
+ newfx->Target=FX_TARGET_PRESET;
+ EffectQueue *fxqueue = new EffectQueue();
+ fxqueue->SetOwner(Owner);
+ fxqueue->AddEffect(newfx);
+ delete newfx;
+
+ //don't detect self? if yes, then use NULL as last parameter
+ fxqueue->AffectAllInRange(target->GetCurrentArea(), target->Pos, (type&0xff000000)>>24, (type&0xff0000)>>16, (type&0xff)*10, target);
+ delete fxqueue;
+ }
+ return FX_APPLIED;
+}
+
+//0xd3 fx_jumble_curse
+int fx_jumble_curse (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf( "fx_jumble_curse (%2d)\n", fx->Opcode );
+
+ if (STATE_GET( STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+ Game *game = core->GetGame();
+ //do a hiccup every 75th refresh
+ if (fx->Parameter3/75!=fx->Parameter4/75) {
+ //hiccups
+ //PST has this hardcoded deep in the engine
+ //gemrb lets you specify the strref in P#1
+ ieStrRef tmp = fx->Parameter1;
+ if (!tmp) tmp = 46633;
+ char *tmpstr = core->GetString(tmp, IE_STR_SPEECH|IE_STR_SOUND);
+ target->DisplayHeadText(tmpstr);
+ //tmpstr shouldn't be freed, it is taken care by Actor
+ target->GetHit();
+ }
+ fx->Parameter4=fx->Parameter3;
+ fx->Parameter3=game->GameTime;
+ STAT_SET( IE_DEADMAGIC, 1);
+ STAT_SET( IE_SPELLFAILUREMAGE, 100);
+ STAT_SET( IE_SPELLFAILUREPRIEST, 100);
+ STAT_SET( IE_SPELLFAILUREINNATE, 100);
+ return FX_APPLIED;
+}
+
+//0xd4 fx_speak_with_dead
+//This opcode is directly employed by the speak with dead projectile in the original engine
+//In GemRB it is used in a custom spell
+int fx_speak_with_dead (Scriptable* /*Owner*/, Actor* target, Effect* fx)
+{
+ if (0) printf("fx_speak_with_dead (%2d)\n", fx->Opcode );
+ if (!STATE_GET( STATE_DEAD) ) {
+ return FX_NOT_APPLIED;
+ }
+
+ //TODO: reverse engineer the original opcode
+ return FX_NOT_APPLIED;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x115A670, "Effect opcodes for the torment branch of the games")
+PLUGIN_INITIALIZER(RegisterTormentOpcodes)
+END_PLUGIN()
diff --git a/gemrb/plugins/SDLAudio/CMakeLists.txt b/gemrb/plugins/SDLAudio/CMakeLists.txt
new file mode 100644
index 0000000..933a452
--- /dev/null
+++ b/gemrb/plugins/SDLAudio/CMakeLists.txt
@@ -0,0 +1,6 @@
+IF (SDLMIXER_LIBRARY)
+ INCLUDE_DIRECTORIES( ${SDL_INCLUDE_DIR} ${SDLMIXER_INCLUDE_DIR})
+
+ ADD_GEMRB_PLUGIN (SDLAudio SDLAudio.cpp )
+ TARGET_LINK_LIBRARIES( SDLAudio ${SDL_LIBRARY} ${SDLMIXER_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} )
+ENDIF (SDLMIXER_LIBRARY)
diff --git a/gemrb/plugins/SDLAudio/Makefile.am b/gemrb/plugins/SDLAudio/Makefile.am
new file mode 100644
index 0000000..d6851bc
--- /dev/null
+++ b/gemrb/plugins/SDLAudio/Makefile.am
@@ -0,0 +1,5 @@
+plugin_LTLIBRARIES = SDLAudio.la
+INCLUDES = $(SDL_CFLAGS)
+SDLAudio_la_LDFLAGS = -module -avoid-version -shared
+SDLAudio_la_LIBADD = @SDL_LIBS@
+SDLAudio_la_SOURCES = SDLAudio.cpp SDLAudio.h
diff --git a/gemrb/plugins/SDLAudio/SDLAudio.cpp b/gemrb/plugins/SDLAudio/SDLAudio.cpp
new file mode 100644
index 0000000..d307552
--- /dev/null
+++ b/gemrb/plugins/SDLAudio/SDLAudio.cpp
@@ -0,0 +1,388 @@
+/* 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 "SDLAudio.h"
+
+#include "win32def.h"
+
+#include "AmbientMgr.h"
+#include "GameData.h"
+#include "Interface.h" // GetMusicMgr()
+#include "MusicMgr.h"
+#include "SoundMgr.h"
+
+#include <SDL.h>
+#include <SDL_mixer.h>
+
+SDLAudio::SDLAudio(void)
+{
+ XPos = 0;
+ YPos = 0;
+ ambim = new AmbientMgr();
+ MusicPlaying = false;
+ OurMutex = NULL;
+}
+
+SDLAudio::~SDLAudio(void)
+{
+ // TODO
+ delete ambim;
+ Mix_HookMusic(NULL, NULL);
+ FreeBuffers();
+ SDL_DestroyMutex(OurMutex);
+ Mix_ChannelFinished(NULL);
+}
+
+// no user data for Mix_ChannelFinished :(
+SDLAudio *g_sdlaudio = NULL;
+
+bool SDLAudio::Init(void)
+{
+ // TODO: we assume SDLVideo already got loaded
+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
+ return false;
+ }
+ OurMutex = SDL_CreateMutex();
+ if (Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 8192) < 0) {
+ return false;
+ }
+ Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
+
+ channel_data.resize(Mix_AllocateChannels(-1));
+ for (unsigned int i = 0; i < channel_data.size(); i++) {
+ channel_data[i] = NULL;
+ }
+
+ g_sdlaudio = this;
+ Mix_ReserveChannels(1); // for speech
+ Mix_ChannelFinished(channel_done_callback);
+ return true;
+}
+
+void SDLAudio::music_callback(void *udata, unsigned short *stream, int len) {
+ SDLAudio *driver = (SDLAudio *)udata;
+ SDL_mutexP(driver->OurMutex);
+
+ do {
+
+ // TODO: conversion? mutexes? sanity checks? :)
+ int num_samples = len / 2;
+ int cnt = driver->MusicReader->read_samples(( short* ) stream, num_samples);
+
+ // Done?
+ if (cnt == num_samples)
+ break;
+
+ // TODO: this shouldn't be in the callback (see also the openal thread)
+ printMessage("SDLAudio", "Playing Next Music\n", WHITE );
+ core->GetMusicMgr()->PlayNext();
+
+ stream = stream + cnt;
+ len = len - (cnt * 2);
+
+ if (!driver->MusicPlaying) {
+ printMessage( "SDLAudio", "No Other Music to play\n", WHITE );
+ memset(stream, 0, len);
+ Mix_HookMusic(NULL, NULL);
+ break;
+ }
+
+ } while(true);
+
+ SDL_mutexV(driver->OurMutex);
+}
+
+void SDLAudio::channel_done_callback(int channel) {
+ SDL_mutexP(g_sdlaudio->OurMutex);
+ assert(g_sdlaudio);
+ assert((unsigned int)channel < g_sdlaudio->channel_data.size());
+ assert(g_sdlaudio->channel_data[channel]);
+ free(g_sdlaudio->channel_data[channel]);
+ g_sdlaudio->channel_data[channel] = NULL;
+ SDL_mutexV(g_sdlaudio->OurMutex);
+}
+
+Holder<SoundHandle> SDLAudio::Play(const char* ResRef, int XPos, int YPos, unsigned int flags, unsigned int *length)
+{
+ // TODO: some panning
+ (void)XPos;
+ (void)YPos;
+
+ // TODO: flags
+ (void)flags;
+
+ if (!ResRef) {
+ if (flags & GEM_SND_SPEECH) {
+ Mix_HaltChannel(0);
+ }
+ return Holder<SoundHandle>();
+ }
+
+ // TODO: move this loading code somewhere central
+ ResourceHolder<SoundMgr> acm(ResRef);
+ if (!acm) {
+ printf("failed acm load\n");
+ return Holder<SoundHandle>();
+ }
+ int cnt = acm->get_length();
+ int riff_chans = acm->get_channels();
+ int samplerate = acm->get_samplerate();
+ // Use 16-bit word for memory allocation because read_samples takes a 16 bit alignment
+ short * memory = (short *) malloc(cnt*2);
+ //multiply always with 2 because it is in 16 bits
+ int cnt1 = acm->read_samples( memory, cnt ) * 2;
+ //Sound Length in milliseconds
+ unsigned int time_length = ((cnt / riff_chans) * 1000) / samplerate;
+
+ if (length) {
+ *length = time_length;
+ }
+
+ // convert our buffer, if necessary
+ SDL_AudioCVT cvt;
+ SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, riff_chans, samplerate,
+ audio_format, audio_channels, audio_rate);
+ cvt.buf = (Uint8*)malloc(cnt1*cvt.len_mult);
+ memcpy(cvt.buf, (char*)memory, cnt1);
+ cvt.len = cnt1;
+ SDL_ConvertAudio(&cvt);
+
+ // free old buffer
+ free(memory);
+
+ // make SDL_mixer chunk
+ Mix_Chunk *chunk = Mix_QuickLoad_RAW(cvt.buf, cvt.len*cvt.len_ratio);
+ if (!chunk) {
+ printf("error loading chunk\n");
+ return Holder<SoundHandle>();
+ }
+
+ // play
+ int channel = -1;
+ if (flags & GEM_SND_SPEECH) {
+ channel = 0;
+ }
+ SDL_mutexP(OurMutex);
+ channel = Mix_PlayChannel(channel, chunk, 0);
+ if (channel < 0) {
+ SDL_mutexV(OurMutex);
+ printf("error playing channel\n");
+ return Holder<SoundHandle>();
+ }
+
+ assert((unsigned int)channel < channel_data.size());
+ channel_data[channel] = cvt.buf;
+ SDL_mutexV(OurMutex);
+
+ // TODO
+ return Holder<SoundHandle>();
+}
+
+int SDLAudio::CreateStream(Holder<SoundMgr> newMusic)
+{
+ printf("SDLAudio setting new music\n");
+ MusicReader = newMusic;
+
+ // TODO
+ return 0;
+}
+
+bool SDLAudio::Stop()
+{
+ // TODO
+ MusicPlaying = false;
+ Mix_HookMusic(NULL, NULL);
+ return true;
+}
+
+bool SDLAudio::Play()
+{
+ MusicPlaying = true;
+ Mix_HookMusic((void (*)(void*, Uint8*, int))music_callback, this);
+ // TODO
+ return true;
+}
+
+void SDLAudio::ResetMusics()
+{
+ // TODO
+ MusicPlaying = false;
+ Mix_HookMusic(NULL, NULL);
+}
+
+bool SDLAudio::CanPlay()
+{
+ return true;
+}
+
+bool SDLAudio::IsSpeaking()
+{
+ return Mix_Playing(0);
+}
+
+void SDLAudio::UpdateListenerPos(int x, int y)
+{
+ // TODO
+ XPos = x;
+ YPos = y;
+}
+
+void SDLAudio::GetListenerPos(int& x, int& y)
+{
+ // TODO
+ x = XPos;
+ y = YPos;
+}
+
+void SDLAudio::buffer_callback(void *udata, char *stream, int len) {
+ SDLAudio *driver = (SDLAudio *)udata;
+ SDL_mutexP(driver->OurMutex);
+ unsigned int remaining = len;
+ while (remaining && driver->buffers.size() > 0) {
+ unsigned int avail = driver->buffers[0].size - driver->curr_buffer_offset;
+ if (avail > remaining) {
+ // more data available in this buffer than we need
+ avail = remaining;
+ memcpy(stream, driver->buffers[0].buf + driver->curr_buffer_offset, avail);
+ driver->curr_buffer_offset += avail;
+ } else {
+ // exhausted this buffer, move to the next one
+ memcpy(stream, driver->buffers[0].buf + driver->curr_buffer_offset, avail);
+ driver->curr_buffer_offset = 0;
+ free(driver->buffers[0].buf);
+ // TODO: inefficient
+ driver->buffers.erase(driver->buffers.begin());
+ }
+ remaining -= avail;
+ stream = stream + avail;
+ }
+ if (remaining > 0) {
+ // underrun (out of buffers)
+ memset(stream, 0, remaining);
+ }
+ SDL_mutexV(driver->OurMutex);
+}
+
+int SDLAudio::SetupNewStream(ieWord x, ieWord y, ieWord z,
+ ieWord gain, bool point, bool Ambient)
+{
+ if (Ambient) {
+ // TODO: ambient sounds
+ return -1;
+ }
+
+ // TODO: maybe don't ignore these
+ (void)x;
+ (void)y;
+ (void)z;
+ (void)gain;
+ (void)point;
+
+ printf("SDLAudio allocating stream\n");
+
+ // TODO: buggy
+ MusicPlaying = false;
+ curr_buffer_offset = 0;
+ Mix_HookMusic((void (*)(void*, Uint8*, int))buffer_callback, this);
+ return 0;
+}
+
+int SDLAudio::QueueAmbient(int, const char*)
+{
+ // TODO: ambient sounds
+ return -1;
+}
+
+bool SDLAudio::ReleaseStream(int stream, bool HardStop)
+{
+ if (stream != 0) {
+ return false;
+ }
+
+ printf("SDLAudio releasing stream\n");
+
+ (void)HardStop;
+
+ assert(!MusicPlaying);
+
+ Mix_HookMusic(NULL, NULL);
+ FreeBuffers();
+
+ return true;
+}
+
+void SDLAudio::FreeBuffers()
+{
+ SDL_mutexP(OurMutex);
+ for (unsigned int i = 0; i < buffers.size(); i++) {
+ free(buffers[i].buf);
+ }
+ buffers.clear();
+ SDL_mutexV(OurMutex);
+}
+
+void SDLAudio::SetAmbientStreamVolume(int, int)
+{
+ // TODO: ambient sounds
+}
+
+void SDLAudio::QueueBuffer(int stream, unsigned short bits,
+ int channels, short* memory, int size, int samplerate)
+{
+ if (stream != 0) {
+ return;
+ }
+
+ assert(!MusicPlaying);
+
+ BufferedData d;
+
+ // convert our buffer, if necessary
+ if (bits != 16 || channels != audio_channels || samplerate != audio_rate) {
+ SDL_AudioCVT cvt;
+ if (SDL_BuildAudioCVT(&cvt, (bits == 8 ? AUDIO_S8 : AUDIO_S16SYS), channels, samplerate,
+ audio_format, audio_channels, audio_rate) == 0) {
+ printMessage("SDLAudio", "Couldn't convert video stream!\n", RED );
+ printf("trying to convert %d bits, %d channels, %d rate\n", bits, channels, samplerate);
+ return;
+ }
+ cvt.buf = (Uint8*)malloc(size*cvt.len_mult);
+ memcpy(cvt.buf, memory, size);
+ cvt.len = size;
+ SDL_ConvertAudio(&cvt);
+
+ d.size = cvt.len*cvt.len_ratio;
+ d.buf = (char *)cvt.buf;
+ } else {
+ d.size = size;
+ d.buf = (char *)malloc(d.size);
+ memcpy(d.buf, memory, d.size);
+ }
+
+ SDL_mutexP(OurMutex);
+ buffers.push_back(d);
+ SDL_mutexV(OurMutex);
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x52C524E, "SDL Audio Driver")
+PLUGIN_DRIVER(SDLAudio, "SDLAudio")
+END_PLUGIN()
diff --git a/gemrb/plugins/SDLAudio/SDLAudio.h b/gemrb/plugins/SDLAudio/SDLAudio.h
new file mode 100644
index 0000000..4bdbbaf
--- /dev/null
+++ b/gemrb/plugins/SDLAudio/SDLAudio.h
@@ -0,0 +1,81 @@
+/* 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 SDLAUDIO_H
+#define SDLAUDIO_H
+
+#include "Audio.h"
+
+#include <vector>
+
+struct BufferedData {
+ char *buf;
+ unsigned int size;
+};
+
+class SDLAudio : public Audio {
+public:
+ SDLAudio(void);
+ ~SDLAudio(void);
+ bool Init(void);
+ Holder<SoundHandle> Play(const char* ResRef, int XPos, int YPos, unsigned int flags = 0, unsigned int *length = 0);
+ int CreateStream(Holder<SoundMgr>);
+ bool Play();
+ bool Stop();
+ bool Pause() { return true; } /*not implemented*/
+ bool Resume() { return true; } /*not implemented*/
+ bool CanPlay();
+ bool IsSpeaking();
+ void ResetMusics();
+ void UpdateListenerPos(int XPos, int YPos);
+ void GetListenerPos(int& XPos, int& YPos);
+ void UpdateVolume(unsigned int) {}
+
+ int SetupNewStream(ieWord x, ieWord y, ieWord z, ieWord gain, bool point, bool Ambient);
+ int QueueAmbient(int stream, const char* sound);
+ bool ReleaseStream(int stream, bool hardstop);
+ void SetAmbientStreamVolume(int stream, int gain);
+ void QueueBuffer(int stream, unsigned short bits, int channels,
+ short* memory, int size, int samplerate);
+
+private:
+ void FreeBuffers();
+
+ static void music_callback(void *udata, unsigned short *stream, int len);
+ static void buffer_callback(void *udata, char *stream, int len);
+ static void channel_done_callback(int channel);
+
+ std::vector<void *> channel_data;
+
+ int XPos, YPos;
+ Holder<SoundMgr> MusicReader;
+
+ bool MusicPlaying;
+ unsigned int curr_buffer_offset;
+ std::vector<BufferedData> buffers;
+
+ int audio_rate;
+ unsigned short audio_format;
+ int audio_channels;
+
+ struct SDL_mutex* OurMutex;
+};
+
+#endif
diff --git a/gemrb/plugins/SDLVideo/CMakeLists.txt b/gemrb/plugins/SDLVideo/CMakeLists.txt
new file mode 100644
index 0000000..5bff68b
--- /dev/null
+++ b/gemrb/plugins/SDLVideo/CMakeLists.txt
@@ -0,0 +1,4 @@
+INCLUDE_DIRECTORIES( ${SDL_INCLUDE_DIR} )
+
+ADD_GEMRB_PLUGIN (SDLVideo SDLVideo.cpp)
+TARGET_LINK_LIBRARIES( SDLVideo ${SDL_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} )
diff --git a/gemrb/plugins/SDLVideo/Makefile.am b/gemrb/plugins/SDLVideo/Makefile.am
new file mode 100644
index 0000000..0f5cde2
--- /dev/null
+++ b/gemrb/plugins/SDLVideo/Makefile.am
@@ -0,0 +1,5 @@
+plugin_LTLIBRARIES = SDLVideo.la
+INCLUDES = $(SDL_CFLAGS)
+SDLVideo_la_LDFLAGS = -module -avoid-version -shared
+SDLVideo_la_LIBADD = @SDL_LIBS@
+SDLVideo_la_SOURCES = SDLVideo.cpp SDLVideo.h SDLVideoDriver.inl TileRenderer.inl
diff --git a/gemrb/plugins/SDLVideo/SDLVideo.cpp b/gemrb/plugins/SDLVideo/SDLVideo.cpp
new file mode 100644
index 0000000..be87bcf
--- /dev/null
+++ b/gemrb/plugins/SDLVideo/SDLVideo.cpp
@@ -0,0 +1,2549 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2003-2006 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 "SDLVideo.h"
+
+#include "TileRenderer.inl"
+
+#include "AnimationFactory.h"
+#include "Audio.h"
+#include "Game.h" // for GetGlobalTint
+#include "GameData.h"
+#include "Interface.h"
+#include "Palette.h"
+#include "SpriteCover.h"
+#include "GUI/Console.h"
+
+#include <cmath>
+#include <cassert>
+#include <cstdio>
+
+SDLVideoDriver::SDLVideoDriver(void)
+{
+ CursorIndex = 0;
+ Cursor[0] = NULL;
+ Cursor[1] = NULL;
+ Cursor[2] = NULL;
+ CursorPos.x = 0;
+ CursorPos.y = 0;
+ DisableMouse = 0;
+ xCorr = 0;
+ yCorr = 0;
+ lastTime = 0;
+ GetTime( lastMouseTime );
+ backBuf=NULL;
+ extra=NULL;
+ subtitlestrref = 0;
+ subtitletext = NULL;
+ overlay = NULL;
+}
+
+SDLVideoDriver::~SDLVideoDriver(void)
+{
+ core->FreeString(subtitletext); //may be NULL
+
+ if(backBuf) SDL_FreeSurface( backBuf );
+ if(extra) SDL_FreeSurface( extra );
+ if (overlay) SDL_FreeYUVOverlay(overlay);
+
+ SDL_Quit();
+
+ // This sprite needs to have been freed earlier, because
+ // all AnimationFactories and Sprites have already been
+ // destructed before the video driver is freed.
+ assert(Cursor[2] == NULL);
+}
+
+int SDLVideoDriver::Init(void)
+{
+ //printf("[SDLVideoDriver]: Init...");
+ if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) {
+ //printf("[ERROR]\n");
+ return GEM_ERROR;
+ }
+ //printf("[OK]\n");
+ SDL_EnableUNICODE( 1 );
+ SDL_EnableKeyRepeat( 500, 50 );
+ SDL_ShowCursor( SDL_DISABLE );
+ fadeColor.a=0; //fadePercent
+ fadeColor.r=0;
+ fadeColor.b=0;
+ fadeColor.g=0;
+ return GEM_OK;
+}
+
+int SDLVideoDriver::CreateDisplay(int w, int h, int b, bool fs)
+{
+ bpp=b;
+ fullscreen=fs;
+ printMessage( "SDLVideo", "Creating display\n", WHITE );
+ ieDword flags = SDL_SWSURFACE;
+ if (fullscreen) {
+ flags |= SDL_FULLSCREEN;
+ }
+ printMessage( "SDLVideo", "SDL_SetVideoMode...", WHITE );
+ disp = SDL_SetVideoMode( w, h, bpp, flags );
+ if (disp == NULL) {
+ printStatus( "ERROR", LIGHT_RED );
+ return GEM_ERROR;
+ }
+ printStatus( "OK", LIGHT_GREEN );
+ printMessage( "SDLVideo", "Checking for HardWare Acceleration...", WHITE );
+ const SDL_VideoInfo* vi = SDL_GetVideoInfo();
+ if (!vi) {
+ printStatus( "ERROR", LIGHT_RED );
+ }
+ printStatus( "OK", LIGHT_GREEN );
+ Viewport.x = Viewport.y = 0;
+ width = disp->w;
+ height = disp->h;
+ Viewport.w = width;
+ Viewport.h = height;
+ printMessage( "SDLVideo", "Creating Main Surface...", WHITE );
+ SDL_Surface* tmp = SDL_CreateRGBSurface( SDL_SWSURFACE, width, height,
+ bpp, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff );
+ printStatus( "OK", LIGHT_GREEN );
+ printMessage( "SDLVideo", "Creating Back Buffer...", WHITE );
+ backBuf = SDL_DisplayFormat( tmp );
+ printStatus( "OK", LIGHT_GREEN );
+ printMessage( "SDLVideo", "Creating Extra Buffer...", WHITE );
+ extra = SDL_DisplayFormat( tmp );
+ printStatus( "OK", LIGHT_GREEN );
+ SDL_LockSurface( extra );
+ long val = SDL_MapRGBA( extra->format, fadeColor.r, fadeColor.g, fadeColor.b, 0 );
+ SDL_FillRect( extra, NULL, val );
+ SDL_UnlockSurface( extra );
+ SDL_FreeSurface( tmp );
+ printMessage( "SDLVideo", "CreateDisplay...", WHITE );
+ printStatus( "OK", LIGHT_GREEN );
+ return GEM_OK;
+}
+
+void SDLVideoDriver::SetDisplayTitle(char* title, char* icon)
+{
+ SDL_WM_SetCaption( title, icon );
+}
+
+bool SDLVideoDriver::ToggleFullscreenMode(int set_reset)
+{
+ if (set_reset==-1) {
+ set_reset=!fullscreen;
+ }
+ if (fullscreen != set_reset) {
+ fullscreen=set_reset;
+ SDL_WM_ToggleFullScreen( disp );
+ //readjust mouse to original position
+ MoveMouse(CursorPos.x, CursorPos.y);
+ //synchronise internal variable
+ core->GetDictionary()->SetAt( "Full Screen", (ieDword) fullscreen );
+ return true;
+ }
+ return false;
+}
+
+inline int GetModState(int modstate)
+{
+ int value = 0;
+ if (modstate&KMOD_SHIFT) value |= GEM_MOD_SHIFT;
+ if (modstate&KMOD_CTRL) value |= GEM_MOD_CTRL;
+ if (modstate&KMOD_ALT) value |= GEM_MOD_ALT;
+ return value;
+}
+
+int SDLVideoDriver::SwapBuffers(void)
+{
+ int ret = GEM_OK;
+ unsigned long time;
+ GetTime( time );
+ if (( time - lastTime ) < 17) {
+ SDL_Delay( 17 - (time - lastTime) );
+ GetTime( time );
+ }
+ lastTime = time;
+
+ bool ConsolePopped = core->ConsolePopped;
+
+ if (ConsolePopped) {
+ core->DrawConsole();
+ }
+
+ ret = PollEvents();
+
+ SDL_BlitSurface( backBuf, NULL, disp, NULL );
+ if (fadeColor.a) {
+ SDL_SetAlpha( extra, SDL_SRCALPHA, fadeColor.a );
+ SDL_Rect src = {
+ 0, 0, Viewport.w, Viewport.h
+ };
+ SDL_Rect dst = {
+ xCorr, yCorr, 0, 0
+ };
+ SDL_BlitSurface( extra, &src, disp, &dst );
+ }
+
+ if (Cursor[CursorIndex] && !(DisableMouse&MOUSE_DISABLED)) {
+ SDL_Surface* temp = backBuf;
+ backBuf = disp; // FIXME: UGLY HACK!
+ if (DisableMouse&MOUSE_GRAYED) {
+ //used for greyscale blitting, fadeColor is unused
+ BlitGameSprite(Cursor[CursorIndex], CursorPos.x, CursorPos.y, BLIT_GREY, fadeColor, NULL, NULL, NULL, true);
+ } else {
+ BlitSprite(Cursor[CursorIndex], CursorPos.x, CursorPos.y, true);
+ }
+ backBuf = temp;
+ }
+
+ //handle tooltips
+ unsigned int delay = core->TooltipDelay;
+ // The multiplication by 10 is there since the last, disabling slider position is the eleventh
+ if (!ConsolePopped && (delay<TOOLTIP_DELAY_FACTOR*10) ) {
+ GetTime( time );
+ /** Display tooltip if mouse is idle */
+ if (( time - lastMouseTime ) > delay) {
+ if (Evnt)
+ Evnt->MouseIdle( time - lastMouseTime );
+ }
+
+ /** This causes the tooltip to be rendered directly to display */
+ SDL_Surface* tmp = backBuf;
+ backBuf = disp; // FIXME: UGLY HACK!
+ core->DrawTooltip();
+ backBuf = tmp;
+ }
+
+ SDL_Flip( disp );
+
+ return ret;
+}
+
+int SDLVideoDriver::PollEvents() {
+ static bool lastevent = false; /* last event was a mousedown */
+ static unsigned long lastmousetime = 0;
+
+ int ret = GEM_OK;
+
+ bool ConsolePopped = core->ConsolePopped;
+ unsigned long time = lastTime;
+ unsigned char key = 0;
+ int x,y;
+
+ while ( SDL_PollEvent(&event) ) {
+ int modstate = GetModState(event.key.keysym.mod);
+
+ /* Loop until there are no events left on the queue */
+ switch (event.type) {
+ /* Process the appropriate event type */
+ case SDL_QUIT:
+ /* Handle a QUIT event */
+ ret = GEM_ERROR;
+ break;
+
+ case SDL_KEYUP:
+ switch(event.key.keysym.sym) {
+ case SDLK_LALT:
+ case SDLK_RALT:
+ key = GEM_ALT;
+ break;
+ case SDLK_SCROLLOCK:
+ key = GEM_GRAB;
+ break;
+ case SDLK_f:
+ if (modstate & GEM_MOD_CTRL) {
+ ToggleFullscreenMode(-1);
+ }
+ break;
+ default:
+ if (event.key.keysym.sym<256) {
+ key=(unsigned char) event.key.keysym.sym;
+ }
+ break;
+ }
+ if (!ConsolePopped && Evnt && ( key != 0 ))
+ Evnt->KeyRelease( key, modstate );
+ break;
+
+ case SDL_KEYDOWN:
+ if ((event.key.keysym.sym == SDLK_SPACE) && modstate & GEM_MOD_CTRL) {
+ core->PopupConsole();
+ break;
+ }
+ key = (unsigned char) event.key.keysym.unicode;
+ if (key < 32 || key == 127) {
+ switch (event.key.keysym.sym) {
+ case SDLK_ESCAPE:
+ key = GEM_ESCAPE;
+ break;
+ case SDLK_END:
+ key = GEM_END;
+ break;
+ case SDLK_HOME:
+ key = GEM_HOME;
+ break;
+ case SDLK_UP:
+ key = GEM_UP;
+ break;
+ case SDLK_DOWN:
+ key = GEM_DOWN;
+ break;
+ case SDLK_LEFT:
+ key = GEM_LEFT;
+ break;
+ case SDLK_RIGHT:
+ key = GEM_RIGHT;
+ break;
+ case SDLK_BACKSPACE:
+ key = GEM_BACKSP;
+ break;
+ case SDLK_DELETE:
+ key = GEM_DELETE;
+ break;
+ case SDLK_RETURN:
+ case SDLK_KP_ENTER:
+ key = GEM_RETURN;
+ break;
+ case SDLK_LALT:
+ case SDLK_RALT:
+ key = GEM_ALT;
+ break;
+ case SDLK_TAB:
+ key = GEM_TAB;
+ break;
+ case SDLK_PAGEUP:
+ key = GEM_PGUP;
+ break;
+ case SDLK_PAGEDOWN:
+ key = GEM_PGDOWN;
+ break;
+ case SDLK_SCROLLOCK:
+ key = GEM_GRAB;
+ break;
+ default:
+ break;
+ }
+ if (ConsolePopped)
+ core->console->OnSpecialKeyPress( key );
+ else if (Evnt)
+ Evnt->OnSpecialKeyPress( key );
+ } else if (( key != 0 )) {
+ if (ConsolePopped)
+ core->console->OnKeyPress( key, modstate);
+ else if (Evnt)
+ Evnt->KeyPress( key, modstate);
+ }
+ break;
+ case SDL_MOUSEMOTION:
+ lastevent = false;
+ MouseMovement(event.motion.x, event.motion.y);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ if ((DisableMouse & MOUSE_DISABLED) || !Evnt)
+ break;
+ lastevent = true;
+ lastmousetime=Evnt->GetRKDelay();
+ if (lastmousetime != (unsigned long) ~0) {
+ lastmousetime += lastmousetime+time;
+ }
+ if (CursorIndex != 2)
+ CursorIndex = 1;
+ CursorPos.x = event.button.x; // - mouseAdjustX[CursorIndex];
+ CursorPos.y = event.button.y; // - mouseAdjustY[CursorIndex];
+ if (!ConsolePopped)
+ Evnt->MouseDown( event.button.x, event.button.y, 1 << ( event.button.button - 1 ), GetModState(SDL_GetModState()) );
+
+ break;
+
+ case SDL_MOUSEBUTTONUP:
+ lastevent = false;
+ if ((DisableMouse & MOUSE_DISABLED) || !Evnt)
+ break;
+ if (CursorIndex != 2)
+ CursorIndex = 0;
+ CursorPos.x = event.button.x;
+ CursorPos.y = event.button.y;
+ if (!ConsolePopped)
+ Evnt->MouseUp( event.button.x, event.button.y, 1 << ( event.button.button - 1 ), GetModState(SDL_GetModState()) );
+
+ break;
+ case SDL_ACTIVEEVENT:
+ if (ConsolePopped) {
+ break;
+ }
+
+ if (event.active.state == SDL_APPMOUSEFOCUS) {
+ if (Evnt && !event.active.gain)
+ Evnt->OnSpecialKeyPress( GEM_MOUSEOUT );
+ }
+ break;
+
+ }
+ }
+
+ if (Evnt && !DisableMouse && lastevent && time>lastmousetime && SDL_GetMouseState(&x,&y)==SDL_BUTTON(1)) {
+ lastmousetime=time+Evnt->GetRKDelay();
+ if (!ConsolePopped)
+ Evnt->MouseUp( x, y, 1 << ( 0 ), GetModState(SDL_GetModState()) );
+ }
+
+ return ret;
+}
+
+bool SDLVideoDriver::ToggleGrabInput()
+{
+ if (SDL_GRAB_OFF == SDL_WM_GrabInput( SDL_GRAB_QUERY )) {
+ SDL_WM_GrabInput( SDL_GRAB_ON );
+ return true;
+ }
+ else {
+ SDL_WM_GrabInput( SDL_GRAB_OFF );
+ MoveMouse(CursorPos.x, CursorPos.y);
+ return false;
+ }
+}
+
+void SDLVideoDriver::InitSpriteCover(SpriteCover* sc, int flags)
+{
+ int i;
+ sc->flags = flags;
+ sc->pixels = new unsigned char[sc->Width * sc->Height];
+ for (i = 0; i < sc->Width*sc->Height; ++i)
+ sc->pixels[i] = 0;
+
+}
+
+void SDLVideoDriver::DestroySpriteCover(SpriteCover* sc)
+{
+ delete[] sc->pixels;
+ sc->pixels = 0;
+}
+
+
+// flags: 0 - never dither (full cover)
+// 1 - dither if polygon wants it
+// 2 - always dither
+void SDLVideoDriver::AddPolygonToSpriteCover(SpriteCover* sc,
+ Wall_Polygon* poly)
+{
+
+ // possible TODO: change the cover to use a set of intervals per line?
+ // advantages: faster
+ // disadvantages: makes the blitter much more complex
+
+ int xoff = sc->worldx - sc->XPos;
+ int yoff = sc->worldy - sc->YPos;
+
+ std::list<Trapezoid>::iterator iter;
+ for (iter = poly->trapezoids.begin(); iter != poly->trapezoids.end();
+ ++iter)
+ {
+ int y_top = iter->y1 - yoff; // inclusive
+ int y_bot = iter->y2 - yoff; // exclusive
+
+ if (y_top < 0) y_top = 0;
+ if ( y_bot > sc->Height) y_bot = sc->Height;
+ if (y_top >= y_bot) continue; // clipped
+
+ int ledge = iter->left_edge;
+ int redge = iter->right_edge;
+ Point& a = poly->points[ledge];
+ Point& b = poly->points[(ledge+1)%(poly->count)];
+ Point& c = poly->points[redge];
+ Point& d = poly->points[(redge+1)%(poly->count)];
+
+ unsigned char* line = sc->pixels + (y_top)*sc->Width;
+ for (int sy = y_top; sy < y_bot; ++sy) {
+ int py = sy + yoff;
+
+ // TODO: maybe use a 'real' line drawing algorithm to
+ // compute these values faster.
+
+ int lt = (b.x * (py - a.y) + a.x * (b.y - py))/(b.y - a.y);
+ int rt = (d.x * (py - c.y) + c.x * (d.y - py))/(d.y - c.y) + 1;
+
+ lt -= xoff;
+ rt -= xoff;
+
+ if (lt < 0) lt = 0;
+ if (rt > sc->Width) rt = sc->Width;
+ if (lt >= rt) { line += sc->Width; continue; } // clipped
+ int dither;
+
+ if (sc->flags == 1) {
+ dither = poly->wall_flag & WF_DITHER;
+ } else {
+ dither = sc->flags;
+ }
+ if (dither) {
+ unsigned char* pix = line + lt;
+ unsigned char* end = line + rt;
+
+ if ((lt + xoff + sy + yoff) % 2) pix++; // CHECKME: aliasing?
+ for (; pix < end; pix += 2)
+ *pix = 1;
+ } else {
+ // we hope memset is faster
+ // condition: lt < rt is true
+ memset (line+lt, 1, rt-lt);
+ }
+ line += sc->Width;
+ }
+ }
+}
+
+
+Sprite2D* SDLVideoDriver::CreateSprite(int w, int h, int bpp, ieDword rMask,
+ ieDword gMask, ieDword bMask, ieDword aMask, void* pixels, bool cK, int index)
+{
+ Sprite2D* spr = new Sprite2D();
+ void* p = SDL_CreateRGBSurfaceFrom( pixels, w, h, bpp, w*( bpp / 8 ),
+ rMask, gMask, bMask, aMask );
+ spr->vptr = p;
+ spr->pixels = pixels;
+ if (cK) {
+ SDL_SetColorKey( ( SDL_Surface * ) p, SDL_SRCCOLORKEY | SDL_RLEACCEL,
+ index );
+ }
+ spr->Width = w;
+ spr->Height = h;
+ spr->Bpp = bpp;
+ return spr;
+}
+
+Sprite2D* SDLVideoDriver::CreateSprite8(int w, int h, int bpp, void* pixels,
+ void* palette, bool cK, int index)
+{
+ Sprite2D* spr = new Sprite2D();
+ void* p = SDL_CreateRGBSurfaceFrom( pixels, w, h, 8, w, 0, 0, 0, 0 );
+ int colorcount;
+ if (bpp == 8) {
+ colorcount = 256;
+ } else {
+ colorcount = 16;
+ }
+ SDL_SetPalette( ( SDL_Surface * ) p, SDL_LOGPAL, ( SDL_Color * ) palette, 0, colorcount );
+ spr->vptr = p;
+ spr->pixels = pixels;
+ if (cK) {
+ SDL_SetColorKey( ( SDL_Surface * ) p, SDL_SRCCOLORKEY, index );
+ }
+ spr->Width = w;
+ spr->Height = h;
+ spr->Bpp = bpp;
+ return spr;
+}
+
+Sprite2D* SDLVideoDriver::CreateSpriteBAM8(int w, int h, bool rle,
+ const unsigned char* pixeldata,
+ AnimationFactory* datasrc,
+ Palette* palette, int transindex)
+{
+ Sprite2D* spr = new Sprite2D();
+ spr->BAM = true;
+ Sprite2D_BAM_Internal* data = new Sprite2D_BAM_Internal;
+ spr->vptr = data;
+
+ palette->IncRef();
+ data->pal = palette;
+ data->transindex = transindex;
+ data->flip_hor = false;
+ data->flip_ver = false;
+ data->RLE = rle;
+ data->source = datasrc;
+ datasrc->IncDataRefCount();
+
+ spr->pixels = (const void*)pixeldata;
+ spr->Width = w;
+ spr->Height = h;
+ spr->Bpp = 8; // FIXME!!!!
+
+ return spr;
+}
+
+void SDLVideoDriver::FreeSprite(Sprite2D*& spr)
+{
+ if(!spr)
+ return;
+ assert(spr->RefCount > 0);
+ if (--spr->RefCount > 0) {
+ spr = NULL;
+ return;
+ }
+
+ if (spr->BAM) {
+ if (spr->vptr) {
+ Sprite2D_BAM_Internal* tmp = (Sprite2D_BAM_Internal*)spr->vptr;
+ tmp->source->DecDataRefCount();
+ delete tmp;
+ // this delete also calls Release() on the used palette
+ }
+ } else {
+ if (spr->vptr) {
+ SDL_FreeSurface( ( SDL_Surface * ) spr->vptr );
+ }
+ free( (void*)spr->pixels );
+ }
+ delete spr;
+ spr = NULL;
+}
+
+Sprite2D* SDLVideoDriver::DuplicateSprite(const Sprite2D* sprite)
+{
+ if (!sprite) return NULL;
+ Sprite2D* dest = 0;
+
+ if (!sprite->BAM) {
+ SDL_Surface* tmp = ( SDL_Surface* ) sprite->vptr;
+ unsigned char *newpixels = (unsigned char*) malloc( sprite->Width*sprite->Height );
+
+ SDL_LockSurface( tmp );
+ memcpy(newpixels, sprite->pixels, sprite->Width*sprite->Height);
+ dest = CreateSprite8(sprite->Width, sprite->Height, 8,
+ newpixels, tmp->format->palette->colors, true, 0);
+ SDL_UnlockSurface( tmp );
+ } else {
+ Sprite2D_BAM_Internal* data = (Sprite2D_BAM_Internal*) sprite->vptr;
+ const unsigned char * rledata;
+
+ rledata = (const unsigned char*) sprite->pixels;
+
+ dest = CreateSpriteBAM8(sprite->Width, sprite->Height, data->RLE,
+ rledata, data->source, data->pal,
+ data->transindex);
+ Sprite2D_BAM_Internal* destdata = (Sprite2D_BAM_Internal*)dest->vptr;
+ destdata->flip_ver = data->flip_ver;
+ destdata->flip_hor = data->flip_hor;
+ }
+
+ return dest;
+}
+
+
+void SDLVideoDriver::BlitSpriteRegion(const Sprite2D* spr, const Region& size, int x,
+ int y, bool anchor, const Region* clip)
+{
+ if (!spr->vptr) return;
+
+ if (!spr->BAM) {
+ //TODO: Add the destination surface and rect to the Blit Pipeline
+ SDL_Rect drect;
+ SDL_Rect t = {
+ size.x, size.y, size.w, size.h
+ };
+ Region c;
+ if (clip) {
+ c = *clip;
+ } else {
+ c.x = 0;
+ c.y = 0;
+ c.w = backBuf->w;
+ c.h = backBuf->h;
+ }
+ if (anchor) {
+ drect.x = x;
+ drect.y = y;
+ } else {
+ drect.x = x - Viewport.x;
+ drect.y = y - Viewport.y;
+ if (clip) {
+ c.x -= Viewport.x;
+ c.y -= Viewport.y;
+ }
+ }
+ if (clip) {
+ if (drect.x + size.w <= c.x)
+ return;
+ if (drect.x >= c.x + c.w)
+ return;
+
+ if (drect.y + size.h <= c.y)
+ return;
+ if (drect.y >= c.y + c.h)
+ return;
+
+ if (drect.x < c.x) {
+ t.x += c.x - drect.x;
+ t.w -= c.x - drect.x;
+ drect.x = c.x;
+ }
+ if (drect.x + t.w > c.x + c.w) {
+ t.w = c.x + c.w - drect.x;
+ }
+
+ if (drect.y < c.y) {
+ t.y += c.y - drect.y;
+ t.h -= c.y - drect.y;
+ drect.y = c.y;
+ }
+ if (drect.y + t.h > c.y + c.h) {
+ t.h = c.y + c.h - drect.y;
+ }
+ }
+ SDL_BlitSurface( ( SDL_Surface * ) spr->vptr, &t, backBuf, &drect );
+ } else {
+ Sprite2D_BAM_Internal* data = (Sprite2D_BAM_Internal*)spr->vptr;
+
+ const Uint8* rle = (const Uint8*)spr->pixels;
+ int tx, ty;
+ if (anchor) {
+ tx = x - spr->XPos;
+ ty = y - spr->YPos;
+ } else {
+ tx = x - spr->XPos - Viewport.x;
+ ty = y - spr->YPos - Viewport.y;
+ }
+ if (tx > backBuf->w) return;
+ if (tx+spr->Width <= 0) return;
+
+ SDL_LockSurface(backBuf);
+
+ int clipx, clipy, clipw, cliph;
+ if (clip) {
+ clipx = clip->x;
+ clipy = clip->y;
+ clipw = clip->w;
+ cliph = clip->h;
+ } else {
+ clipx = 0;
+ clipy = 0;
+ clipw = backBuf->w;
+ cliph = backBuf->h;
+ }
+
+ SDL_Rect cliprect;
+ SDL_GetClipRect(backBuf, &cliprect);
+ if (cliprect.x > clipx) {
+ clipw -= (cliprect.x - clipx);
+ clipx = cliprect.x;
+ }
+ if (cliprect.y > clipy) {
+ cliph -= (cliprect.y - clipy);
+ clipy = cliprect.y;
+ }
+ if (clipx+clipw > cliprect.x+cliprect.w) {
+ clipw = cliprect.x+cliprect.w-clipx;
+ }
+ if (clipy+cliph > cliprect.y+cliprect.h) {
+ cliph = cliprect.y+cliprect.h-clipy;
+ }
+
+ if (clipx < tx+size.x) {
+ clipw -= (tx+size.x - clipx);
+ clipx = tx+size.x;
+ }
+ if (clipy < ty+size.y) {
+ cliph -= (ty+size.y - clipy);
+ clipy = ty+size.y;
+ }
+ if (clipx+clipw > tx+size.x+size.w) {
+ clipw = tx+size.x+size.w-clipx;
+ }
+ if (clipy+cliph > ty+size.y+size.h) {
+ cliph = ty+size.y+size.h-clipy;
+ }
+
+#define ALREADYCLIPPED
+#define SPECIALPIXEL
+#define FLIP
+#define HFLIP_CONDITIONAL data->flip_hor
+#define VFLIP_CONDITIONAL data->flip_ver
+#define RLE data->RLE
+#define PAL data->pal
+#define SRCDATA rle
+#define USE_PALETTE
+#undef COVER
+#undef TINT
+
+ if (backBuf->format->BytesPerPixel == 4) {
+
+#undef BPP16
+ if (data->pal->alpha) {
+
+#define PALETTE_ALPHA
+#include "SDLVideoDriver.inl"
+
+ } else {
+
+#undef PALETTE_ALPHA
+#include "SDLVideoDriver.inl"
+
+ }
+
+ } else {
+
+#define BPP16
+ if (data->pal->alpha) {
+
+#define PALETTE_ALPHA
+#include "SDLVideoDriver.inl"
+
+ } else {
+
+#undef PALETTE_ALPHA
+#include "SDLVideoDriver.inl"
+
+ }
+
+ }
+
+#undef BPP16
+#undef ALREADYCLIPPED
+#undef SPECIALPIXEL
+#undef FLIP
+#undef HFLIP_CONDITIONAL
+#undef VFLIP_CONDITIONAL
+#undef RLE
+#undef PAL
+#undef SRCDATA
+#undef USE_PALETTE
+
+ SDL_UnlockSurface(backBuf);
+ }
+}
+
+void SDLVideoDriver::BlitTile(const Sprite2D* spr, const Sprite2D* mask, int x, int y, const Region* clip, bool trans)
+{
+ if (spr->BAM) {
+ printMessage( "SDLVideo", "Tile blit not supported for this sprite\n", LIGHT_RED );
+ return;
+ }
+
+ x -= Viewport.x;
+ y -= Viewport.y;
+
+ int clipx, clipy, clipw, cliph;
+ if (clip) {
+ clipx = clip->x;
+ clipy = clip->y;
+ clipw = clip->w;
+ cliph = clip->h;
+ } else {
+ clipx = 0;
+ clipy = 0;
+ clipw = backBuf->w;
+ cliph = backBuf->h;
+ }
+
+ int rx = 0,ry = 0;
+ int w = 64,h = 64;
+
+ if (x < clipx) {
+ rx += (clipx - x);
+ w -= (clipx - x);
+ }
+ if (y < clipy) {
+ ry += (clipy - y);
+ h -= (clipy - y);
+ }
+ if (x + w > clipx + clipw)
+ w -= (x + w - clipx - clipw);
+ if (y + h > clipy + cliph)
+ h -= (y + h - clipy - cliph);
+
+ const Uint8* data = (Uint8*) (( SDL_Surface * ) spr->vptr)->pixels;
+ const SDL_Color* pal = (( SDL_Surface * ) spr->vptr)->format->palette->colors;
+
+ const Uint8* mask_data = 0;
+ Uint8 ck = 0;
+ if (mask) {
+ mask_data = (Uint8*) (( SDL_Surface * ) mask->vptr)->pixels;
+ ck = (( SDL_Surface * ) mask->vptr)->format->colorkey;
+ }
+
+ bool tint = false;
+ Color tintcol = {0,0,0,0};
+
+ if (core->GetGame()) {
+ const Color* totint = core->GetGame()->GetGlobalTint();
+ if (totint) {
+ tintcol = *totint;
+ tint = true;
+ }
+ }
+
+#define DO_BLIT \
+ if (backBuf->format->BytesPerPixel == 4) \
+ BlitTile_internal<Uint32>(backBuf, x, y, rx, ry, w, h, data, pal, mask_data, ck, T, B); \
+ else \
+ BlitTile_internal<Uint16>(backBuf, x, y, rx, ry, w, h, data, pal, mask_data, ck, T, B); \
+
+
+ if (trans) {
+ TRBlender_HalfTrans B(backBuf->format);
+
+ if (tint) {
+ TRTinter_Tint T(tintcol);
+ DO_BLIT
+ } else {
+ TRTinter_NoTint T;
+ DO_BLIT
+ }
+ } else {
+ TRBlender_Opaque B(backBuf->format);
+
+ if (tint) {
+ TRTinter_Tint T(tintcol);
+ DO_BLIT
+ } else {
+ TRTinter_NoTint T;
+ DO_BLIT
+ }
+ }
+
+#undef DO_BLIT
+
+}
+
+
+void SDLVideoDriver::BlitSprite(const Sprite2D* spr, int x, int y, bool anchor,
+ const Region* clip)
+{
+ if (!spr->vptr) return;
+
+ if (!spr->BAM) {
+ //TODO: Add the destination surface and rect to the Blit Pipeline
+ SDL_Rect drect;
+ SDL_Rect t;
+ SDL_Rect* srect = NULL;
+ if (anchor) {
+ drect.x = x - spr->XPos;
+ drect.y = y - spr->YPos;
+ } else {
+ drect.x = x - spr->XPos - Viewport.x;
+ drect.y = y - spr->YPos - Viewport.y;
+ }
+
+ if (clip) {
+ if (drect.x + spr->Width <= clip->x)
+ return;
+ if (drect.x >= clip->x + clip->w)
+ return;
+
+ if (drect.y + spr->Height <= clip->y)
+ return;
+ if (drect.y >= clip->y + clip->h)
+ return;
+
+ t.x = 0;
+ t.w = spr->Width;
+ if (drect.x < clip->x) {
+ t.x += clip->x - drect.x;
+ t.w -= clip->x - drect.x;
+ drect.x = clip->x;
+ }
+ if (drect.x + t.w > clip->x + clip->w) {
+ t.w = clip->x + clip->w - drect.x;
+ }
+
+ t.y = 0;
+ t.h = spr->Height;
+ if (drect.y < clip->y) {
+ t.y += clip->y - drect.y;
+ t.h -= clip->y - drect.y;
+ drect.y = clip->y;
+ }
+ if (drect.y + t.h > clip->y + clip->h) {
+ t.h = clip->y + clip->h - drect.y;
+ }
+ srect = &t;
+
+ }
+ SDL_BlitSurface( ( SDL_Surface * ) spr->vptr, srect, backBuf, &drect );
+ } else {
+ Sprite2D_BAM_Internal* data = (Sprite2D_BAM_Internal*)spr->vptr;
+
+ const Uint8* rle = (const Uint8*)spr->pixels;
+ int tx, ty;
+ if (anchor) {
+ tx = x - spr->XPos;
+ ty = y - spr->YPos;
+ } else {
+ tx = x - spr->XPos - Viewport.x;
+ ty = y - spr->YPos - Viewport.y;
+ }
+ if (tx > backBuf->w) return;
+ if (tx+spr->Width <= 0) return;
+
+ SDL_LockSurface(backBuf);
+
+#define SPECIALPIXEL
+#undef BPP16
+#define FLIP
+#define HFLIP_CONDITIONAL data->flip_hor
+#define VFLIP_CONDITIONAL data->flip_ver
+#define RLE data->RLE
+#define PAL data->pal
+#define SRCDATA rle
+#define USE_PALETTE
+#undef COVER
+#undef TINT
+
+ if (backBuf->format->BytesPerPixel == 4) {
+
+#undef BPP16
+ if (data->pal->alpha) {
+
+#define PALETTE_ALPHA
+#include "SDLVideoDriver.inl"
+
+ } else {
+
+#undef PALETTE_ALPHA
+#include "SDLVideoDriver.inl"
+
+ }
+
+ } else {
+
+#define BPP16
+ if (data->pal->alpha) {
+
+#define PALETTE_ALPHA
+#include "SDLVideoDriver.inl"
+
+ } else {
+
+#undef PALETTE_ALPHA
+#include "SDLVideoDriver.inl"
+
+ }
+
+ }
+
+#undef BPP16
+#undef FLIP
+#undef HFLIP_CONDITIONAL
+#undef VFLIP_CONDITIONAL
+#undef RLE
+#undef PAL
+#undef SPECIALPIXEL
+#undef PALETTE_ALPHA
+#undef SRCDATA
+#undef USE_PALETTE
+
+ SDL_UnlockSurface(backBuf);
+ }
+}
+
+//cannot make const reference from tint, it is modified locally
+void SDLVideoDriver::BlitGameSprite(const Sprite2D* spr, int x, int y,
+ unsigned int flags, Color tint,
+ SpriteCover* cover, Palette *palette,
+ const Region* clip, bool anchor)
+{
+ if (!spr->vptr) return;
+
+ // WARNING: this pointer is only valid with BAM sprites
+ Sprite2D_BAM_Internal* data = 0;
+
+ if (!spr->BAM) {
+ SDL_Surface* surf = ( SDL_Surface * ) spr->vptr;
+ if (surf->format->BytesPerPixel != 4) {
+ // TODO...
+ printMessage( "SDLVideo", "BlitGameSprite not supported for this sprite\n", LIGHT_RED );
+ BlitSprite(spr, x, y, false, clip);
+ return;
+ }
+ } else {
+ data = (Sprite2D_BAM_Internal*)spr->vptr;
+ if (!palette)
+ palette = data->pal;
+ }
+
+ // global tint
+ if (!anchor && core->GetGame()) {
+ const Color *totint = core->GetGame()->GetGlobalTint();
+ if (totint) {
+ if (flags & BLIT_TINTED) {
+ tint.r = (tint.r * totint->r) >> 8;
+ tint.g = (tint.g * totint->g) >> 8;
+ tint.b = (tint.b * totint->b) >> 8;
+ } else {
+ flags |= BLIT_TINTED;
+ tint = *totint;
+ tint.a = 255;
+ }
+ }
+ }
+
+
+ // implicit flags:
+ const unsigned int blit_COVERED = 0x20000000U;
+ const unsigned int blit_TINTALPHA = 0x40000000U;
+ const unsigned int blit_PALETTEALPHA = 0x80000000U;
+
+ if (cover) flags |= blit_COVERED;
+ if ((flags & BLIT_TINTED) && tint.a != 255) flags |= blit_TINTALPHA;
+ if (spr->BAM && palette->alpha) flags |= blit_PALETTEALPHA;
+
+ // flag combinations which are often used:
+ // (ignoring MIRRORX/Y since those are handled conditionally)
+
+ // most game sprites:
+ // covered, BLIT_TINTED
+ // covered, BLIT_TINTED | BLIT_TRANSSHADOW
+ // covered, BLIT_TINTED | BLIT_NOSHADOW
+
+ // area-animations?
+ // BLIT_TINTED
+
+ // (hopefully) most video overlays:
+ // BLIT_HALFTRANS
+ // covered, BLIT_HALFTRANS
+ // covered
+ // none
+
+ // other combinations use general case
+
+
+ const Uint8* rle = (const Uint8*)spr->pixels;
+ //int tx = x - spr->XPos - Viewport.x;
+ //int ty = y - spr->YPos - Viewport.y;
+ int tx = x - spr->XPos;
+ int ty = y - spr->YPos;
+ if (!anchor) {
+ tx-=Viewport.x;
+ ty-=Viewport.y;
+ }
+ if (tx > backBuf->w) return;
+ if (tx+spr->Width <= 0) return;
+ SDL_LockSurface(backBuf);
+
+ bool hflip = spr->BAM ? data->flip_hor : false;
+ bool vflip = spr->BAM ? data->flip_ver : false;
+ if (flags & BLIT_MIRRORX) hflip = !hflip;
+ if (flags & BLIT_MIRRORY) vflip = !vflip;
+
+ Uint32 shadowcol32 = 0, mask32;
+ Uint16 shadowcol16 = 0, mask16;
+
+ if (flags & BLIT_TRANSSHADOW) {
+ shadowcol32 = SDL_MapRGBA(backBuf->format, palette->col[1].r/2,
+ palette->col[1].g/2, palette->col[1].b/2, 0);
+ shadowcol16 = (Uint16)shadowcol32;
+ }
+
+ mask32 = (backBuf->format->Rmask >> 1) & backBuf->format->Rmask;
+ mask32 |= (backBuf->format->Gmask >> 1) & backBuf->format->Gmask;
+ mask32 |= (backBuf->format->Bmask >> 1) & backBuf->format->Bmask;
+ mask16 = (Uint16)mask32;
+
+ unsigned int remflags = flags & ~(BLIT_MIRRORX | BLIT_MIRRORY);
+ if (remflags & BLIT_NOSHADOW) remflags &= ~BLIT_TRANSSHADOW;
+
+
+#define FLIP
+#define HFLIP_CONDITIONAL hflip
+#define VFLIP_CONDITIONAL vflip
+#define RLE data->RLE
+#define PAL palette
+#define COVERX (cover->XPos - spr->XPos)
+#define COVERY (cover->YPos - spr->YPos)
+#define USE_PALETTE
+#define SRCDATA rle
+#undef TINT_ALPHA
+#undef PALETTE_ALPHA
+
+ if (spr->BAM && remflags == (blit_COVERED | BLIT_TINTED)) {
+
+#define COVER
+#define SPECIALPIXEL
+#define TINT
+
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+
+#undef COVER
+#undef TINT
+#undef SPECIALPIXEL
+
+ } else if (spr->BAM && remflags == (blit_COVERED | BLIT_TINTED | BLIT_TRANSSHADOW)) {
+
+#define COVER
+#define TINT
+
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#define SPECIALPIXEL if (p == 1) { *pix = ((*pix >> 1)&mask32) + shadowcol32; } else
+#include "SDLVideoDriver.inl"
+#undef SPECIALPIXEL
+ } else {
+#define BPP16
+#define SPECIALPIXEL if (p == 1) { *pix = ((*pix >> 1)&mask16) + shadowcol16; } else
+#include "SDLVideoDriver.inl"
+#undef SPECIALPIXEL
+ }
+
+#undef COVER
+#undef TINT
+
+ } else if (spr->BAM && remflags == (blit_COVERED | BLIT_TINTED | BLIT_NOSHADOW)) {
+
+#define COVER
+#define TINT
+#define SPECIALPIXEL if (p != 1)
+
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+
+#undef SPECIALPIXEL
+#undef COVER
+#undef TINT
+
+
+ } else if (spr->BAM && remflags == BLIT_TINTED) {
+
+#define SPECIALPIXEL
+#define TINT
+
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+
+#undef TINT
+#undef SPECIALPIXEL
+
+ } else if (spr->BAM && remflags == BLIT_HALFTRANS) {
+
+#define HALFALPHA
+#define SPECIALPIXEL
+
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+
+#undef HALFALPHA
+#undef SPECIALPIXEL
+
+ } else if (spr->BAM && remflags == (blit_COVERED | BLIT_HALFTRANS)) {
+
+#define HALFALPHA
+#define COVER
+#define SPECIALPIXEL
+
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+
+#undef HALFALPHA
+#undef COVER
+#undef SPECIALPIXEL
+
+ } else if (spr->BAM && remflags == blit_COVERED) {
+
+#define COVER
+#define SPECIALPIXEL
+
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+
+#undef COVER
+#undef SPECIALPIXEL
+
+ }/* else if (remflags == 0) {
+
+#define SPECIALPIXEL
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+#undef SPECIALPIXEL
+
+}*/ else if (spr->BAM) {
+ // handling the following effects with conditionals:
+ // halftrans
+ // noshadow
+ // transshadow
+ // grey
+ // red
+ // glow (not yet)
+ // blended (not yet)
+
+ // handling the following effects by repeated inclusion:
+ // palettealpha
+ // tinted
+ // covered
+
+// printf("Unoptimized blit: %04X\n", flags);
+
+#define SPECIALPIXEL int ia=0; if ((remflags & BLIT_HALFTRANS) || (p == 1 && (remflags & BLIT_TRANSSHADOW))) ia = 1; if (p == 1 && (remflags & BLIT_NOSHADOW)) { } else
+
+#define CUSTOMBLENDING
+#define ALPHAADJUST(a) ((a)>>ia)
+#define CUSTOMBLEND(r,g,b) do { if (remflags & BLIT_GREY) { unsigned int t = (r)+(g)+(b); t /= 3; (r)=t; (g)=t; (b)=t; } if (remflags & BLIT_RED) { (g) /= 2; (b) /= 2; } } while(0)
+
+#define TINT_ALPHA
+
+ if (!(remflags & BLIT_TINTED)) tint.a = 255;
+
+ if (remflags & blit_PALETTEALPHA) {
+#define PALETTE_ALPHA
+ if (remflags & blit_COVERED) {
+#define COVER
+ if (remflags & BLIT_TINTED) {
+#define TINT
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+#undef TINT
+ } else {
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+ }
+#undef COVER
+ } else {
+ if (remflags & BLIT_TINTED) {
+#define TINT
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+#undef TINT
+ } else {
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+ }
+ }
+#undef PALETTE_ALPHA
+ } else {
+ if (remflags & blit_COVERED) {
+#define COVER
+ if (remflags & BLIT_TINTED) {
+#define TINT
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+#undef TINT
+ } else {
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+ }
+#undef COVER
+ } else {
+ if (remflags & BLIT_TINTED) {
+#define TINT
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+#undef TINT
+ } else {
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+ }
+ }
+ }
+
+#undef SPECIALPIXEL
+#undef CUSTOMBLENDING
+#undef USE_PALETTE
+#undef ALPHAADJUST
+#undef CUSTOMBLEND
+#undef TINT_ALPHA
+
+ } else {
+ // non-BAM Blitting
+
+ // handling the following effects with conditionals:
+ // halftrans
+ // grey
+ // red
+ // glow (not yet)
+ // blended (not yet)
+
+ // handling the following effects by repeated inclusion:
+ // tinted
+ // covered
+
+ // not handling the following effects at all:
+ // noshadow (impossible with 32bpp)
+ // transshadow (impossible with 32bpp)
+ // palettealpha (always set)
+
+// printf("Unoptimized blit: %04X\n", flags);
+
+#define SPECIALPIXEL int ia=0; if ((remflags & BLIT_HALFTRANS)) ia = 1; if (p == 1 && (remflags & BLIT_NOSHADOW)) { } else
+
+#define PALETTE_ALPHA
+#define CUSTOMBLENDING
+#define ALPHAADJUST(a) ((a)>>ia)
+#define CUSTOMBLEND(r,g,b) do { if (remflags & BLIT_GREY) { unsigned int t = (r)+(g)+(b); t /= 3; (r)=t; (g)=t; (b)=t; } if (remflags & BLIT_RED) { (g) /= 2; (b) /= 2; } } while(0)
+
+#undef SRCDATA
+#define SRCDATA ((const Uint32*)spr->pixels)
+
+#define TINT_ALPHA
+
+ if (!(remflags & BLIT_TINTED)) tint.a = 255;
+
+ if (remflags & blit_COVERED) {
+#define COVER
+ if (remflags & BLIT_TINTED) {
+#define TINT
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+#undef TINT
+ } else {
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+ }
+#undef COVER
+ } else {
+ if (remflags & BLIT_TINTED) {
+#define TINT
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+#undef TINT
+ } else {
+ if (backBuf->format->BytesPerPixel == 4) {
+#undef BPP16
+#include "SDLVideoDriver.inl"
+ } else {
+#define BPP16
+#include "SDLVideoDriver.inl"
+ }
+ }
+ }
+
+#undef SPECIALPIXEL
+#undef CUSTOMBLENDING
+#undef ALPHAADJUST
+#undef CUSTOMBLEND
+#undef TINT_ALPHA
+#undef PALETTE_ALPHA
+
+ }
+
+#undef FLIP
+#undef HFLIP_CONDITIONAL
+#undef VFLIP_CONDITIONAL
+#undef RLE
+#undef PAL
+#undef COVERX
+#undef COVERY
+#undef SRCDATA
+
+ SDL_UnlockSurface(backBuf);
+
+}
+
+
+
+
+
+void SDLVideoDriver::SetCursor(Sprite2D* up, Sprite2D* down)
+{
+ if (up) {
+ Cursor[0] = up;
+ } else {
+ Cursor[0] = NULL;
+ }
+ if (down) {
+ Cursor[1] = down;
+ } else {
+ Cursor[1] = NULL;
+ }
+ return;
+}
+
+// Drag cursor is shown instead of all other cursors
+void SDLVideoDriver::SetDragCursor(Sprite2D* drag)
+{
+ FreeSprite(Cursor[2]);
+ if (drag) {
+ Cursor[2] = drag;
+ CursorIndex = 2;
+ } else {
+ CursorIndex = 0;
+ Cursor[2] = NULL;
+ }
+}
+
+Sprite2D* SDLVideoDriver::GetScreenshot( Region r )
+{
+ int Width = r.w ? r.w : disp->w;
+ int Height = r.h ? r.h : disp->h;
+ SDL_Rect src = {r.x, r.y, r.w, r.h};
+
+
+ SDL_Surface* surf = SDL_CreateRGBSurface( SDL_SWSURFACE, Width, Height, 24,
+ 0xFF0000, 0x00FF00, 0x0000FF, 0x000000 );
+ SDL_BlitSurface( backBuf, (r.w && r.h) ? &src : NULL, surf, NULL);
+ void* pixels = malloc( Width * Height * 3 );
+ memcpy( pixels, surf->pixels, Width * Height * 3 );
+ //freeing up temporary surface as we copied its pixels
+ Sprite2D* screenshot = CreateSprite( Width, Height, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000, pixels, false, 0 );
+ SDL_FreeSurface(surf);
+ return screenshot;
+}
+
+/** No descriptions */
+void SDLVideoDriver::SetPalette(void *data, Palette* pal)
+{
+ SDL_Surface* sur = ( SDL_Surface* ) data;
+ SDL_SetPalette( sur, SDL_LOGPAL, ( SDL_Color * ) pal->col, 0, 256 );
+}
+
+void SDLVideoDriver::ConvertToVideoFormat(Sprite2D* sprite)
+{
+ if (!sprite->BAM) {
+ SDL_Surface* ss = ( SDL_Surface* ) sprite->vptr;
+ if (ss->format->Amask != 0) //Surface already converted
+ {
+ return;
+ }
+ SDL_Surface* ns = SDL_DisplayFormatAlpha( ss );
+ if (ns == NULL) {
+ return;
+ }
+ SDL_FreeSurface( ss );
+ free( (void*)sprite->pixels );
+ sprite->pixels = NULL;
+ sprite->vptr = ns;
+ }
+}
+
+/** This function Draws the Border of a Rectangle as described by the Region parameter. The Color used to draw the rectangle is passes via the Color parameter. */
+void SDLVideoDriver::DrawRect(const Region& rgn, const Color& color, bool fill, bool clipped)
+{
+ SDL_Rect drect = {
+ rgn.x, rgn.y, rgn.w, rgn.h
+ };
+ if (fill) {
+ if ( SDL_ALPHA_TRANSPARENT == color.a ) {
+ return;
+ } else if ( SDL_ALPHA_OPAQUE == color.a ) {
+ long val = SDL_MapRGBA( backBuf->format, color.r, color.g, color.b, color.a );
+ SDL_FillRect( backBuf, &drect, val );
+ } else {
+ SDL_Surface * rectsurf = SDL_CreateRGBSurface( SDL_SWSURFACE | SDL_SRCALPHA, rgn.w, rgn.h, 8, 0, 0, 0, 0 );
+ SDL_Color c;
+ c.r = color.r;
+ c.b = color.b;
+ c.g = color.g;
+ SDL_SetPalette( rectsurf, SDL_LOGPAL, &c, 0, 1 );
+ SDL_SetAlpha( rectsurf, SDL_SRCALPHA | SDL_RLEACCEL, color.a );
+ SDL_BlitSurface( rectsurf, NULL, backBuf, &drect );
+ SDL_FreeSurface( rectsurf );
+ }
+ } else {
+ DrawHLine( rgn.x, rgn.y, rgn.x + rgn.w - 1, color, clipped );
+ DrawVLine( rgn.x, rgn.y, rgn.y + rgn.h - 1, color, clipped );
+ DrawHLine( rgn.x, rgn.y + rgn.h - 1, rgn.x + rgn.w - 1, color, clipped );
+ DrawVLine( rgn.x + rgn.w - 1, rgn.y, rgn.y + rgn.h - 1, color, clipped );
+ }
+}
+
+/** This function Draws a clipped sprite */
+void SDLVideoDriver::DrawRectSprite(const Region& rgn, const Color& color, const Sprite2D* sprite)
+{
+ if (sprite->BAM) {
+ printMessage( "SDLVideo", "DrawRectSprite not supported for this sprite\n", LIGHT_RED );
+ return;
+ }
+
+ SDL_Surface* surf = ( SDL_Surface* ) sprite->vptr;
+ SDL_Rect drect = {
+ rgn.x, rgn.y, rgn.w, rgn.h
+ };
+ if ( SDL_ALPHA_TRANSPARENT == color.a ) {
+ return;
+ } else if ( SDL_ALPHA_OPAQUE == color.a ) {
+ long val = SDL_MapRGBA( surf->format, color.r, color.g, color.b, color.a );
+ SDL_FillRect( surf, &drect, val );
+ } else {
+ SDL_Surface * rectsurf = SDL_CreateRGBSurface( SDL_SWSURFACE | SDL_SRCALPHA, rgn.w, rgn.h, 8, 0, 0, 0, 0 );
+ SDL_Color c;
+ c.r = color.r;
+ c.b = color.b;
+ c.g = color.g;
+ SDL_SetPalette( rectsurf, SDL_LOGPAL, &c, 0, 1 );
+ SDL_SetAlpha( rectsurf, SDL_SRCALPHA | SDL_RLEACCEL, color.a );
+ SDL_BlitSurface( rectsurf, NULL, surf, &drect );
+ SDL_FreeSurface( rectsurf );
+ }
+}
+
+inline void ReadPixel(long &val, unsigned char *pixels, int BytesPerPixel) {
+ if (BytesPerPixel == 1) {
+ val = *pixels;
+ } else if (BytesPerPixel == 2) {
+ val = *(Uint16 *)pixels;
+ } else if (BytesPerPixel == 3) {
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ val = pixels[0] + ((unsigned int)pixels[1] << 8) + ((unsigned int)pixels[2] << 16);
+#else
+ val = pixels[2] + ((unsigned int)pixels[1] << 8) + ((unsigned int)pixels[0] << 16);
+#endif
+ } else if (BytesPerPixel == 4) {
+ val = *(Uint32 *)pixels;
+ }
+}
+
+inline void WritePixel(const long val, unsigned char *pixels, int BytesPerPixel) {
+ if (BytesPerPixel == 1) {
+ *pixels = (unsigned char)val;
+ } else if (BytesPerPixel == 2) {
+ *(Uint16 *)pixels = (Uint16)val;
+ } else if (BytesPerPixel == 3) {
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ pixels[0] = val & 0xff;
+ pixels[1] = (val >> 8) & 0xff;
+ pixels[2] = (val >> 16) & 0xff;
+#else
+ pixels[2] = val & 0xff;
+ pixels[1] = (val >> 8) & 0xff;
+ pixels[0] = (val >> 16) & 0xff;
+#endif
+ } else if (BytesPerPixel == 4) {
+ *(Uint32 *)pixels = val;
+ }
+}
+
+void SDLVideoDriver::SetPixel(short x, short y, const Color& color, bool clipped)
+{
+ //printf("x: %d; y: %d; XC: %d; YC: %d, VX: %d, VY: %d, VW: %d, VH: %d\n", x, y, xCorr, yCorr, Viewport.x, Viewport.y, Viewport.w, Viewport.h);
+ if (clipped) {
+ x += xCorr;
+ y += yCorr;
+ if (( x >= ( xCorr + Viewport.w ) ) || ( y >= ( yCorr + Viewport.h ) )) {
+ return;
+ }
+ if (( x < xCorr ) || ( y < yCorr )) {
+ return;
+ }
+ } else {
+ if (( x >= disp->w ) || ( y >= disp->h )) {
+ return;
+ }
+ if (( x < 0 ) || ( y < 0 )) {
+ return;
+ }
+ }
+
+ unsigned char * pixels = ( ( unsigned char * ) backBuf->pixels ) +
+ ( ( y * disp->w + x) * disp->format->BytesPerPixel );
+
+ long val = SDL_MapRGBA( backBuf->format, color.r, color.g, color.b, color.a );
+ SDL_LockSurface( backBuf );
+ WritePixel(val, pixels, backBuf->format->BytesPerPixel);
+ SDL_UnlockSurface( backBuf );
+}
+
+void SDLVideoDriver::GetPixel(short x, short y, Color& c)
+{
+ SDL_LockSurface( backBuf );
+ unsigned char * pixels = ( ( unsigned char * ) backBuf->pixels ) +
+ ( ( y * disp->w + x) * disp->format->BytesPerPixel );
+ long val = 0;
+ ReadPixel(val, pixels, backBuf->format->BytesPerPixel);
+ SDL_UnlockSurface( backBuf );
+
+ SDL_GetRGBA( val, backBuf->format, (Uint8 *) &c.r, (Uint8 *) &c.g, (Uint8 *) &c.b, (Uint8 *) &c.a );
+}
+
+void SDLVideoDriver::GetPixel(void *vptr, unsigned short x, unsigned short y, Color &c)
+{
+ SDL_Surface *surf = (SDL_Surface*)(vptr);
+
+ SDL_LockSurface( surf );
+ unsigned char * pixels = ( ( unsigned char * ) surf->pixels ) +
+ ( ( y * surf->w + x) * surf->format->BytesPerPixel );
+ long val = 0;
+ ReadPixel(val, pixels, surf->format->BytesPerPixel);
+ SDL_UnlockSurface( surf );
+
+ SDL_GetRGBA( val, surf->format, (Uint8 *) &c.r, (Uint8 *) &c.g, (Uint8 *) &c.b, (Uint8 *) &c.a );
+}
+
+long SDLVideoDriver::GetPixel(void *vptr, unsigned short x, unsigned short y)
+{
+ SDL_Surface *surf = (SDL_Surface*)(vptr);
+
+ SDL_LockSurface( surf );
+ unsigned char * pixels = ( ( unsigned char * ) surf->pixels ) +
+ ( ( y * surf->w + x) * surf->format->BytesPerPixel );
+ long val = 0;
+ ReadPixel(val, pixels, surf->format->BytesPerPixel);
+ SDL_UnlockSurface( surf );
+
+ return val;
+}
+
+/*
+ * Draws horizontal line. When clipped=true, it draws the line relative
+ * to Area origin and clips it by Area viewport borders,
+ * else it draws relative to screen origin and ignores the vieport
+ */
+void SDLVideoDriver::DrawHLine(short x1, short y, short x2, const Color& color, bool clipped)
+{
+ if (x1 > x2) {
+ short tmpx = x1;
+ x1 = x2;
+ x2 = tmpx;
+ }
+ if (clipped) {
+ x1 -= Viewport.x;
+ y -= Viewport.y;
+ x2 -= Viewport.x;
+ }
+ for (; x1 <= x2 ; x1++ )
+ SetPixel( x1, y, color, clipped );
+}
+
+/*
+ * Draws vertical line. When clipped=true, it draws the line relative
+ * to Area origin and clips it by Area viewport borders,
+ * else it draws relative to screen origin and ignores the vieport
+ */
+void SDLVideoDriver::DrawVLine(short x, short y1, short y2, const Color& color, bool clipped)
+{
+ if (y1 > y2) {
+ short tmpy = y1;
+ y1 = y2;
+ y2 = tmpy;
+ }
+ if (clipped) {
+ x -= Viewport.x;
+ y1 -= Viewport.y;
+ y2 -= Viewport.y;
+ }
+
+ for (; y1 <= y2 ; y1++ )
+ SetPixel( x, y1, color, clipped );
+}
+
+void SDLVideoDriver::DrawLine(short x1, short y1, short x2, short y2,
+ const Color& color, bool clipped)
+{
+ if (clipped) {
+ x1 -= Viewport.x;
+ x2 -= Viewport.x;
+ y1 -= Viewport.y;
+ y2 -= Viewport.y;
+ }
+ bool yLonger = false;
+ int shortLen = y2 - y1;
+ int longLen = x2 - x1;
+ if (abs( shortLen ) > abs( longLen )) {
+ int swap = shortLen;
+ shortLen = longLen;
+ longLen = swap;
+ yLonger = true;
+ }
+ int decInc;
+ if (longLen == 0) {
+ decInc = 0;
+ } else {
+ decInc = ( shortLen << 16 ) / longLen;
+ }
+
+ if (yLonger) {
+ if (longLen > 0) {
+ longLen += y1;
+ for (int j = 0x8000 + ( x1 << 16 ); y1 <= longLen; ++y1) {
+ SetPixel( j >> 16, y1, color, clipped );
+ j += decInc;
+ }
+ return;
+ }
+ longLen += y1;
+ for (int j = 0x8000 + ( x1 << 16 ); y1 >= longLen; --y1) {
+ SetPixel( j >> 16, y1, color, clipped );
+ j -= decInc;
+ }
+ return;
+ }
+
+ if (longLen > 0) {
+ longLen += x1;
+ for (int j = 0x8000 + ( y1 << 16 ); x1 <= longLen; ++x1) {
+ SetPixel( x1, j >> 16, color, clipped );
+ j += decInc;
+ }
+ return;
+ }
+ longLen += x1;
+ for (int j = 0x8000 + ( y1 << 16 ); x1 >= longLen; --x1) {
+ SetPixel( x1, j >> 16, color, clipped );
+ j -= decInc;
+ }
+}
+/** This functions Draws a Circle */
+void SDLVideoDriver::DrawCircle(short cx, short cy, unsigned short r,
+ const Color& color, bool clipped)
+{
+ //Uses the Breshenham's Circle Algorithm
+ long x, y, xc, yc, re;
+
+ x = r;
+ y = 0;
+ xc = 1 - ( 2 * r );
+ yc = 1;
+ re = 0;
+
+ if (SDL_MUSTLOCK( disp )) {
+ SDL_LockSurface( disp );
+ }
+ while (x >= y) {
+ SetPixel( cx + ( short ) x, cy + ( short ) y, color, clipped );
+ SetPixel( cx - ( short ) x, cy + ( short ) y, color, clipped );
+ SetPixel( cx - ( short ) x, cy - ( short ) y, color, clipped );
+ SetPixel( cx + ( short ) x, cy - ( short ) y, color, clipped );
+ SetPixel( cx + ( short ) y, cy + ( short ) x, color, clipped );
+ SetPixel( cx - ( short ) y, cy + ( short ) x, color, clipped );
+ SetPixel( cx - ( short ) y, cy - ( short ) x, color, clipped );
+ SetPixel( cx + ( short ) y, cy - ( short ) x, color, clipped );
+
+ y++;
+ re += yc;
+ yc += 2;
+
+ if (( ( 2 * re ) + xc ) > 0) {
+ x--;
+ re += xc;
+ xc += 2;
+ }
+ }
+ if (SDL_MUSTLOCK( disp )) {
+ SDL_UnlockSurface( disp );
+ }
+}
+
+static double ellipseradius(unsigned short xr, unsigned short yr, double angle) {
+ double one = (xr * sin(angle));
+ double two = (yr * cos(angle));
+ return sqrt(xr*xr*yr*yr / (one*one + two*two));
+}
+
+/** This functions Draws an Ellipse Segment */
+void SDLVideoDriver::DrawEllipseSegment(short cx, short cy, unsigned short xr,
+ unsigned short yr, const Color& color, double anglefrom, double angleto, bool drawlines, bool clipped)
+{
+ /* beware, dragons and clockwise angles be here! */
+ double radiusfrom = ellipseradius(xr, yr, anglefrom);
+ double radiusto = ellipseradius(xr, yr, angleto);
+ long xfrom = (long)round(radiusfrom * cos(anglefrom));
+ long yfrom = (long)round(radiusfrom * sin(anglefrom));
+ long xto = (long)round(radiusto * cos(angleto));
+ long yto = (long)round(radiusto * sin(angleto));
+
+ if (drawlines) {
+ // TODO: DrawLine's clipping code works differently to the clipping
+ // here so we add Viewport.x/Viewport.y as a hack for now
+ DrawLine(cx + Viewport.x, cy + Viewport.y,
+ cx + xfrom + Viewport.x, cy + yfrom + Viewport.y, color, clipped);
+ DrawLine(cx + Viewport.x, cy + Viewport.y,
+ cx + xto + Viewport.x, cy + yto + Viewport.y, color, clipped);
+ }
+
+ // *Attempt* to calculate the correct x/y boundaries.
+ // TODO: this doesn't work very well - you can't actually bound many
+ // arcs this way (imagine a segment with a small piece cut out).
+ if (xfrom > xto) {
+ long tmp = xfrom; xfrom = xto; xto = tmp;
+ }
+ if (yfrom > yto) {
+ long tmp = yfrom; yfrom = yto; yto = tmp;
+ }
+ if (xfrom >= 0 && yto >= 0) xto = xr;
+ if (xto <= 0 && yto >= 0) xfrom = -xr;
+ if (yfrom >= 0 && xto >= 0) yto = yr;
+ if (yto <= 0 && xto >= 0) yfrom = -yr;
+
+ //Uses Bresenham's Ellipse Algorithm
+ long x, y, xc, yc, ee, tas, tbs, sx, sy;
+
+ if (SDL_MUSTLOCK( disp )) {
+ SDL_LockSurface( disp );
+ }
+ tas = 2 * xr * xr;
+ tbs = 2 * yr * yr;
+ x = xr;
+ y = 0;
+ xc = yr * yr * ( 1 - ( 2 * xr ) );
+ yc = xr * xr;
+ ee = 0;
+ sx = tbs * xr;
+ sy = 0;
+
+ while (sx >= sy) {
+ if (x >= xfrom && x <= xto && y >= yfrom && y <= yto)
+ SetPixel( cx + ( short ) x, cy + ( short ) y, color, clipped );
+ if (-x >= xfrom && -x <= xto && y >= yfrom && y <= yto)
+ SetPixel( cx - ( short ) x, cy + ( short ) y, color, clipped );
+ if (-x >= xfrom && -x <= xto && -y >= yfrom && -y <= yto)
+ SetPixel( cx - ( short ) x, cy - ( short ) y, color, clipped );
+ if (x >= xfrom && x <= xto && -y >= yfrom && -y <= yto)
+ SetPixel( cx + ( short ) x, cy - ( short ) y, color, clipped );
+ y++;
+ sy += tas;
+ ee += yc;
+ yc += tas;
+ if (( 2 * ee + xc ) > 0) {
+ x--;
+ sx -= tbs;
+ ee += xc;
+ xc += tbs;
+ }
+ }
+
+ x = 0;
+ y = yr;
+ xc = yr * yr;
+ yc = xr * xr * ( 1 - ( 2 * yr ) );
+ ee = 0;
+ sx = 0;
+ sy = tas * yr;
+
+ while (sx <= sy) {
+ if (x >= xfrom && x <= xto && y >= yfrom && y <= yto)
+ SetPixel( cx + ( short ) x, cy + ( short ) y, color, clipped );
+ if (-x >= xfrom && -x <= xto && y >= yfrom && y <= yto)
+ SetPixel( cx - ( short ) x, cy + ( short ) y, color, clipped );
+ if (-x >= xfrom && -x <= xto && -y >= yfrom && -y <= yto)
+ SetPixel( cx - ( short ) x, cy - ( short ) y, color, clipped );
+ if (x >= xfrom && x <= xto && -y >= yfrom && -y <= yto)
+ SetPixel( cx + ( short ) x, cy - ( short ) y, color, clipped );
+ x++;
+ sx += tbs;
+ ee += xc;
+ xc += tbs;
+ if (( 2 * ee + yc ) > 0) {
+ y--;
+ sy -= tas;
+ ee += yc;
+ yc += tas;
+ }
+ }
+ if (SDL_MUSTLOCK( disp )) {
+ SDL_UnlockSurface( disp );
+ }
+}
+
+
+/** This functions Draws an Ellipse */
+void SDLVideoDriver::DrawEllipse(short cx, short cy, unsigned short xr,
+ unsigned short yr, const Color& color, bool clipped)
+{
+ //Uses Bresenham's Ellipse Algorithm
+ long x, y, xc, yc, ee, tas, tbs, sx, sy;
+
+ if (SDL_MUSTLOCK( disp )) {
+ SDL_LockSurface( disp );
+ }
+ tas = 2 * xr * xr;
+ tbs = 2 * yr * yr;
+ x = xr;
+ y = 0;
+ xc = yr * yr * ( 1 - ( 2 * xr ) );
+ yc = xr * xr;
+ ee = 0;
+ sx = tbs * xr;
+ sy = 0;
+
+ while (sx >= sy) {
+ SetPixel( cx + ( short ) x, cy + ( short ) y, color, clipped );
+ SetPixel( cx - ( short ) x, cy + ( short ) y, color, clipped );
+ SetPixel( cx - ( short ) x, cy - ( short ) y, color, clipped );
+ SetPixel( cx + ( short ) x, cy - ( short ) y, color, clipped );
+ y++;
+ sy += tas;
+ ee += yc;
+ yc += tas;
+ if (( 2 * ee + xc ) > 0) {
+ x--;
+ sx -= tbs;
+ ee += xc;
+ xc += tbs;
+ }
+ }
+
+ x = 0;
+ y = yr;
+ xc = yr * yr;
+ yc = xr * xr * ( 1 - ( 2 * yr ) );
+ ee = 0;
+ sx = 0;
+ sy = tas * yr;
+
+ while (sx <= sy) {
+ SetPixel( cx + ( short ) x, cy + ( short ) y, color, clipped );
+ SetPixel( cx - ( short ) x, cy + ( short ) y, color, clipped );
+ SetPixel( cx - ( short ) x, cy - ( short ) y, color, clipped );
+ SetPixel( cx + ( short ) x, cy - ( short ) y, color, clipped );
+ x++;
+ sx += tbs;
+ ee += xc;
+ xc += tbs;
+ if (( 2 * ee + yc ) > 0) {
+ y--;
+ sy -= tas;
+ ee += yc;
+ yc += tas;
+ }
+ }
+ if (SDL_MUSTLOCK( disp )) {
+ SDL_UnlockSurface( disp );
+ }
+}
+
+void SDLVideoDriver::DrawPolyline(Gem_Polygon* poly, const Color& color, bool fill)
+{
+ if (!poly->count) {
+ return;
+ }
+
+ if (poly->BBox.x > Viewport.x + Viewport.w) return;
+ if (poly->BBox.y > Viewport.y + Viewport.h) return;
+ if (poly->BBox.x + poly->BBox.w < Viewport.x) return;
+ if (poly->BBox.y + poly->BBox.h < Viewport.y) return;
+
+ if (fill) {
+ Uint32 alphacol32 = SDL_MapRGBA(backBuf->format, color.r/2, color.g/2, color.b/2, 0);
+ Uint16 alphacol16 = (Uint16)alphacol32;
+
+ // color mask for doing a 50/50 alpha blit
+ Uint32 mask32 = (backBuf->format->Rmask >> 1) & backBuf->format->Rmask;
+ mask32 |= (backBuf->format->Gmask >> 1) & backBuf->format->Gmask;
+ mask32 |= (backBuf->format->Bmask >> 1) & backBuf->format->Bmask;
+
+ Uint16 mask16 = (Uint16)mask32;
+
+ SDL_LockSurface(backBuf);
+ std::list<Trapezoid>::iterator iter;
+ for (iter = poly->trapezoids.begin(); iter != poly->trapezoids.end();
+ ++iter)
+ {
+ int y_top = iter->y1 - Viewport.y; // inclusive
+ int y_bot = iter->y2 - Viewport.y; // exclusive
+
+ if (y_top < 0) y_top = 0;
+ if (y_bot > Viewport.h) y_bot = Viewport.h;
+ if (y_top >= y_bot) continue; // clipped
+
+ int ledge = iter->left_edge;
+ int redge = iter->right_edge;
+ Point& a = poly->points[ledge];
+ Point& b = poly->points[(ledge+1)%(poly->count)];
+ Point& c = poly->points[redge];
+ Point& d = poly->points[(redge+1)%(poly->count)];
+
+ Uint8* line = (Uint8*)(backBuf->pixels) + (y_top+yCorr)*backBuf->pitch;
+
+ for (int y = y_top; y < y_bot; ++y) {
+ int py = y + Viewport.y;
+
+ // TODO: maybe use a 'real' line drawing algorithm to
+ // compute these values faster.
+
+ int lt = (b.x * (py - a.y) + a.x * (b.y - py))/(b.y - a.y);
+ int rt = (d.x * (py - c.y) + c.x * (d.y - py))/(d.y - c.y) + 1;
+
+ lt -= Viewport.x;
+ rt -= Viewport.x;
+
+ if (lt < 0) lt = 0;
+ if (rt > Viewport.w) rt = Viewport.w;
+ if (lt >= rt) { line += backBuf->pitch; continue; } // clipped
+
+
+ // Draw a 50% alpha line from (y,lt) to (y,rt)
+
+ if (backBuf->format->BytesPerPixel == 2) {
+ Uint16* pix = (Uint16*)line + lt + xCorr;
+ Uint16* end = pix + (rt - lt);
+ for (; pix < end; pix++)
+ *pix = ((*pix >> 1)&mask16) + alphacol16;
+ } else if (backBuf->format->BytesPerPixel == 4) {
+ Uint32* pix = (Uint32*)line + lt + xCorr;
+ Uint32* end = pix + (rt - lt);
+ for (; pix < end; pix++)
+ *pix = ((*pix >> 1)&mask32) + alphacol32;
+ } else {
+ assert(false);
+ }
+ line += backBuf->pitch;
+ }
+ }
+ SDL_UnlockSurface(backBuf);
+ }
+
+ short lastX = poly->points[0]. x, lastY = poly->points[0].y;
+ unsigned int i;
+
+ for (i = 1; i < poly->count; i++) {
+ DrawLine( lastX, lastY, poly->points[i].x, poly->points[i].y, color, true );
+ lastX = poly->points[i].x;
+ lastY = poly->points[i].y;
+ }
+ DrawLine( lastX, lastY, poly->points[0].x, poly->points[0].y, color, true );
+
+ return;
+}
+
+/** Send a Quit Signal to the Event Queue */
+bool SDLVideoDriver::Quit()
+{
+ SDL_Event evnt;
+ evnt.type = SDL_QUIT;
+ if (SDL_PushEvent( &evnt ) == -1) {
+ return false;
+ }
+ return true;
+}
+
+Palette* SDLVideoDriver::GetPalette(void *vptr)
+{
+ SDL_Surface* s = ( SDL_Surface* ) vptr;
+ if (s->format->BitsPerPixel != 8) {
+ return NULL;
+ }
+ Palette* pal = new Palette();
+ for (int i = 0; i < s->format->palette->ncolors; i++) {
+ pal->col[i].r = s->format->palette->colors[i].r;
+ pal->col[i].g = s->format->palette->colors[i].g;
+ pal->col[i].b = s->format->palette->colors[i].b;
+ }
+ return pal;
+}
+
+// Flips given sprite vertically (up-down). If MirrorAnchor=true,
+// flips its anchor (i.e. origin//base point) as well
+// returns new sprite
+
+Sprite2D *SDLVideoDriver::MirrorSpriteVertical(const Sprite2D* sprite, bool MirrorAnchor)
+{
+ if (!sprite || !sprite->vptr)
+ return NULL;
+
+ Sprite2D* dest = 0;
+
+
+ if (!sprite->BAM) {
+ dest = DuplicateSprite(sprite);
+ for (int x = 0; x < dest->Width; x++) {
+ unsigned char * dst = ( unsigned char * ) dest->pixels + x;
+ unsigned char * src = dst + ( dest->Height - 1 ) * dest->Width;
+ for (int y = 0; y < dest->Height / 2; y++) {
+ unsigned char swp = *dst;
+ *dst = *src;
+ *src = swp;
+ dst += dest->Width;
+ src -= dest->Width;
+ }
+ }
+ } else {
+ dest = DuplicateSprite(sprite);
+ Sprite2D_BAM_Internal* destdata = (Sprite2D_BAM_Internal*)dest->vptr;
+ destdata->flip_ver = !destdata->flip_ver;
+ }
+
+ dest->XPos = dest->XPos;
+ if (MirrorAnchor)
+ dest->YPos = sprite->Height - sprite->YPos;
+ else
+ dest->YPos = sprite->YPos;
+
+ return dest;
+}
+
+// Flips given sprite horizontally (left-right). If MirrorAnchor=true,
+// flips its anchor (i.e. origin//base point) as well
+Sprite2D *SDLVideoDriver::MirrorSpriteHorizontal(const Sprite2D* sprite, bool MirrorAnchor)
+{
+ if (!sprite || !sprite->vptr)
+ return NULL;
+
+ Sprite2D* dest = 0;
+
+ if (!sprite->BAM) {
+ dest = DuplicateSprite(sprite);
+ for (int y = 0; y < dest->Height; y++) {
+ unsigned char * dst = (unsigned char *) dest->pixels + ( y * dest->Width );
+ unsigned char * src = dst + dest->Width - 1;
+ for (int x = 0; x < dest->Width / 2; x++) {
+ unsigned char swp=*dst;
+ *dst++ = *src;
+ *src-- = swp;
+ }
+ }
+ } else {
+ dest = DuplicateSprite(sprite);
+ Sprite2D_BAM_Internal* destdata = (Sprite2D_BAM_Internal*)dest->vptr;
+ destdata->flip_hor = !destdata->flip_hor;
+ }
+
+ if (MirrorAnchor)
+ dest->XPos = sprite->Width - sprite->XPos;
+ else
+ dest->XPos = sprite->XPos;
+ dest->YPos = sprite->YPos;
+
+ return dest;
+}
+
+void SDLVideoDriver::SetFadeColor(int r, int g, int b)
+{
+ if (r>255) r=255;
+ else if(r<0) r=0;
+ fadeColor.r=r;
+ if (g>255) g=255;
+ else if(g<0) g=0;
+ fadeColor.g=g;
+ if (b>255) b=255;
+ else if(b<0) b=0;
+ fadeColor.b=b;
+ long val = SDL_MapRGBA( extra->format, fadeColor.r, fadeColor.g, fadeColor.b, fadeColor.a );
+ SDL_FillRect( extra, NULL, val );
+}
+
+void SDLVideoDriver::SetFadePercent(int percent)
+{
+ if (percent>100) percent = 100;
+ else if (percent<0) percent = 0;
+ fadeColor.a = (255 * percent ) / 100;
+}
+
+void SDLVideoDriver::SetClipRect(const Region* clip)
+{
+ if (clip) {
+ SDL_Rect tmp;
+ tmp.x = clip->x;
+ tmp.y = clip->y;
+ tmp.w = clip->w;
+ tmp.h = clip->h;
+ SDL_SetClipRect( backBuf, &tmp );
+ } else {
+ SDL_SetClipRect( backBuf, NULL );
+ }
+}
+
+void SDLVideoDriver::GetClipRect(Region& clip)
+{
+ SDL_Rect tmp;
+ SDL_GetClipRect( backBuf, &tmp );
+
+ clip.x = tmp.x;
+ clip.y = tmp.y;
+ clip.w = tmp.w;
+ clip.h = tmp.h;
+}
+
+
+void SDLVideoDriver::GetMousePos(int &x, int &y)
+{
+ x=CursorPos.x;
+ y=CursorPos.y;
+}
+
+void SDLVideoDriver::MouseMovement(int x, int y)
+{
+ GetTime( lastMouseTime );
+ if (DisableMouse&MOUSE_DISABLED)
+ return;
+ CursorPos.x = x; // - mouseAdjustX[CursorIndex];
+ CursorPos.y = y; // - mouseAdjustY[CursorIndex];
+ if (Evnt)
+ Evnt->MouseMove(x, y);
+}
+
+/* no idea how elaborate this should be*/
+void SDLVideoDriver::MoveMouse(unsigned int x, unsigned int y)
+{
+ SDL_WarpMouse(x,y);
+}
+
+void SDLVideoDriver::ClickMouse(unsigned int button)
+{
+ MouseClickEvent(SDL_MOUSEBUTTONDOWN, (Uint8) button);
+ MouseClickEvent(SDL_MOUSEBUTTONUP, (Uint8) button);
+ if (button&GEM_MB_DOUBLECLICK) {
+ MouseClickEvent(SDL_MOUSEBUTTONDOWN, (Uint8) button);
+ MouseClickEvent(SDL_MOUSEBUTTONUP, (Uint8) button);
+ }
+}
+
+void SDLVideoDriver::MouseClickEvent(Uint8 type, Uint8 button)
+{
+ SDL_Event *event = new SDL_Event();
+ event->type = type;
+ event->button.type = type;
+ event->button.button = button;
+ event->button.state = (type==SDL_MOUSEBUTTONDOWN)?SDL_PRESSED:SDL_RELEASED;
+ event->button.x = CursorPos.x;
+ event->button.y = CursorPos.y;
+ SDL_PushEvent(event);
+}
+
+void SDLVideoDriver::InitMovieScreen(int &w, int &h, bool yuv)
+{
+ if (yuv) {
+ if (overlay) {
+ SDL_FreeYUVOverlay(overlay);
+ }
+ // BIKPlayer outputs PIX_FMT_YUV420P which is YV12
+ overlay = SDL_CreateYUVOverlay(w, h, SDL_YV12_OVERLAY, disp);
+ }
+ SDL_LockSurface( disp );
+ memset( disp->pixels, 0,
+ disp->w * disp->h * disp->format->BytesPerPixel );
+ SDL_UnlockSurface( disp );
+ SDL_Flip( disp );
+ w = disp->w;
+ h = disp->h;
+ //setting the subtitle region to the bottom 1/4th of the screen
+ subtitleregion.w = w;
+ subtitleregion.h = h/4;
+ subtitleregion.x = 0;
+ subtitleregion.y = h-h/4;
+
+ //same for SDL
+ subtitleregion_sdl.w = w;
+ subtitleregion_sdl.h = h/4;
+ subtitleregion_sdl.x = 0;
+ subtitleregion_sdl.y = h-h/4;
+}
+
+void SDLVideoDriver::showFrame(unsigned char* buf, unsigned int bufw,
+ unsigned int bufh, unsigned int sx, unsigned int sy, unsigned int w,
+ unsigned int h, unsigned int dstx, unsigned int dsty,
+ int g_truecolor, unsigned char *pal, ieDword titleref)
+{
+ int i;
+ SDL_Surface* sprite;
+ SDL_Rect srcRect, destRect;
+
+ assert( bufw == w && bufh == h );
+
+ if (g_truecolor) {
+ sprite = SDL_CreateRGBSurfaceFrom( buf, bufw, bufh, 16, 2 * bufw,
+ 0x7C00, 0x03E0, 0x001F, 0 );
+ } else {
+ sprite = SDL_CreateRGBSurfaceFrom( buf, bufw, bufh, 8, bufw, 0x7C00,
+ 0x03E0, 0x001F, 0 );
+
+ for (i = 0; i < 256; i++) {
+ sprite->format->palette->colors[i].r = ( *pal++ ) << 2;
+ sprite->format->palette->colors[i].g = ( *pal++ ) << 2;
+ sprite->format->palette->colors[i].b = ( *pal++ ) << 2;
+ sprite->format->palette->colors[i].unused = 0;
+ }
+ }
+
+ srcRect.x = sx;
+ srcRect.y = sy;
+ srcRect.w = w;
+ srcRect.h = h;
+ destRect.x = dstx;
+ destRect.y = dsty;
+ destRect.w = w;
+ destRect.h = h;
+
+ SDL_FillRect(disp, &subtitleregion_sdl, 0);
+ SDL_BlitSurface( sprite, &srcRect, disp, &destRect );
+ if (titleref>0)
+ DrawMovieSubtitle( titleref );
+ SDL_Flip( disp );
+ SDL_FreeSurface( sprite );
+}
+
+void SDLVideoDriver::showYUVFrame(unsigned char** buf, unsigned int *strides,
+ unsigned int /*bufw*/, unsigned int bufh,
+ unsigned int w, unsigned int h,
+ unsigned int dstx, unsigned int dsty,
+ ieDword titleref) {
+ SDL_Rect destRect;
+
+ assert( /* bufw == w && */ bufh == h );
+
+ SDL_LockYUVOverlay(overlay);
+ for (unsigned int plane = 0; plane < 3; plane++) {
+ unsigned char *data = buf[plane];
+ unsigned int size = overlay->pitches[plane];
+ if (strides[plane] < size) {
+ size = strides[plane];
+ }
+ unsigned int srcoffset = 0, destoffset = 0;
+ for (unsigned int i = 0; i < ((plane == 0) ? bufh : (bufh / 2)); i++) {
+ memcpy(overlay->pixels[plane] + destoffset,
+ data + srcoffset, size);
+ srcoffset += strides[plane];
+ destoffset += overlay->pitches[plane];
+ }
+ }
+ SDL_UnlockYUVOverlay(overlay);
+ destRect.x = dstx;
+ destRect.y = dsty;
+ destRect.w = w;
+ destRect.h = h;
+ SDL_FillRect(disp, &subtitleregion_sdl, 0);
+ SDL_DisplayYUVOverlay(overlay, &destRect);
+ if (titleref>0)
+ DrawMovieSubtitle( titleref );
+ //Maybe we don't need this?
+ //SDL_Flip( disp );
+}
+
+int SDLVideoDriver::PollMovieEvents()
+{
+ SDL_Event event;
+
+ while (SDL_PollEvent( &event )) {
+ switch (event.type) {
+ case SDL_QUIT:
+ case SDL_MOUSEBUTTONUP:
+ return 1;
+ case SDL_KEYDOWN:
+ switch (event.key.keysym.sym) {
+ case SDLK_ESCAPE:
+ case SDLK_q:
+ return 1;
+ case SDLK_f:
+ ToggleFullscreenMode(-1);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void SDLVideoDriver::SetMovieFont(Font *stfont, Palette *pal)
+{
+ subtitlefont = stfont;
+ subtitlepal = pal;
+}
+
+void SDLVideoDriver::DrawMovieSubtitle(ieDword strRef)
+{
+ if (strRef!=subtitlestrref) {
+ core->FreeString(subtitletext);
+ if (!strRef)
+ return;
+ subtitletext = core->GetString(strRef);
+ subtitlestrref = strRef;
+ }
+ if (subtitlefont && subtitletext) {
+ // FIXME: ugly hack!
+ SDL_Surface* temp = backBuf;
+ backBuf = disp;
+
+ //FYI: that 0 is pitch black
+ //SDL_FillRect(disp, &subtitleregion_sdl, 0);
+ subtitlefont->Print(subtitleregion, (unsigned char *) subtitletext, subtitlepal, IE_FONT_ALIGN_LEFT|IE_FONT_ALIGN_BOTTOM, true);
+ backBuf = temp;
+ }
+}
+// sets brightness and contrast
+// FIXME:SetGammaRamp doesn't seem to work
+// WARNING: SDL 1.2.13 crashes in SetGamma on Windows (it was fixed in SDL's #3533 Revision)
+void SDLVideoDriver::SetGamma(int brightness, int /*contrast*/)
+{
+ SDL_SetGamma(0.8+brightness/50.0,0.8+brightness/50.0,0.8+brightness/50.0);
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xDBAAB50, "SDL Video Driver")
+PLUGIN_DRIVER(SDLVideoDriver, "sdl")
+END_PLUGIN()
diff --git a/gemrb/plugins/SDLVideo/SDLVideo.h b/gemrb/plugins/SDLVideo/SDLVideo.h
new file mode 100644
index 0000000..e24ad9f
--- /dev/null
+++ b/gemrb/plugins/SDLVideo/SDLVideo.h
@@ -0,0 +1,173 @@
+/* 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 SDLVIDEODRIVER_H
+#define SDLVIDEODRIVER_H
+
+#include "Video.h"
+
+#include "win32def.h"
+
+#include <SDL.h>
+
+class SDLVideoDriver : public Video {
+private:
+ SDL_Surface* disp;
+ SDL_Surface* backBuf;
+ SDL_Surface* extra;
+ std::vector< Region> upd;//Regions of the Screen to Update in the next SwapBuffer operation.
+ Sprite2D* Cursor[3];
+ SDL_Rect CursorPos;
+ unsigned short CursorIndex;
+ Color fadeColor;
+ unsigned long lastTime;
+ unsigned long lastMouseTime;
+ SDL_Event event; /* Event structure */
+ //subtitle specific variables
+ Font *subtitlefont;
+ Palette *subtitlepal;
+ Region subtitleregion;
+ SDL_Rect subtitleregion_sdl; //we probably have the same stuff, twice
+ char *subtitletext;
+ ieDword subtitlestrref;
+ /* yuv overlay for bink movie */
+ SDL_Overlay *overlay;
+public:
+ SDLVideoDriver(void);
+ ~SDLVideoDriver(void);
+ int Init(void);
+ int CreateDisplay(int width, int height, int bpp, bool fullscreen);
+ void SetDisplayTitle(char* title, char* icon);
+ bool ToggleFullscreenMode(int set_reset=-1);
+ int SwapBuffers(void);
+ int PollEvents();
+ bool ToggleGrabInput();
+ short GetWidth() { return ( disp ? disp->w : 0 ); }
+ short GetHeight() { return ( disp ? disp->h : 0 ); }
+
+ void InitSpriteCover(SpriteCover* sc, int flags);
+ void AddPolygonToSpriteCover(SpriteCover* sc, Wall_Polygon* poly);
+ void DestroySpriteCover(SpriteCover* sc);
+
+ void GetMousePos(int &x, int &y);
+ void MouseMovement(int x, int y);
+ void MoveMouse(unsigned int x, unsigned int y);
+ void ClickMouse(unsigned int button);
+ void MouseClickEvent(Uint8 type, Uint8 button);
+ Sprite2D* CreateSprite(int w, int h, int bpp, ieDword rMask,
+ ieDword gMask, ieDword bMask, ieDword aMask, void* pixels,
+ bool cK = false, int index = 0);
+ Sprite2D* CreateSprite8(int w, int h, int bpp, void* pixels,
+ void* palette, bool cK = false, int index = 0);
+ Sprite2D* CreateSpriteBAM8(int w, int h, bool RLE,
+ const unsigned char* pixeldata, AnimationFactory* datasrc,
+ Palette* palette, int transindex);
+ bool SupportsBAMSprites() { return true; }
+ void FreeSprite(Sprite2D* &spr);
+ Sprite2D* DuplicateSprite(const Sprite2D* spr);
+ void BlitTile(const Sprite2D* spr, const Sprite2D* mask, int x, int y, const Region* clip, bool trans);
+ void BlitSprite(const Sprite2D* spr, int x, int y, bool anchor = false,
+ const Region* clip = NULL);
+ void BlitSpriteRegion(const Sprite2D* spr, const Region& size, int x, int y,
+ bool anchor = true, const Region* clip = NULL);
+ void BlitGameSprite(const Sprite2D* spr, int x, int y,
+ unsigned int flags, Color tint,
+ SpriteCover* cover, Palette *palette = NULL,
+ const Region* clip = NULL, bool anchor = false);
+ void SetCursor(Sprite2D* up, Sprite2D* down);
+ void SetDragCursor(Sprite2D* drag);
+ Sprite2D* GetScreenshot( Region r );
+ void ConvertToVideoFormat(Sprite2D* sprite);
+ /** Sets the palette of an SDL surface pointed by */
+ void SetPalette(void* data, Palette* pal);
+ /** This function Draws the Border of a Rectangle as described by the Region parameter. The Color used to draw the rectangle is passes via the Color parameter. */
+ void DrawRect(const Region& rgn, const Color& color, bool fill = true, bool clipped = false);
+ void DrawRectSprite(const Region& rgn, const Color& color, const Sprite2D* sprite);
+ /** This functions Draws a Circle */
+ void SetPixel(short x, short y, const Color& color, bool clipped = true);
+ /** Gets the pixel of the backbuffer surface */
+ void GetPixel(short x, short y, Color& color);
+ /** Gets the pixel of any supplied surface */
+ void GetPixel(void *vptr, unsigned short x, unsigned short y, Color& color);
+ void DrawCircle(short cx, short cy, unsigned short r, const Color& color, bool clipped = true);
+ /** This functions Draws an Ellipse Segment */
+ void DrawEllipseSegment(short cx, short cy, unsigned short xr, unsigned short yr, const Color& color,
+ double anglefrom, double angleto, bool drawlines = true, bool clipped = true);
+ /** This functions Draws an Ellipse */
+ void DrawEllipse(short cx, short cy, unsigned short xr, unsigned short yr,
+ const Color& color, bool clipped = true);
+ /** This function Draws a Polygon on the Screen */
+ void DrawPolyline(Gem_Polygon* poly, const Color& color, bool fill = false);
+ inline void DrawHLine(short x1, short y, short x2, const Color& color, bool clipped = false);
+ inline void DrawVLine(short x, short y1, short y2, const Color& color, bool clipped = false);
+ inline void DrawLine(short x1, short y1, short x2, short y2, const Color& color, bool clipped = false);
+ /** Blits a Sprite filling the Region */
+ void BlitTiled(Region rgn, const Sprite2D* img, bool anchor = false);
+ /** Send a Quit Signal to the Event Queue */
+ bool Quit();
+ /** Get the Palette of a surface */
+ Palette* GetPalette(void *vptr);
+ /** Flips sprite vertically */
+ Sprite2D *MirrorSpriteVertical(const Sprite2D *sprite, bool MirrorAnchor);
+ /** Flips sprite horizontally */
+ Sprite2D *MirrorSpriteHorizontal(const Sprite2D *sprite, bool MirrorAnchor);
+ /** Set Clip Rect */
+ void SetClipRect(const Region* clip);
+ /** Get Clip Rect */
+ void GetClipRect(Region& clip);
+ /** Convers a Screen Coordinate to a Game Coordinate */
+
+ void ConvertToGame(short& x, short& y)
+ {
+ x += Viewport.x;
+ y += Viewport.y;
+ }
+
+ void ConvertToScreen(short&x, short& y)
+ {
+ x -= Viewport.x;
+ y -= Viewport.y;
+ }
+
+ void SetFadeColor(int r, int g, int b);
+ void SetFadePercent(int percent);
+ void InitMovieScreen(int &w, int &h, bool yuv=false);
+ void SetMovieFont(Font *stfont, Palette *pal);
+ void showFrame(unsigned char* buf, unsigned int bufw,
+ unsigned int bufh, unsigned int sx, unsigned int sy, unsigned int w,
+ unsigned int h, unsigned int dstx, unsigned int dsty, int truecolor,
+ unsigned char *palette, ieDword strRef);
+ void showYUVFrame(unsigned char** buf, unsigned int *strides,
+ unsigned int bufw, unsigned int bufh,
+ unsigned int w, unsigned int h,
+ unsigned int dstx, unsigned int dsty,
+ ieDword titleref);
+ int PollMovieEvents();
+
+private:
+ void DrawMovieSubtitle(ieDword strRef);
+
+public:
+ long GetPixel(void *data, unsigned short x, unsigned short y);
+ void SetGamma(int brightness, int contrast);
+ void SetMouseScrollSpeed(int speed);
+};
+
+#endif
diff --git a/gemrb/plugins/SDLVideo/SDLVideoDriver.inl b/gemrb/plugins/SDLVideo/SDLVideoDriver.inl
new file mode 100644
index 0000000..4020ae8
--- /dev/null
+++ b/gemrb/plugins/SDLVideo/SDLVideoDriver.inl
@@ -0,0 +1,539 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2005-2006 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.
+ *
+ *
+ */
+
+// Input macros:
+//
+// SRCDATA
+// pointer to the pixel data
+// RLE
+// is the sprite RLE-encoded?
+
+// USE_PALETTE
+// does the sprite use a palette? (If defined, define PAL too)
+// PAL
+// the palette
+// PALETTE_ALPHA
+// use the alpha values from the palette
+
+// BPP16
+// output surface is 16bpp instead of the default 32bpp
+
+// ALREADYCLIPPED
+// the variables clipx, clipy, clipw, cliph have already been set
+// FLIP
+// enable code for flipping sprite H/V, through the following two macros:
+// HFLIP_CONDITIONAL
+// boolean expression for when to flip the sprite horizontally
+// VFLIP_CONDITIONAL
+// boolean expression for when to flip the sprite vertically
+
+// COVER
+// apply wallcovers (The wallcover data is taken from the 'cover' variable)
+
+// TINT
+// enable tinting (The colour to tint with is taken from the 'tint' variable)
+// TINT_ALPHA
+// use the alpha component of the tint
+
+// SPECIALPIXEL
+// special code to be inserted right before rendering a pixel
+
+// CUSTOMBLENDING
+// if defined, use CUSTOMBLEND and ALPHAADJUST to pre-process source colour
+// CUSTOMBLEND(r,g,b)
+// called on colour right before blending with output pixel (post-tinting)
+// APLHAADJUST(a)
+// called on colour right before blending with output pixel (post-tinting)
+
+// HALFALPHA
+// draw sprite 50% transparent.
+// Cannot be used together with TINT_ALPHA, PALETTE_ALPHA or CUSTOMBLENDING
+
+// HIGHLIGHTCOVER
+// (debugging) make covered pixels white instead of black
+
+
+//#define HIGHLIGHTCOVER
+
+#define TARGET backBuf
+#define WIDTH spr->Width
+#define HEIGHT spr->Height
+
+
+// XNEG(x) is -x when flipping horizontally, x otherwise
+// YNEG(y) is -y when flipping vertically, y otherwise
+#ifdef FLIP
+ #define XNEG(x) (((x)+xneg)^xneg)
+ #define YNEG(y) (((y)+yneg)^yneg)
+#else
+ #define XNEG(x) (x)
+ #define YNEG(y) (y)
+#endif
+
+
+// Output 16/32 bpp
+#ifdef BPP16
+ #define PTYPE Uint16
+ #define PITCHMULT 2
+ #define MASK mask16
+#else
+ #define PITCHMULT 4
+ #define PTYPE Uint32
+ #define MASK mask32
+#endif
+
+// Input indexed/32 bpp
+#ifdef USE_PALETTE
+ #define RVALUE(p) (col[(p)].r)
+ #define GVALUE(p) (col[(p)].g)
+ #define BVALUE(p) (col[(p)].b)
+ #define AVALUE(p) (col[(p)].a)
+ #define ISTRANSPARENT(p) (p == (Uint8)data->transindex)
+ #define SRCTYPE Uint8
+#else
+ // TODO: These shifts will need fixing if we ever use this functionality
+ // for sprites not created by SDLVideoDriver::CreateLight
+ #define RVALUE(p) (p & 0xff)
+ #define GVALUE(p) ((p >> 8) & 0xff)
+ #define BVALUE(p) ((p >> 16) & 0xff)
+ #define AVALUE(p) (p >> 24)
+ #define ISTRANSPARENT(p) (AVALUE(p) == 0)
+ #define SRCTYPE Uint32
+#endif
+
+// Make ALPHAADJUST, CUSTOMBLEND nops if not doing CUSTOMBLENDING
+#ifndef CUSTOMBLENDING
+ #define ALPHAADJUST(a) a
+ #define CUSTOMBLEND(r,g,b)
+#endif
+
+
+// Define ALPHA, ALPHAVALUE macros appropriately for palette/tint alpha settings
+#ifdef PALETTE_ALPHA
+ #define ALPHA
+ #ifdef TINT_ALPHA
+ #define ALPHAVALUE(p) (ALPHAADJUST(((AVALUE(p)) * tint.a)>>8))
+ #else
+ #define ALPHAVALUE(p) (ALPHAADJUST(AVALUE(p)))
+ #endif
+#else
+ #ifdef TINT_ALPHA
+ #define ALPHA
+ #define ALPHAVALUE(p) (ALPHAADJUST(tint.a))
+ #else
+ #undef ALPHA
+ #define ALPHAVALUE(p) (ALPHAADJUST(255))
+ #endif
+#endif
+
+#ifdef COVER
+assert(cover);
+#endif
+
+// TODO: preconvert palette to surface-specific color values, where possible
+
+
+// Define BLENDPIXEL macro for blending of source and dest pixels
+
+#ifdef ALPHA
+ #ifdef TINT
+ // alpha, tinted
+ #define BLENDPIXEL(target,cr,cg,cb,ca,curval) do { \
+ if ((ca) != 0) { \
+ dR = ((tint.r*(cr)) >> 8); \
+ dG = ((tint.g*(cg)) >> 8); \
+ dB = ((tint.b*(cb)) >> 8); \
+ CUSTOMBLEND(dR,dG,dB); \
+ dR = 1 + (ca)*dR \
+ + ((((curval)>>rshift)<<rloss)&0xFF)*(255-(ca)); \
+ dR = (dR + (dR >> 8)) >> 8; \
+ dG = 1 + (ca)*dG \
+ + ((((curval)>>gshift)<<gloss)&0xFF)*(255-(ca)); \
+ dG = (dG + (dG >> 8)) >> 8; \
+ dB = 1 + (ca)*dB \
+ + ((((curval)>>bshift)<<bloss)&0xFF)*(255-(ca)); \
+ dB = (dB + (dB >> 8)) >> 8; \
+ target = (PTYPE) ( ((dR) >> rloss) << rshift \
+ | ((dG) >> gloss) << gshift \
+ | ((dB) >> bloss) << bshift); \
+ } \
+ } while (0)
+ #else
+ // alpha, untinted
+ #define BLENDPIXEL(target,cr,cg,cb,ca,curval) do { \
+ if ((ca) != 0) { \
+ dR = (cr); \
+ dG = (cg); \
+ dB = (cb); \
+ CUSTOMBLEND(dR,dG,dB); \
+ dR = 1 + (ca)*dR \
+ + ((((curval)>>rshift)<<rloss)&0xFF)*(255-(ca)); \
+ dR = (dR + (dR >> 8)) >> 8; \
+ dG = 1 + (ca)*dG \
+ + ((((curval)>>gshift)<<gloss)&0xFF)*(255-(ca)); \
+ dG = (dG + (dG >> 8)) >> 8; \
+ dB = 1 + (ca)*dB \
+ + ((((curval)>>bshift)<<bloss)&0xFF)*(255-(ca)); \
+ dB = (dB + (dB >> 8)) >> 8; \
+ target = (PTYPE) ( ((dR) >> rloss) << rshift \
+ | ((dG) >> gloss) << gshift \
+ | ((dB) >> bloss) << bshift); \
+ } \
+ } while (0)
+ #endif
+
+#else
+
+ #ifdef HALFALPHA
+
+ #ifdef TINT
+ // 50% transparent, tinted
+ #define BLENDPIXEL(target,cr,cg,cb,ca,curval) \
+ target = ((curval >> 1)&MASK) + \
+ ((((PTYPE)( ((tint.r*(cr)) >> (rloss+8)) << rshift \
+ | ((tint.g*(cg)) >> (gloss+8)) << gshift \
+ | ((tint.b*(cb)) >> (bloss+8)) << bshift)) >> 1)\
+ &MASK)
+
+ #else
+ // 50% transparent, untinted
+ #define BLENDPIXEL(target,cr,cg,cb,ca,curval) \
+ target = ((curval >> 1)&MASK) + \
+ ((((PTYPE)( ((cr) >> rloss) << rshift \
+ | ((cg) >> gloss) << gshift \
+ | ((cb) >> bloss) << bshift)) >> 1)&MASK)
+
+ #endif
+
+ #else
+
+ #ifdef CUSTOMBLENDING
+
+ #ifdef TINT
+ // no alpha, tinted (custom blending)
+ #define BLENDPIXEL(target,cr,cg,cb,ca,curval) do { \
+ dR = ((tint.r*(cr)) >> 8); \
+ dG = ((tint.g*(cg)) >> 8); \
+ dB = ((tint.b*(cb)) >> 8); \
+ CUSTOMBLEND(dR,dG,dB); \
+ target = (PTYPE) ( ((dR) >> rloss) << rshift \
+ | ((dG) >> gloss) << gshift \
+ | ((dB) >> bloss) << bshift); \
+ } while(0)
+ #else
+ // no alpha, untinted (custom blending)
+ #define BLENDPIXEL(target,cr,cg,cb,ca,curval) do { \
+ dR = (cr); \
+ dG = (cg); \
+ dB = (cb); \
+ CUSTOMBLEND(dR,dG,dB); \
+ target = (PTYPE) ( ((dR) >> rloss) << rshift \
+ | ((dG) >> gloss) << gshift \
+ | ((dB) >> bloss) << bshift); \
+ } while(0)
+ #endif
+
+ #else
+ #ifdef TINT
+ // no alpha, tinted
+ #define BLENDPIXEL(target,cr,cg,cb,ca,curval) \
+ target = (PTYPE)( ((tint.r*(cr)) >> (rloss+8)) << rshift \
+ | ((tint.g*(cg)) >> (gloss+8)) << gshift \
+ | ((tint.b*(cb)) >> (bloss+8)) << bshift)
+ #else
+ // no alpha, untinted
+ #define BLENDPIXEL(target,cr,cg,cb,ca,curval) \
+ target = (PTYPE)( ((cr) >> rloss) << rshift \
+ | ((cg) >> gloss) << gshift \
+ | ((cb) >> bloss) << bshift)
+ #endif
+ #endif
+ #endif
+#endif
+
+
+// Main rendering code
+
+do {
+#ifdef FLIP
+ const int xneg = (HFLIP_CONDITIONAL)?-1:0;
+ const int yneg = (VFLIP_CONDITIONAL)?-1:0;
+#else
+ const int xneg = 0;
+ const int yneg = 0;
+#endif
+
+ const int rloss = (TARGET)->format->Rloss;
+ const int gloss = (TARGET)->format->Gloss;
+ const int bloss = (TARGET)->format->Bloss;
+ const int rshift = (TARGET)->format->Rshift;
+ const int gshift = (TARGET)->format->Gshift;
+ const int bshift = (TARGET)->format->Bshift;
+#ifdef USE_PALETTE
+ const Color* const col = (PAL)->col;
+#endif
+ const SRCTYPE* srcdata = (SRCDATA);
+
+#if (defined(ALPHA) || defined(CUSTOMBLENDING))
+ unsigned int dR;
+ unsigned int dG;
+ unsigned int dB;
+#endif
+
+#ifndef ALREADYCLIPPED
+ int clipx, clipy, clipw, cliph;
+ if (clip) {
+ clipx = clip->x;
+ clipy = clip->y;
+ clipw = clip->w;
+ cliph = clip->h;
+ } else {
+ clipx = 0;
+ clipy = 0;
+ clipw = (TARGET)->w;
+ cliph = (TARGET)->h;
+ }
+ SDL_Rect cliprect;
+ SDL_GetClipRect((TARGET), &cliprect);
+ if (cliprect.x > clipx) {
+ clipw -= (cliprect.x - clipx);
+ clipx = cliprect.x;
+ }
+ if (cliprect.y > clipy) {
+ cliph -= (cliprect.y - clipy);
+ clipy = cliprect.y;
+ }
+ if (clipx+clipw > cliprect.x+cliprect.w) {
+ clipw = cliprect.x+cliprect.w-clipx;
+ }
+ if (clipy+cliph > cliprect.y+cliprect.h) {
+ cliph = cliprect.y+cliprect.h-clipy;
+ }
+#endif
+
+#ifdef USE_PALETTE
+ if (RLE) {
+#else
+ if (false) {
+#endif
+
+ PTYPE* line = (PTYPE*)(TARGET)->pixels +
+ (ty - yneg*((HEIGHT)-1))*(TARGET)->pitch/(PITCHMULT);
+ PTYPE* end = line + YNEG(HEIGHT)*(TARGET)->pitch/(PITCHMULT);
+ PTYPE* clipstartline = (PTYPE*)(TARGET)->pixels
+ + clipy*((TARGET)->pitch)/PITCHMULT;
+ PTYPE* clipendline = clipstartline + cliph*((TARGET)->pitch)/PITCHMULT;
+#ifdef COVER
+ Uint8* coverline = (Uint8*)cover->pixels + ((COVERY) - yneg*((HEIGHT)-1))*cover->Width;
+#endif
+
+ if (yneg) {
+ if (end < clipstartline)
+ end = clipstartline - ((TARGET)->pitch)/PITCHMULT;
+ } else {
+ if (end > clipendline)
+ end = clipendline;
+ }
+
+ int translength = 0;
+ for (; YNEG((int)(end - line)) > 0; line += YNEG((TARGET)->pitch/(PITCHMULT)))
+ {
+ PTYPE* pix = line + tx + translength - xneg*((WIDTH)-1);
+ PTYPE* endpix = line + tx - xneg*((WIDTH)-1) + XNEG(WIDTH);
+ PTYPE* clipstartpix = line + clipx;
+ PTYPE* clipendpix = clipstartpix + clipw;
+#ifdef COVER
+ Uint8* coverpix = coverline + (COVERX) + translength - xneg*((WIDTH)-1);
+#endif
+ if (yneg) {
+ if (line >= clipendline) clipstartpix = clipendpix;
+ } else {
+ if (line < clipstartline) clipstartpix = clipendpix;
+ }
+ while (XNEG((int)(endpix - pix)) > 0)
+ {
+ Uint8 p = *srcdata++;
+ if (ISTRANSPARENT(p)) {
+ int count = XNEG((*srcdata++) + 1);
+ pix += count;
+#ifdef COVER
+ coverpix += count;
+#endif
+ } else {
+ if (pix >= clipstartpix && pix < clipendpix) {
+#ifdef COVER
+ if (!*coverpix)
+#endif
+ {
+ SPECIALPIXEL {
+ BLENDPIXEL(*pix, (RVALUE(p)), (GVALUE(p)), (BVALUE(p)), (ALPHAVALUE(p)), *pix);
+ }
+ }
+#if defined(COVER) && defined(HIGHLIGHTCOVER)
+ else {
+ BLENDPIXEL(*pix, 255, 255, 255, 255, *pix);
+ }
+#endif
+ }
+ pix += XNEG(1);
+#ifdef COVER
+ coverpix += XNEG(1);
+#endif
+ }
+ }
+ translength = pix - endpix;
+#ifdef COVER
+ coverline += YNEG(cover->Width);
+#endif
+ }
+ } else {
+#ifdef COVER
+ Uint8* coverline = (Uint8*)cover->pixels + ((COVERY) - yneg*((HEIGHT)-1))*cover->Width;
+#endif
+ int starty, endy;
+
+ if (!yneg) {
+ starty = ty;
+ if (clipy > starty) starty = clipy;
+ endy = ty + (HEIGHT);
+ if (clipy+cliph < endy) endy = clipy+cliph;
+
+ if (starty >= endy) break;
+
+ // skip clipped lines at start
+ srcdata += (starty - ty) * (WIDTH);
+#ifdef COVER
+ coverline += (starty - ty) * cover->Width;
+#endif
+ } else {
+ starty = ty + (HEIGHT) - 1;
+ if (clipy+cliph <= starty) starty = clipy+cliph-1;
+ endy = ty - 1;
+ if (clipy-1 > endy) endy = clipy-1;
+
+ if (starty <= endy) break;
+
+ // skip clipped lines at start
+ srcdata += (ty + (HEIGHT) - 1 - starty) * (WIDTH);
+#ifdef COVER
+ coverline -= (ty + (HEIGHT) - 1 - starty) * cover->Width;
+#endif
+ }
+
+ int startx, endx;
+ int prelineskip = 0;
+ int postlineskip = 0;
+
+ if (!xneg) {
+ startx = tx;
+ if (clipx > startx) startx = clipx;
+ endx = tx + (WIDTH);
+ if (clipx+clipw < endx) endx = clipx+cliph;
+
+ if (startx >= endx) break;
+
+ prelineskip = startx - tx;
+ postlineskip = tx + (WIDTH) - endx;
+ } else {
+ startx = tx + (WIDTH) - 1;
+ if (clipx+clipw <= startx) startx = clipx+clipw-1;
+ endx = tx - 1;
+ if (clipx-1 > endx) endx = clipx-1;
+
+ if (startx <= endx) break;
+
+ prelineskip = (tx + (WIDTH) - 1) - startx;
+ postlineskip = endx - (tx-1);
+ }
+
+
+ PTYPE* line = (PTYPE*)(TARGET)->pixels +
+ starty*(TARGET)->pitch/(PITCHMULT);
+ PTYPE* endline = (PTYPE*)(TARGET)->pixels +
+ endy*(TARGET)->pitch/(PITCHMULT);
+
+ while (line != endline) {
+ PTYPE* pix = line + startx;
+ PTYPE* endpix = line + endx;
+#ifdef COVER
+ Uint8* coverpix = coverline + (COVERX) + XNEG(prelineskip) - xneg*((WIDTH)-1);
+#endif
+ srcdata += prelineskip;
+
+ while (pix != endpix)
+ {
+ SRCTYPE p = *srcdata++;
+ if (!(ISTRANSPARENT(p))) {
+#ifdef COVER
+ if (!*coverpix)
+#endif
+ {
+ SPECIALPIXEL {
+ BLENDPIXEL(*pix, (RVALUE(p)), (GVALUE(p)), (BVALUE(p)), ( (ALPHAVALUE(p))), *pix);
+ }
+ }
+#if defined(COVER) && defined(HIGHLIGHTCOVER)
+ else {
+ BLENDPIXEL(*pix, 255, 255, 255, 255, *pix);
+ }
+#endif
+ }
+ pix += XNEG(1);
+#ifdef COVER
+ coverpix += XNEG(1);
+#endif
+ }
+
+ srcdata += postlineskip;
+
+ line += YNEG((TARGET)->pitch/(PITCHMULT));
+#ifdef COVER
+ coverline += YNEG(cover->Width);
+#endif
+ }
+ }
+
+} while(0);
+
+
+
+#undef XNEG
+#undef YNEG
+#undef PITCHMULT
+#undef PTYPE
+#undef BLENDPIXEL
+#undef TARGET
+#undef WIDTH
+#undef HEIGHT
+#undef ALPHA
+#undef ALPHAVALUE
+#undef HIGHLIGHTCOVER
+#undef MASK
+#undef RVALUE
+#undef GVALUE
+#undef BVALUE
+#undef AVALUE
+#undef ISTRANSPARENT
+#undef SRCTYPE
+
+#ifndef CUSTOMBLENDING
+#undef ALPHAADJUST
+#undef CUSTOMBLEND
+#endif
diff --git a/gemrb/plugins/SDLVideo/TileRenderer.inl b/gemrb/plugins/SDLVideo/TileRenderer.inl
new file mode 100644
index 0000000..21825e0
--- /dev/null
+++ b/gemrb/plugins/SDLVideo/TileRenderer.inl
@@ -0,0 +1,122 @@
+/* GemRB - Infinity Engine Emulator
+ * Copyright (C) 2010 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.
+ *
+ *
+ */
+
+
+struct TRTinter_NoTint {
+ Uint8 r(Uint8 v) const { return v; }
+ Uint8 g(Uint8 v) const { return v; }
+ Uint8 b(Uint8 v) const { return v; }
+};
+
+struct TRTinter_Tint {
+ TRTinter_Tint(const Color& t) : tint(t) { }
+
+ Uint8 r(Uint8 v) const { return (tint.r * v) >> 8; }
+ Uint8 g(Uint8 v) const { return (tint.g * v) >> 8; }
+ Uint8 b(Uint8 v) const { return (tint.b * v) >> 8; }
+
+ Color tint;
+};
+
+struct TRBlender_Opaque {
+ TRBlender_Opaque(const SDL_PixelFormat*) { }
+
+ Uint32 operator()(Uint32 p, Uint32) const {
+ return p;
+ }
+};
+
+struct TRBlender_HalfTrans {
+ TRBlender_HalfTrans(const SDL_PixelFormat* format)
+ {
+ mask = (0x7F >> format->Rloss) << format->Rshift
+ | (0x7F >> format->Gloss) << format->Gshift
+ | (0x7F >> format->Bloss) << format->Bshift;
+ }
+
+ Uint32 operator()(Uint32 p, Uint32 v) const {
+ return ((p>>1)&mask) + ((v >> 1)&mask);
+ }
+
+ Uint32 mask;
+};
+
+
+//the dummy variable is a hint for MSVC6, otherwise it compiles bad code
+//because it cannot select between the 16 and 32 bit variants
+template<typename PixelType, class Tinter, class Blender>
+static void BlitTile_internal(SDL_Surface* target,
+ int tx, int ty,
+ int rx, int ry,
+ int w, int h,
+ const Uint8* data, const SDL_Color* pal,
+ const Uint8* mask, Uint8 mask_key,
+ Tinter& tint, Blender& blend, PixelType /*dummy*/=0)
+{
+ PixelType* buf_line = (PixelType*)(target->pixels) + (ty+ry)*(target->pitch / sizeof(PixelType));
+ const Uint8* data_line = data + ry*64;
+
+ PixelType opal[256];
+
+ for (unsigned int i = 0; i < 256; ++i)
+ {
+ Uint8 r = tint.r(pal[i].r);
+ Uint8 g = tint.g(pal[i].g);
+ Uint8 b = tint.b(pal[i].b);
+ opal[i] = (r >> target->format->Rloss) << target->format->Rshift
+ | (g >> target->format->Gloss) << target->format->Gshift
+ | (b >> target->format->Bloss) << target->format->Bshift;
+ }
+
+ if (mask) {
+ const Uint8* mask_line = mask + ry*64;
+ for (int y = 0; y < h; ++y) {
+ PixelType* buf = buf_line + tx + rx;
+ data = data_line + rx;
+ mask = mask_line + rx;
+ for (int x = 0; x < w; ++x) {
+ Uint8 p = *data++;
+ Uint8 m = *mask++;
+ if (m == mask_key)
+ *buf = (PixelType)blend(opal[p],*buf);
+ buf++;
+ }
+ buf_line += target->pitch / sizeof(PixelType);
+ mask_line += 64;
+ data_line += 64;
+ }
+
+ } else {
+
+ for (int y = 0; y < h; ++y) {
+ PixelType* buf = buf_line + tx + rx;
+ data = data_line + rx;
+ for (int x = 0; x < w; ++x) {
+ Uint8 p = *data++;
+ *buf = (PixelType)blend(opal[p],*buf);
+ buf++;
+ }
+ buf_line += target->pitch / sizeof(PixelType);
+ data_line += 64;
+ }
+
+ }
+}
+
diff --git a/gemrb/plugins/SPLImporter/CMakeLists.txt b/gemrb/plugins/SPLImporter/CMakeLists.txt
new file mode 100644
index 0000000..a76b78b
--- /dev/null
+++ b/gemrb/plugins/SPLImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (SPLImporter SPLImporter.cpp)
diff --git a/gemrb/plugins/SPLImporter/Makefile.am b/gemrb/plugins/SPLImporter/Makefile.am
new file mode 100644
index 0000000..8e1996b
--- /dev/null
+++ b/gemrb/plugins/SPLImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = SPLImporter.la
+SPLImporter_la_LDFLAGS = -module -avoid-version -shared
+SPLImporter_la_SOURCES = SPLImporter.cpp SPLImporter.h
diff --git a/gemrb/plugins/SPLImporter/SPLImporter.cpp b/gemrb/plugins/SPLImporter/SPLImporter.cpp
new file mode 100644
index 0000000..c8c9bfe
--- /dev/null
+++ b/gemrb/plugins/SPLImporter/SPLImporter.cpp
@@ -0,0 +1,259 @@
+/* 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 "SPLImporter.h"
+
+#include "win32def.h"
+
+#include "EffectMgr.h"
+#include "Interface.h"
+#include "TableMgr.h" //needed for autotable
+
+int *cgsounds = NULL;
+int cgcount = -1;
+
+//cannot call this at the time of initialization because the tablemanager isn't alive yet
+void Initializer()
+{
+ if (cgsounds) {
+ free(cgsounds);
+ cgsounds = NULL;
+ }
+ cgcount = 0;
+ AutoTable tm("cgtable");
+ if (!tm) {
+ printStatus( "ERROR", LIGHT_RED );
+ printf( "Cannot find cgtable.2da.\n");
+ return;
+ }
+ cgcount = tm->GetRowCount();
+ cgsounds = (int *) calloc( cgcount, sizeof(int) );
+ for (int i = 0; i < cgcount; i++) {
+ cgsounds[i] = atoi(tm->QueryField( i, 1 ) );
+ }
+}
+
+void ReleaseMemorySPL()
+{
+ free(cgsounds);
+ cgsounds = NULL;
+ cgcount = -1;
+}
+
+int GetCGSound(ieDword CastingGraphics)
+{
+ if (cgcount<0) {
+ Initializer();
+ }
+
+ if (CastingGraphics>=(ieDword) cgcount) {
+ return -1;
+ }
+ int ret = -1;
+ if (core->HasFeature(GF_CASTING_SOUNDS) ) {
+ ret = cgsounds[CastingGraphics];
+ if (core->HasFeature(GF_CASTING_SOUNDS2) ) {
+ ret |= 0x100;
+ }
+ }
+ return ret;
+}
+
+SPLImporter::SPLImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+SPLImporter::~SPLImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+}
+
+bool SPLImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "SPL V1 ", 8 ) == 0) {
+ version = 1;
+ } else if (strncmp( Signature, "SPL V2.0", 8 ) == 0) {
+ version = 20;
+ } else {
+ printf( "[SPLImporter]: This file is not a valid SPL File\n" );
+ return false;
+ }
+
+ return true;
+}
+
+Spell* SPLImporter::GetSpell(Spell *s, bool /*silent*/)
+{
+ unsigned int i;
+
+ str->ReadDword( &s->SpellName );
+ str->ReadDword( &s->SpellNameIdentified );
+ str->ReadResRef( s->CompletionSound );
+ str->ReadDword( &s->Flags );
+ str->ReadWord( &s->SpellType );
+ str->ReadWord( &s->ExclusionSchool );
+ str->ReadWord( &s->PriestType );
+ str->ReadWord( &s->CastingGraphics );
+ s->CastingSound = GetCGSound(s->CastingGraphics);
+ str->Read( &s->unknown1, 1 );
+ str->ReadWord( &s->PrimaryType );
+ str->Read( &s->SecondaryType, 1 );
+ str->ReadDword( &s->unknown2 );
+ str->ReadDword( &s->unknown3 );
+ str->ReadDword( &s->unknown4 );
+ str->ReadDword( &s->SpellLevel );
+ str->ReadWord( &s->unknown5 );
+ str->ReadResRef( s->SpellbookIcon );
+ //this hack is needed in ToB at least
+ if (core->HasFeature(GF_SPELLBOOKICONHACK)) {
+ i=strlen(s->SpellbookIcon);
+ if (i) s->SpellbookIcon[i-1]='c';
+ }
+
+ str->ReadWord( &s->unknown6 );
+ str->ReadDword( &s->unknown7 );
+ str->ReadDword( &s->unknown8 );
+ str->ReadDword( &s->unknown9 );
+ str->ReadDword( &s->SpellDesc );
+ str->ReadDword( &s->SpellDescIdentified );
+ str->ReadDword( &s->unknown10 );
+ str->ReadDword( &s->unknown11 );
+ str->ReadDword( &s->unknown12 );
+ str->ReadDword( &s->ExtHeaderOffset );
+ str->ReadWord( &s->ExtHeaderCount );
+ str->ReadDword( &s->FeatureBlockOffset );
+ str->ReadWord( &s->CastingFeatureOffset );
+ str->ReadWord( &s->CastingFeatureCount );
+
+ memset( s->unknown13, 0, 8 );
+ if (version == 20) {
+ //these fields are used in simplified duration
+ str->ReadDword( &s->TimePerLevel );
+ str->ReadDword( &s->TimeConstant );
+ str->Read( s->unknown13, 8 );
+ //moving some bits, because bg2 uses them differently
+ //the low byte is unused, so we can keep the iwd2 bits there
+ s->Flags|=(s->Flags>>8)&0xc0;
+ s->Flags&=~0xc000;
+ } else {
+ //in case of old format, use some unused fields for gemrb's simplified duration
+ //to simulate IWD2's useful feature (this is needed for some pst projectiles)
+ if (s->Flags&SF_SIMPLIFIED_DURATION) {
+ s->TimePerLevel = s->unknown2;
+ s->TimeConstant = s->unknown3;
+ } else {
+ s->TimePerLevel = 0;
+ s->TimeConstant = 0;
+ }
+ }
+
+ s->ext_headers = core->GetSPLExt(s->ExtHeaderCount);
+
+ for (i = 0; i < s->ExtHeaderCount; i++) {
+ str->Seek( s->ExtHeaderOffset + i * 40, GEM_STREAM_START );
+ GetExtHeader( s, s->ext_headers+i );
+ }
+
+ s->casting_features = core->GetFeatures(s->CastingFeatureCount);
+ str->Seek( s->FeatureBlockOffset + 48*s->CastingFeatureOffset,
+ GEM_STREAM_START );
+ for (i = 0; i < s->CastingFeatureCount; i++) {
+ GetFeature(s, s->casting_features+i);
+ }
+
+ return s;
+}
+
+void SPLImporter::GetExtHeader(Spell *s, SPLExtHeader* eh)
+{
+ ieByte tmpByte;
+
+ str->Read( &eh->SpellForm, 1 );
+ str->Read( &eh->unknown1, 1 );
+ str->Read( &eh->Location, 1 );
+ str->Read( &eh->unknown2, 1 );
+ str->ReadResRef( eh->MemorisedIcon );
+ str->Read( &eh->Target, 1 );
+
+ //this hack is to let gemrb target dead actors by some spells
+ if (eh->Target == 1) {
+ if (core->GetSpecialSpell(s->Name)&SPEC_DEAD) {
+ eh->Target = 3;
+ }
+ }
+ str->Read( &tmpByte,1 );
+ if (!tmpByte) {
+ tmpByte = 1;
+ }
+ eh->TargetNumber = tmpByte;
+ str->ReadWord( &eh->Range );
+ str->ReadWord( &eh->RequiredLevel );
+ str->ReadDword( &eh->CastingTime );
+ str->ReadWord( &eh->DiceSides );
+ str->ReadWord( &eh->DiceThrown );
+ str->ReadWord( &eh->DamageBonus );
+ str->ReadWord( &eh->DamageType );
+ str->ReadWord( &eh->FeatureCount );
+ str->ReadWord( &eh->FeatureOffset );
+ str->ReadWord( &eh->Charges );
+ str->ReadWord( &eh->ChargeDepletion );
+ str->ReadWord( &eh->ProjectileAnimation );
+
+ //for some odd reasons 0 and 1 are the same
+ if (eh->ProjectileAnimation) {
+ eh->ProjectileAnimation--;
+ }
+ eh->features = core->GetFeatures( eh->FeatureCount );
+ str->Seek( s->FeatureBlockOffset + 48*eh->FeatureOffset, GEM_STREAM_START );
+ for (unsigned int i = 0; i < eh->FeatureCount; i++) {
+ GetFeature(s, eh->features+i);
+ }
+}
+
+void SPLImporter::GetFeature(Spell *s, Effect *fx)
+{
+ PluginHolder<EffectMgr> eM(IE_EFF_CLASS_ID);
+ eM->Open( str, false );
+ eM->GetEffect( fx );
+ memcpy(fx->Source, s->Name, 9);
+ fx->PrimaryType = s->PrimaryType;
+ fx->SecondaryType = s->SecondaryType;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xA8D1014, "SPL File Importer")
+PLUGIN_CLASS(IE_SPL_CLASS_ID, SPLImporter)
+PLUGIN_CLEANUP(ReleaseMemorySPL)
+END_PLUGIN()
diff --git a/gemrb/plugins/SPLImporter/SPLImporter.h b/gemrb/plugins/SPLImporter/SPLImporter.h
new file mode 100644
index 0000000..2b355bb
--- /dev/null
+++ b/gemrb/plugins/SPLImporter/SPLImporter.h
@@ -0,0 +1,48 @@
+/* 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 SPLIMPORTER_H
+#define SPLIMPORTER_H
+
+#include "SpellMgr.h"
+
+#include "ie_types.h"
+
+#include "Spell.h"
+
+
+class SPLImporter : public SpellMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ int version;
+
+public:
+ SPLImporter(void);
+ ~SPLImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ Spell* GetSpell(Spell *spl, bool silent=false);
+private:
+ void GetExtHeader(Spell *s, SPLExtHeader* eh);
+ void GetFeature(Spell *s, Effect *f);
+};
+
+
+#endif
diff --git a/gemrb/plugins/STOImporter/CMakeLists.txt b/gemrb/plugins/STOImporter/CMakeLists.txt
new file mode 100644
index 0000000..78e57d5
--- /dev/null
+++ b/gemrb/plugins/STOImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (STOImporter STOImporter.cpp)
diff --git a/gemrb/plugins/STOImporter/Makefile.am b/gemrb/plugins/STOImporter/Makefile.am
new file mode 100644
index 0000000..8a2eeca
--- /dev/null
+++ b/gemrb/plugins/STOImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = STOImporter.la
+STOImporter_la_LDFLAGS = -module -avoid-version -shared
+STOImporter_la_SOURCES = STOImporter.cpp STOImporter.h
diff --git a/gemrb/plugins/STOImporter/STOImporter.cpp b/gemrb/plugins/STOImporter/STOImporter.cpp
new file mode 100644
index 0000000..14dabd0
--- /dev/null
+++ b/gemrb/plugins/STOImporter/STOImporter.cpp
@@ -0,0 +1,410 @@
+/* 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 "STOImporter.h"
+
+#include "win32def.h"
+
+#include "GameData.h"
+#include "Interface.h"
+#include "Inventory.h"
+#include "GameScript/GameScript.h"
+
+STOImporter::STOImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+STOImporter::~STOImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+}
+
+bool STOImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "STORV1.0", 8 ) == 0) {
+ version = 10;
+ } else if (strncmp( Signature, "STORV1.1", 8 ) == 0) {
+ version = 11;
+ } else if (strncmp( Signature, "STORV9.0", 8 ) == 0) {
+ version = 90;
+ } else if (strncmp( Signature, "STORV0.0", 8 ) == 0) {
+ //GemRB's internal version with all known fields supported
+ version = 0;
+ } else {
+ printf( "[STOImporter]: This file is not a valid STO File\n" );
+ return false;
+ }
+
+ return true;
+}
+
+Store* STOImporter::GetStore(Store *s)
+{
+ unsigned int i;
+
+ if (!s)
+ return NULL;
+
+ // saving in original version requires the original version
+ // otherwise it is set to 0 at construction time
+ if (core->SaveAsOriginal) {
+ s->version = version;
+ }
+
+ str->ReadDword( &s->Type );
+ str->ReadDword( &s->StoreName );
+ str->ReadDword( &s->Flags );
+ str->ReadDword( &s->SellMarkup );
+ str->ReadDword( &s->BuyMarkup );
+ str->ReadDword( &s->DepreciationRate );
+ str->ReadWord( &s->StealFailureChance );
+ str->ReadWord( &s->Capacity ); //will be overwritten for V9.0
+ str->Read( s->unknown, 8 );
+ str->ReadDword( &s->PurchasedCategoriesOffset );
+ str->ReadDword( &s->PurchasedCategoriesCount );
+ str->ReadDword( &s->ItemsOffset );
+ str->ReadDword( &s->ItemsCount );
+ str->ReadDword( &s->Lore );
+ str->ReadDword( &s->IDPrice );
+ str->ReadResRef( s->RumoursTavern );
+ str->ReadDword( &s->DrinksOffset );
+ str->ReadDword( &s->DrinksCount );
+ str->ReadResRef( s->RumoursTemple );
+ str->ReadDword( &s->AvailableRooms );
+ for (i = 0; i < 4; i++) {
+ str->ReadDword( &s->RoomPrices[i] );
+ }
+ str->ReadDword( &s->CuresOffset );
+ str->ReadDword( &s->CuresCount );
+ str->Read( s->unknown2, 36 );
+
+ if (version == 90) { //iwd stores
+ ieDword tmp;
+
+ str->ReadDword( &tmp );
+ s->Capacity = (ieWord) tmp;
+ str->Read( s->unknown3, 80 );
+ } else {
+ memset( s->unknown3, 0, 80 );
+ }
+
+ //Allocation must be done in the same place as destruction.
+ //Yeah, this is intentionally so ugly, someone who doesn't like this
+ //may fix it.
+ core->DoTheStoreHack(s);
+
+ str->Seek( s->PurchasedCategoriesOffset, GEM_STREAM_START );
+ GetPurchasedCategories( s );
+
+ str->Seek( s->ItemsOffset, GEM_STREAM_START );
+ for (i = 0; i < s->ItemsCount; i++) {
+ STOItem *item = s->items[i];
+ GetItem(item);
+ //it is important to handle this field as signed
+ if (item->InfiniteSupply>0) {
+ char *TriggerCode = core->GetString( (ieStrRef) item->InfiniteSupply );
+ item->trigger = GenerateTrigger(TriggerCode);
+ core->FreeString(TriggerCode);
+
+ //if there are no triggers, GetRealStockSize is simpler
+ //also it is compatible only with pst/gemrb saved stores
+ s->HasTriggers=true;
+ }
+ }
+
+ str->Seek( s->DrinksOffset, GEM_STREAM_START );
+ for (i = 0; i < s->DrinksCount; i++) {
+ GetDrink(s->drinks+i);
+ }
+
+ str->Seek( s->CuresOffset, GEM_STREAM_START );
+ for (i = 0; i < s->CuresCount; i++) {
+ GetCure(s->cures+i);
+ }
+
+ return s;
+}
+
+void STOImporter::GetItem(STOItem *it)
+{
+ core->ReadItem(str, (CREItem *) it);
+
+ str->ReadDword( &it->AmountInStock );
+ //if there was no item on stock, how this could be 0
+ //we hack-fix this here so it won't cause trouble
+ if (!it->AmountInStock) {
+ it->AmountInStock = 1;
+ }
+ //another hack-fix
+ Item *item = gamedata->GetItem( it->ItemResRef );
+ if (item) {
+ if (!item->LoreToID) {
+ it->Flags |= IE_INV_ITEM_IDENTIFIED;
+ }
+ gamedata->FreeItem( item, it->ItemResRef, false );
+ }
+ str->ReadDword( (ieDword *) &it->InfiniteSupply );
+ ieDwordSigned tmp;
+
+ switch (version) {
+ case 11: //pst
+ if (it->InfiniteSupply) {
+ it->InfiniteSupply=-1;
+ }
+ str->ReadDword( (ieDword *) &tmp );
+ if (tmp>0) {
+ it->InfiniteSupply=tmp;
+ }
+ str->Read( it->unknown2, 56 );
+ break;
+ case 0: //gemrb version stores trigger ref in infinitesupply
+ memset( it->unknown2, 0, 56 );
+ break;
+ default:
+ if (it->InfiniteSupply) {
+ it->InfiniteSupply=-1;
+ }
+ memset( it->unknown2, 0, 56 );
+ }
+}
+
+void STOImporter::GetDrink(STODrink *dr)
+{
+ str->ReadResRef( dr->RumourResRef );
+ str->ReadDword( &dr->DrinkName );
+ str->ReadDword( &dr->Price );
+ str->ReadDword( &dr->Strength );
+}
+
+void STOImporter::GetCure(STOCure *cu)
+{
+ str->ReadResRef( cu->CureResRef );
+ str->ReadDword( &cu->Price );
+}
+
+void STOImporter::GetPurchasedCategories(Store* s)
+{
+ for (unsigned int i = 0; i < s->PurchasedCategoriesCount; i++) {
+ str->ReadDword( &s->purchased_categories[i] );
+ }
+}
+
+//call this before any write, it updates offsets!
+int STOImporter::GetStoredFileSize(Store *s)
+{
+ int headersize, itemsize;
+
+ //header
+ switch (s->version) {
+ case 90:
+ //capacity on a dword and 80 bytes of crap
+ headersize = 156 + 84;
+ itemsize = 28;
+ break;
+ case 11:
+ headersize = 156;
+ //trigger ref on a dword and 56 bytes of crap
+ itemsize = 28 + 60;
+ default:
+ headersize = 156;
+ itemsize = 28;
+ break;
+ }
+
+ //drinks
+ s->DrinksOffset = headersize;
+ headersize += s->DrinksCount * 20; //8+4+4+4
+
+ //cures
+ s->CuresOffset = headersize;
+ headersize += s->CuresCount * 12; //8+4
+
+ //purchased items
+ s->PurchasedCategoriesOffset = headersize;
+ headersize += s->PurchasedCategoriesCount * sizeof(ieDword);
+
+ //items
+ s->ItemsOffset = headersize;
+ headersize += s->ItemsCount * itemsize;
+
+ return headersize;
+}
+
+int STOImporter::PutPurchasedCategories(DataStream *stream, Store* s)
+{
+ for (unsigned int i = 0; i < s->PurchasedCategoriesCount; i++) {
+ stream->WriteDword( s->purchased_categories+i );
+ }
+ return 0;
+}
+
+int STOImporter::PutHeader(DataStream *stream, Store *s)
+{
+ char Signature[8];
+ ieDword tmpDword;
+ ieWord tmpWord;
+
+ version = s->version;
+ memcpy( Signature, "STORV0.0", 8);
+ Signature[5]+=version/10;
+ Signature[7]+=version%10;
+ stream->Write( Signature, 8);
+ stream->WriteDword( &s->Type);
+ stream->WriteDword( &s->StoreName);
+ stream->WriteDword( &s->Flags);
+ stream->WriteDword( &s->SellMarkup);
+ stream->WriteDword( &s->BuyMarkup);
+ stream->WriteDword( &s->DepreciationRate);
+ stream->WriteWord( &s->StealFailureChance);
+
+ switch (version) {
+ case 10: case 0: // bg2, gemrb
+ tmpWord = s->Capacity;
+ break;
+ default:
+ tmpWord = 0;
+ break;
+ }
+ stream->WriteWord( &tmpWord);
+
+ stream->Write( s->unknown, 8);
+ stream->WriteDword( &s->PurchasedCategoriesOffset);
+ stream->WriteDword( &s->PurchasedCategoriesCount);
+ stream->WriteDword( &s->ItemsOffset);
+ stream->WriteDword( &s->ItemsCount);
+ stream->WriteDword( &s->Lore);
+ stream->WriteDword( &s->IDPrice);
+ stream->WriteResRef( s->RumoursTavern);
+ stream->WriteDword( &s->DrinksOffset);
+ stream->WriteDword( &s->DrinksCount);
+ stream->WriteResRef( s->RumoursTemple);
+ stream->WriteDword( &s->AvailableRooms);
+ for (int i=0;i<4;i++) {
+ stream->WriteDword( s->RoomPrices+i );
+ }
+ stream->WriteDword( &s->CuresOffset);
+ stream->WriteDword( &s->CuresCount);
+ stream->Write (s->unknown3, 36); //use these as padding
+ if (version==90) {
+ tmpDword = s->Capacity;
+ stream->WriteDword( &tmpDword);
+ stream->Write( s->unknown3, 80); //writing out original fillers
+ }
+ return 0;
+}
+
+int STOImporter::PutItems(DataStream *stream, Store *store)
+{
+ for (unsigned int ic=0;ic<store->ItemsCount;ic++) {
+ STOItem *it = store->items[ic];
+
+ stream->WriteResRef( it->ItemResRef);
+ stream->WriteWord( &it->PurchasedAmount);
+ for (unsigned int i=0;i<CHARGE_COUNTERS;i++) {
+ stream->WriteWord( it->Usages+i );
+ }
+ stream->WriteDword( &it->Flags );
+ stream->WriteDword( &it->AmountInStock );
+ if (version==11) {
+ stream->WriteDword( (ieDword *) &it->InfiniteSupply);
+ stream->WriteDword( (ieDword *) &it->InfiniteSupply);
+ stream->Write( it->unknown2, 56);
+ } else {
+ stream->WriteDword( (ieDword *) &it->InfiniteSupply );
+ }
+ }
+ return 0;
+}
+
+int STOImporter::PutCures(DataStream *stream, Store *s)
+{
+ for (unsigned int i=0;i<s->CuresCount;i++) {
+ STOCure *c = s->cures+i;
+ stream->WriteResRef( c->CureResRef);
+ stream->WriteDword( &c->Price);
+ }
+ return 0;
+}
+
+int STOImporter::PutDrinks(DataStream *stream, Store *s)
+{
+ for (unsigned int i=0;i<s->DrinksCount;i++) {
+ STODrink *d = s->drinks+i;
+ stream->WriteResRef( d->RumourResRef); //?
+ stream->WriteDword( &d->DrinkName);
+ stream->WriteDword( &d->Price);
+ stream->WriteDword( &d->Strength);
+ }
+ return 0;
+}
+
+//saves the store into a datastream, be it memory or file
+int STOImporter::PutStore(DataStream *stream, Store *store)
+{
+ int ret;
+
+ if (!stream || !store) {
+ return -1;
+ }
+
+ ret = PutHeader( stream, store);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutDrinks( stream, store);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutCures( stream, store);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutPurchasedCategories (stream, store);
+ if (ret) {
+ return ret;
+ }
+
+ ret = PutItems( stream, store);
+
+ return ret;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x1CDFC160, "STO File Importer")
+PLUGIN_CLASS(IE_STO_CLASS_ID, STOImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/STOImporter/STOImporter.h b/gemrb/plugins/STOImporter/STOImporter.h
new file mode 100644
index 0000000..c5c2025
--- /dev/null
+++ b/gemrb/plugins/STOImporter/STOImporter.h
@@ -0,0 +1,62 @@
+/* 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 STOIMPORTER_H
+#define STOIMPORTER_H
+
+#include "StoreMgr.h"
+
+#include "ie_types.h"
+
+#include "Store.h"
+
+
+class STOImporter : public StoreMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ int version;
+
+public:
+ STOImporter(void);
+ ~STOImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ Store* GetStore(Store *store);
+
+ //returns saved size, updates internal offsets before save
+ int GetStoredFileSize(Store *st);
+ //saves file
+ int PutStore(DataStream *stream, Store *store);
+
+private:
+ void GetItem(STOItem *item);
+ void GetDrink(STODrink *drink);
+ void GetCure(STOCure *cure);
+ void GetPurchasedCategories(Store* s);
+
+ int PutItems(DataStream *stream, Store* s);
+ int PutDrinks(DataStream *stream, Store* s);
+ int PutCures(DataStream *stream, Store* s);
+ int PutPurchasedCategories(DataStream *stream, Store* s);
+ int PutHeader(DataStream *stream, Store *store);
+};
+
+
+#endif
diff --git a/gemrb/plugins/TISImporter/CMakeLists.txt b/gemrb/plugins/TISImporter/CMakeLists.txt
new file mode 100644
index 0000000..2ceea26
--- /dev/null
+++ b/gemrb/plugins/TISImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (TISImporter TISImporter.cpp)
diff --git a/gemrb/plugins/TISImporter/Makefile.am b/gemrb/plugins/TISImporter/Makefile.am
new file mode 100644
index 0000000..9df7bf8
--- /dev/null
+++ b/gemrb/plugins/TISImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = TISImporter.la
+TISImporter_la_LDFLAGS = -module -avoid-version -shared
+TISImporter_la_SOURCES = TISImporter.cpp TISImporter.h
diff --git a/gemrb/plugins/TISImporter/TISImporter.cpp b/gemrb/plugins/TISImporter/TISImporter.cpp
new file mode 100644
index 0000000..3a5cb19
--- /dev/null
+++ b/gemrb/plugins/TISImporter/TISImporter.cpp
@@ -0,0 +1,145 @@
+/* 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 "TISImporter.h"
+
+#include "RGBAColor.h"
+#include "win32def.h"
+
+#include "Interface.h"
+#include "Video.h"
+
+TISImporter::TISImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+TISImporter::~TISImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+}
+
+bool TISImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ headerShift = 0;
+ if (Signature[0] == 'T' && Signature[1] == 'I' && Signature[2] == 'S') {
+ if (strncmp( Signature, "TIS V1 ", 8 ) != 0) {
+ printf( "[TISImporter]: Not a Valid TIS File.\n" );
+ return false;
+ }
+ str->ReadDword( &TilesCount );
+ str->ReadDword( &TilesSectionLen );
+ str->ReadDword( &headerShift );
+ str->ReadDword( &TileSize );
+ } else {
+ str->Seek( -8, GEM_CURRENT_POS );
+ }
+ return true;
+}
+
+Tile* TISImporter::GetTile(unsigned short* indexes, int count,
+ unsigned short* secondary)
+{
+ Animation* ani = new Animation( count );
+ ani->x = ani->y = 0;
+ //pause key stops animation
+ ani->gameAnimation = true;
+ for (int i = 0; i < count; i++) {
+ ani->AddFrame( GetTile( indexes[i] ), i );
+ }
+ if (secondary) {
+ Animation* sec = new Animation( count );
+ sec->x = sec->y = 0;
+ for (int i = 0; i < count; i++) {
+ sec->AddFrame( GetTile( secondary[i] ), i );
+ }
+ return new Tile( ani, sec );
+ }
+ return new Tile( ani );
+}
+
+Sprite2D* TISImporter::GetTile(int index)
+{
+ RevColor RevCol[256];
+ Color Palette[256];
+ void* pixels = malloc( 4096 );
+ unsigned long pos = index *(1024+4096) + headerShift;
+ if(str->Size()<pos+1024+4096) {
+ // try to only report error once per file
+ static TISImporter *last_corrupt = NULL;
+ if (last_corrupt != this) {
+ /*printf("Invalid tile index: %d\n",index);
+ printf("FileSize: %ld\n", str->Size() );
+ printf("Position: %ld\n", pos);
+ printf("Shift: %d\n", headerShift);*/
+ printf("Corrupt WED file encountered; couldn't find any more tiles at tile %d\n", index);
+ last_corrupt = this;
+ }
+
+ // original PS:T AR0609 and AR0612 report far more tiles than are actually present :(
+ memset(pixels, 0, 4096);
+ memset(Palette, 0, 256 * sizeof(Color));
+ Palette[0].g = 200;
+ Sprite2D* spr = core->GetVideoDriver()->CreateSprite8( 64, 64, 8, pixels, Palette, false, 0 );
+ spr->XPos = spr->YPos = 0;
+ return spr;
+ }
+ str->Seek( ( index * ( 1024 + 4096 ) + headerShift ), GEM_STREAM_START );
+ str->Read( &RevCol, 1024 );
+ int transindex = 0;
+ bool transparent = false;
+ for (int i = 0; i < 256; i++) {
+ Palette[i].r = RevCol[i].r;
+ Palette[i].g = RevCol[i].g;
+ Palette[i].b = RevCol[i].b;
+ Palette[i].a = RevCol[i].a;
+ if (Palette[i].g==255 && !Palette[i].r && !Palette[i].b) {
+ if (transparent) {
+ printMessage( "TISImporter", "Tile has two green (transparent) palette entries\n", LIGHT_RED );
+ } else {
+ transparent = true;
+ transindex = i;
+ }
+ }
+ }
+ str->Read( pixels, 4096 );
+ Sprite2D* spr = core->GetVideoDriver()->CreateSprite8( 64, 64, 8, pixels, Palette, transparent, transindex );
+ spr->XPos = spr->YPos = 0;
+ return spr;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x19F91578, "TIS File Importer")
+PLUGIN_CLASS(IE_TIS_CLASS_ID, TISImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/TISImporter/TISImporter.h b/gemrb/plugins/TISImporter/TISImporter.h
new file mode 100644
index 0000000..0c049d9
--- /dev/null
+++ b/gemrb/plugins/TISImporter/TISImporter.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 TISIMPORTER_H
+#define TISIMPORTER_H
+
+#include "TileSetMgr.h"
+
+class TISImporter : public TileSetMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+ ieDword headerShift;
+ ieDword TilesCount, TilesSectionLen, TileSize;
+public:
+ TISImporter(void);
+ ~TISImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ Tile* GetTile(unsigned short* indexes, int count,
+ unsigned short* secondary = NULL);
+ Sprite2D* GetTile(int index);
+public:
+};
+
+#endif
diff --git a/gemrb/plugins/TLKImporter/CMakeLists.txt b/gemrb/plugins/TLKImporter/CMakeLists.txt
new file mode 100644
index 0000000..6628d21
--- /dev/null
+++ b/gemrb/plugins/TLKImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (TLKImporter TLKImporter.cpp TlkOverride.cpp)
diff --git a/gemrb/plugins/TLKImporter/Makefile.am b/gemrb/plugins/TLKImporter/Makefile.am
new file mode 100644
index 0000000..746e67e
--- /dev/null
+++ b/gemrb/plugins/TLKImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = TLKImporter.la
+TLKImporter_la_LDFLAGS = -module -avoid-version -shared
+TLKImporter_la_SOURCES = TLKImporter.cpp TLKImporter.h TlkOverride.cpp TlkOverride.h
diff --git a/gemrb/plugins/TLKImporter/TLKImporter.cpp b/gemrb/plugins/TLKImporter/TLKImporter.cpp
new file mode 100644
index 0000000..ed5732f
--- /dev/null
+++ b/gemrb/plugins/TLKImporter/TLKImporter.cpp
@@ -0,0 +1,496 @@
+/* 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 "TLKImporter.h"
+
+#include "win32def.h"
+
+#include "Audio.h"
+#include "Calendar.h"
+#include "DialogHandler.h"
+#include "Game.h"
+#include "Interface.h"
+#include "GUI/GameControl.h"
+
+//set this to -1 if charname is gabber (iwd2)
+static int charname=0;
+struct gt_type
+{
+ int type;
+ ieStrRef male;
+ ieStrRef female;
+};
+static Variables gtmap;
+
+TLKImporter::TLKImporter(void)
+{
+ int gtcount;
+
+ gtmap.RemoveAll(NULL);
+ gtmap.SetType(GEM_VARIABLES_POINTER);
+
+ if (core->HasFeature(GF_CHARNAMEISGABBER)) {
+ charname=-1;
+ } else {
+ charname=0;
+ }
+ str = NULL;
+ override = NULL;
+ autoFree = false;
+
+ AutoTable tm("gender");
+ if (tm) {
+ gtcount = tm->GetRowCount();
+ } else {
+ gtcount = 0;
+ }
+ for(int i=0;i<gtcount;i++) {
+ ieVariable key;
+
+ strnuprcpy(key, tm->GetRowName(i), sizeof(ieVariable)-1 );
+ gt_type *entry = (gt_type *) new gt_type;
+ entry->type = atoi(tm->QueryField(i,0));
+ entry->male = atoi(tm->QueryField(i,1));
+ entry->female = atoi(tm->QueryField(i,2));
+ gtmap.SetAt(key, (void *) entry);
+ }
+}
+
+void ReleaseGtEntry(void *poi)
+{
+ delete (gt_type *) poi;
+}
+
+TLKImporter::~TLKImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+
+
+ gtmap.RemoveAll(ReleaseGtEntry);
+
+ CloseAux();
+}
+
+void TLKImporter::CloseAux()
+{
+ if (override) {
+ delete override;
+ }
+ override = NULL;
+}
+
+void TLKImporter::OpenAux()
+{
+ CloseAux();
+ override = new CTlkOverride();
+ if (override) {
+ if (!override->Init()) {
+ CloseAux();
+ printMessage("TlkImporter","Cannot open tlk override!\n", LIGHT_RED);
+ }
+ }
+}
+
+bool TLKImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "TLK\x20V1\x20\x20", 8 ) != 0) {
+ printMessage( "TLKImporter","Not a valid TLK File.\n", LIGHT_RED );
+ return false;
+ }
+ str->Seek( 2, GEM_CURRENT_POS );
+ str->ReadDword( &StrRefCount );
+ str->ReadDword( &Offset );
+ return true;
+}
+
+inline char* mystrncpy(char* dest, const char* source, int maxlength,
+ char delim)
+{
+ while (*source && ( *source != delim ) && maxlength--) {
+ *dest++ = *source++;
+ }
+ *dest = 0;
+ return dest;
+}
+
+/* -1 - GABBER
+ 0 - PROTAGONIST
+ 1-9 - PLAYERx
+*/
+inline Actor *GetActorFromSlot(int slot)
+{
+ if (slot==-1) {
+ GameControl *gc = core->GetGameControl();
+ if (gc) {
+ return gc->dialoghandler->GetSpeaker();
+ }
+ return NULL;
+ }
+ Game *game = core->GetGame();
+ if (!game) {
+ return NULL;
+ }
+ if (slot==0) {
+ return game->GetPC(0, false); //protagonist
+ }
+ return game->FindPC(slot);
+}
+
+char *TLKImporter::Gabber()
+{
+ Actor *act;
+
+ act=core->GetGameControl()->dialoghandler->GetSpeaker();
+ if (act) {
+ return override->CS(act->LongName);
+ }
+ return override->CS("?");
+}
+
+char *TLKImporter::CharName(int slot)
+{
+ Actor *act;
+
+ act=GetActorFromSlot(slot);
+ if (act) {
+ return override->CS(act->LongName);
+ }
+ return override->CS("?");
+}
+
+int TLKImporter::RaceStrRef(int slot)
+{
+ Actor *act;
+ int race;
+
+ act=GetActorFromSlot(slot);
+ if (act) {
+ race=act->GetStat(IE_RACE);
+ } else {
+ race=0;
+ }
+
+ AutoTable tab("races");
+ if (!tab) {
+ return -1;
+ }
+ int row = tab->FindTableValue(3, race, 0);
+ return atoi(tab->QueryField(row,0) );
+}
+
+int TLKImporter::GenderStrRef(int slot, int malestrref, int femalestrref)
+{
+ Actor *act;
+
+ act = GetActorFromSlot(slot);
+ if (act && (act->GetStat(IE_SEX)==SEX_FEMALE) ) {
+ return femalestrref;
+ }
+ return malestrref;
+}
+
+//if this function returns -1 then it is not a built in token, dest may be NULL
+int TLKImporter::BuiltinToken(char* Token, char* dest)
+{
+ char* Decoded = NULL;
+ int TokenLength; //decoded token length
+ gt_type *entry;
+
+ //these are gender specific tokens, they are customisable by gender.2da
+ if (gtmap.Lookup(Token, (void *&) entry) ) {
+ Decoded = GetString( GenderStrRef(entry->type, entry->male, entry->female) );
+ goto exit_function;
+ }
+
+ //these are hardcoded, all engines are the same or don't use them
+ if (!strcmp( Token, "DAYANDMONTH")) {
+ ieDword dayandmonth=0;
+ core->GetDictionary()->Lookup("DAYANDMONTH",dayandmonth);
+ //preparing sub-tokens
+ core->GetCalendar()->GetMonthName((int) dayandmonth);
+ Decoded = GetString( 15981, 0 );
+ goto exit_function;
+ }
+
+ if (!strcmp( Token, "FIGHTERTYPE" )) {
+ Decoded = GetString( 10174, 0 );
+ goto exit_function;
+ }
+ if (!strcmp( Token, "RACE" )) {
+ Decoded = GetString( RaceStrRef(-1), 0);
+ goto exit_function;
+ }
+ if (!strncmp( Token, "PLAYER",6 )) {
+ Decoded = CharName(Token[6]-'1');
+ goto exit_function;
+ }
+
+ if (!strcmp( Token, "GABBER" )) {
+ Decoded = Gabber();
+ goto exit_function;
+ }
+ if (!strcmp( Token, "CHARNAME" )) {
+ Decoded = CharName(charname);
+ goto exit_function;
+ }
+ if (!strcmp( Token, "PRO_RACE" )) {
+ Decoded = GetString( RaceStrRef(0), 0);
+ goto exit_function;
+ }
+ if (!strcmp( Token, "MAGESCHOOL" )) {
+ ieDword row = 0; //default value is 0 (generalist)
+ //this is subject to change, the row number in magesch.2da
+ core->GetDictionary()->Lookup( "MAGESCHOOL", row );
+ AutoTable tm("magesch");
+ if (tm) {
+ const char* value = tm->QueryField( row, 2 );
+ Decoded = GetString( atoi( value ), 0 );
+ goto exit_function;
+ }
+ }
+
+ return -1; //not decided
+
+ exit_function:
+ if (Decoded) {
+ TokenLength = ( int ) strlen( Decoded );
+ if (dest) {
+ memcpy( dest, Decoded, TokenLength );
+ }
+ //Decoded is always a copy
+ free( Decoded );
+ return TokenLength;
+ }
+ return -1;
+}
+
+bool TLKImporter::ResolveTags(char* dest, char* source, int Length)
+{
+ int NewLength;
+ char Token[MAX_VARIABLE_LENGTH + 1];
+
+ NewLength = 0;
+ for (int i = 0; source[i]; i++) {
+ if (source[i] == '<') {
+ i += (int) (mystrncpy( Token, source + i + 1, MAX_VARIABLE_LENGTH, '>' ) - Token) + 1;
+ int TokenLength = BuiltinToken( Token, dest + NewLength );
+ if (TokenLength == -1) {
+ TokenLength = core->GetTokenDictionary()->GetValueLength( Token );
+ if (TokenLength) {
+ if (TokenLength + NewLength > Length)
+ return false;
+ core->GetTokenDictionary()->Lookup( Token, dest + NewLength, TokenLength );
+ }
+ }
+ NewLength += TokenLength;
+ } else {
+ if (source[i] == '[') {
+ const char* tmppoi = strchr( source + i + 1, ']' );
+ if (tmppoi)
+ i = (int) (tmppoi - source);
+ else
+ break;
+ } else
+ dest[NewLength++] = source[i];
+ if (NewLength > Length)
+ return false;
+ }
+ }
+ dest[NewLength] = 0;
+ return true;
+}
+
+bool TLKImporter::GetNewStringLength(char* string, int& Length)
+{
+ int NewLength;
+ bool lChange;
+ char Token[MAX_VARIABLE_LENGTH + 1];
+
+ lChange = false;
+ NewLength = 0;
+ for (int i = 0; i < Length; i++) {
+ if (string[i] == '<') {
+ // token
+ lChange = true;
+ i += (int) (mystrncpy( Token, string + i + 1, MAX_VARIABLE_LENGTH, '>' ) - Token) + 1;
+ int TokenLength = BuiltinToken( Token, NULL );
+ if (TokenLength == -1) {
+ NewLength += core->GetTokenDictionary()->GetValueLength( Token );
+ } else {
+ NewLength += TokenLength;
+ }
+ } else {
+ if (string[i] == '[') {
+ //voice actor directives
+ lChange = true;
+ const char* tmppoi = strchr( string + i + 1, ']' );
+ if (tmppoi)
+ i += (int) (tmppoi - string) - i;
+ else
+ break;
+ } else {
+ NewLength++;
+ }
+ }
+ }
+ Length = NewLength;
+ return lChange;
+}
+
+ieStrRef TLKImporter::UpdateString(ieStrRef strref, const char *newvalue)
+{
+ if (!override) {
+ printMessage("TLKImporter", "Custom string is not supported by this game format.\n", LIGHT_RED);
+ return 0xffffffff;
+ }
+
+ if(strref>STRREF_START || (strref>=BIO_START && strref<=BIO_END) ) {
+ return override->UpdateString(strref, newvalue);
+ }
+
+ printMessage("TLKImporter", "Cannot set custom string.\n", LIGHT_RED);
+ return 0xffffffff;
+}
+
+char* TLKImporter::GetString(ieStrRef strref, ieDword flags)
+{
+ char* string;
+
+ if (!(flags&IE_STR_ALLOW_ZERO) && !strref) {
+ goto empty;
+ }
+ ieWord type;
+ int Length;
+ ieResRef SoundResRef;
+
+ if((strref>=STRREF_START) || (strref>=BIO_START && strref<=BIO_END) ) {
+empty:
+ string = override->ResolveAuxString(strref, Length);
+ type = 0;
+ SoundResRef[0]=0;
+ } else {
+ ieDword Volume, Pitch, StrOffset;
+ ieDword l;
+ str->Seek( 18 + ( strref * 0x1A ), GEM_STREAM_START );
+ str->ReadWord( &type );
+ str->ReadResRef( SoundResRef );
+ str->ReadDword( &Volume );
+ str->ReadDword( &Pitch );
+ str->ReadDword( &StrOffset );
+ str->ReadDword( &l );
+ if (l > 65535) {
+ Length = 65535; //safety limit, it could be a dword actually
+ }
+ else {
+ Length = l;
+ }
+
+ if (type & 1) {
+ str->Seek( StrOffset + Offset, GEM_STREAM_START );
+ string = ( char * ) malloc( Length + 1 );
+ str->Read( string, Length );
+ } else {
+ Length = 0;
+ string = ( char * ) malloc( 1 );
+ }
+ string[Length] = 0;
+ }
+
+ //tagged text, bg1 and iwd don't mark them specifically, all entries are tagged
+ if (core->HasFeature( GF_ALL_STRINGS_TAGGED ) || ( type & 4 )) {
+ //GetNewStringLength will look in string and return true
+ //if the new Length will change due to tokens
+ //if there is no new length, we are done
+ while (GetNewStringLength( string, Length )) {
+ char* string2 = ( char* ) malloc( Length + 1 );
+ //ResolveTags will copy string to string2
+ ResolveTags( string2, string, Length );
+ free( string );
+ string = string2;
+ }
+ }
+ if (( type & 2 ) && ( flags & IE_STR_SOUND )) {
+ //if flags&IE_STR_SOUND play soundresref
+ if (SoundResRef[0] != 0) {
+ int xpos = 0;
+ int ypos = 0;
+ unsigned int flag = GEM_SND_RELATIVE | (flags&GEM_SND_SPEECH);
+ //IE_STR_SPEECH will stop the previous sound source
+ core->GetAudioDrv()->Play( SoundResRef, xpos, ypos, flag);
+ }
+ }
+ if (flags & IE_STR_STRREFON) {
+ char* string2 = ( char* ) malloc( Length + 13 );
+ sprintf( string2, "%u: %s", strref, string );
+ free( string );
+ return string2;
+ }
+ // remove the linefeed and carriage return if requested
+ if ((flags & IE_STR_REMOVE_NEWLINE)) {
+ core->StripLine( string, Length);
+ }
+ return string;
+}
+
+StringBlock TLKImporter::GetStringBlock(ieStrRef strref, unsigned int flags)
+{
+ StringBlock sb;
+
+ if (!(flags&IE_STR_ALLOW_ZERO) && !strref) {
+ goto empty;
+ }
+ if (strref >= StrRefCount) {
+empty:
+ sb.text = ( char * ) malloc( 1 );
+ sb.text[0] = 0;
+ sb.Sound[0] = 0;
+ return sb;
+ }
+ sb.text = GetString( strref, flags );
+ ieWord type;
+ str->Seek( 18 + ( strref * 0x1A ), GEM_STREAM_START );
+ str->ReadWord( &type );
+ str->ReadResRef( sb.Sound );
+ return sb;
+}
+
+void TLKImporter::FreeString(char *str)
+{
+ free(str);
+}
+
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0xBB6F380, "TLK File Importer")
+PLUGIN_CLASS(IE_TLK_CLASS_ID, TLKImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/TLKImporter/TLKImporter.h b/gemrb/plugins/TLKImporter/TLKImporter.h
new file mode 100644
index 0000000..c2a03e0
--- /dev/null
+++ b/gemrb/plugins/TLKImporter/TLKImporter.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 TLKIMPORTER_H
+#define TLKIMPORTER_H
+
+#include "StringMgr.h"
+
+#include "TlkOverride.h"
+
+class TLKImporter : public StringMgr {
+private:
+ DataStream* str;
+ bool autoFree;
+
+ //Data
+ ieDword StrRefCount, Offset;
+ CTlkOverride *override;
+
+public:
+ TLKImporter(void);
+ ~TLKImporter(void);
+ /** open string refs coming from saved game */
+ void OpenAux();
+ /** purge string defs coming from saved game */
+ void CloseAux();
+ bool Open(DataStream* stream, bool autoFree = true);
+ /** construct a new custom string */
+ ieStrRef UpdateString(ieStrRef strref, const char *newvalue);
+ /** resolve a string reference */
+ char* GetString(ieStrRef strref, ieDword flags = 0);
+ StringBlock GetStringBlock(ieStrRef strref, unsigned int flags = 0);
+ void FreeString(char *str);
+private:
+ /** resolves day and monthname tokens */
+ void GetMonthName(int dayandmonth);
+ /** replaces tags in dest, don't exceed Length */
+ bool ResolveTags(char* dest, char* source, int Length);
+ /** returns the needed length in Length,
+ if there was no token, returns false */
+ bool GetNewStringLength(char* string, int& Length);
+ /**returns the decoded length of the built-in token
+ if dest is not NULL it also returns the decoded value */
+ int BuiltinToken(char* Token, char* dest);
+ int RaceStrRef(int slot);
+ int GenderStrRef(int slot, int malestrref, int femalestrref);
+ char *Gabber();
+ char *CharName(int slot);
+};
+
+#endif
diff --git a/gemrb/plugins/TLKImporter/TlkOverride.cpp b/gemrb/plugins/TLKImporter/TlkOverride.cpp
new file mode 100644
index 0000000..74a1498
--- /dev/null
+++ b/gemrb/plugins/TLKImporter/TlkOverride.cpp
@@ -0,0 +1,342 @@
+/* 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.
+ *
+ *
+ * TlkOverride.cpp: implementation of the saved game specific
+ * (dynamic) part of the talk table (tlk)
+ */
+
+#include "TlkOverride.h"
+
+#include <cstdio>
+#include <cassert>
+
+CTlkOverride::CTlkOverride()
+{
+ tot_str=NULL;
+ toh_str=NULL;
+}
+
+CTlkOverride::~CTlkOverride()
+{
+ CloseResources();
+}
+
+char *CTlkOverride::CS(const char *src)
+{
+ if(!src) return NULL;
+ int len=strlen(src)+1;
+ char *ret = (char *) malloc(len);
+ memcpy(ret, src, len);
+ return ret;
+}
+
+bool CTlkOverride::Init()
+{
+ CloseResources();
+ //Creation of the headers should be game specific, some games don't have these
+ toh_str = GetAuxHdr(true);
+ tot_str = GetAuxTlk(true);
+ if (toh_str == NULL) {
+ return false;
+ }
+ if (tot_str == NULL) {
+ return false;
+ }
+
+ char Signature[8];
+
+ memset(Signature,0,8);
+ toh_str->Read( Signature, 4 );
+ if (strncmp( Signature, "TLK ", 4 ) != 0) {
+ printMessage("TLKImporter", "Not a valid TOH file.\n", LIGHT_RED);
+ return false;
+ }
+ toh_str->Seek( 8, GEM_CURRENT_POS );
+ toh_str->ReadDword( &AuxCount );
+
+ tot_str->ReadDword( &FreeOffset );
+ tot_str->Read(Signature,4);
+ if (strncmp( Signature, "\xff\xff\xff\xff",4) !=0) {
+ printMessage("TLKImporter", "Not a valid TOT file.\n", LIGHT_RED);
+ return false;
+ }
+
+ return true;
+}
+
+void CTlkOverride::UpdateFreeOffset(ieDword NewFree)
+{
+ if (NewFree!=0xffffffff) {
+ tot_str->Seek(NewFree,GEM_STREAM_START);
+ tot_str->WriteDword( &FreeOffset);
+ }
+ FreeOffset=NewFree;
+ tot_str->Seek(0, GEM_STREAM_START);
+ tot_str->WriteDword( &FreeOffset );
+}
+
+void CTlkOverride::CloseResources()
+{
+ if (toh_str) {
+ delete toh_str;
+ toh_str=NULL;
+ }
+ if (tot_str) {
+ delete tot_str;
+ tot_str=NULL;
+ }
+#ifdef CACHE_TLK_OVERRIDE
+ stringmap.clear();
+#endif
+}
+
+//gets the length of a stored string which might span more than one segment
+ieDword CTlkOverride::GetLength()
+{
+ ieDword tmp;
+ char buffer[SEGMENT_SIZE];
+
+ ieDword length = 0;
+ do
+ {
+ memset(buffer,0,sizeof(buffer));
+ tot_str->Read(buffer, SEGMENT_SIZE);
+ tot_str->ReadDword(&tmp);
+ if (tmp!=0xffffffff) {
+ tot_str->Seek(tmp+8,GEM_STREAM_START);
+ length+=SEGMENT_SIZE;
+ }
+ }
+ while(tmp!=0xffffffff);
+ length += strlen(buffer);
+ return length;
+}
+
+//returns a string stored at a given offset of the .tot file
+char* CTlkOverride::LocateString2(ieDword offset)
+{
+ if (!tot_str) {
+ return NULL;
+ }
+
+ if (tot_str->Seek(offset+8, GEM_STREAM_START)!=GEM_OK) {
+ return NULL;
+ }
+ ieDword length = GetLength();
+ //assuming char is one byte
+ char *ret = (char *) malloc(length+1);
+ char *pos = ret;
+ ret[length]=0;
+ while(length) {
+ tot_str->Seek(offset+8, GEM_STREAM_START);
+ ieDword tmp = length>SEGMENT_SIZE?SEGMENT_SIZE:length;
+ tot_str->Read(pos, tmp);
+ tot_str->Seek(SEGMENT_SIZE-tmp, GEM_CURRENT_POS);
+ tot_str->ReadDword(&offset);
+ length-=tmp;
+ pos+=tmp;
+ }
+ return ret;
+}
+
+ieStrRef CTlkOverride::UpdateString(ieStrRef strref, const char *newvalue)
+{
+ ieDword memoffset = 0;
+ bool tookfree = false;
+ ieDword offset = LocateString(strref);
+
+ if (offset==0xffffffff) {
+ strref=GetNewStrRef();
+ offset=LocateString(strref);
+ assert(strref!=0xffffffff);
+ }
+
+ ieDword length = strlen(newvalue);
+ if(length>65535) length=65535;
+ length++;
+
+ //set the backpointer of the first string segment
+ ieDword backp = 0xffffffff;
+
+ do
+ {
+ //fill the backpointer
+ tot_str->Seek(offset+4, GEM_STREAM_START);
+ tot_str->WriteDword(&backp);
+ backp = offset;
+ ieDword tmp = length>SEGMENT_SIZE?SEGMENT_SIZE:length;
+ tot_str->Write(newvalue+memoffset, tmp);
+ length-=tmp;
+ memoffset+=tmp;
+ tot_str->Seek(backp+SEGMENT_SIZE+8, GEM_STREAM_START);
+ tot_str->ReadDword(&offset);
+
+ //end of string
+ if(!length) {
+ if(offset!=0xffffffff) {
+ tookfree = true;
+ }
+ tot_str->Seek(-4,GEM_CURRENT_POS);
+ backp = offset+4;
+ offset = 0xffffffff;
+ tot_str->WriteDword(&offset);
+ break;
+ }
+
+ if (offset==0xffffffff) {
+ //no more space, but we need some
+ offset = FreeOffset;
+ tookfree = true;
+ if (offset == 0xffffffff) {
+ //to the end of file
+ offset = tot_str->Size();
+ }
+ }
+ tot_str->Seek(-4,GEM_CURRENT_POS);
+ tot_str->WriteDword(&offset);
+ }
+ while(length);
+
+ //adjust the free list
+ if (tookfree) {
+ UpdateFreeOffset(backp);
+ }
+ return strref;
+}
+
+ieStrRef CTlkOverride::GetNewStrRef()
+{
+ EntryType entry;
+
+ memset(&entry,0,sizeof(entry));
+
+ if (!AuxCount) {
+ entry.strref = STRREF_START;
+ entry.offset = 8;
+ } else {
+ toh_str->Seek(sizeof(entry), GEM_STREAM_END );
+ toh_str->ReadDword(&entry.strref);
+ toh_str->Read(entry.dummy, 20);
+ entry.strref++;
+ entry.offset = tot_str->Size();
+ }
+ toh_str->Seek(0,GEM_STREAM_END);
+ toh_str->WriteDword(&entry.strref);
+ toh_str->Write(entry.dummy, 20);
+ toh_str->WriteDword(&entry.offset);
+ AuxCount++;
+ toh_str->Seek(8,GEM_STREAM_START);
+ toh_str->WriteDword(&AuxCount);
+ return entry.strref;
+}
+
+ieDword CTlkOverride::LocateString(ieStrRef strref)
+{
+ ieDword strref2;
+ ieDword offset;
+
+ if (!toh_str) return 0xffffffff;
+ toh_str->Seek(TOH_HEADER_SIZE,GEM_STREAM_START);
+ for(ieDword i=0;i<AuxCount;i++) {
+ toh_str->ReadDword(&strref2);
+ toh_str->Seek(20,GEM_CURRENT_POS);
+ toh_str->ReadDword(&offset);
+ if (strref2==strref) {
+ return offset;
+ }
+ }
+ return 0xffffffff;
+}
+
+//this function handles all of the .tlk override mechanism with caching
+//strings it once found
+//it is possible to turn off caching
+char* CTlkOverride::ResolveAuxString(ieStrRef strref, int &Length)
+{
+ char *string;
+
+ if (!this) {
+ Length = 0;
+ string = ( char* ) malloc( 1 );
+ string[0] = 0;
+ return string;
+ }
+
+#ifdef CACHE_TLK_OVERRIDE
+ StringMapType::iterator tmp = stringmap.find(strref);
+ if (tmp!=stringmap.end()) {
+ return CS((*tmp).second);
+ }
+#endif
+
+ ieDword offset = LocateString(strref);
+ if (offset!=0xffffffff) {
+ string = LocateString2(offset);
+ Length = strlen(string);
+ } else {
+ Length = 0;
+ string = ( char* ) malloc( 1 );
+ string[0] = 0;
+ }
+#ifdef CACHE_TLK_OVERRIDE
+ stringmap[strref]=CS(string);
+#endif
+ return string;
+}
+
+DataStream* CTlkOverride::GetAuxHdr(bool create)
+{
+ char nPath[_MAX_PATH];
+ char Signature[TOH_HEADER_SIZE];
+
+ PathJoin( nPath, core->CachePath, "default.toh", NULL );
+ FileStream* fs = new FileStream();
+retry:
+ if (fs->Modify( nPath, true )) {
+ return fs;
+ }
+ if (create) {
+ fs->Create( "default", IE_TOH_CLASS_ID);
+ memset(Signature,0,sizeof(Signature));
+ memcpy(Signature,"TLK ",4);
+ fs->Write(Signature, sizeof(Signature));
+ create = false;
+ goto retry;
+ }
+ delete fs;
+ return NULL;
+}
+
+DataStream* CTlkOverride::GetAuxTlk(bool create)
+{
+ char nPath[_MAX_PATH];
+ PathJoin( nPath, core->CachePath, "default.tot", NULL );
+ FileStream* fs = new FileStream();
+retry:
+ if (fs->Modify( nPath, true )) {
+ return fs;
+ }
+ if (create) {
+ fs->Create( "default", IE_TOT_CLASS_ID);
+ create = false;
+ goto retry;
+ }
+ delete fs;
+ return NULL;
+}
+
diff --git a/gemrb/plugins/TLKImporter/TlkOverride.h b/gemrb/plugins/TLKImporter/TlkOverride.h
new file mode 100644
index 0000000..a8b3a25
--- /dev/null
+++ b/gemrb/plugins/TLKImporter/TlkOverride.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.
+ *
+ *
+ */
+// TlkOverride.h: interface for the CTlkOverride class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#ifndef TLKOVERRIDE_H
+#define TLKOVERRIDE_H
+
+#include "globals.h"
+
+#include "Interface.h"
+#include "System/FileStream.h"
+
+#ifdef CACHE_TLK_OVERRIDE
+#include <map>
+
+typedef std::map<ieStrRef, char *> StringMapType;
+#endif
+
+#define STRREF_START 300000
+#define SEGMENT_SIZE 512
+#define TOH_HEADER_SIZE 20
+
+//the original games used these strings for custom biography (another quirk of the IE)
+#define BIO_START 62016 //first BIO string
+#define BIO_END (BIO_START+5) //last BIO string
+
+typedef struct
+{
+ ieDword strref;
+ ieByte dummy[20];
+ ieDword offset;
+} EntryType;
+
+class CTlkOverride
+{
+private:
+#ifdef CACHE_TLK_OVERRIDE
+ StringMapType stringmap;
+#endif
+ DataStream *tot_str;
+ DataStream *toh_str;
+ ieDword AuxCount;
+ ieDword FreeOffset;
+
+ void UpdateFreeOffset(ieDword NewFree);
+ void CloseResources();
+ DataStream *GetAuxHdr(bool create);
+ DataStream *GetAuxTlk(bool create);
+ ieStrRef GetNewStrRef();
+ ieDword LocateString(ieStrRef strref);
+ char* LocateString2(ieDword offset);
+ ieDword GetLength();
+public:
+ CTlkOverride();
+ virtual ~CTlkOverride();
+
+ bool Init();
+ char *ResolveAuxString(ieStrRef strref, int &Length);
+ ieStrRef UpdateString(ieStrRef strref, const char *newvalue);
+ char *CS(const char *src);
+};
+
+#endif //TLKOVERRIDE_H
+
diff --git a/gemrb/plugins/WAVReader/CMakeLists.txt b/gemrb/plugins/WAVReader/CMakeLists.txt
new file mode 100644
index 0000000..89df945
--- /dev/null
+++ b/gemrb/plugins/WAVReader/CMakeLists.txt
@@ -0,0 +1,3 @@
+FILE( GLOB WAVReader_files *.cpp )
+
+ADD_GEMRB_PLUGIN (WAVReader ${WAVReader_files})
diff --git a/gemrb/plugins/WAVReader/Makefile.am b/gemrb/plugins/WAVReader/Makefile.am
new file mode 100644
index 0000000..768fb1e
--- /dev/null
+++ b/gemrb/plugins/WAVReader/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = WAVReader.la
+WAVReader_la_LDFLAGS = -module -avoid-version -shared
+WAVReader_la_SOURCES = WAVReader.cpp WAVReader.h
diff --git a/gemrb/plugins/WAVReader/WAVReader.cpp b/gemrb/plugins/WAVReader/WAVReader.cpp
new file mode 100644
index 0000000..90c7010
--- /dev/null
+++ b/gemrb/plugins/WAVReader/WAVReader.cpp
@@ -0,0 +1,193 @@
+/* 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 "WAVReader.h"
+
+#include <utility>
+
+// WAVEFORMATEX structure (from MS SDK)
+struct cWAVEFORMATEX {
+ unsigned short wFormatTag; /* format type */
+ unsigned short nChannels; /* number of channels (i.e. mono, stereo...) */
+ unsigned int nSamplesPerSec; /* sample rate */
+ unsigned int nAvgBytesPerSec; /* for buffer estimation */
+ unsigned short nBlockAlign; /* block size of data */
+ unsigned short wBitsPerSample; /* number of bits per sample of mono data */
+ unsigned short cbSize; /* the count in bytes of the size of */
+ /* extra information (after cbSize) */
+};
+
+struct RIFF_CHUNK {
+ unsigned int fourcc;
+ unsigned int length;
+};
+
+const unsigned char RIFF_4cc[] = {
+ 'R', 'I', 'F', 'F'
+};
+const unsigned char WAVE_4cc[] = {
+ 'W', 'A', 'V', 'E'
+};
+const unsigned char fmt_4cc[] = {
+ 'f', 'm', 't', ' '
+};
+const unsigned char fact_4cc[] = {
+ 'f', 'a', 'c', 't'
+};
+const unsigned char data_4cc[] = {
+ 'd', 'a', 't', 'a'
+};
+
+bool RawPCMReader::Open(DataStream* stream)
+{
+ str = stream;
+
+ samples = str->Size();
+ str->Seek( 0, GEM_STREAM_START );
+ if (is16bit) {
+ samples >>= 1; // each sample has 16 bit
+ }
+ samples_left = samples;
+ return 1;
+}
+
+inline void fix_endian(ieDword &dest)
+{
+ std::swap(((unsigned char *) &dest)[0],((unsigned char *) &dest)[3]);
+ std::swap(((unsigned char *) &dest)[1],((unsigned char *) &dest)[2]);
+}
+
+inline void fix_endian(ieWord &dest)
+{
+ std::swap(((unsigned char *) &dest)[0],((unsigned char *) &dest)[1]);
+}
+
+int RawPCMReader::read_samples(short* buffer, int count)
+{
+ if (count > samples_left) {
+ count = samples_left;
+ }
+ int res = 0;
+ if (count) {
+ res = str->Read( buffer, count * ( ( is16bit ? 2 : 1 ) ) );
+ }
+ if (!is16bit) {
+ char* alt_buff = ( char* ) buffer;
+ int i = res;
+ while(i--) {
+ alt_buff[( i << 1 ) + 1] = ( char ) ( alt_buff[i] - 0x80 );
+ alt_buff[i << 1] = 0;
+ }
+ }
+ if(is16bit) {
+ res >>= 1;
+ if (str->IsEndianSwitch()) {
+ for (size_t i = 0; i < (size_t)count; i++) {
+ fix_endian(((ieWord *)buffer)[i]);
+ }
+ }
+ }
+ samples_left -= res;
+ return res;
+}
+
+bool WavPCMReader::Open(DataStream* stream)
+{
+ if (!RawPCMReader::Open(stream))
+ return false;
+
+ char Signature[4];
+ stream->Read( Signature, 4 );
+ stream->Seek( 0, GEM_STREAM_START );
+ if(strnicmp(Signature, "RIFF", 4) != 0)
+ return false;
+
+ cWAVEFORMATEX fmt;
+ RIFF_CHUNK r_hdr, fmt_hdr, data_hdr;
+ unsigned int wave;
+ memset( &fmt, 0, sizeof( fmt ) );
+
+ //str->Read( &r_hdr, sizeof( r_hdr ) );
+ //don't swap this
+ str->Read(&r_hdr.fourcc, 4);
+ str->ReadDword(&r_hdr.length);
+ //don't swap this
+ str->Read( &wave, 4 );
+ if (memcmp(&r_hdr.fourcc, RIFF_4cc, 4) != 0 ||
+ memcmp(&wave, WAVE_4cc, 4) != 0) {
+ return false;
+ }
+
+ //str->Read( &fmt_hdr, sizeof( fmt_hdr ) );
+ //don't swap this
+ str->Read(&fmt_hdr.fourcc,4);
+ str->ReadDword(&fmt_hdr.length);
+ if (memcmp(&fmt_hdr.fourcc, fmt_4cc, 4) != 0 ||
+ fmt_hdr.length > sizeof( cWAVEFORMATEX )) {
+ return false;
+ }
+ memset(&fmt,0,sizeof(fmt) );
+ str->Read( &fmt, fmt_hdr.length );
+ //hmm, we should swap fmt bytes if we are on a mac
+ //but we don't know exactly how much of the structure we'll read
+ //so we have to swap the bytes after reading them
+ if (str->IsEndianSwitch()) {
+ fix_endian(fmt.wFormatTag);
+ fix_endian(fmt.nChannels);
+ fix_endian(fmt.nSamplesPerSec);
+ fix_endian(fmt.wBitsPerSample);
+ //we don't use these fields, so who cares
+ //fix_endian(fmt.nAvgBytesPerSec);
+ //fix_endian(fmt.nBlockAlign);
+ //fix_endian(fmt.cbSize);
+ }
+ if (fmt.wFormatTag != 1) {
+ return false;
+ }
+ is16bit = ( fmt.wBitsPerSample == 16 );
+
+ //str->Read( &data_hdr, sizeof( data_hdr ) );
+ //don't swap this
+ str->Read(&data_hdr.fourcc,4);
+ str->ReadDword(&data_hdr.length);
+
+ if (memcmp(&data_hdr.fourcc, fact_4cc, 4) == 0) {
+ str->Seek( data_hdr.length, GEM_CURRENT_POS );
+ //str->Read( &data_hdr, sizeof( data_hdr ) );
+ str->ReadDword(&data_hdr.fourcc);
+ str->ReadDword(&data_hdr.length);
+ }
+ if (memcmp(&data_hdr.fourcc, data_4cc, 4) != 0) {
+ return false;
+ }
+
+ samples = data_hdr.length;
+ if (is16bit) {
+ samples >>= 1;
+ }
+ samples_left = samples;
+ channels = fmt.nChannels;
+ samplerate = fmt.nSamplesPerSec;
+ return true;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x11BB1288, "WAV File Importer")
+PLUGIN_IE_RESOURCE(WavPCMReader, ".wav", (ieWord)IE_WAV_CLASS_ID)
+END_PLUGIN()
diff --git a/gemrb/plugins/WAVReader/WAVReader.h b/gemrb/plugins/WAVReader/WAVReader.h
new file mode 100644
index 0000000..72b947b
--- /dev/null
+++ b/gemrb/plugins/WAVReader/WAVReader.h
@@ -0,0 +1,59 @@
+/* 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 WAVREADER_H
+#define WAVREADER_H
+
+#include "SoundMgr.h"
+
+#include "System/DataStream.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+// RAW file reader
+class RawPCMReader : public SoundMgr {
+protected:
+ // one sample consists of
+ // channels * (is16bit ? 2 : 1) bytes
+ int samples_left; // count of unread samples
+ int is16bit; // 1 - if 16 bit file, 0 - otherwise
+public:
+ RawPCMReader(int bits)
+ : is16bit( bits == 16 )
+ {
+ }
+
+ bool Open(DataStream* stream);
+ virtual int read_samples(short* buffer, int count);
+};
+
+// WAV files
+class WavPCMReader : public RawPCMReader {
+public:
+ WavPCMReader()
+ : RawPCMReader( 16 )
+ {
+ }
+ bool Open(DataStream* stream);
+};
+
+#endif
diff --git a/gemrb/plugins/WEDImporter/CMakeLists.txt b/gemrb/plugins/WEDImporter/CMakeLists.txt
new file mode 100644
index 0000000..349c7f7
--- /dev/null
+++ b/gemrb/plugins/WEDImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (WEDImporter WEDImporter.cpp)
diff --git a/gemrb/plugins/WEDImporter/Makefile.am b/gemrb/plugins/WEDImporter/Makefile.am
new file mode 100644
index 0000000..56eee65
--- /dev/null
+++ b/gemrb/plugins/WEDImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = WEDImporter.la
+WEDImporter_la_LDFLAGS = -module -avoid-version -shared
+WEDImporter_la_SOURCES = WEDImporter.cpp WEDImporter.h
diff --git a/gemrb/plugins/WEDImporter/WEDImporter.cpp b/gemrb/plugins/WEDImporter/WEDImporter.cpp
new file mode 100644
index 0000000..802937d
--- /dev/null
+++ b/gemrb/plugins/WEDImporter/WEDImporter.cpp
@@ -0,0 +1,360 @@
+/* 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.
+ *
+ *
+ */
+
+#if defined(__HAIKU__)
+#include <unistd.h>
+#endif
+
+#ifdef ANDROID
+#include "swab.h"
+#endif
+
+#include "WEDImporter.h"
+
+#include "win32def.h"
+
+#include "GameData.h"
+#include "Interface.h"
+#include "TileSetMgr.h"
+
+struct wed_polygon {
+ ieDword FirstVertex;
+ ieDword CountVertex;
+ ieWord Flags;
+ ieWord MinX, MaxX, MinY, MaxY;
+};
+
+//the net sizeof(wed_polygon) is 0x12 but not all compilers know that
+#define WED_POLYGON_SIZE 0x12
+
+WEDImporter::WEDImporter(void)
+{
+ str = NULL;
+ autoFree = false;
+}
+
+WEDImporter::~WEDImporter(void)
+{
+ if (str && autoFree) {
+ delete( str );
+ }
+}
+
+bool WEDImporter::Open(DataStream* stream, bool autoFree)
+{
+ if (stream == NULL) {
+ return false;
+ }
+ if (str && this->autoFree) {
+ delete( str );
+ }
+ str = stream;
+ this->autoFree = autoFree;
+ char Signature[8];
+ str->Read( Signature, 8 );
+ if (strncmp( Signature, "WED V1.3", 8 ) != 0) {
+ printf( "[WEDImporter]: This file is not a valid WED File\n" );
+ return false;
+ }
+ str->ReadDword( &OverlaysCount );
+ str->ReadDword( &DoorsCount );
+ str->ReadDword( &OverlaysOffset );
+ str->ReadDword( &SecHeaderOffset );
+ str->ReadDword( &DoorsOffset );
+ str->ReadDword( &DoorTilesOffset );
+ str->Seek( OverlaysOffset, GEM_STREAM_START );
+ for (unsigned int i = 0; i < OverlaysCount; i++) {
+ Overlay o;
+ str->ReadWord( &o.Width );
+ str->ReadWord( &o.Height );
+ str->ReadResRef( o.TilesetResRef );
+ str->ReadDword( &o.unknown );
+ str->ReadDword( &o.TilemapOffset );
+ str->ReadDword( &o.TILOffset );
+ overlays.push_back( o );
+ }
+ //Reading the Secondary Header
+ str->Seek( SecHeaderOffset, GEM_STREAM_START );
+ str->ReadDword( &WallPolygonsCount );
+ DoorPolygonsCount = 0;
+ str->ReadDword( &PolygonsOffset );
+ str->ReadDword( &VerticesOffset );
+ str->ReadDword( &WallGroupsOffset );
+ str->ReadDword( &PILTOffset );
+ return true;
+}
+
+int WEDImporter::AddOverlay(TileMap *tm, Overlay *overlays, bool rain)
+{
+ ieResRef res;
+ int usedoverlays = 0;
+
+ memcpy(res, overlays->TilesetResRef,sizeof(ieResRef));
+ if (rain) {
+ if (strlen(res) < 8)
+ strcat(res,"R");
+ //no rain tileset available, rolling back
+ if (!gamedata->Exists(res,IE_TIS_CLASS_ID)) {
+ memcpy(res, overlays->TilesetResRef,sizeof(ieResRef));
+ }
+ }
+ TileOverlay *over = new TileOverlay( overlays->Width, overlays->Height );
+ DataStream* tisfile = gamedata->GetResource(res, IE_TIS_CLASS_ID);
+ if (!tisfile) {
+ delete over;
+ return -1;
+ }
+ PluginHolder<TileSetMgr> tis(IE_TIS_CLASS_ID);
+ tis->Open( tisfile );
+ for (int y = 0; y < overlays->Height; y++) {
+ for (int x = 0; x < overlays->Width; x++) {
+ str->Seek( overlays->TilemapOffset +
+ ( y * overlays->Width + x) * 10,
+ GEM_STREAM_START );
+ ieWord startindex, count, secondary;
+ ieByte overlaymask;
+ str->ReadWord( &startindex );
+ str->ReadWord( &count );
+ //should be always 0xffff
+ str->ReadWord( &secondary );
+ //should be always 0
+ str->Read( &overlaymask, 1 );
+ str->Seek( overlays->TILOffset + ( startindex * 2 ),
+ GEM_STREAM_START );
+ ieWord* indices = ( ieWord* ) calloc( count, sizeof(ieWord) );
+ str->Read( indices, count * sizeof(ieWord) );
+ if( DataStream::IsEndianSwitch()) {
+ swab( (char*) indices, (char*) indices, count * sizeof(ieWord) );
+ }
+ Tile* tile;
+ if (secondary == 0xffff) {
+ tile = tis->GetTile( indices, count );
+ } else {
+ tile = tis->GetTile( indices, 1, &secondary );
+ }
+ tile->om = overlaymask;
+ usedoverlays |= overlaymask;
+ over->AddTile( tile );
+ free( indices );
+ }
+ }
+
+ if (rain) {
+ tm->AddRainOverlay( over );
+ } else {
+ tm->AddOverlay( over );
+ }
+ return usedoverlays;
+}
+
+//this will replace the tileset of an existing tilemap, or create a new one
+TileMap* WEDImporter::GetTileMap(TileMap *tm)
+{
+ int usedoverlays;
+ bool freenew = false;
+
+ if (!overlays.size()) {
+ return NULL;
+ }
+
+ if (!tm) {
+ tm = new TileMap();
+ freenew = true;
+ }
+
+ usedoverlays = AddOverlay(tm, &overlays.at(0), false);
+ if (usedoverlays == -1) {
+ if (freenew) {
+ delete tm;
+ }
+ return NULL;
+ }
+ // rain_overlays[0] is never used
+ // XXX: should fix AddOverlay not to load an overlay twice if there's no rain version!!
+ //AddOverlay(tm, &overlays.at(0), true);
+ tm->AddRainOverlay( NULL );
+
+ //reading additional overlays
+ int mask=2;
+ for(ieDword i=1;i<OverlaysCount;i++) {
+ //skipping unused overlays
+ if (!(mask&usedoverlays)) {
+ tm->AddOverlay( NULL );
+ tm->AddRainOverlay( NULL );
+ mask<<=1;
+ continue;
+ }
+ mask<<=1;
+
+ AddOverlay(tm, &overlays.at(i), false);
+ AddOverlay(tm, &overlays.at(i), true);
+ }
+ return tm;
+}
+
+void WEDImporter::GetDoorPolygonCount(ieWord count, ieDword offset)
+{
+ ieDword basecount = offset-PolygonsOffset;
+ if (basecount%WED_POLYGON_SIZE) {
+ basecount+=WED_POLYGON_SIZE;
+ printf("[WEDImporter]: Found broken door polygon header!\n");
+ }
+ ieDword polycount = basecount/WED_POLYGON_SIZE+count-WallPolygonsCount;
+ if (polycount>DoorPolygonsCount) {
+ DoorPolygonsCount=polycount;
+ }
+}
+
+void WEDImporter::SetupClosedDoor(unsigned int &index, unsigned int &count)
+{
+ index = (ClosedPolyOffset-PolygonsOffset)/WED_POLYGON_SIZE;
+ count = ClosedPolyCount;
+}
+
+void WEDImporter::SetupOpenDoor(unsigned int &index, unsigned int &count)
+{
+ index = (OpenPolyOffset-PolygonsOffset)/WED_POLYGON_SIZE;
+ count = OpenPolyCount;
+}
+
+ieWord* WEDImporter::GetDoorIndices(char* ResRef, int* count, bool& BaseClosed)
+{
+ ieWord DoorClosed, DoorTileStart, DoorTileCount, * DoorTiles;
+ ieResRef Name;
+ unsigned int i;
+
+ for (i = 0; i < DoorsCount; i++) {
+ str->Seek( DoorsOffset + ( i * 0x1A ), GEM_STREAM_START );
+ str->ReadResRef( Name );
+ if (strnicmp( Name, ResRef, 8 ) == 0)
+ break;
+ }
+ //The door has no representation in the WED file
+ if (i == DoorsCount) {
+ *count = 0;
+ printf( "[WEDImporter]: Found door without WED entry!\n" );
+ return NULL;
+ }
+
+ str->ReadWord( &DoorClosed );
+ str->ReadWord( &DoorTileStart );
+ str->ReadWord( &DoorTileCount );
+ str->ReadWord( &OpenPolyCount );
+ str->ReadWord( &ClosedPolyCount );
+ str->ReadDword( &OpenPolyOffset );
+ str->ReadDword( &ClosedPolyOffset );
+
+ GetDoorPolygonCount(OpenPolyCount, OpenPolyOffset);
+ GetDoorPolygonCount(ClosedPolyCount, ClosedPolyOffset);
+
+ //Reading Door Tile Cells
+ str->Seek( DoorTilesOffset + ( DoorTileStart * 2 ), GEM_STREAM_START );
+ DoorTiles = ( ieWord* ) calloc( DoorTileCount, sizeof( ieWord) );
+ str->Read( DoorTiles, DoorTileCount * sizeof( ieWord ) );
+ if( DataStream::IsEndianSwitch()) {
+ swab( (char*) DoorTiles, (char*) DoorTiles, DoorTileCount * sizeof( ieWord) );
+ }
+ *count = DoorTileCount;
+ if (DoorClosed) {
+ BaseClosed = true;
+ } else {
+ BaseClosed = false;
+ }
+ return DoorTiles;
+}
+
+Wall_Polygon **WEDImporter::GetWallGroups()
+{
+ ieDword polygonCount = WallPolygonsCount+DoorPolygonsCount;
+
+ Wall_Polygon **Polygons = (Wall_Polygon **) calloc( polygonCount, sizeof(Wall_Polygon *) );
+
+ wed_polygon *PolygonHeaders = new wed_polygon[polygonCount];
+
+ str->Seek (PolygonsOffset, GEM_STREAM_START);
+
+ ieDword i; //msvc6.0 isn't ISO compatible, so this variable cannot be declared in 'for'
+ for (i=0;i<polygonCount;i++) {
+ str->ReadDword ( &PolygonHeaders[i].FirstVertex);
+ str->ReadDword ( &PolygonHeaders[i].CountVertex);
+ str->ReadWord ( &PolygonHeaders[i].Flags);
+ str->ReadWord ( &PolygonHeaders[i].MinX);
+ str->ReadWord ( &PolygonHeaders[i].MaxX);
+ str->ReadWord ( &PolygonHeaders[i].MinY);
+ str->ReadWord ( &PolygonHeaders[i].MaxY);
+ }
+
+ for (i=0;i<polygonCount;i++) {
+ str->Seek (PolygonHeaders[i].FirstVertex*4+VerticesOffset, GEM_STREAM_START);
+ //compose polygon
+ ieDword count = PolygonHeaders[i].CountVertex;
+ if (count<3) {
+ //danger, danger
+ continue;
+ }
+ ieDword flags = PolygonHeaders[i].Flags&~(WF_BASELINE|WF_HOVER);
+ Point base0, base1;
+ if (PolygonHeaders[i].Flags&WF_HOVER) {
+ count-=2;
+ ieWord x,y;
+ str->ReadWord (&x);
+ str->ReadWord (&y);
+ base0 = Point(x,y);
+ str->ReadWord (&x);
+ str->ReadWord (&y);
+ base1 = Point(x,y);
+ flags |= WF_BASELINE;
+ }
+ Point *points = new Point[count];
+ str->Read (points, count * sizeof (Point) );
+ if( DataStream::IsEndianSwitch()) {
+ swab( (char*) points, (char*) points, count * sizeof (Point) );
+ }
+
+ if (!(flags&WF_BASELINE) ) {
+ if (PolygonHeaders[i].Flags&WF_BASELINE) {
+ base0 = points[0];
+ base1 = points[1];
+ flags |= WF_BASELINE;
+ }
+ }
+ Region rgn;
+ rgn.x = PolygonHeaders[i].MinX;
+ rgn.y = PolygonHeaders[i].MinY;
+ rgn.w = PolygonHeaders[i].MaxX - PolygonHeaders[i].MinX;
+ rgn.h = PolygonHeaders[i].MaxY - PolygonHeaders[i].MinY;
+ Polygons[i] = new Wall_Polygon(points, count, &rgn);
+ delete [] points;
+ if (flags&WF_BASELINE) {
+ Polygons[i]->SetBaseline(base0, base1);
+ }
+ Polygons[i]->SetPolygonFlag(flags);
+ }
+ delete [] PolygonHeaders;
+
+ return Polygons;
+}
+
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x7486BE7, "WED File Importer")
+PLUGIN_CLASS(IE_WED_CLASS_ID, WEDImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/WEDImporter/WEDImporter.h b/gemrb/plugins/WEDImporter/WEDImporter.h
new file mode 100644
index 0000000..f725b78
--- /dev/null
+++ b/gemrb/plugins/WEDImporter/WEDImporter.h
@@ -0,0 +1,66 @@
+/* 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 WEDIMPORTER_H
+#define WEDIMPORTER_H
+
+#include "TileMapMgr.h"
+
+struct Overlay {
+ ieWord Width;
+ ieWord Height;
+ ieResRef TilesetResRef;
+ ieDword unknown;
+ ieDword TilemapOffset;
+ ieDword TILOffset;
+};
+
+class WEDImporter : public TileMapMgr {
+private:
+ std::vector< Overlay> overlays;
+ DataStream* str;
+ bool autoFree;
+ ieDword OverlaysCount, DoorsCount, OverlaysOffset, SecHeaderOffset,
+ DoorsOffset, DoorTilesOffset;
+ ieDword WallPolygonsCount, PolygonsOffset, VerticesOffset,
+ WallGroupsOffset, PILTOffset;
+ ieDword DoorPolygonsCount;
+ //these will change as doors are being read, so get them in time!
+ ieWord OpenPolyCount, ClosedPolyCount;
+ ieDword OpenPolyOffset, ClosedPolyOffset;
+
+private:
+ void GetDoorPolygonCount(ieWord count, ieDword offset);
+ int AddOverlay(TileMap *tm, Overlay *overlays, bool rain);
+public:
+ WEDImporter(void);
+ ~WEDImporter(void);
+ bool Open(DataStream* stream, bool autoFree = true);
+ //if tilemap already exists, don't create it
+ TileMap* GetTileMap(TileMap *tm);
+ ieWord* GetDoorIndices(char* ResRef, int* count, bool& BaseClosed);
+ Wall_Polygon **GetWallGroups();
+ ieDword GetWallPolygonsCount() { return WallPolygonsCount; }
+ ieDword GetPolygonsCount() { return WallPolygonsCount+DoorPolygonsCount; }
+ void SetupOpenDoor(unsigned int &index, unsigned int &count);
+ void SetupClosedDoor(unsigned int &index, unsigned int &count);
+};
+
+#endif
diff --git a/gemrb/plugins/WMPImporter/CMakeLists.txt b/gemrb/plugins/WMPImporter/CMakeLists.txt
new file mode 100644
index 0000000..7ad075b
--- /dev/null
+++ b/gemrb/plugins/WMPImporter/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_GEMRB_PLUGIN (WMPImporter WMPImporter.cpp)
diff --git a/gemrb/plugins/WMPImporter/Makefile.am b/gemrb/plugins/WMPImporter/Makefile.am
new file mode 100644
index 0000000..20ece02
--- /dev/null
+++ b/gemrb/plugins/WMPImporter/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = WMPImporter.la
+WMPImporter_la_LDFLAGS = -module -avoid-version -shared
+WMPImporter_la_SOURCES = WMPImporter.cpp WMPImporter.h
diff --git a/gemrb/plugins/WMPImporter/WMPImporter.cpp b/gemrb/plugins/WMPImporter/WMPImporter.cpp
new file mode 100644
index 0000000..929c76b
--- /dev/null
+++ b/gemrb/plugins/WMPImporter/WMPImporter.cpp
@@ -0,0 +1,441 @@
+/* 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 "WMPImporter.h"
+
+#include "win32def.h"
+
+#include "GameData.h"
+#include "ImageMgr.h"
+#include "Interface.h"
+
+WMPImporter::WMPImporter(void)
+{
+ str1 = NULL;
+ str2 = NULL;
+ autoFree = false;
+}
+
+WMPImporter::~WMPImporter(void)
+{
+ if (str1 && autoFree) {
+ delete( str1 );
+ }
+ if (str2 && autoFree) {
+ delete( str2 );
+ }
+}
+
+bool WMPImporter::Open(DataStream* stream1, DataStream* stream2, bool autoFree)
+{
+ if ((stream1 == NULL) && (stream2 == NULL) ) {
+ return false;
+ }
+ if (str1 && this->autoFree) {
+ delete( str1 );
+ }
+ if (str2 && this->autoFree) {
+ delete( str2 );
+ }
+ str1 = stream1;
+ str2 = stream2;
+
+ this->autoFree = autoFree;
+ char Signature[8];
+
+ if (str1) {
+ str1->Read( Signature, 8 );
+ if (strncmp( Signature, "WMAPV1.0", 8 ) != 0) {
+ printMessage( "WMPImporter","This file is not a valid WMP File\n", LIGHT_RED);
+ printf( "-->%s<--\n", stream1->filename);
+ return false;
+ }
+ str1->ReadDword( &WorldMapsCount1 );
+ str1->ReadDword( &WorldMapsOffset1 );
+ } else {
+ WorldMapsCount1 = 0;
+ WorldMapsOffset1 = 0;
+ }
+
+ if (str2) {
+ str2->Read( Signature, 8 );
+ if (strncmp( Signature, "WMAPV1.0", 8 ) != 0) {
+ printMessage( "WMPImporter","This file is not a valid WMP File\n", LIGHT_RED);
+ printf( "-->%s<--\n", stream2->filename);
+ return false;
+ }
+ str2->ReadDword( &WorldMapsCount2 );
+ str2->ReadDword( &WorldMapsOffset2 );
+ } else {
+ WorldMapsCount2 = 0;
+ WorldMapsOffset2 = 0;
+ }
+
+ WorldMapsCount = WorldMapsCount1 + WorldMapsCount2;
+ return true;
+}
+
+WorldMapArray* WMPImporter::GetWorldMapArray()
+{
+ unsigned int i;
+
+ assert(WorldMapsCount == WorldMapsCount1 + WorldMapsCount2);
+
+ WorldMapArray* ma = core->NewWorldMapArray(WorldMapsCount);
+ for (i=0;i<WorldMapsCount1; i++) {
+ WorldMap *m = ma->NewWorldMap( i );
+ GetWorldMap( str1, m, i );
+ }
+
+ for (i=0;i<WorldMapsCount2; i++) {
+ WorldMap *m = ma->NewWorldMap( i + WorldMapsCount1);
+ GetWorldMap( str2, m, i );
+ }
+ return ma;
+}
+
+void WMPImporter::GetWorldMap(DataStream *str, WorldMap *m, unsigned int index)
+{
+ unsigned int i;
+ unsigned int WorldMapsOffset;
+
+ if (index && str==str2) {
+ WorldMapsOffset=WorldMapsOffset2;
+ }
+ else {
+ WorldMapsOffset=WorldMapsOffset1;
+ }
+
+ str->Seek( WorldMapsOffset + index * 184, GEM_STREAM_START );
+ str->ReadResRef( m->MapResRef );
+ str->ReadDword( &m->Width );
+ str->ReadDword( &m->Height );
+ str->ReadDword( &m->MapNumber );
+ str->ReadDword( &m->AreaName );
+ str->ReadDword( &m->unknown1 );
+ str->ReadDword( &m->unknown2 );
+ str->ReadDword( &m->AreaEntriesCount );
+ str->ReadDword( &m->AreaEntriesOffset );
+ str->ReadDword( &m->AreaLinksOffset );
+ str->ReadDword( &m->AreaLinksCount );
+ str->ReadResRef( m->MapIconResRef );
+
+ // Load map bitmap
+ ResourceHolder<ImageMgr> mos(m->MapResRef);
+ if (!mos) {
+ printMessage( "WMPImporter","Worldmap image not found.\n", LIGHT_RED );
+ } else {
+ m->SetMapMOS(mos->GetSprite2D());
+ }
+
+ // Load location icon bam
+ if (!core->IsAvailable( IE_BAM_CLASS_ID )) {
+ printMessage( "WMPImporter","No BAM Importer Available.\n", LIGHT_RED );
+ } else {
+ AnimationFactory* af = ( AnimationFactory* )
+ gamedata->GetFactoryResource( m->MapIconResRef, IE_BAM_CLASS_ID, IE_NORMAL );
+ if (af)
+ m->SetMapIcons( af );
+ }
+
+ str->Seek( m->AreaEntriesOffset, GEM_STREAM_START );
+
+
+ WMPAreaLink al;
+ for (i = 0; i < m->AreaEntriesCount; i++) {
+ //this weird stuff is requires so we don't create
+ //data here, all data is created in the core
+ m->SetAreaEntry(i,GetAreaEntry(str, m->GetNewAreaEntry()));
+ }
+
+ str->Seek( m->AreaLinksOffset, GEM_STREAM_START );
+ for (i = 0; i < m->AreaLinksCount; i++) {
+ m->SetAreaLink(i,GetAreaLink(str, &al));
+ }
+
+}
+
+WMPAreaEntry* WMPImporter::GetAreaEntry(DataStream *str, WMPAreaEntry* ae)
+{
+ str->ReadResRef( ae->AreaName );
+ str->ReadResRef( ae->AreaResRef );
+ str->Read( ae->AreaLongName, 32 );
+ ieDword tmpDword;
+ str->ReadDword( &tmpDword );
+ str->ReadDword( &ae->IconSeq );
+ //this should be set after iconseq is known
+ ae->SetAreaStatus( tmpDword, BM_SET );
+ str->ReadDword( &ae->X );
+ str->ReadDword( &ae->Y );
+ str->ReadDword( &ae->LocCaptionName );
+ str->ReadDword( &ae->LocTooltipName );
+ str->ReadResRef( ae->LoadScreenResRef );
+
+ for (unsigned int dir = 0; dir < 4; dir++) {
+ str->ReadDword( &ae->AreaLinksIndex[dir] );
+ str->ReadDword( &ae->AreaLinksCount[dir] );
+ }
+ str->Seek( 128, GEM_CURRENT_POS );
+
+ return ae;
+}
+
+WMPAreaLink* WMPImporter::GetAreaLink(DataStream *str, WMPAreaLink* al)
+{
+ str->ReadDword( &al->AreaIndex );
+ str->Read( al->DestEntryPoint, 32 );
+ str->ReadDword( &al->DistanceScale );
+ str->ReadDword( &al->DirectionFlags );
+ for (unsigned k = 0; k < 5; k++) {
+ str->ReadResRef( al->EncounterAreaResRef[k] );
+ }
+ str->ReadDword( &al->EncounterChance );
+ str->Seek( 128, GEM_CURRENT_POS );
+
+ return al;
+}
+
+int WMPImporter::GetStoredFileSize(WorldMapArray *wmap, unsigned int index)
+{
+ assert(!index || !wmap->IsSingle());
+
+ int headersize = 16;
+ int WorldMapsOffset;
+
+ WorldMapsCount = wmap->GetMapCount();
+ if (index>WorldMapsCount || index>1) return 0;
+
+ WorldMapsOffset = headersize;
+ if (index) {
+ WorldMapsCount2 = 0;
+ } else {
+ WorldMapsCount1 = 0;
+ }
+
+ for (unsigned int i=index;i<WorldMapsCount; i++) {
+ if (index) {
+ WorldMapsCount2++;
+ } else {
+ WorldMapsCount1++;
+ }
+
+ headersize += 184;
+ WorldMap *map = wmap->GetWorldMap(i);
+
+ //Update the links and entries counts now, in case the worldmap has changed
+ map->AreaEntriesCount = map->GetEntryCount();
+ headersize += map->AreaEntriesCount * 240;
+
+ map->AreaLinksCount = map->GetLinkCount();
+ headersize += map->AreaLinksCount * 216;
+
+ //put the first array into the first map
+ //the rest into the second map if not single
+ if (!wmap->IsSingle() && !index) {
+ break;
+ }
+ }
+
+ if (index) {
+ WorldMapsOffset2 = WorldMapsOffset;
+ }
+ else {
+ WorldMapsOffset1 = WorldMapsOffset;
+ }
+ return headersize;
+}
+
+int WMPImporter::PutWorldMap(DataStream *stream1, DataStream *stream2, WorldMapArray *wmap)
+{
+ if (! (stream1 || stream2) || !wmap) {
+ return -1;
+ }
+
+ if (stream1) {
+ stream1->Write( "WMAPV1.0", 8);
+ stream1->WriteDword( &WorldMapsCount1);
+ stream1->WriteDword( &WorldMapsOffset1);
+ }
+
+ if (stream2 && !wmap->IsSingle()) {
+ stream2->Write( "WMAPV1.0", 8);
+ stream2->WriteDword( &WorldMapsCount2);
+ stream2->WriteDword( &WorldMapsOffset2);
+ }
+ return PutMaps( stream1, stream2, wmap);
+}
+
+int WMPImporter::PutLinks(DataStream *stream, WorldMap *wmap)
+{
+ char filling[128];
+
+ memset (filling,0,sizeof(filling));
+ for(unsigned i=0;i<wmap->AreaLinksCount;i++) {
+ WMPAreaLink *al = wmap->GetLink(i);
+
+ stream->WriteDword( &al->AreaIndex );
+ stream->Write( al->DestEntryPoint, 32 );
+ stream->WriteDword( &al->DistanceScale );
+ stream->WriteDword( &al->DirectionFlags );
+ for (unsigned k = 0; k < 5; k++) {
+ stream->WriteResRef( al->EncounterAreaResRef[k] );
+ }
+ stream->WriteDword( &al->EncounterChance );
+ stream->Write(filling,128);
+ }
+ return 0;
+}
+
+int WMPImporter::PutAreas(DataStream *stream, WorldMap *wmap)
+{
+ char filling[128];
+ ieDword tmpDword;
+
+ memset (filling,0,sizeof(filling));
+ for(unsigned i=0;i<wmap->AreaEntriesCount;i++) {
+ WMPAreaEntry *ae = wmap->GetEntry(i);
+
+ stream->WriteResRef( ae->AreaName );
+ stream->WriteResRef( ae->AreaResRef );
+ stream->Write( ae->AreaLongName, 32 );
+ tmpDword = ae->GetAreaStatus();
+ stream->WriteDword( &tmpDword );
+ stream->WriteDword( &ae->IconSeq );
+ stream->WriteDword( &ae->X );
+ stream->WriteDword( &ae->Y );
+ stream->WriteDword( &ae->LocCaptionName );
+ stream->WriteDword( &ae->LocTooltipName );
+ stream->WriteResRef( ae->LoadScreenResRef );
+
+ for (unsigned int dir = 0; dir < 4; dir++) {
+ stream->WriteDword( &ae->AreaLinksIndex[dir] );
+ stream->WriteDword( &ae->AreaLinksCount[dir] );
+ }
+ stream->Write(filling,128);
+ }
+ return 0;
+}
+
+int WMPImporter::PutMaps(DataStream *stream1, DataStream *stream2, WorldMapArray *wmap)
+{
+ int ret = PutMap(stream1, wmap, 0);
+ if (ret) return ret;
+
+ if (stream2 && !wmap->IsSingle() ) {
+ ret = PutMap(stream2, wmap, 1);
+ }
+ return ret;
+}
+
+int WMPImporter::PutMap(DataStream *stream, WorldMapArray *wmap, unsigned int index)
+{
+ unsigned int i;
+ unsigned int WorldMapsOffset;
+ unsigned int count;
+ int ret;
+ char filling[128];
+
+ assert(!index || !wmap->IsSingle());
+
+ if (index) {
+ WorldMapsOffset = WorldMapsOffset2;
+ count = WorldMapsCount2;
+ } else {
+ WorldMapsOffset = WorldMapsOffset1;
+ count = WorldMapsCount1;
+ }
+
+ memset (filling,0,sizeof(filling));
+ ieDword AreaEntriesOffset = WorldMapsOffset + count * 184;
+ ieDword AreaLinksOffset = AreaEntriesOffset;
+ for (i=index;i<WorldMapsCount; i++) {
+ WorldMap *map = wmap->GetWorldMap(i);
+
+ AreaLinksOffset += map->GetEntryCount() * 240;
+ if (!wmap->IsSingle() && !index) {
+ break;
+ }
+ }
+
+ //map headers
+ for (i=index;i<WorldMapsCount; i++) {
+ ieDword AreaEntriesCount, AreaLinksCount;
+
+ WorldMap *map = wmap->GetWorldMap(i);
+ AreaLinksCount = map->GetLinkCount();
+ AreaEntriesCount = map->GetEntryCount();
+
+ stream->WriteResRef( map->MapResRef );
+ stream->WriteDword( &map->Width );
+ stream->WriteDword( &map->Height );
+ stream->WriteDword( &map->MapNumber );
+ stream->WriteDword( &map->AreaName );
+ stream->WriteDword( &map->unknown1 );
+ stream->WriteDword( &map->unknown2 );
+ //???
+
+ stream->WriteDword( &AreaEntriesCount );
+ stream->WriteDword( &AreaEntriesOffset );
+ stream->WriteDword( &AreaLinksOffset );
+ stream->WriteDword( &AreaLinksCount );
+ stream->WriteResRef( map->MapIconResRef );
+ AreaEntriesOffset += AreaEntriesCount * 240;
+ AreaLinksOffset += AreaLinksCount * 216;
+
+ stream->Write( filling, 128);
+
+ if (!wmap->IsSingle() && !index) {
+ break;
+ }
+ }
+
+ //area entries
+ for (i=index;i<WorldMapsCount; i++) {
+ WorldMap *map = wmap->GetWorldMap(i);
+
+ ret = PutAreas( stream, map);
+ if (ret) {
+ return ret;
+ }
+ if (!wmap->IsSingle() && !index) {
+ break;
+ }
+ }
+
+ //links
+ for (i=index;i<WorldMapsCount; i++) {
+ WorldMap *map = wmap->GetWorldMap(i);
+
+ ret = PutLinks( stream, map);
+ if (ret) {
+ return ret;
+ }
+ if (!wmap->IsSingle() && !index) {
+ break;
+ }
+ }
+ return 0;
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x77918C6, "WMP File Importer")
+PLUGIN_CLASS(IE_WMP_CLASS_ID, WMPImporter)
+END_PLUGIN()
diff --git a/gemrb/plugins/WMPImporter/WMPImporter.h b/gemrb/plugins/WMPImporter/WMPImporter.h
new file mode 100644
index 0000000..9c8d515
--- /dev/null
+++ b/gemrb/plugins/WMPImporter/WMPImporter.h
@@ -0,0 +1,61 @@
+/* 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 WMPIMPORTER_H
+#define WMPIMPORTER_H
+
+#include "WorldMapMgr.h"
+
+#include "ie_types.h"
+
+#include "WorldMap.h"
+
+
+class WMPImporter : public WorldMapMgr {
+private:
+ DataStream* str1;
+ DataStream* str2;
+ bool autoFree;
+
+ ieDword WorldMapsCount;
+ ieDword WorldMapsCount1, WorldMapsCount2;
+ ieDword WorldMapsOffset1, WorldMapsOffset2;
+
+public:
+ WMPImporter(void);
+ ~WMPImporter(void);
+ bool Open(DataStream* stream1, DataStream* stream2, bool autoFree = true);
+ WorldMapArray *GetWorldMapArray();
+
+ int GetStoredFileSize(WorldMapArray *wmap, unsigned int index);
+ int PutWorldMap(DataStream* stream1, DataStream* stream2, WorldMapArray *wmap);
+private:
+ void GetWorldMap(DataStream *str, WorldMap *m, unsigned int index);
+
+ WMPAreaEntry* GetAreaEntry(DataStream *str, WMPAreaEntry* ae);
+ WMPAreaLink* GetAreaLink(DataStream *str, WMPAreaLink* al);
+ int PutMaps(DataStream *stream1, DataStream *stream2, WorldMapArray *wmap);
+ int PutMap(DataStream *stream, WorldMapArray *wmap, unsigned int index);
+ int PutLinks(DataStream *stream, WorldMap *wmap);
+ int PutAreas(DataStream *stream, WorldMap *wmap);
+};
+
+
+#endif
diff --git a/gemrb/plugins/ZLibManager/CMakeLists.txt b/gemrb/plugins/ZLibManager/CMakeLists.txt
new file mode 100644
index 0000000..59df6f6
--- /dev/null
+++ b/gemrb/plugins/ZLibManager/CMakeLists.txt
@@ -0,0 +1,3 @@
+INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
+ADD_GEMRB_PLUGIN (ZLibManager ZLibManager.cpp)
+TARGET_LINK_LIBRARIES( ZLibManager ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} )
diff --git a/gemrb/plugins/ZLibManager/Makefile.am b/gemrb/plugins/ZLibManager/Makefile.am
new file mode 100644
index 0000000..33c8242
--- /dev/null
+++ b/gemrb/plugins/ZLibManager/Makefile.am
@@ -0,0 +1,3 @@
+plugin_LTLIBRARIES = ZLibManager.la
+ZLibManager_la_LDFLAGS = -module -avoid-version -shared
+ZLibManager_la_SOURCES = ZLibManager.cpp ZLibManager.h
diff --git a/gemrb/plugins/ZLibManager/ZLibManager.cpp b/gemrb/plugins/ZLibManager/ZLibManager.cpp
new file mode 100644
index 0000000..858ae71
--- /dev/null
+++ b/gemrb/plugins/ZLibManager/ZLibManager.cpp
@@ -0,0 +1,159 @@
+/* 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 "ZLibManager.h"
+
+#include "globals.h"
+#include "win32def.h"
+
+#include <zlib.h>
+
+
+ZLibManager::ZLibManager(void)
+{
+}
+
+ZLibManager::~ZLibManager(void)
+{
+}
+
+
+#define INPUTSIZE 8192
+#define OUTPUTSIZE 8192
+
+// ZLib Decompression Routine
+int ZLibManager::Decompress(FILE* dest, DataStream* source, unsigned int size_guess) const
+{
+ unsigned char bufferin[INPUTSIZE], bufferout[OUTPUTSIZE];
+ z_stream stream;
+ int result;
+
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ result = inflateInit( &stream );
+ if (result != Z_OK) {
+ return GEM_ERROR;
+ }
+
+ stream.avail_in = 0;
+ while (1) {
+ stream.next_out = bufferout;
+ stream.avail_out = OUTPUTSIZE;
+ if (stream.avail_in == 0) {
+ stream.next_in = bufferin;
+ if (size_guess) {
+ stream.avail_in = size_guess;
+ }
+ if (!stream.avail_in || stream.avail_in > source->Remains()) {
+ //Read doesn't allow partial reads, but provides Remains
+ stream.avail_in = source->Remains();
+ }
+ if (stream.avail_in > INPUTSIZE) {
+ stream.avail_in=INPUTSIZE;
+ }
+ if (size_guess) {
+ if (size_guess < stream.avail_in)
+ size_guess = 0;
+ else
+ size_guess -= stream.avail_in;
+ }
+ if (source->Read( bufferin, stream.avail_in) != (int) stream.avail_in) {
+ return GEM_ERROR;
+ }
+ }
+ result = inflate( &stream, Z_NO_FLUSH );
+ if (( result != Z_OK ) && ( result != Z_STREAM_END )) {
+ return GEM_ERROR;
+ }
+ if (fwrite( bufferout, 1, OUTPUTSIZE - stream.avail_out, dest ) <
+ OUTPUTSIZE - stream.avail_out) {
+ return GEM_ERROR;
+ }
+ if (result == Z_STREAM_END) {
+ if (stream.avail_in > 0)
+ source->Seek( -stream.avail_in, GEM_CURRENT_POS );
+ result = inflateEnd( &stream );
+ if (result != Z_OK)
+ return GEM_ERROR;
+ return GEM_OK;
+ }
+ }
+}
+
+int ZLibManager::Compress(DataStream* dest, DataStream* source) const
+{
+ unsigned char bufferin[INPUTSIZE], bufferout[OUTPUTSIZE];
+ z_stream stream;
+ int result;
+
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ //result = deflateInit( &stream, Z_DEFAULT_COMPRESSION );
+ result = deflateInit( &stream, Z_BEST_COMPRESSION );
+ if (result != Z_OK) {
+ return GEM_ERROR;
+ }
+
+ stream.avail_in = 0;
+ while (1) {
+ stream.next_out = bufferout;
+ stream.avail_out = OUTPUTSIZE;
+ if (stream.avail_in == 0) {
+ stream.next_in = bufferin;
+ //Read doesn't allow partial reads, but provides Remains
+ stream.avail_in = source->Remains();
+ if (stream.avail_in > INPUTSIZE) {
+ stream.avail_in=INPUTSIZE;
+ }
+ if (source->Read( bufferin, stream.avail_in) != (int) stream.avail_in) {
+ return GEM_ERROR;
+ }
+ }
+ if (stream.avail_in == 0) {
+ result = deflate( &stream, Z_FINISH);
+ } else {
+ result = deflate( &stream, Z_NO_FLUSH );
+ }
+ if (( result != Z_OK ) && ( result != Z_STREAM_END )) {
+ return GEM_ERROR;
+ }
+ if (dest->Write( bufferout, OUTPUTSIZE - stream.avail_out) == GEM_ERROR) {
+ return GEM_ERROR;
+ }
+ if (result == Z_STREAM_END) {
+ if (stream.avail_in > 0)
+ source->Seek( -stream.avail_in, GEM_CURRENT_POS );
+ result = deflateEnd( &stream );
+ if (result != Z_OK)
+ return GEM_ERROR;
+ return GEM_OK;
+ }
+ }
+}
+
+#include "plugindef.h"
+
+GEMRB_PLUGIN(0x2477C688, "ZLib Compression Manager")
+PLUGIN_CLASS(PLUGIN_COMPRESSION_ZLIB, ZLibManager)
+END_PLUGIN()
diff --git a/gemrb/plugins/ZLibManager/ZLibManager.h b/gemrb/plugins/ZLibManager/ZLibManager.h
new file mode 100644
index 0000000..784494c
--- /dev/null
+++ b/gemrb/plugins/ZLibManager/ZLibManager.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 ZLIBMANAGER_H
+#define ZLIBMANAGER_H
+
+#include "Compressor.h"
+
+class ZLibManager : public Compressor {
+public:
+ ZLibManager(void);
+ ~ZLibManager(void);
+ // ZLib Decompression Routine
+ int Decompress(FILE* dest, DataStream* source, unsigned int size_guess) const;
+ // ZLib Compression
+ int Compress(DataStream* dest, DataStream* source) const;
+};
+
+#endif
diff --git a/gemrb/templates/cpp_template b/gemrb/templates/cpp_template
new file mode 100644
index 0000000..6afef5d
--- /dev/null
+++ b/gemrb/templates/cpp_template
@@ -0,0 +1,16 @@
+/***************************************************************************
+ |FILENAME| - description
+ -------------------
+ begin : |DATE|
+ copyright : (C) |YEAR| by |AUTHOR|
+ email : |EMAIL|
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
diff --git a/gemrb/templates/header_template b/gemrb/templates/header_template
new file mode 100644
index 0000000..6afef5d
--- /dev/null
+++ b/gemrb/templates/header_template
@@ -0,0 +1,16 @@
+/***************************************************************************
+ |FILENAME| - description
+ -------------------
+ begin : |DATE|
+ copyright : (C) |YEAR| by |AUTHOR|
+ email : |EMAIL|
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
diff --git a/gemrb/tests/CMakeLists.txt b/gemrb/tests/CMakeLists.txt
new file mode 100644
index 0000000..14f9367
--- /dev/null
+++ b/gemrb/tests/CMakeLists.txt
@@ -0,0 +1 @@
+INSTALL( DIRECTORY minimal DESTINATION ${DATA_DIR} )
diff --git a/gemrb/tests/minimal/README b/gemrb/tests/minimal/README
new file mode 100644
index 0000000..cd583ec
--- /dev/null
+++ b/gemrb/tests/minimal/README
@@ -0,0 +1,17 @@
+This is a simple test case to see if gemrb works. It contains the minimal
+dummy dataset required to run.
+
+If you installed gemrb system-wide use fhs.cfg:
+gemrb -c /usr/share/gemrb/minimal/fhs.cfg
+
+Unless gemrb is installed elsewhere, it should run and print a bunch of
+text and then automatically quit. You may briefly see a small window
+appear. The last thing you should see in the console of a successful run is:
+[SDLVideo]: Creating Extra Buffer...[OK]
+[SDLVideo]: CreateDisplay...[OK]
+[GUIScript]: Loading Script Start...[OK]
+
+
+If it doesn't get that far and dies complaining about missing files, you
+need to adjust the config to point to the right paths.
+
diff --git a/gemrb/tests/minimal/chitin.key b/gemrb/tests/minimal/chitin.key
new file mode 100644
index 0000000..c3b170e
Binary files /dev/null and b/gemrb/tests/minimal/chitin.key differ
diff --git a/gemrb/tests/minimal/data/action.ids b/gemrb/tests/minimal/data/action.ids
new file mode 100644
index 0000000..e69de29
diff --git a/gemrb/tests/minimal/data/carot.bam b/gemrb/tests/minimal/data/carot.bam
new file mode 100644
index 0000000..b3af33b
Binary files /dev/null and b/gemrb/tests/minimal/data/carot.bam differ
diff --git a/gemrb/tests/minimal/data/chrmodst.2da b/gemrb/tests/minimal/data/chrmodst.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/chrmodst.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/cursors.bam b/gemrb/tests/minimal/data/cursors.bam
new file mode 100644
index 0000000..0105ab3
Binary files /dev/null and b/gemrb/tests/minimal/data/cursors.bam differ
diff --git a/gemrb/tests/minimal/data/defsound.2da b/gemrb/tests/minimal/data/defsound.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/defsound.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/dexmod.2da b/gemrb/tests/minimal/data/dexmod.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/dexmod.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/effects.ids b/gemrb/tests/minimal/data/effects.ids
new file mode 100644
index 0000000..e69de29
diff --git a/gemrb/tests/minimal/data/fogowar.bam b/gemrb/tests/minimal/data/fogowar.bam
new file mode 100644
index 0000000..b3af33b
Binary files /dev/null and b/gemrb/tests/minimal/data/fogowar.bam differ
diff --git a/gemrb/tests/minimal/data/fonts.2da b/gemrb/tests/minimal/data/fonts.2da
new file mode 100644
index 0000000..5f0eccb
--- /dev/null
+++ b/gemrb/tests/minimal/data/fonts.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+
+ RESREF
+DEFAULT FOGOWAR
diff --git a/gemrb/tests/minimal/data/gametime.2da b/gemrb/tests/minimal/data/gametime.2da
new file mode 100644
index 0000000..54ac077
--- /dev/null
+++ b/gemrb/tests/minimal/data/gametime.2da
@@ -0,0 +1,5 @@
+2DA V1.0
+0
+ DURATION
+ROUND_SECONDS 6
+TURN_SECONDS 60
diff --git a/gemrb/tests/minimal/data/gemrb.ini b/gemrb/tests/minimal/data/gemrb.ini
new file mode 100644
index 0000000..faf2c3c
--- /dev/null
+++ b/gemrb/tests/minimal/data/gemrb.ini
@@ -0,0 +1,84 @@
+; 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.ini - game type-specific settings for GemRB engine
+
+[resources]
+
+; Bitmap resource for cursors
+CursorBAM = CAROT
+
+; Bitmap resource for scroll cursor arrow
+ScrollCursorBAM = CURSARW
+
+; Bitmap resource for dialog buttons font
+ButtonFont = STONESML
+
+; Font used to display subtitles (ToB)
+MovieFont = STONESML
+
+; Font used to display tooltips
+TooltipFont = TOOLFONT
+
+; Sprite displayed behind the tooltip text, if any
+TooltipBack = TOOLSCRL
+
+; Tooltip text color (RGBA)
+TooltipColor = #f0b08000
+
+; Space between tooltip text and sides of TooltipBack (x2)
+#TooltipMargin = 10
+TooltipMargin = 5
+
+; INI file from the original games
+INIConfig = baldur.ini
+
+; Palette bitmaps in various widths
+Palette16 = palette
+Palette32 = palette
+Palette256 = palette
+
+MaximumAbility = 25
+IgnoreButtonFrames = 1
+AllStringsTagged = 0
+HasDPLAYER = 1
+HasPickSound = 0
+HasDescIcon = 1
+HasEXPTABLE = 0
+SoundFolders = 0
+HasSongList = 1
+UpperButtonText = 1
+LowerLabelText = 0
+HasPartyINI = 0
+HasBeastsINI = 0
+ForceStereo = 0
+ReverseToHit = 1
+IWDMapDimensions = 0
+SmallFog = 0
+ReverseDoor = 0
+DialogueScrolls = 0
+RedrawTile = 0
+CheckAbilities = 1
+SpellBookIconHack = 1
+DeathOnZeroStat = 1
+BreakableWeapons = 0
+SelectiveMagicRes = 1
+HasHideInShadows = 1
+ProperBackstab = 1
+HasSpecificDamageBonus = 0
diff --git a/gemrb/tests/minimal/data/hpconbon.2da b/gemrb/tests/minimal/data/hpconbon.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/hpconbon.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/instant.ids b/gemrb/tests/minimal/data/instant.ids
new file mode 100644
index 0000000..e69de29
diff --git a/gemrb/tests/minimal/data/intmod.2da b/gemrb/tests/minimal/data/intmod.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/intmod.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/itemtype.2da b/gemrb/tests/minimal/data/itemtype.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/itemtype.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/lorebon.2da b/gemrb/tests/minimal/data/lorebon.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/lorebon.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/object.ids b/gemrb/tests/minimal/data/object.ids
new file mode 100644
index 0000000..e69de29
diff --git a/gemrb/tests/minimal/data/palette.png b/gemrb/tests/minimal/data/palette.png
new file mode 100644
index 0000000..e2880a9
Binary files /dev/null and b/gemrb/tests/minimal/data/palette.png differ
diff --git a/gemrb/tests/minimal/data/script.2da b/gemrb/tests/minimal/data/script.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/script.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/slottype.2da b/gemrb/tests/minimal/data/slottype.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/slottype.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/strings.2da b/gemrb/tests/minimal/data/strings.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/strings.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/strmod.2da b/gemrb/tests/minimal/data/strmod.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/strmod.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/strmodex.2da b/gemrb/tests/minimal/data/strmodex.2da
new file mode 100644
index 0000000..eb83234
--- /dev/null
+++ b/gemrb/tests/minimal/data/strmodex.2da
@@ -0,0 +1,4 @@
+2DA V1.0
+*
+ RESREF
+DEFAULT *
diff --git a/gemrb/tests/minimal/data/toolscrl.bam b/gemrb/tests/minimal/data/toolscrl.bam
new file mode 100644
index 0000000..8668e2a
Binary files /dev/null and b/gemrb/tests/minimal/data/toolscrl.bam differ
diff --git a/gemrb/tests/minimal/data/trigger.ids b/gemrb/tests/minimal/data/trigger.ids
new file mode 100644
index 0000000..e69de29
diff --git a/gemrb/tests/minimal/dialog.tlk b/gemrb/tests/minimal/dialog.tlk
new file mode 100644
index 0000000..518dc05
Binary files /dev/null and b/gemrb/tests/minimal/dialog.tlk differ
diff --git a/gemrb/tests/minimal/fhs.cfg b/gemrb/tests/minimal/fhs.cfg
new file mode 100644
index 0000000..6267df9
--- /dev/null
+++ b/gemrb/tests/minimal/fhs.cfg
@@ -0,0 +1,9 @@
+GameType=test
+CaseSensitive=1
+Width=1
+Height=1
+GamePath=/usr/share/gemrb/minimal
+GemRBPath=/usr/share/gemrb
+GameOverridePath=/data
+CachePath=/tmp/cache/
+PluginsPath=/usr/lib/gemrb/plugins
diff --git a/gemrb/tests/minimal/test.cfg b/gemrb/tests/minimal/test.cfg
new file mode 100644
index 0000000..d3f1b3d
--- /dev/null
+++ b/gemrb/tests/minimal/test.cfg
@@ -0,0 +1,9 @@
+GameType=test
+CaseSensitive=1
+Width=1
+Height=1
+GamePath=.
+GemRBPath=../..
+GameOverridePath=./data
+CachePath=./cache/
+PluginsPath=../../plugins
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/gemrb.git
More information about the Pkg-games-commits
mailing list